Rhythm Time Signatures
This commit is contained in:
+15
-12
@@ -375,13 +375,14 @@ BOOST_AUTO_TEST_CASE(flat_practice_wraps_and_empty_catalog_returns_to_main) {
|
||||
for (auto index = std::uint32_t{2}; index <= 366U; ++index) {
|
||||
auto exercise = first;
|
||||
exercise.id = std::to_string(index);
|
||||
exercise.id.insert(0U, 4U - exercise.id.size(), '0');
|
||||
catalog->practice.exercises.push_back(std::move(exercise));
|
||||
}
|
||||
state.catalog = catalog;
|
||||
state.mode = vamp_zero::app_mode::PRACTICE_EXERCISE_MENU;
|
||||
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::PREVIOUS);
|
||||
BOOST_TEST(state.selected_practice_exercise_index == 366U);
|
||||
BOOST_TEST(vamp_zero::selected_practice_exercise(state)->id == "366");
|
||||
BOOST_TEST(vamp_zero::selected_practice_exercise(state)->id == "0366");
|
||||
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::NEXT);
|
||||
BOOST_TEST(state.selected_practice_exercise_index == 0U);
|
||||
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::PREVIOUS);
|
||||
@@ -415,6 +416,8 @@ BOOST_AUTO_TEST_CASE(database_reload_preserves_all_generated_practice_parts) {
|
||||
BOOST_REQUIRE_MESSAGE(library.has_value(), (library ? "" : library.error().describe()));
|
||||
BOOST_TEST(library->exercises.size() == expected_exercises);
|
||||
for (auto const &exercise : library->exercises) {
|
||||
BOOST_TEST(exercise.id.size() == 4U);
|
||||
BOOST_TEST(exercise.id.find_first_not_of("0123456789") == std::string::npos);
|
||||
BOOST_REQUIRE_EQUAL(exercise.parts.size(), 3U);
|
||||
auto parts = vamp_zero::practice_part_names();
|
||||
for (auto index = std::size_t{}; index < parts.size(); ++index) {
|
||||
@@ -447,7 +450,7 @@ BOOST_AUTO_TEST_CASE(numbered_practice_paths_keep_duplicate_titles_separate_and_
|
||||
auto assets = root / "assets";
|
||||
auto config = test_database_config(root / "vamp.db");
|
||||
std::filesystem::create_directories(assets / "songs");
|
||||
for (auto name : {"010", "002", "001", "127"}) {
|
||||
for (auto name : {"0010", "0002", "0001", "0127"}) {
|
||||
for (auto part_name : vamp_zero::practice_part_names()) {
|
||||
write_text(assets / "practice" / name / vamp_test::part_filename(part_name), score_asset_json("Shared Title", "C", std::string{part_name}));
|
||||
}
|
||||
@@ -459,24 +462,24 @@ BOOST_AUTO_TEST_CASE(numbered_practice_paths_keep_duplicate_titles_separate_and_
|
||||
auto library = future.get();
|
||||
BOOST_REQUIRE(library);
|
||||
BOOST_REQUIRE_EQUAL(library->exercises.size(), 4U);
|
||||
BOOST_TEST(library->exercises[0].id == "001");
|
||||
BOOST_TEST(library->exercises[1].id == "002");
|
||||
BOOST_TEST(library->exercises[2].id == "010");
|
||||
BOOST_TEST(library->exercises[3].id == "127");
|
||||
BOOST_TEST(library->exercises[0].id == "0001");
|
||||
BOOST_TEST(library->exercises[1].id == "0002");
|
||||
BOOST_TEST(library->exercises[2].id == "0010");
|
||||
BOOST_TEST(library->exercises[3].id == "0127");
|
||||
for (auto const &exercise : library->exercises) {
|
||||
BOOST_TEST(exercise.title == "Shared Title");
|
||||
}
|
||||
BOOST_REQUIRE(vamp_test::build_catalog(assets, config.path));
|
||||
std::filesystem::rename(assets / "practice/002", assets / "practice/099");
|
||||
std::filesystem::rename(assets / "practice/0002", assets / "practice/0099");
|
||||
BOOST_REQUIRE(vamp_test::build_catalog(assets, config.path));
|
||||
auto rows = query_database(root / "vamp.db", "SELECT id FROM practice_exercises ORDER BY id");
|
||||
BOOST_REQUIRE(rows);
|
||||
BOOST_REQUIRE_EQUAL(rows->rows.size(), 12U);
|
||||
BOOST_TEST(!has_value(*rows, 0U, "002-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "099-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "010-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "001-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "127-acoustic"));
|
||||
BOOST_TEST(!has_value(*rows, 0U, "0002-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "0099-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "0010-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "0001-acoustic"));
|
||||
BOOST_TEST(has_value(*rows, 0U, "0127-acoustic"));
|
||||
std::filesystem::remove_all(root);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class CatalogTests(unittest.TestCase):
|
||||
def test_every_numbered_source_is_wired_to_three_part_paths(self):
|
||||
source = Path('data/practice')
|
||||
sources = sorted(source.glob('*.xml'))
|
||||
self.assertEqual([p.name for p in sources], [f'{i:03}.xml' for i in range(1, 367)])
|
||||
self.assertEqual([p.name for p in sources], [f'{i:04}.xml' for i in range(1, 367)])
|
||||
expected = {Path(path.stem) / (part + '.json') for path in sources for part in PART_TYPES}
|
||||
self.assertEqual(len(expected), 1098)
|
||||
with tempfile.TemporaryDirectory() as temporary, patch('generator.practice.generate._practice_document', side_effect=lambda document, part: document):
|
||||
@@ -28,18 +28,18 @@ class CatalogTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
source, output = Path(temporary) / 'source', Path(temporary) / 'output'
|
||||
source.mkdir(parents=True)
|
||||
for name in ('000.xml', '001.xml', '010.xml', '366.xml'):
|
||||
for name in ('1000.xml', '0000.xml', '0001.xml', '0999.xml', '0010.xml', '0366.xml'):
|
||||
(source / name).write_text(xml_score(xml_note()), encoding='utf-8')
|
||||
self.assertEqual(generate_practice_scores(source, output), 12)
|
||||
self.assertEqual(sorted(p.name for p in output.iterdir()), ['000', '001', '010', '366'])
|
||||
self.assertEqual(generate_practice_scores(source, output), 18)
|
||||
self.assertEqual(sorted(p.name for p in output.iterdir()), ['0000', '0001', '0010', '0366', '0999', '1000'])
|
||||
for directory in output.iterdir():
|
||||
self.assertEqual({p.name for p in directory.glob('*.json')}, {(part + '.json') for part in PART_TYPES})
|
||||
self.assertEqual(json.loads((directory / 'vocal.json').read_text())['metadata']['title'], 'Fixture')
|
||||
def test_invalid_names_and_legacy_nesting_preserve_output(self):
|
||||
for name in ('1.xml', '001-copy.xml', '001_copy.xml', 'chord/001.xml', 'practice/melody/lesson/001.xml'):
|
||||
for name in ('1.xml', '001.xml', '00001.xml', '\u0660\u0660\u0660\u0661.xml', '0001-copy.xml', '0001_copy.xml', 'chord/0001.xml', 'practice/melody/lesson/0001.xml'):
|
||||
with self.subTest(name=name), tempfile.TemporaryDirectory() as temporary:
|
||||
source, output = Path(temporary) / 'source', Path(temporary) / 'output'
|
||||
for relative in ('002.xml', name):
|
||||
for relative in ('0002.xml', name):
|
||||
path = source / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(xml_score(xml_note()), encoding='utf-8')
|
||||
@@ -53,7 +53,7 @@ class CatalogTests(unittest.TestCase):
|
||||
def test_empty_part_directory_fails(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
(root / '001').mkdir()
|
||||
(root / '0001').mkdir()
|
||||
with self.assertRaisesRegex(ValueError, 'practice generator drift'):
|
||||
validate_practice_parts(root)
|
||||
def test_songs_generate_only_authored_key_parts(self):
|
||||
|
||||
@@ -34,7 +34,7 @@ class CatalogTests(unittest.TestCase):
|
||||
document = bundle('Song', 'F')
|
||||
write_score(self.root, 'songs/tune.json', document)
|
||||
for part in ('Acoustic', 'Electric', 'Vocal'):
|
||||
write_score(self.root, f'practice/001/{part.lower()}.json', bundle('Exercise', 'Eb', part))
|
||||
write_score(self.root, f'practice/0001/{part.lower()}.json', bundle('Exercise', 'Eb', part))
|
||||
database = build_catalog(self.root)
|
||||
self.assertEqual(database, self.root / 'catalog.db')
|
||||
song = self.rows('songs')[0]
|
||||
@@ -42,8 +42,8 @@ class CatalogTests(unittest.TestCase):
|
||||
self.assertEqual((song['id'], song['part'], song['source_path']), ('tune', 'Lead', 'songs/tune.json'))
|
||||
self.assertEqual((song['measure_count'], song['total_pages']), (1, 1))
|
||||
practice = self.rows('practice_exercises')
|
||||
self.assertEqual([row['id'] for row in practice], ['001-acoustic', '001-electric', '001-vocal'])
|
||||
self.assertEqual(practice[0]['exercise_id'], '001')
|
||||
self.assertEqual([row['id'] for row in practice], ['0001-acoustic', '0001-electric', '0001-vocal'])
|
||||
self.assertEqual(practice[0]['exercise_id'], '0001')
|
||||
self.assertNotIn('practice_type_id', practice[0])
|
||||
self.assertNotIn('lesson_group_id', practice[0])
|
||||
self.assertEqual(practice[0]['key_signature'], 'Eb')
|
||||
@@ -82,15 +82,15 @@ class CatalogTests(unittest.TestCase):
|
||||
self.assertFalse(list(self.root.glob('.vamp-catalog-*')))
|
||||
def test_missing_parts_and_metadata_disagreement_fail(self):
|
||||
for part in ('Acoustic', 'Electric'):
|
||||
write_score(self.root, f'practice/001/{part.lower()}.json', bundle(part=part))
|
||||
write_score(self.root, f'practice/0001/{part.lower()}.json', bundle(part=part))
|
||||
with self.assertRaisesRegex(ValueError, 'all three parts'):
|
||||
build_catalog(self.root)
|
||||
write_score(self.root, 'practice/001/vocal.json', bundle(key='F', part='Vocal'))
|
||||
write_score(self.root, 'practice/0001/vocal.json', bundle(key='F', part='Vocal'))
|
||||
with self.assertRaisesRegex(ValueError, 'disagree'):
|
||||
build_catalog(self.root)
|
||||
def test_keyless_parts_preserve_their_original_key(self):
|
||||
for part in ('Acoustic', 'Electric', 'Vocal'):
|
||||
write_score(self.root, f'practice/001/{part.lower()}.json', bundle(key='No key', part=part))
|
||||
write_score(self.root, f'practice/0001/{part.lower()}.json', bundle(key='No key', part=part))
|
||||
build_catalog(self.root)
|
||||
self.assertTrue(all(row['key_signature'] == 'No key' for row in self.rows('practice_exercises')))
|
||||
def test_invalid_layout_and_escaping_source_fail(self):
|
||||
|
||||
@@ -138,7 +138,7 @@ struct catalog_database {
|
||||
}
|
||||
};
|
||||
|
||||
auto catalog_exercise(std::string authored = "Eb", std::string id = "001") -> vamp_zero::practice_exercise {
|
||||
auto catalog_exercise(std::string authored = "Eb", std::string id = "0001") -> vamp_zero::practice_exercise {
|
||||
auto result = vamp_zero::practice_exercise{
|
||||
.id = std::move(id),
|
||||
.title = "Exercise",
|
||||
@@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(catalog_snapshots_heartbeat_and_navigation_share_immutable_
|
||||
{.id = "other", .title = "A Tune", .composer = "Composer", .part = "Lead", .total_pages = 1},
|
||||
{.id = "chosen", .title = "B Tune", .composer = "Composer", .part = "Lead", .total_pages = 2},
|
||||
});
|
||||
auto practice = vamp_zero::group_practice_exercises({catalog_exercise("Eb", "002"), catalog_exercise("Eb", "001")});
|
||||
auto practice = vamp_zero::group_practice_exercises({catalog_exercise("Eb", "0002"), catalog_exercise("Eb", "0001")});
|
||||
BOOST_REQUIRE(practice);
|
||||
auto catalog = std::make_shared<vamp_zero::catalog const>(vamp_zero::catalog{std::move(performance), std::move(*practice)});
|
||||
auto original = vamp_zero::app_state{
|
||||
@@ -249,11 +249,11 @@ BOOST_AUTO_TEST_CASE(catalog_practice_requires_complete_ordered_parts_and_key_ag
|
||||
first.parts.pop_back();
|
||||
auto fragment = catalog_exercise(authored);
|
||||
fragment.parts = {last};
|
||||
auto grouped = vamp_zero::group_practice_exercises({catalog_exercise("F", "002"), first, fragment});
|
||||
auto grouped = vamp_zero::group_practice_exercises({catalog_exercise("F", "0002"), first, fragment});
|
||||
BOOST_REQUIRE(grouped);
|
||||
BOOST_REQUIRE_EQUAL(grouped->exercises.size(), 2U);
|
||||
auto const &exercise = grouped->exercises[0];
|
||||
BOOST_TEST(exercise.id == "001");
|
||||
BOOST_TEST(exercise.id == "0001");
|
||||
BOOST_TEST(exercise.parts.front().key_signature == authored);
|
||||
BOOST_REQUIRE_EQUAL(exercise.parts.size(), 3U);
|
||||
for (auto index = std::size_t{}; index < exercise.parts.size(); ++index) {
|
||||
@@ -284,7 +284,7 @@ BOOST_AUTO_TEST_CASE(catalog_individual_score_lookup_uses_category_and_reports_m
|
||||
auto exercise = catalog_bundle("Exercise", "D", "Electric");
|
||||
db.write("songs/shared.json", song);
|
||||
for (auto part : vamp_zero::practice_part_names()) {
|
||||
db.write(std::filesystem::path{"practice/001"} / vamp_test::part_filename(part), catalog_bundle("Exercise", "D", part));
|
||||
db.write(std::filesystem::path{"practice/0001"} / vamp_test::part_filename(part), catalog_bundle("Exercise", "D", part));
|
||||
}
|
||||
BOOST_REQUIRE(db.build());
|
||||
db.query("UPDATE practice_exercises SET id = 'shared' WHERE part = 'Electric'");
|
||||
@@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(catalog_individual_score_lookup_uses_category_and_reports_m
|
||||
BOOST_TEST(document->title == (is_song ? "Song" : "Exercise"));
|
||||
BOOST_TEST(document->key_signature == (is_song ? "F" : "D"));
|
||||
BOOST_TEST(document->source_json == (is_song ? song : exercise));
|
||||
BOOST_TEST(document->source_path == (db.assets / (is_song ? "songs/shared.json" : "practice/001/electric.json")).string());
|
||||
BOOST_TEST(document->source_path == (db.assets / (is_song ? "songs/shared.json" : "practice/0001/electric.json")).string());
|
||||
BOOST_TEST(document->measure_count == 3U);
|
||||
BOOST_REQUIRE_EQUAL(document->pages.size(), 2U);
|
||||
BOOST_TEST(document->pages[1].path == "fixture.pages/2.png");
|
||||
@@ -346,9 +346,9 @@ BOOST_AUTO_TEST_CASE(catalog_reload_uses_cached_metadata_without_parsing_score_j
|
||||
for (auto part_name : vamp_zero::practice_part_names()) {
|
||||
db.query("INSERT INTO practice_exercises(id, exercise_id, "
|
||||
"title, composer, key_signature, source_json, source_path, measure_count, total_pages, part) "
|
||||
"VALUES(?1, '001', 'Metadata Exercise', 'Composer', 'Eb', '{}', ?3, 3, 2, ?2)",
|
||||
"VALUES(?1, '0001', 'Metadata Exercise', 'Composer', 'Eb', '{}', ?3, 3, 2, ?2)",
|
||||
{noto::sqlite_value{std::string{"practice_part-"} + std::string{part_name}}, noto::sqlite_value{std::string{part_name}},
|
||||
noto::sqlite_value{std::string{"practice/001/"} + std::string{part_name} + ".json"}});
|
||||
noto::sqlite_value{std::string{"practice/0001/"} + std::string{part_name} + ".json"}});
|
||||
}
|
||||
auto songs = db.rows("songs");
|
||||
auto exercises = db.rows("practice_exercises");
|
||||
@@ -445,7 +445,7 @@ BOOST_AUTO_TEST_CASE(catalog_reads_readonly_database_without_json_and_resolves_r
|
||||
auto db = catalog_database{};
|
||||
db.write("songs/tune.json", catalog_bundle("Relocated"));
|
||||
for (auto part_name : vamp_zero::practice_part_names()) {
|
||||
db.write(std::filesystem::path{"practice/001"} / vamp_test::part_filename(part_name), catalog_bundle("Exercise", "Eb", part_name));
|
||||
db.write(std::filesystem::path{"practice/0001"} / vamp_test::part_filename(part_name), catalog_bundle("Exercise", "Eb", part_name));
|
||||
}
|
||||
BOOST_REQUIRE(db.build());
|
||||
std::filesystem::remove(db.assets / "songs/tune.json");
|
||||
|
||||
@@ -189,7 +189,7 @@ class EngravingCacheTests(unittest.TestCase):
|
||||
data, output = self.root / 'data', self.root / 'output'
|
||||
(data / 'songs').mkdir(parents=True)
|
||||
(data / 'practice').mkdir()
|
||||
source = data / 'practice/001.xml'
|
||||
source = data / 'practice/0001.xml'
|
||||
source.write_text(xml_score(xml_note()))
|
||||
args = argparse.Namespace(data_dir=data, output_dir=output, static_assets_dir=Path('package/assets'), check=False, engraving_cache=self.cache, jobs=2)
|
||||
generate_assets(args)
|
||||
|
||||
@@ -160,13 +160,13 @@ class GuitarTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
source, output = Path(temporary) / 'source', Path(temporary) / 'output'
|
||||
source.mkdir()
|
||||
(source / '000.xml').write_text(xml_score(xml_note('E', alter=-1), '<key><fifths>-3</fifths></key>'))
|
||||
(source / '0000.xml').write_text(xml_score(xml_note('E', alter=-1), '<key><fifths>-3</fifths></key>'))
|
||||
self.assertEqual(generate_practice_scores(source, output), 3)
|
||||
validate_practice_parts(output)
|
||||
first = {p.name: p.read_bytes() for p in output.rglob('*.json')}
|
||||
generate_practice_scores(source, output)
|
||||
self.assertEqual(first, {p.name: p.read_bytes() for p in output.rglob('*.json')})
|
||||
(output / '000/vocal.json').unlink()
|
||||
(output / '0000/vocal.json').unlink()
|
||||
with self.assertRaisesRegex(ValueError, 'practice generator drift'):
|
||||
validate_practice_parts(output)
|
||||
def test_unplayable_generation_preserves_previous_assets(self):
|
||||
@@ -174,12 +174,12 @@ class GuitarTests(unittest.TestCase):
|
||||
root = Path(temporary)
|
||||
(root / 'data/songs').mkdir(parents=True)
|
||||
(root / 'data/practice').mkdir()
|
||||
(root / 'data/practice/001.xml').write_text(xml_score(xml_note(octave=7, alter=1)))
|
||||
(root / 'data/practice/0001.xml').write_text(xml_score(xml_note(octave=7, alter=1)))
|
||||
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'), check=False)
|
||||
with self.assertRaisesRegex(ValueError, '001.xml, part electric:.*0-20'):
|
||||
with self.assertRaisesRegex(ValueError, '0001.xml, part electric:.*0-20'):
|
||||
generate_assets(args)
|
||||
self.assertEqual([p.name for p in output.iterdir()], ['last-good'])
|
||||
self.assertEqual((output / 'last-good').read_text(), 'preserved')
|
||||
|
||||
@@ -88,7 +88,7 @@ class HarmonyTests(unittest.TestCase):
|
||||
source = parse(xml_score(chord_xml('7', 'dominant', extra='<degree><degree-value>9</degree-value><degree-alter>-1</degree-alter><degree-type>add</degree-type></degree>') + xml_note()))
|
||||
self.assertEqual(source['staves'][0]['measures'][0]['harmonies'][0]['symbol'], 'C7b9')
|
||||
def test_current_exercise_and_song_exports_import_without_edits(self):
|
||||
exercise = load_musicxml(Path('data/practice/001.xml'))
|
||||
exercise = load_musicxml(Path('data/practice/0001.xml'))
|
||||
measures = exercise['staves'][0]['measures']
|
||||
self.assertEqual(len(measures), 20)
|
||||
self.assertEqual(sum(len(n['pitches']) for m in measures for v in m['voices'] for n in v['notes']), 70)
|
||||
|
||||
@@ -187,7 +187,7 @@ auto make_assets(vamp_zero::config const &config) -> void {
|
||||
for (auto part_name : vamp_zero::practice_part_names()) {
|
||||
auto name = std::string{part_name};
|
||||
name.front() = static_cast<std::char_t>(name.front() - 'A' + 'a');
|
||||
write_score(root / "practice" / "001" / (name + ".json"), std::string{part_name});
|
||||
write_score(root / "practice" / "0001" / (name + ".json"), std::string{part_name});
|
||||
}
|
||||
BOOST_REQUIRE(vamp_test::build_catalog(root, config.database.path));
|
||||
}
|
||||
@@ -375,7 +375,7 @@ BOOST_AUTO_TEST_CASE(readiness_reads_packaged_catalog_without_hardware_or_heartb
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(readiness_validates_later_pages_and_parts) {
|
||||
for (std::char_t const *relative : {"songs/first.pages/2.png", "songs/second.pages/2.png", "practice/001/electric.pages/2.png"}) {
|
||||
for (std::char_t const *relative : {"songs/first.pages/2.png", "songs/second.pages/2.png", "practice/0001/electric.pages/2.png"}) {
|
||||
auto root = temporary_directory{};
|
||||
auto config = fixture_config(root.path);
|
||||
make_assets(config);
|
||||
@@ -394,7 +394,7 @@ BOOST_AUTO_TEST_CASE(readiness_validates_generated_audio_using_configured_asset_
|
||||
auto assets = std::filesystem::path{config.runtime.assets_path};
|
||||
auto fixture = std::filesystem::path{VAMP_TEST_PLAYBACK_ASSETS_DIR};
|
||||
std::filesystem::copy(fixture / "audio", assets / "audio", std::filesystem::copy_options::recursive);
|
||||
auto path = assets / "practice/001/acoustic.json";
|
||||
auto path = assets / "practice/0001/acoustic.json";
|
||||
write_score(path, "Acoustic");
|
||||
auto score = boost::json::parse(read_file(path));
|
||||
score.as_object()["playback"] = boost::json::parse(read_file(fixture / "score.json")).at("playback");
|
||||
|
||||
+3
-3
@@ -54,7 +54,7 @@ class PartGenerationTests(unittest.TestCase):
|
||||
source = parse(xml_score(xml_harmony() + xml_note(duration=1, kind='quarter') + '<attributes><time><beats>6</beats><beat-type>8</beat-type></time></attributes>' + xml_note(duration=3, kind='half', extra='<dot/>')))
|
||||
self.assertEqual(len(generated_voice(source['staves'][0]['measures'][0], 'accompaniment', None)), 1)
|
||||
def test_exercise_006_has_accompaniment_in_every_measure(self):
|
||||
result = practice_document(load_musicxml(Path('data/practice/006.xml')), 'acoustic')
|
||||
result = practice_document(load_musicxml(Path('data/practice/0006.xml')), 'acoustic')
|
||||
self.assertEqual(len(result['staves'][0]['measures']), 12)
|
||||
for measure in result['staves'][0]['measures']:
|
||||
self.assertTrue(any(n['pitches'] for v in measure['voices'] for n in v['notes']), measure['id'])
|
||||
@@ -96,7 +96,7 @@ class PartGenerationTests(unittest.TestCase):
|
||||
def test_pipeline_parses_and_normalizes_each_source_once(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
for category, name in (('songs', 'song'), ('practice', '001')):
|
||||
for category, name in (('songs', 'song'), ('practice', '0001')):
|
||||
(root / 'data' / category).mkdir(parents=True)
|
||||
(root / 'data' / category / (name + '.xml')).write_text(xml_score(xml_harmony() + xml_note()))
|
||||
args = argparse.Namespace(data_dir=root / 'data', output_dir=root / 'output', static_assets_dir=Path('package/assets'), check=False)
|
||||
@@ -106,7 +106,7 @@ class PartGenerationTests(unittest.TestCase):
|
||||
practice_load.assert_called_once()
|
||||
self.assertEqual(normalize.call_count, 2)
|
||||
original = {p.relative_to(args.output_dir): p.read_bytes() for p in args.output_dir.rglob('*') if p.is_file()}
|
||||
(root / 'data/practice/002.xml').write_text('<broken')
|
||||
(root / 'data/practice/0002.xml').write_text('<broken')
|
||||
with self.assertRaises(ValueError):
|
||||
generate_assets(args)
|
||||
self.assertEqual(original, {p.relative_to(args.output_dir): p.read_bytes() for p in args.output_dir.rglob('*') if p.is_file()})
|
||||
|
||||
@@ -28,18 +28,24 @@ class ScoreBundleTests(unittest.TestCase):
|
||||
data, output = root / 'data', root / 'output'
|
||||
(data / 'songs').mkdir(parents=True)
|
||||
(data / 'practice').mkdir()
|
||||
source = data / 'practice/001.xml'
|
||||
source = data / 'practice/0001.xml'
|
||||
source.write_text(xml_score(xml_note()))
|
||||
args = argparse.Namespace(data_dir=data, output_dir=output, static_assets_dir=Path('package/assets'), check=False)
|
||||
generate_assets(args)
|
||||
original = {p.relative_to(output): p.read_bytes() for p in output.rglob('*') if p.is_file()}
|
||||
self.assertTrue(list(output.rglob('*.png')))
|
||||
legacy_source = source.with_name('001.xml')
|
||||
source.rename(legacy_source)
|
||||
with self.assertRaisesRegex(ValueError, 'four-digit'):
|
||||
generate_assets(args)
|
||||
self.assertEqual(original, {p.relative_to(output): p.read_bytes() for p in output.rglob('*') if p.is_file()})
|
||||
legacy_source.rename(source)
|
||||
(data / 'rhythms' / 'drafts').mkdir(parents=True)
|
||||
for path in ('rhythms/001.xml', 'rhythms/drafts/002.xml'):
|
||||
for path in ('rhythms/0001.xml', 'rhythms/drafts/0002.xml'):
|
||||
(data / path).write_text('<unfinished')
|
||||
generate_assets(args)
|
||||
self.assertEqual(original, {p.relative_to(output): p.read_bytes() for p in output.rglob('*') if p.is_file()})
|
||||
misplaced = data / '001.xml'
|
||||
misplaced = data / '0001.xml'
|
||||
misplaced.write_text(xml_score(xml_note()))
|
||||
with self.assertRaisesRegex(ValueError, 'belong directly under'):
|
||||
generate_assets(args)
|
||||
@@ -57,12 +63,16 @@ class ScoreBundleTests(unittest.TestCase):
|
||||
generate_assets(args)
|
||||
self.assertEqual(original, {p.relative_to(output): p.read_bytes() for p in output.rglob('*') if p.is_file()})
|
||||
source.write_text(xml_score(xml_note()))
|
||||
renamed = data / 'practice/127.xml'
|
||||
renamed = data / 'practice/0127.xml'
|
||||
source.rename(renamed)
|
||||
source = renamed
|
||||
legacy_output = output / 'practice/001'
|
||||
legacy_output.mkdir()
|
||||
(legacy_output / 'electric.json').write_text('{}')
|
||||
generate_assets(args)
|
||||
self.assertFalse((output / 'practice/001').exists())
|
||||
self.assertEqual(len(list((output / 'practice/127').glob('*.json'))), 3)
|
||||
self.assertFalse(legacy_output.exists())
|
||||
self.assertFalse((output / 'practice/0001').exists())
|
||||
self.assertEqual(len(list((output / 'practice/0127').glob('*.json'))), 3)
|
||||
source.unlink()
|
||||
generate_assets(args)
|
||||
self.assertFalse(list(output.rglob('*.png')))
|
||||
|
||||
Reference in New Issue
Block a user