Fret Range & Cleanup
ci / checks (push) Failing after 3h14m51s

This commit is contained in:
khodowany
2026-09-19 22:29:54 -05:00
parent 85cab9d6f6
commit c07586e06a
274 changed files with 362 additions and 164309 deletions
+23 -21
View File
@@ -31,17 +31,21 @@ def all_pitches(document):
class GuitarPracticeTests(unittest.TestCase):
def test_fret_boundaries_and_crossing_twelve(self):
self.assertIn(TabPosition(6, 0, 40), positions_for_pitch(Fraction(40)))
self.assertIn(TabPosition(1, 15, 79), positions_for_pitch(Fraction(79)))
self.assertFalse(positions_for_pitch(Fraction(80)))
self.assertIn(TabPosition(1, 20, 84), positions_for_pitch(Fraction(84)))
self.assertFalse(positions_for_pitch(Fraction(85)))
self.assertFalse(positions_for_pitch(Fraction(129, 2)))
self.assertEqual([p.fret for p in positions_for_string(1, 64, tuple(range(12)))], list(range(16)))
self.assertEqual([p.fret for p in positions_for_string(1, 64, tuple(range(12)))], list(range(21)))
for fret in range(16, 21):
self.assertIn(TabPosition(6, fret, 40 + fret), positions_for_pitch(Fraction(40 + fret)))
self.assertTrue(within_fret_span((TabPosition(4, 11, 61), TabPosition(3, 12, 67), TabPosition(2, 14, 73))))
self.assertTrue(within_fret_span((TabPosition(6, 0, 40), TabPosition(1, 15, 79))))
self.assertFalse(within_fret_span((TabPosition(4, 10, 60), TabPosition(1, 15, 79))))
content = xml_note('G', extra=technical(1, 15), octave=5)
self.assertTrue(within_fret_span((TabPosition(6, 0, 40), TabPosition(1, 20, 84))))
self.assertFalse(within_fret_span((TabPosition(4, 10, 60), TabPosition(1, 20, 84))))
content = xml_note('C', extra=technical(1, 20), octave=6)
validate_guitar_practice(parse(xml_score(content, TAB_STAFF)))
with self.assertRaisesRegex(ValueError, '0-15'):
validate_guitar_practice(parse(xml_score(xml_note('G', alter=1, extra=technical(1, 16), octave=5), TAB_STAFF)))
with self.assertRaisesRegex(ValueError, '0-20'):
validate_guitar_practice(parse(xml_score(xml_note('C', alter=1, extra=technical(1, 21), octave=6), TAB_STAFF)))
with self.assertRaisesRegex(ValueError, '0-20'):
validate_guitar_practice(parse(xml_score(xml_note('D', alter=-1, extra=technical(6, 21)), TAB_STAFF)))
def test_arranger_does_not_truncate_candidate_positions(self):
def candidates(string, open_midi, pitch_classes):
if string == 6:
@@ -89,7 +93,7 @@ class GuitarPracticeTests(unittest.TestCase):
self.assertEqual(notes(result)[0]['pitches'][0]['letter'], 'c')
self.assertEqual(fraction(notes(result)[0]['pitches'][0]['alter']), 2)
self.assertEqual(result['staves'][0]['measures'][0]['attributes'][0]['key'], key_from_fifths(1))
def test_keyless_reference_required_for_both_practice_types(self):
def test_keyless_reference_required_for_practice(self):
source = parse(xml_score(xml_note(), '<key><fifths>0</fifths><mode>none</mode></key>'))
with self.assertRaisesRegex(ValueError, 'vamp:tonic'):
transpose_guitar_practice(source, 'D')
@@ -197,31 +201,29 @@ class GuitarPracticeTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
(root / 'data/songs').mkdir(parents=True)
source = root / 'data/practice/chord/lesson/wide.xml'
source.parent.mkdir(parents=True)
source = root / 'data/001.xml'
source.parent.mkdir(parents=True, exist_ok=True)
source.write_text(xml_score(content), encoding='utf-8')
output = root / 'output'
output.mkdir()
(output / 'last-good').write_text('preserved')
args = argparse.Namespace(data_dir=root / 'data', output_dir=output, static_assets_dir=Path('package/assets'), clean=True, check=False)
args = argparse.Namespace(data_dir=root / 'data', output_dir=output, static_assets_dir=Path('package/assets'), check=False)
with patch('generator.__main__.engrave_tree') as engrave:
with self.assertRaisesRegex(ValueError, r'wide.xml, target key C:.*measure 1'):
with self.assertRaisesRegex(ValueError, r'001.xml, target key C:.*measure 1'):
generate_assets(args)
engrave.assert_not_called()
self.assertEqual((output / 'last-good').read_text(), 'preserved')
self.assertEqual(list(output.iterdir()), [output / 'last-good'])
def test_all_practice_types_require_twelve_deterministic_variants(self):
def test_practice_requires_twelve_deterministic_variants(self):
with tempfile.TemporaryDirectory() as temp:
source, output = Path(temp) / 'source', Path(temp) / 'output'
for practice_type in ('chord', 'melody'):
path = source / practice_type / '001_lesson' / '001-exercise.xml'
path.parent.mkdir(parents=True)
path.write_text(xml_score(xml_note()), encoding='utf-8')
self.assertEqual(generate_practice_scores(source, output), 24)
source.mkdir()
(source / '001.xml').write_text(xml_score(xml_note()), encoding='utf-8')
self.assertEqual(generate_practice_scores(source, output), 12)
validate_practice_variants(output)
first = {p.relative_to(output): p.read_bytes() for p in output.rglob('*.json')}
generate_practice_scores(source, output)
self.assertEqual(first, {p.relative_to(output): p.read_bytes() for p in output.rglob('*.json')})
next((output / 'chord').rglob('g.json')).unlink()
with self.assertRaisesRegex(ValueError, 'chord practice generator drift'):
(output / '001/g.json').unlink()
with self.assertRaisesRegex(ValueError, 'practice generator drift'):
validate_practice_variants(output)