From 42234da6500e251b7461604e1e8ea3e9706f5fdc Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sat, 28 Mar 2020 00:26:50 +0100 Subject: [PATCH] Add tests for the new parser --- tests/ParsingT.cpp | 288 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) diff --git a/tests/ParsingT.cpp b/tests/ParsingT.cpp index cadcaa8b..968f1e57 100644 --- a/tests/ParsingT.cpp +++ b/tests/ParsingT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/SfzHelpers.h" +#include "sfizz/parser/Parser.h" #include #include "catch2/catch.hpp" +#include "absl/strings/string_view.h" using namespace Catch::literals; void includeTest(const std::string& line, const std::string& fileName) @@ -126,3 +128,289 @@ TEST_CASE("[Parsing] Member") memberTest("sample=..\\Samples\\SMD Cymbals Stereo (Samples)\\Hi-Hat (Samples)\\01 Hat Tight 1\\RR1\\09_Hat_Tight_Cnt_RR1.wav", "sample", "..\\Samples\\SMD Cymbals Stereo (Samples)\\Hi-Hat (Samples)\\01 Hat Tight 1\\RR1\\09_Hat_Tight_Cnt_RR1.wav"); memberTest("sample=..\\G&S CW-Drum Kit-1\\SnareFX\\SNR-OFF-V08-CustomWorks-6x13.wav", "sample", "..\\G&S CW-Drum Kit-1\\SnareFX\\SNR-OFF-V08-CustomWorks-6x13.wav"); } + +// New parser + +struct ParsingMocker: sfz::Parser::Listener +{ + void onParseBegin() override + { + beginnings++; + } + void onParseEnd() override + { + endings++; + } + void onParseHeader(const sfz::SourceRange&, const std::string& header) override + { + headers.push_back(header); + } + void onParseOpcode(const sfz::SourceRange&, const sfz::SourceRange&, const std::string& name, const std::string& value) override + { + opcodes.emplace_back(name, value); + } + void onParseError(const sfz::SourceRange& range, const std::string&) override + { + errors.push_back(range); + } + void onParseWarning(const sfz::SourceRange& range, const std::string&) override + { + warnings.push_back(range); + } + // high-level parsing + void onParseFullBlock(const std::string& header, const std::vector& opcodes) override + { + fullBlockHeaders.push_back(header); + fullBlockMembers.push_back(opcodes); + } + + int beginnings { 0 }; + int endings { 0 }; + std::vector errors; + std::vector warnings; + std::vector opcodes; + std::vector headers; + std::vector fullBlockHeaders; + std::vector> fullBlockMembers; +}; + +TEST_CASE("[Parsing] Empty") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(""); + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes.empty()); + REQUIRE(mock.headers.empty()); + REQUIRE(mock.fullBlockHeaders.empty()); + REQUIRE(mock.fullBlockMembers.empty()); +} + +static const absl::string_view emptySfz { +R"( + +)" +}; + +TEST_CASE("[Parsing] Empty2") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(emptySfz); + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes.empty()); + REQUIRE(mock.headers.empty()); + REQUIRE(mock.fullBlockHeaders.empty()); + REQUIRE(mock.fullBlockMembers.empty()); +} + +namespace sfz{ +bool operator==(const Opcode& lhs, const Opcode& rhs) +{ + return (lhs.opcode == rhs.opcode) && (lhs.value == rhs.value); +} +} + +TEST_CASE("[Parsing] Jpcima good region") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(R"( + sample=*silence key=69 +sample=My Directory/My Wave.wav // path with spaces and a comment +sample=My Directory/My Wave.wav key=69 // path with spaces, and other opcode following +)"); + std::vector> expectedMembers = { + {{"sample", "*silence"}, {"key", "69"}, {"sample", "My Directory/My Wave.wav"}, {"sample", "My Directory/My Wave.wav"}, {"key", "69"}} + }; + std::vector expectedHeaders = { + "region" + }; + std::vector expectedOpcodes; + + for (auto& members: expectedMembers) + for (auto& opcode: members) + expectedOpcodes.push_back(opcode); + + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes == expectedOpcodes); + REQUIRE(mock.headers == expectedHeaders); + REQUIRE(mock.fullBlockHeaders == expectedHeaders); + REQUIRE(mock.fullBlockMembers == expectedMembers); +} + +void memberTestNew(absl::string_view member, absl::string_view opcode, absl::string_view value) +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(absl::StrCat(" ", member)); + REQUIRE(mock.opcodes.size() == 1); + REQUIRE(mock.headers.size() == 1); + REQUIRE(mock.fullBlockHeaders.size() == 1); + REQUIRE(mock.fullBlockMembers.size() == 1); + REQUIRE(mock.headers[0] == "region"); + REQUIRE(mock.opcodes[0].opcode == opcode); + REQUIRE(mock.opcodes[0].value == value); + REQUIRE(mock.fullBlockHeaders[0] == "region"); + REQUIRE(mock.fullBlockMembers[0][0].opcode == opcode); + REQUIRE(mock.fullBlockMembers[0][0].value == value); +} + +TEST_CASE("[Parsing] Members (new parser)") +{ + memberTestNew("param=value", "param", "value"); + memberTestNew("param=113", "param", "113"); + memberTestNew("param1=value", "param1", "value"); + memberTestNew("param_1=value", "param_1", "value"); + memberTestNew("param_1=value", "param_1", "value"); + memberTestNew("ampeg_sustain_oncc74=-100", "ampeg_sustain_oncc74", "-100"); + memberTestNew("lorand=0.750", "lorand", "0.750"); + memberTestNew("sample=value", "sample", "value"); + memberTestNew("sample=value-()*", "sample", "value-()*"); + memberTestNew("sample=../sample.wav", "sample", "../sample.wav"); + memberTestNew("sample=..\\sample.wav", "sample", "..\\sample.wav"); + memberTestNew("sample=subdir\\subdir\\sample.wav", "sample", "subdir\\subdir\\sample.wav"); + memberTestNew("sample=subdir/subdir/sample.wav", "sample", "subdir/subdir/sample.wav"); + memberTestNew("sample=subdir_underscore\\sample.wav", "sample", "subdir_underscore\\sample.wav"); + memberTestNew("sample=subdir space\\sample.wav", "sample", "subdir space\\sample.wav"); + memberTestNew("sample=..\\Samples\\pizz\\a0_vl3_rr3.wav", "sample", "..\\Samples\\pizz\\a0_vl3_rr3.wav"); + memberTestNew("sample=..\\Samples\\SMD Cymbals Stereo (Samples)\\Hi-Hat (Samples)\\01 Hat Tight 1\\RR1\\09_Hat_Tight_Cnt_RR1.wav", "sample", "..\\Samples\\SMD Cymbals Stereo (Samples)\\Hi-Hat (Samples)\\01 Hat Tight 1\\RR1\\09_Hat_Tight_Cnt_RR1.wav"); + memberTestNew("sample=..\\G&S CW-Drum Kit-1\\SnareFX\\SNR-OFF-V08-CustomWorks-6x13.wav", "sample", "..\\G&S CW-Drum Kit-1\\SnareFX\\SNR-OFF-V08-CustomWorks-6x13.wav"); +} + +TEST_CASE("[Parsing] bad headers") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString( +R"(<> + dummy_member=no +)" + ); + REQUIRE(mock.errors.size() == 2); + REQUIRE(mock.errors[0].start.lineNumber == 0); + REQUIRE(mock.errors[0].start.columnNumber == 0); + REQUIRE(mock.errors[1].start.lineNumber == 1); + REQUIRE(mock.errors[1].start.columnNumber == 0); + REQUIRE(mock.errors[0].end.lineNumber == 0); + REQUIRE(mock.errors[0].end.columnNumber == 2); + REQUIRE(mock.errors[1].end.lineNumber == 1); + REQUIRE(mock.errors[1].end.columnNumber == 7); +} + +void defineTestNew(const std::string& directive, const std::string& variable, const std::string& value) +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(directive); + const auto defines = parser.getDefines(); + REQUIRE(defines.contains(variable)); + REQUIRE(defines.at(variable) == value); +} + +TEST_CASE("[Parsing] #define (new parser)") +{ + defineTestNew("#define $empty ", "empty", ""); + defineTestNew("#define $number 1", "number", "1"); + defineTestNew("#define $letters QWERasdf", "letters", "QWERasdf"); + defineTestNew("#define $alphanum asr1t44", "alphanum", "asr1t44"); + defineTestNew("#define $whitespace asr1t44 ", "whitespace", "asr1t44"); + // The new parser does greedy matching + // defineTestNew("#define $lazyMatching matched bfasd ", "lazyMatching", "matched"); + defineTestNew("#define $stircut -12", "stircut", "-12"); + defineTestNew("#define $_ht_under_score_ 3fd", "_ht_under_score_", "3fd"); + defineTestNew("#define $ht_under_score 3fd", "ht_under_score", "3fd"); +} + +TEST_CASE("[Parsing] Malformed includes") +{ + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString(R"(#include "MyFileWhichDoesNotExist1.sfz +#include MyFileWhichDoesNotExist1.sfz)"); + REQUIRE(mock.errors.size() == 2); + REQUIRE(mock.errors[0].start.lineNumber == 0); + REQUIRE(mock.errors[0].start.columnNumber == 0); + REQUIRE(mock.errors[1].start.lineNumber == 1); + REQUIRE(mock.errors[1].start.columnNumber == 0); + REQUIRE(mock.errors[0].end.lineNumber == 0); + REQUIRE(mock.errors[0].end.columnNumber == 38); + REQUIRE(mock.errors[1].end.lineNumber == 1); + REQUIRE(mock.errors[1].end.columnNumber == 9); +} + +TEST_CASE("[Parsing] Headers (new parser)") +{ + SECTION("Basic header match") + { + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("
param1=value1 param2=value2 "); + std::vector> expectedMembers = { + {{"param1", "value1"}, {"param2", "value2"}}, + {} + }; + std::vector expectedHeaders = { + "header", "next" + }; + std::vector expectedOpcodes; + + for (auto& members: expectedMembers) + for (auto& opcode: members) + expectedOpcodes.push_back(opcode); + + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes == expectedOpcodes); + REQUIRE(mock.headers == expectedHeaders); + REQUIRE(mock.fullBlockHeaders == expectedHeaders); + REQUIRE(mock.fullBlockMembers == expectedMembers); + } + + SECTION("EOL header match") + { + sfz::Parser parser; + ParsingMocker mock; + parser.setListener(&mock); + parser.parseString("
param1=value1 param2=value2"); + std::vector> expectedMembers = { + {{"param1", "value1"}, {"param2", "value2"}} + }; + std::vector expectedHeaders = { + "header" + }; + std::vector expectedOpcodes; + + for (auto& members: expectedMembers) + for (auto& opcode: members) + expectedOpcodes.push_back(opcode); + + REQUIRE(mock.beginnings == 1); + REQUIRE(mock.endings == 1); + REQUIRE(mock.errors.empty()); + REQUIRE(mock.warnings.empty()); + REQUIRE(mock.opcodes == expectedOpcodes); + REQUIRE(mock.headers == expectedHeaders); + REQUIRE(mock.fullBlockHeaders == expectedHeaders); + REQUIRE(mock.fullBlockMembers == expectedMembers); + } +}