Compare commits

...
Author SHA1 Message Date
branderson 4272189bca tests: replace copyrighted lyrics with placeholder text
The align, transcribe, lyrics and notes tests quoted lines from published
songs, and the UltraStar parser fixtures were complete community charts of
commercial songs. Replace them with counting words and two hand-written
charts that keep what the parser tests rely on: CRLF line endings,
Latin-1 bytes, a decimal-comma BPM, freestyle/golden/rap notes, negative
pitch and a two-number line break.
2026-10-09 17:17:02 -07:00
branderson 936641e13c Merge pull request 'Keep matched words that fall inside a missing-lyrics span' (#13) from missing-notes into main
Reviewed-on: #13
Reviewed-by: branderson <brad@branderson.io>
2026-10-09 16:54:17 -07:00
4 changed files with 67 additions and 10 deletions
+36
View File
@@ -0,0 +1,36 @@
#TITLE:Placeholder Chart
#ARTIST:Test Artist
#LANGUAGE:English
#YEAR:2000
#CREATOR:songtrace tests
#GENRE:Test
#COVER:Test Artist - Placeholder Chart [CO].jpg
#MP3:Test Artist - Placeholder Chart.mp3
#VIDEO:Test Artist - Placeholder Chart.mp4
#BPM:217,98
#GAP:4880
#MEDLEYSTARTBEAT:113
#MEDLEYENDBEAT:240
: 0 3 16 ah
: 5 3 20 ah
: 11 3 23 la
: 17 3 16 la
: 22 28 15 ah
: 51 2 16 doo
: 54 31 16 ~
- 95
F 113 3 18 la
F 117 3 21 la
F 121 3 21 la
* 125 3 21 doo
* 129 12 19 ~
- 150
: 155 4 12 Na
R 160 4 0 na
G 165 4 0 na
: 170 6 14 la
- 190
: 194 3 16 La
: 198 3 18 la
* 202 10 20 ah
E
+21
View File
@@ -0,0 +1,21 @@
#TITLE:Café Placeholder
#ARTIST:Tést Artist
#LANGUAGE:English
#GENRE:Test
#YEAR:2000
#MP3:Tést Artist - Café Placeholder.mp3
#CREATOR:songtrace tests
#BPM:256
#GAP:49673
: 0 4 2 Doo
: 6 4 4 doo
: 12 8 5 laé
- 28 30
: 32 4 0 Na
: 38 4 -2 na
* 44 12 -9 ah
- 64
F 70 4 3 La
: 76 6 -5 la
: 84 10 -9 ~
E
+5 -5
View File
@@ -78,7 +78,7 @@ def test_syllabify():
assert syllabify("reminds", "English") == ["re", "minds"]
assert syllabify("a", "English") == ["a"]
assert syllabify("xyzzy", None) == ["xyzzy"]
assert syllabify("She's", "English") == ["She's"]
assert syllabify("It's", "English") == ["It's"]
def test_syllabify_preserves_word_characters():
@@ -96,12 +96,12 @@ def test_syllabify_language_without_pyphen_dict_is_not_mishandled_as_english():
def test_build_notes_basic_line():
times, f0, conf = flat_pitch()
rows = [
{"word": "She's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "It's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "got", "start": 1.6, "end": 2.0, "score": 0.9, "line_index": 0},
{"word": "Now", "start": 5.0, "end": 5.5, "score": 0.9, "line_index": 1},
]
notes, breaks, warnings = build_notes(rows, times, f0, conf, "English")
assert notes[0].text == "She's" # line-initial: no leading space
assert notes[0].text == "It's" # line-initial: no leading space
assert notes[1].text == " got" # mid-line: leading space
assert notes[2].text == "Now" # new line: no leading space
assert len(breaks) == 1 and 2.0 <= breaks[0] <= 5.0
@@ -278,7 +278,7 @@ def test_notes_stage_writes_json(tmp_path):
times, f0, conf = flat_pitch()
np.savez(work / "pitch.npz", times=times, f0=f0, conf=conf)
(work / "alignment.json").write_text(json.dumps([
{"word": "She's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "It's", "start": 1.0, "end": 1.5, "score": 0.9, "line_index": 0},
{"word": "gone", "start": 3.0, "end": 3.5, "score": 0.9, "line_index": 1},
]))
ctx = Ctx(inp=SongInput(title="T", artist="A", lyrics_text="x", language="English"),
@@ -287,7 +287,7 @@ def test_notes_stage_writes_json(tmp_path):
assert stage.outputs(ctx) == [work / "notes_sec.json"]
stage.run(ctx)
data = json.loads((work / "notes_sec.json").read_text())
assert [n["text"] for n in data["notes"]] == ["She's", "gone"]
assert [n["text"] for n in data["notes"]] == ["It's", "gone"]
assert data["breaks"] == [2.25]
assert data["warnings"] == []
assert TimedNote(**data["notes"][0]).pitch == 0
+5 -5
View File
@@ -10,8 +10,8 @@ def load(name):
return (FIXTURES / name).read_text(encoding="utf-8", errors="replace")
def test_parse_headers_and_notes():
song = parse_ultrastar(load("sanctuary.txt"))
assert get_header(song, "artist") == "Utada Hikaru"
song = parse_ultrastar(load("chart_crlf.txt"))
assert get_header(song, "artist") == "Test Artist"
assert parse_bpm(get_header(song, "BPM")) == 217.98
first = song.body[0]
assert first == Note(kind=":", beat=0, length=3, pitch=16, text="ah")
@@ -19,16 +19,16 @@ def test_parse_headers_and_notes():
assert any(n.kind == "F" for n in song.body if isinstance(n, Note))
def test_note_text_keeps_leading_space():
song = parse_ultrastar(load("sanctuary.txt"))
song = parse_ultrastar(load("chart_crlf.txt"))
notes = [n for n in song.body if isinstance(n, Note)]
assert notes[1].text == " ah"
def test_negative_pitch():
song = parse_ultrastar(load("sweet_child.txt"))
song = parse_ultrastar(load("chart_latin1.txt"))
assert any(n.pitch == -9 for n in song.body if isinstance(n, Note))
def test_round_trip():
for name in ("sweet_child.txt", "sanctuary.txt"):
for name in ("chart_latin1.txt", "chart_crlf.txt"):
song = parse_ultrastar(load(name))
again = parse_ultrastar(serialize_ultrastar(song))
assert again == song