Tab Generation
ci / checks (push) Successful in 1h19m14s

This commit is contained in:
khodowany
2026-09-20 16:20:43 -05:00
parent 68ded4d8c8
commit cf7c0c010c
423 changed files with 1422 additions and 1332 deletions
+51 -59
View File
@@ -92,7 +92,7 @@ auto has_value(noto::query_result const &result, std::size_t column, std::string
});
}
auto score_asset_json(std::string title, std::string key = "C") -> std::string {
auto score_asset_json(std::string title, std::string key = "C", std::string part = "Lead") -> std::string {
auto value = boost::json::parse(R"json({
"schema_version":3,
"music":{"metadata":{"title":"Fixture","composer":"Composer","key_signature":"C","time_signature":"4/4"},
@@ -103,6 +103,7 @@ auto score_asset_json(std::string title, std::string key = "C") -> std::string {
auto &metadata = value.as_object()["music"].as_object()["metadata"].as_object();
metadata["title"] = std::move(title);
metadata["key_signature"] = std::move(key);
value.at("music").at("staves").as_array().front().as_object()["name"] = std::move(part);
return boost::json::serialize(value);
}
@@ -117,8 +118,7 @@ auto navigation_state() -> vamp_zero::app_state {
.measure_count = 1,
.total_pages = 2,
};
auto variant = vamp_zero::practice_exercise::score_variant{
.id = "exercise-c",
auto practice_part = vamp_zero::library_part{
.key_signature = "C",
.source_path = "exercise.json",
.measure_count = 1,
@@ -128,14 +128,11 @@ auto navigation_state() -> vamp_zero::app_state {
.id = "exercise-1",
.title = "C Major",
.composer = "Composer",
.authored_key_signature = "C",
.variants = {variant},
};
exercise.variants.clear();
for (auto key : vamp_zero::practice_key_names()) {
variant.id = "exercise-" + std::string{key};
variant.key_signature = key;
exercise.variants.push_back(variant);
for (auto part_name : vamp_zero::practice_part_names()) {
practice_part.id = "exercise-" + std::string{part_name};
practice_part.part = part_name;
exercise.parts.push_back(practice_part);
}
auto practice = vamp_zero::group_practice_exercises({exercise});
BOOST_REQUIRE(practice);
@@ -286,12 +283,12 @@ BOOST_AUTO_TEST_CASE(score_parser_loads_the_generated_catalogue) {
BOOST_TEST(count > 0U);
}
BOOST_AUTO_TEST_CASE(practice_key_navigation_and_remote_display) {
BOOST_AUTO_TEST_CASE(practice_part_navigation_and_remote_display) {
auto state = navigation_state();
auto catalog = std::make_shared<vamp_zero::catalogue>(*state.catalog);
auto &variant = catalog->practice.exercises.front().variants[2];
variant.source_path = "exercise-d.json";
variant.total_pages = 3U;
auto &practice_part = catalog->practice.exercises.front().parts[2];
practice_part.source_path = "exercise-vocal.json";
practice_part.total_pages = 3U;
state.catalog = catalog;
state.selected_main_menu_index = 1U;
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::SELECT);
@@ -299,23 +296,23 @@ BOOST_AUTO_TEST_CASE(practice_key_navigation_and_remote_display) {
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::NEXT);
state.audio.metronome_enabled = true;
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::SELECT);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU);
BOOST_TEST(vamp_zero::practice_key_row_count(state) == 13U);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_PART_MENU);
BOOST_TEST(vamp_zero::practice_part_row_count(state) == 4U);
for (auto index = std::uint32_t{}; index < 3U; ++index) {
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::NEXT);
}
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::SELECT);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_SCORE);
BOOST_TEST(state.audio.metronome_enabled);
auto selected = vamp_zero::selected_practice_variant(state, state.catalog->practice.exercises.front());
auto selected = vamp_zero::selected_practice_part(state, state.catalog->practice.exercises.front());
BOOST_REQUIRE(selected != nullptr);
BOOST_TEST(selected->id == "exercise-D");
BOOST_TEST(selected->source_path == "exercise-d.json");
BOOST_TEST(selected->id == "exercise-Vocal");
BOOST_TEST(selected->source_path == "exercise-vocal.json");
BOOST_TEST(vamp_zero::active_pages(state) == 3U);
auto remote = vamp_pico::remote_display_state{};
BOOST_REQUIRE(vamp_pico::parse_remote_display_body(vamp_zero::remote_display_text(state), remote));
BOOST_TEST(remote.active);
BOOST_TEST(std::string{remote.part} == "Key: D");
BOOST_TEST(std::string{remote.part} == "Vocal");
BOOST_TEST(remote.pages == 3U);
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::NEXT);
BOOST_TEST(state.page_index == 1U);
@@ -323,11 +320,11 @@ BOOST_AUTO_TEST_CASE(practice_key_navigation_and_remote_display) {
playing.audio.playback_enabled = true;
auto control = vamp_zero::control_state{{}, playing};
control.finish_playback(playing.score_session);
BOOST_CHECK(control.snapshot().mode == vamp_zero::app_mode::PRACTICE_KEY_MENU);
BOOST_TEST(control.snapshot().selected_practice_key_index == 3U);
BOOST_CHECK(control.snapshot().mode == vamp_zero::app_mode::PRACTICE_PART_MENU);
BOOST_TEST(control.snapshot().selected_practice_part_index == 3U);
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::SELECT);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU);
BOOST_TEST(state.selected_practice_key_index == 3U);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_PART_MENU);
BOOST_TEST(state.selected_practice_part_index == 3U);
BOOST_TEST(state.active_practice_id.empty());
for (auto index = std::uint32_t{}; index < 3U; ++index) {
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::PREVIOUS);
@@ -362,7 +359,7 @@ BOOST_AUTO_TEST_CASE(flat_practice_wraps_and_empty_catalogue_returns_to_main) {
BOOST_TEST(state.selected_practice_exercise_index == 0U);
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::PREVIOUS);
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::SELECT);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_KEY_MENU);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_PART_MENU);
state.catalog = vamp_zero::empty_catalogue();
state = vamp_zero::apply_app_command(std::move(state), vamp_protocol::page_command::NEXT);
BOOST_CHECK(state.mode == vamp_zero::app_mode::PRACTICE_EXERCISE_MENU);
@@ -372,11 +369,11 @@ BOOST_AUTO_TEST_CASE(flat_practice_wraps_and_empty_catalogue_returns_to_main) {
BOOST_CHECK(state.mode == vamp_zero::app_mode::MAIN_MENU);
}
BOOST_AUTO_TEST_CASE(database_reload_preserves_all_generated_practice_keys) {
auto root = std::filesystem::temp_directory_path() / ("product-vamp-keys-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
BOOST_AUTO_TEST_CASE(database_reload_preserves_all_generated_practice_parts) {
auto root = std::filesystem::temp_directory_path() / ("product-vamp-parts-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
auto config = test_database_config(root / "vamp.db");
auto expected_exercises = std::size_t{};
for (auto const &entry : std::filesystem::directory_iterator{std::filesystem::path{VAMP_TEST_SOURCE_DIR} / "data"}) {
for (auto const &entry : std::filesystem::directory_iterator{std::filesystem::path{VAMP_TEST_SOURCE_DIR} / "data" / "practice"}) {
if (entry.is_regular_file() && entry.path().extension() == ".xml") {
++expected_exercises;
}
@@ -391,27 +388,26 @@ BOOST_AUTO_TEST_CASE(database_reload_preserves_all_generated_practice_keys) {
BOOST_REQUIRE_MESSAGE(library.has_value(), (library ? "" : library.error().describe()));
BOOST_TEST(library->exercises.size() == expected_exercises);
for (auto const &exercise : library->exercises) {
BOOST_REQUIRE_EQUAL(exercise.variants.size(), 12U);
auto keys = vamp_zero::practice_key_names();
for (auto index = std::size_t{}; index < keys.size(); ++index) {
auto const &key = exercise.variants[index].key_signature;
BOOST_TEST(key.substr(0, key.find(' ')) == keys[index]);
BOOST_TEST(exercise.variants[index].total_pages > 0U);
BOOST_TEST(exercise.variants[index].measure_count > 0U);
BOOST_REQUIRE_EQUAL(exercise.parts.size(), 3U);
auto parts = vamp_zero::practice_part_names();
for (auto index = std::size_t{}; index < parts.size(); ++index) {
BOOST_TEST(exercise.parts[index].part == parts[index]);
BOOST_TEST(exercise.parts[index].total_pages > 0U);
BOOST_TEST(exercise.parts[index].measure_count > 0U);
if (iteration == 0U) {
auto const &variant = exercise.variants[index];
auto const &practice_part = exercise.parts[index];
io.restart();
auto loaded = boost::asio::co_spawn(io, vamp_zero::async_load_score(io.get_executor(), config, {vamp_zero::score_category::PRACTICE, variant.id}, VAMP_TEST_ASSETS_DIR), boost::asio::use_future);
auto loaded = boost::asio::co_spawn(io, vamp_zero::async_load_score(io.get_executor(), config, {vamp_zero::score_category::PRACTICE, practice_part.id}, VAMP_TEST_ASSETS_DIR), boost::asio::use_future);
io.run();
auto score = loaded.get();
BOOST_REQUIRE_MESSAGE(score, (score ? "" : score.error().describe()));
BOOST_TEST(score->title == exercise.title);
BOOST_TEST(score->composer == exercise.composer);
BOOST_TEST(score->key_signature == variant.key_signature);
BOOST_TEST(score->authored_key_signature == exercise.authored_key_signature);
BOOST_TEST(score->measure_count == variant.measure_count);
BOOST_TEST(score->pages.size() == variant.total_pages);
BOOST_TEST(score->source_path == variant.source_path);
BOOST_TEST(score->key_signature == practice_part.key_signature);
BOOST_TEST(score->part == practice_part.part);
BOOST_TEST(score->measure_count == practice_part.measure_count);
BOOST_TEST(score->pages.size() == practice_part.total_pages);
BOOST_TEST(score->source_path == practice_part.source_path);
}
}
}
@@ -425,8 +421,8 @@ BOOST_AUTO_TEST_CASE(numbered_practice_paths_keep_duplicate_titles_separate_and_
auto config = test_database_config(root / "vamp.db");
std::filesystem::create_directories(assets / "songs");
for (auto name : {"010", "002", "001", "127"}) {
for (auto key : vamp_zero::practice_key_names()) {
write_text(assets / "practice" / name / (std::string{key} + ".json"), score_asset_json("Shared Title", std::string{key}));
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}));
}
}
BOOST_REQUIRE(vamp_test::build_catalogue(assets, config.path));
@@ -448,25 +444,21 @@ BOOST_AUTO_TEST_CASE(numbered_practice_paths_keep_duplicate_titles_separate_and_
BOOST_REQUIRE(vamp_test::build_catalogue(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(), 48U);
BOOST_TEST(!has_value(*rows, 0U, "002-C"));
BOOST_TEST(has_value(*rows, 0U, "099-C"));
BOOST_TEST(has_value(*rows, 0U, "010-C"));
BOOST_TEST(has_value(*rows, 0U, "001-C"));
BOOST_TEST(has_value(*rows, 0U, "127-C"));
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"));
std::filesystem::remove_all(root);
}
BOOST_AUTO_TEST_CASE(keyless_practice_variants_display_their_transposition_reference) {
auto source = boost::json::parse(score_asset_json("Keyless", "No key"));
auto &music = source.as_object()["music"].as_object();
music["metadata"].as_object()["variant_tonic"] = "D";
music["staves"].as_array().front().as_object()["arrangement_part_type"] = "practice";
auto document = vamp_zero::parse_score_document(boost::json::serialize(source), "d.json");
BOOST_AUTO_TEST_CASE(keyless_scores_keep_their_authored_key) {
auto source = score_asset_json("Keyless", "No key", "Vocal");
auto document = vamp_zero::parse_score_document(source, "vocal.json");
BOOST_REQUIRE(document);
BOOST_TEST(document->key_signature == "D (no key)");
auto retained = boost::json::parse(document->source_json);
BOOST_TEST(retained.as_object()["music"].as_object()["metadata"].as_object()["key_signature"].as_string() == "No key");
BOOST_TEST(document->key_signature == "No key");
BOOST_TEST(document->part == "Vocal");
}
BOOST_AUTO_TEST_CASE(integer_parsing_checks_range_sign_and_entire_input) {