1
0
mirror of synced 2024-12-04 19:17:55 +01:00

[eve] Tests pass !

Add load_options to dump & load test strategy
This commit is contained in:
Stepland 2021-05-12 12:58:22 +02:00
parent f72e875898
commit 9c0850f98d
4 changed files with 35 additions and 22 deletions

View File

@ -43,11 +43,11 @@ def _load_eve(lines: List[str], file_path: Path, *, beat_snap: int = 240) -> son
] ]
time_map = TimeMap.from_seconds(bpms) time_map = TimeMap.from_seconds(bpms)
tap_notes: List[AnyNote] = [ tap_notes: List[AnyNote] = [
make_tap_note(e.time, e.value, time_map) make_tap_note(e.time, e.value, time_map, beat_snap)
for e in events_by_command[Command.PLAY] for e in events_by_command[Command.PLAY]
] ]
long_notes: List[AnyNote] = [ long_notes: List[AnyNote] = [
make_long_notes(e.time, e.value, time_map) make_long_note(e.time, e.value, time_map, beat_snap)
for e in events_by_command[Command.LONG] for e in events_by_command[Command.LONG]
] ]
all_notes = sorted(tap_notes + long_notes, key=lambda n: (n.time, n.position)) all_notes = sorted(tap_notes + long_notes, key=lambda n: (n.time, n.position))
@ -102,22 +102,26 @@ def parse_event(line: str) -> Event:
return Event(tick, command, value) return Event(tick, command, value)
def make_tap_note(ticks: int, value: int, time_map: TimeMap) -> song.TapNote: def make_tap_note(
ticks: int, value: int, time_map: TimeMap, beat_snap: int
) -> song.TapNote:
seconds = ticks_to_seconds(ticks) seconds = ticks_to_seconds(ticks)
raw_beats = time_map.beats_at(seconds) raw_beats = time_map.beats_at(seconds)
beats = round_beats(raw_beats) beats = round_beats(raw_beats, beat_snap)
position = song.NotePosition.from_index(value) position = song.NotePosition.from_index(value)
return song.TapNote(time=beats, position=position) return song.TapNote(time=beats, position=position)
def make_long_notes(ticks: int, value: int, time_map: TimeMap) -> song.LongNote: def make_long_note(
ticks: int, value: int, time_map: TimeMap, beat_snap: int
) -> song.LongNote:
seconds = ticks_to_seconds(ticks) seconds = ticks_to_seconds(ticks)
raw_beats = time_map.beats_at(seconds) raw_beats = time_map.beats_at(seconds)
beats = round_beats(raw_beats) beats = round_beats(raw_beats, beat_snap)
eve_long = EveLong.from_value(value) eve_long = EveLong.from_value(value)
seconds_duration = ticks_to_seconds(eve_long.duration) seconds_duration = ticks_to_seconds(eve_long.duration)
raw_beats_duration = time_map.beats_at(seconds + seconds_duration) - raw_beats raw_beats_duration = time_map.beats_at(seconds + seconds_duration) - raw_beats
beats_duration = round_beats(raw_beats_duration) beats_duration = round_beats(raw_beats_duration, beat_snap)
position = song.NotePosition.from_index(eve_long.position) position = song.NotePosition.from_index(eve_long.position)
direction = VALUE_TO_DIRECTION[eve_long.direction] direction = VALUE_TO_DIRECTION[eve_long.direction]
step_vector = song.TAIL_DIRECTION_TO_OUTWARDS_VECTOR[direction] step_vector = song.TAIL_DIRECTION_TO_OUTWARDS_VECTOR[direction]

View File

