[eve] Tests pass !
Add load_options to dump & load test strategy
This commit is contained in:
parent
f72e875898
commit
9c0850f98d
@ -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]
|
||||||
|
@ -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},
|
||||||
)
|
)
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user