@@ -0,0 +1,20 @@
|
||||
add_executable(product-vamp-test
|
||||
test.cpp
|
||||
)
|
||||
target_include_directories(product-vamp-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_link_libraries(product-vamp-test PRIVATE
|
||||
vamp-zero-core
|
||||
)
|
||||
target_compile_features(product-vamp-test PRIVATE cxx_std_23)
|
||||
add_test(NAME product-vamp COMMAND product-vamp-test)
|
||||
set_tests_properties(product-vamp PROPERTIES TIMEOUT 60)
|
||||
add_custom_target(check
|
||||
COMMAND ${CMAKE_CTEST_COMMAND}
|
||||
--test-dir ${CMAKE_BINARY_DIR}
|
||||
-R ^product-vamp$
|
||||
--output-on-failure
|
||||
--timeout 60
|
||||
USES_TERMINAL
|
||||
)
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
#include <test.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <vamp_zero/library.hpp>
|
||||
#include <vamp_zero/score.hpp>
|
||||
#include <vamp_zero/types.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
auto pitch(std::char_t letter, std::string accidental, std::int32_t octave = 4) -> vamp_zero::score_pitch {
|
||||
return {
|
||||
.letter = letter,
|
||||
.octave = octave,
|
||||
.accidental = std::move(accidental),
|
||||
};
|
||||
}
|
||||
|
||||
auto note(
|
||||
std::initializer_list<vamp_zero::score_pitch> pitches,
|
||||
std::bool_t tie_from_previous = false,
|
||||
std::string notehead = {},
|
||||
std::bool_t hidden = false
|
||||
) -> vamp_zero::score_note {
|
||||
return {
|
||||
.kind = "note",
|
||||
.hidden = hidden,
|
||||
.duration_ticks = 4096,
|
||||
.display_ticks = 4096,
|
||||
.notehead = std::move(notehead),
|
||||
.tie_from_previous = tie_from_previous,
|
||||
.pitches = std::vector<vamp_zero::score_pitch>{pitches},
|
||||
};
|
||||
}
|
||||
|
||||
auto rest(std::initializer_list<vamp_zero::score_pitch> pitches) -> vamp_zero::score_note {
|
||||
return {
|
||||
.kind = "rest",
|
||||
.duration_ticks = 4096,
|
||||
.display_ticks = 4096,
|
||||
.pitches = std::vector<vamp_zero::score_pitch>{pitches},
|
||||
};
|
||||
}
|
||||
|
||||
auto mark_name(vamp_zero::score_accidental_mark mark) -> std::string {
|
||||
switch (mark) {
|
||||
case vamp_zero::score_accidental_mark::NONE:
|
||||
return "none";
|
||||
case vamp_zero::score_accidental_mark::FLAT:
|
||||
return "flat";
|
||||
case vamp_zero::score_accidental_mark::NATURAL:
|
||||
return "natural";
|
||||
case vamp_zero::score_accidental_mark::SHARP:
|
||||
return "sharp";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto mark_names(std::vector<vamp_zero::score_accidental_mark> const& marks) -> std::vector<std::string> {
|
||||
auto names = std::vector<std::string>{};
|
||||
names.reserve(marks.size());
|
||||
for (auto mark : marks) {
|
||||
names.push_back(mark_name(mark));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
void expect_marks(std::vector<vamp_zero::score_accidental_mark> const& marks, std::initializer_list<std::string_view> expected) {
|
||||
auto actual_names = mark_names(marks);
|
||||
BOOST_REQUIRE_EQUAL(actual_names.size(), expected.size());
|
||||
auto index = std::size_t{};
|
||||
for (auto name : expected) {
|
||||
BOOST_TEST(actual_names[index] == std::string{name});
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(score_parser_preserves_notation_preamble) {
|
||||
auto parsed = vamp_zero::parse_score_document(R"json(
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"title": "Standard Tune",
|
||||
"composer": "Composer",
|
||||
"key_signature": "Eb",
|
||||
"time_signature": "3/4"
|
||||
},
|
||||
"staves": [
|
||||
{
|
||||
"id": "staff-lead",
|
||||
"name": "Lead",
|
||||
"clef": "treble",
|
||||
"role": "lead",
|
||||
"measures": [
|
||||
{
|
||||
"id": "measure-1",
|
||||
"time_signature": "3/4",
|
||||
"key_signature": "Eb",
|
||||
"voices": [
|
||||
{
|
||||
"id": "voice-1",
|
||||
"notes": [
|
||||
{
|
||||
"id": "note-1",
|
||||
"kind": "note",
|
||||
"duration_ticks": 4096,
|
||||
"pitches": [
|
||||
{
|
||||
"letter": "b",
|
||||
"octave": 4,
|
||||
"accidental": "b"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
)json", "standard-tune.json");
|
||||
|
||||
BOOST_REQUIRE(static_cast<std::bool_t>(parsed));
|
||||
BOOST_TEST(parsed->title == "Standard Tune");
|
||||
BOOST_TEST(parsed->composer == "Composer");
|
||||
BOOST_TEST(parsed->key_signature == "Eb");
|
||||
BOOST_TEST(parsed->time_signature == "3/4");
|
||||
BOOST_TEST(parsed->clef == "treble");
|
||||
BOOST_TEST(parsed->part == "Lead");
|
||||
BOOST_REQUIRE_EQUAL(parsed->measures.size(), 1U);
|
||||
BOOST_TEST(parsed->measures.front().key_signature == "Eb");
|
||||
BOOST_TEST(parsed->measures.front().time_signature == "3/4");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(accidentals_are_suppressed_when_the_key_signature_already_covers_the_pitch) {
|
||||
auto c_major = vamp_zero::score_measure{
|
||||
.key_signature = "C",
|
||||
.notes = {
|
||||
note({pitch('c', "n")}),
|
||||
note({pitch('d', "n")}),
|
||||
note({pitch('e', "n")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(c_major, "C"), {"none", "none", "none"});
|
||||
|
||||
auto f_major = vamp_zero::score_measure{
|
||||
.key_signature = "F",
|
||||
.notes = {
|
||||
note({pitch('b', "b")}),
|
||||
note({pitch('e', "n")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(f_major, "C"), {"none", "none"});
|
||||
|
||||
auto d_major = vamp_zero::score_measure{
|
||||
.key_signature = "D",
|
||||
.notes = {
|
||||
note({pitch('f', "#")}),
|
||||
note({pitch('c', "#")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(d_major, "C"), {"none", "none"});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(accidentals_follow_measure_state_after_the_first_printed_change) {
|
||||
auto measure = vamp_zero::score_measure{
|
||||
.key_signature = "F",
|
||||
.notes = {
|
||||
note({pitch('b', "b")}),
|
||||
note({pitch('b', "n")}),
|
||||
note({pitch('b', "n")}),
|
||||
note({pitch('b', "b")}),
|
||||
note({pitch('b', "b")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(measure, "C"), {"none", "natural", "none", "flat", "none"});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(measure_state_resets_for_each_measure) {
|
||||
auto first = vamp_zero::score_measure{
|
||||
.key_signature = "C",
|
||||
.notes = {
|
||||
note({pitch('f', "#")}),
|
||||
note({pitch('f', "#")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(first, "C"), {"sharp", "none"});
|
||||
|
||||
auto second = vamp_zero::score_measure{
|
||||
.key_signature = "C",
|
||||
.notes = {
|
||||
note({pitch('f', "#")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(second, "C"), {"sharp"});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tied_rests_hidden_and_slash_notes_do_not_emit_accidentals) {
|
||||
auto measure = vamp_zero::score_measure{
|
||||
.key_signature = "C",
|
||||
.notes = {
|
||||
note({pitch('f', "#")}, true),
|
||||
rest({pitch('b', "b")}),
|
||||
note({pitch('c', "#")}, false, "slash"),
|
||||
note({pitch('d', "#")}, false, {}, true),
|
||||
note({pitch('e', "b")}),
|
||||
},
|
||||
};
|
||||
expect_marks(vamp_zero::notated_accidentals_for_measure(measure, "C"), {"none", "flat"});
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define BOOST_TEST_MODULE product_vamp
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
Reference in New Issue
Block a user