@ -4,7 +4,7 @@ from decimal import Decimal
from pathlib import Path from pathlib import Path
from typing import Iterator from typing import Iterator
from hypothesis import Verbosity, given, settings from hypothesis import given
from hypothesis import strategies as st from hypothesis import strategies as st
from jubeatools import song from jubeatools import song
@ -20,19 +20,24 @@ simple_beat_strat = jbst.beat_time(
@st.composite @st.composite
def eve_compatible_song(draw: DrawFunc) -> song.Song: def eve_compatible_song(draw: DrawFunc) -> song.Song:
"""eve only keeps notes, timing info and difficulty""" """eve only keeps notes, timing info and difficulty,
the precision you can get out of it is also severly limited"""
diff = draw(st.sampled_from(list(song.Difficulty))) diff = draw(st.sampled_from(list(song.Difficulty)))
chart = draw( chart = draw(
jbst.chart( jbst.chart(
timing_strat=jbst.timing_info( timing_strat=jbst.timing_info(
with_bpm_changes=True, with_bpm_changes=True,
bpm_strat=st.decimals(min_value=1, max_value=500, places=2), bpm_strat=st.decimals(min_value=50, max_value=300, places=2),
beat_zero_offset_strat=st.decimals(min_value=0, max_value=20, places=2), beat_zero_offset_strat=st.decimals(min_value=0, max_value=20, places=2),
time_strat=simple_beat_strat, time_strat=jbst.beat_time(
min_section=1,
max_section=10,
denominator_strat=st.sampled_from([4, 8, 3]),
),
), ),
notes_strat=jbst.notes( notes_strat=jbst.notes(
note_strat=st.one_of( note_strat=st.one_of(
jbst.tap_note(time_start=simple_beat_strat), jbst.tap_note(time_strat=simple_beat_strat),
jbst.long_note( jbst.long_note(
time_strat=simple_beat_strat, time_strat=simple_beat_strat,
duration_strat=jbst.beat_time( duration_strat=jbst.beat_time(
@ -41,7 +46,8 @@ def eve_compatible_song(draw: DrawFunc) -> song.Song:
denominator_strat=st.sampled_from([4, 8, 3]), denominator_strat=st.sampled_from([4, 8, 3]),
), ),
), ),
) ),
beat_time_strat=simple_beat_strat,
), ),
level_strat=st.just(Decimal(0)), level_strat=st.just(Decimal(0)),
) )
@ -59,11 +65,11 @@ def open_temp_dir() -> Iterator[Path]:
@given(eve_compatible_song()) @given(eve_compatible_song())
@settings(verbosity=Verbosity.normal)
def test_that_full_chart_roundtrips(song: song.Song) -> None: def test_that_full_chart_roundtrips(song: song.Song) -> None:
dump_and_load_then_compare( dump_and_load_then_compare(
Format.EVE, Format.EVE,
song, song,
temp_path=open_temp_dir(), temp_path=open_temp_dir(),
bytes_decoder=lambda b: b.decode("ascii"), bytes_decoder=lambda b: b.decode("ascii"),
load_options={"beat_snap": 24},
) )

View File

@ -70,9 +70,9 @@ def note_position(draw: DrawFunc) -> NotePosition:
@st.composite @st.composite
def tap_note( def tap_note(
draw: DrawFunc, time_start: st.SearchStrategy[BeatsTime] = beat_time(max_section=10) draw: DrawFunc, time_strat: st.SearchStrategy[BeatsTime] = beat_time(max_section=10)
) -> TapNote: ) -> TapNote:
time = draw(time_start) time = draw(time_strat)
position = draw(note_position()) position = draw(note_position())
return TapNote(time, position) return TapNote(time, position)
@ -117,9 +117,6 @@ def notes(
tap_note(), long_note() tap_note(), long_note()
), ),
beat_time_strat: st.SearchStrategy[BeatsTime] = beat_time(max_section=3), beat_time_strat: st.SearchStrategy[BeatsTime] = beat_time(max_section=3),
beat_interval_strat: st.SearchStrategy[BeatsTime] = beat_time(
min_numerator=1, max_section=3
),
) -> Set[Union[TapNote, LongNote]]: ) -> Set[Union[TapNote, LongNote]]:
raw_notes: Set[Union[TapNote, LongNote]] = draw(st.sets(note_strat, max_size=32)) raw_notes: Set[Union[TapNote, LongNote]] = draw(st.sets(note_strat, max_size=32))
@ -135,7 +132,11 @@ def notes(
if last_note_time is None: if last_note_time is None:
new_time = draw(beat_time_strat) new_time = draw(beat_time_strat)
else: else:
new_time = last_note_time + draw(beat_interval_strat) numerator = draw(
st.integers(min_value=1, max_value=last_note_time.denominator * 4)
)
distance = BeatsTime(numerator, last_note_time.denominator)
new_time = last_note_time + distance
if isinstance(note, LongNote): if isinstance(note, LongNote):
notes.add( notes.add(
LongNote( LongNote(

View File

@ -14,16 +14,18 @@ def dump_and_load_then_compare(
song: song.Song, song: song.Song,
temp_path: ContextManager[Path], temp_path: ContextManager[Path],
bytes_decoder: Callable[[bytes], str], bytes_decoder: Callable[[bytes], str],
load_options: Optional[dict] = None,
dump_options: Optional[dict] = None, dump_options: Optional[dict] = None,
) -> None: ) -> None:
load_options = load_options or {}
dump_options = dump_options or {} dump_options = dump_options or {}
loader = LOADERS[format_] loader = LOADERS[format_]
dumper = DUMPERS[format_] dumper = DUMPERS[format_]
with temp_path as path: with temp_path as path:
files = dumper(song, path, **(dump_options or {})) files = dumper(song, path, **dump_options)
for path, bytes_ in files.items(): for path, bytes_ in files.items():
path.write_bytes(bytes_) path.write_bytes(bytes_)
note(f"Wrote to {path} :\n{bytes_decoder(bytes_)}") note(f"Wrote to {path} :\n{bytes_decoder(bytes_)}")
assert guess_format(path) == format_ assert guess_format(path) == format_
recovered_song = loader(path) recovered_song = loader(path, **load_options)
assert recovered_song == song assert recovered_song == song