From 34eb56ac5070cdce7aa133883b158aa3574e9325 Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Wed, 21 Sep 2022 16:40:40 +0200 Subject: [PATCH] Removed unused tests --- README.md | 4 - examples/.tests_cache/factorial.mq.json | 2 +- scripts/test.mk | 19 +- src/tests/context.cc | 52 --- src/tests/environment.cc | 61 ---- src/tests/interpreter.cc | 423 ------------------------ src/tests/lex.cc | 216 ------------ src/tests/main.cc | 12 - src/tests/number.cc | 98 ------ src/tests/parser.cc | 224 ------------- src/tests/unicode.cc | 38 --- src/tests/value.cc | 188 ----------- 12 files changed, 2 insertions(+), 1335 deletions(-) delete mode 100644 src/tests/context.cc delete mode 100644 src/tests/environment.cc delete mode 100644 src/tests/interpreter.cc delete mode 100644 src/tests/lex.cc delete mode 100644 src/tests/main.cc delete mode 100644 src/tests/number.cc delete mode 100644 src/tests/parser.cc delete mode 100644 src/tests/unicode.cc delete mode 100644 src/tests/value.cc diff --git a/README.md b/README.md index cec9d2b..d4f73b9 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,6 @@ $ make bin/musique ### Testowanie - `make test` - Uruchom wszystkie dostępne testy automatyczne -- `make unit-tests` - Uruchamia testy jednostkowe interpretera -- `make unit-test-coverage` - Uruchamia raport pokrycia kodu przez testy jednostkowe - `scripts/test.py test examples` - Uruchamia testy zachowań przykładów - `scripts/test.py record examples` - Nagrywa testy zachowań przykładów @@ -69,7 +67,6 @@ $ make bin/musique ``` . ├── bin Miejsce produkcji plików wykonywalnych -├── coverage ├── doc Dokumentacja języka, interpretera │   └── build Miejsce produkcji dokumentacji ├── editor Pluginy do edytorów dodające wsparcie dla języka @@ -79,7 +76,6 @@ $ make bin/musique └── include Główny katalog z plikami nagłówkowymi ├── scripts Skrypty wspierające budowanie i tworzenie └── src Główny katalog z plikami źródłowymi - └── tests Katalog z testami jednostkowymi ``` ## Kolorowanie składni diff --git a/examples/.tests_cache/factorial.mq.json b/examples/.tests_cache/factorial.mq.json index 3547c27..05a9aac 100644 --- a/examples/.tests_cache/factorial.mq.json +++ b/examples/.tests_cache/factorial.mq.json @@ -1,6 +1,6 @@ { "returncode": 0, - "stdout": "1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n3628800\n1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n3628800\n", + "stdout": "1\n1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n1\n1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n1\n1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n", "stderr": "", "flags": [] } \ No newline at end of file diff --git a/scripts/test.mk b/scripts/test.mk index 7ee2e43..8e98711 100644 --- a/scripts/test.mk +++ b/scripts/test.mk @@ -11,23 +11,6 @@ Tests= \ Test_Obj=$(addprefix bin/debug/tests/,$(Tests)) -test: unit-tests +test: bin/debug/musique scripts/test.py test examples -unit-tests: bin/unit-tests - ./$< - -bin/unit-tests: $(Test_Obj) $(Debug_Obj) - @echo "CXX $@" - @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(DEBUG_FLAGS) -o $@ $^ - -unit-test-coverage: - @which gcov >/dev/null || ( echo "[ERROR] gcov is required for test coverage report"; false ) - @which gcovr >/dev/null || ( echo "[ERROR] gcovr is required for test coverage report"; false ) - CXXFLAGS=--coverage $(MAKE) bin/unit-tests -B - bin/unit-tests - rm -rf coverage - mkdir coverage - gcovr -e '.*\.hpp' -e 'src/tests/.*' -e 'src/pretty.cc' --html --html-details -o coverage/index.html - rm -rf bin/debug - xdg-open coverage/index.html diff --git a/src/tests/context.cc b/src/tests/context.cc deleted file mode 100644 index 1adb45d..0000000 --- a/src/tests/context.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -void test_seconds_compute( - unsigned bpm, - Number length, - float value, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Context const ctx { .bpm = bpm }; - auto const dur = ctx.length_to_duration(length); - - static_assert(std::same_as>, - "tests provided by this function expects Context::length_to_duration to return seconds"); - - expect(dur.count() == _f(value), sl); -} - -suite context_suite = [] { - "Context"_test = [] { - should("resolve note duration length to seconds") = [] { - test_seconds_compute(60, Number(2,1), 8); - test_seconds_compute(60, Number(1,1), 4); - test_seconds_compute(60, Number(1,2), 2); - test_seconds_compute(60, Number(1,4), 1); - - test_seconds_compute(96, Number(2,1), 5); - test_seconds_compute(96, Number(1,1), 2.5); - test_seconds_compute(96, Number(1,2), 1.25); - test_seconds_compute(96, Number(1,4), 0.625); - - test_seconds_compute(120, Number(2,1), 4); - test_seconds_compute(120, Number(1,1), 2); - test_seconds_compute(120, Number(1,2), 1); - test_seconds_compute(120, Number(1,4), 0.5); - }; - - should("fill notes with default context values") = [] { - Context ctx; - ctx.length = Number(1, 42); - ctx.octave = 8; - - expect(eq(ctx.fill({ .base = 0 }), Note { .base = 0, .octave = 8, .length = Number(1, 42) })); - expect(eq(ctx.fill({ .base = 0, .length = Number(4) }), Note { .base = 0, .octave = 8, .length = Number(4) })); - expect(eq(ctx.fill({ .base = 0, .octave = 1 }), Note { .base = 0, .octave = 1, .length = Number(1, 42) })); - expect(eq(ctx.fill({ .base = 0, .octave = 1, .length = Number(4) }), Note { .base = 0, .octave = 1, .length = Number(4) })); - }; - }; -}; diff --git a/src/tests/environment.cc b/src/tests/environment.cc deleted file mode 100644 index 66f9bd6..0000000 --- a/src/tests/environment.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -static void equals( - Value *received, - Value expected, - reflection::source_location sl = reflection::source_location::current()) -{ - expect(received != nullptr, sl) << "Value was not found"; - expect(eq(*received, expected), sl); -} - -suite environment_test = [] { - "Global enviroment exists"_test = [] { - expect(not bool(Env::global)) << "Before interpreter global environment does not exists"; - { - Interpreter i; - expect(bool(Env::global)) << "When interpreter exists global environment exists"; - } - expect(not bool(Env::global)) << "After interpreter destruction, global environment does not exists"; - }; - - "Environment scoping"_test = [] { - should("nested scoping preserve outer scope") = [] { - Interpreter i; - - i.env->force_define("x", Value::from(Number(10))); - i.env->force_define("y", Value::from(Number(20))); - - equals(i.env->find("x"), Value::from(Number(10))); - equals(i.env->find("y"), Value::from(Number(20))); - - i.enter_scope(); - { - i.env->force_define("x", Value::from(Number(30))); - equals(i.env->find("x"), Value::from(Number(30))); - equals(i.env->find("y"), Value::from(Number(20))); - } - i.leave_scope(); - - equals(i.env->find("x"), Value::from(Number(10))); - equals(i.env->find("y"), Value::from(Number(20))); - }; - - should("nested variables missing from outer scope") = [] { - Interpreter i; - - i.enter_scope(); - { - i.env->force_define("x", Value::from(Number(30))); - equals(i.env->find("x"), Value::from(Number(30))); - } - i.leave_scope(); - - expect(eq(i.env->find("x"), nullptr)); - }; - }; -}; diff --git a/src/tests/interpreter.cc b/src/tests/interpreter.cc deleted file mode 100644 index a08081b..0000000 --- a/src/tests/interpreter.cc +++ /dev/null @@ -1,423 +0,0 @@ -#include -#include - -#include -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -static auto capture_errors(auto lambda, reflection::source_location sl = reflection::source_location::current()) -{ - return [=] { - auto result = lambda(); - if (not result.has_value()) { - expect(false, sl) << "failed test with error: " << result.error(); - return false; - } - return true; - }; -} - -auto evaluates_to(Value value, std::string_view source_code, reflection::source_location sl = reflection::source_location::current()) -{ - return capture_errors([=]() -> Result { - Interpreter interpreter; - auto result = Try(interpreter.eval(Try(Parser::parse(source_code, "test")))); - expect(eq(result, value), sl); - return {}; - }, sl)(); -} - -template -void expect_alternative( - auto const& variant, - boost::ut::reflection::source_location sl = boost::ut::reflection::source_location::current()) -{ - using namespace boost::ut; - expect(std::holds_alternative(variant), sl) << "Expected to hold alternative but failed"; -} - -suite intepreter_test = [] { - "Interpreter"_test = [] { - should("evaluate literals") = [] { - evaluates_to(Value::from(false), "false"); - evaluates_to(Value::from(true), "true"); - evaluates_to(Value::from(Number(10)), "10"); - evaluates_to(Value{}, "nil"); - evaluates_to(Value::from("foo"), "'foo"); - }; - - should("evaluate arithmetic") = [] { - evaluates_to(Value::from(Number(10)), "5 + 3 + 2"); - evaluates_to(Value::from(Number(25)), "5 * (3 + 2)"); - evaluates_to(Value::from(Number(1, 2)), "1 / 2"); - evaluates_to(Value::from(Number(-10)), "10 - 20"); - }; - - should("call builtin functions") = [] { - evaluates_to(Value::from("nil"), "typeof nil"); - evaluates_to(Value::from("number"), "typeof 100"); - }; - - should("allows only for calling which is callable") = [] { - evaluates_to(Value::from(Number(0)), "[i|i] 0"); - { - Interpreter i; - { - auto result = Parser::parse("10 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); - expect(!result.has_value()) << "Expected code to have failed"; - expect_alternative(result.error().details); - } - { - i.env->force_define("call_me", Value::from(Number(10))); - auto result = Parser::parse("call_me 20", "test").and_then([&](Ast &&ast) { return i.eval(std::move(ast)); }); - expect(!result.has_value()) << "Expected code to have failed"; - expect_alternative(result.error().details); - } - } - }; - - should("allow for value (in)equality comparisons") = [] { - evaluates_to(Value::from(true), "nil == nil"); - evaluates_to(Value::from(false), "nil != nil"); - - evaluates_to(Value::from(true), "true == true"); - evaluates_to(Value::from(false), "true != true"); - evaluates_to(Value::from(false), "true == false"); - evaluates_to(Value::from(true), "true != false"); - - evaluates_to(Value::from(true), "0 == 0"); - evaluates_to(Value::from(false), "0 != 0"); - evaluates_to(Value::from(true), "1 != 0"); - evaluates_to(Value::from(false), "1 == 0"); - }; - - should("allow for value ordering comparisons") = [] { - evaluates_to(Value::from(false), "true < true"); - evaluates_to(Value::from(true), "true <= true"); - evaluates_to(Value::from(true), "false < true"); - evaluates_to(Value::from(false), "false > true"); - - evaluates_to(Value::from(false), "0 < 0"); - evaluates_to(Value::from(true), "0 <= 0"); - evaluates_to(Value::from(true), "1 < 2"); - evaluates_to(Value::from(false), "1 > 2"); - - evaluates_to(Value::from(true), "c == c"); - evaluates_to(Value::from(false), "c != c"); - - evaluates_to(Value::from(true), "c < d"); - evaluates_to(Value::from(true), "c != (c 4)"); - evaluates_to(Value::from(true), "(c 4) == (c 4)"); - evaluates_to(Value::from(true), "(c 3) != (c 4)"); - evaluates_to(Value::from(true), "(c 3) < (c 4)"); - evaluates_to(Value::from(true), "((c+12) 3) == (c 4)"); - - // Value is partially ordered, ensure that different types - // are always not equal and not ordered - evaluates_to(Value::from(false), "0 < c"); - evaluates_to(Value::from(false), "0 > c"); - evaluates_to(Value::from(false), "0 <= c"); - evaluates_to(Value::from(false), "0 >= c"); - evaluates_to(Value::from(true), "0 != c"); - }; - - // Added to explicitly test against bug that was in old implementation of enviroments. - // Previously this test would segfault - should("allow assigning result of function calls to a variable") = [] { - evaluates_to(Value::from(Number(42)), "var x = [i|i] 42; x"); - }; - - // Added to explicitly test against bug - // Previously this test would return 10 - should("respect parens in block") = [] { - evaluates_to(Value::from(Number(42)), "[(10;42)].0"); - }; - - should("allow modifying declared variable") = [] { - evaluates_to(Value::from(Number(43)), "var x = 42; x = 43; 10; x"); - }; - - should("support array programming") = [] { - evaluates_to(Value::from(Array { .elements = {{ - Value::from(Number(2)), - Value::from(Number(4)), - Value::from(Number(6)), - Value::from(Number(8)) - }}}), "2 * [1;2;3;4]"); - - evaluates_to(Value::from(Array { .elements = {{ - Value::from(Note { .base = 1 }), - Value::from(Note { .base = 2 }), - Value::from(Note { .base = 3 }), - Value::from(Note { .base = 4 }) - }}}), "c + [1;2;3;4]"); - - evaluates_to(Value::from(Array { .elements = {{ - Value::from(Number(2)), - Value::from(Number(4)), - Value::from(Number(6)), - Value::from(Number(8)) - }}}), "[1;2;3;4] * 2"); - - evaluates_to(Value::from(Array { .elements = {{ - Value::from(Note { .base = 1 }), - Value::from(Note { .base = 2 }), - Value::from(Note { .base = 3 }), - Value::from(Note { .base = 4 }) - }}}), "[1;2;3;4] + c"); - }; - - should("support number - music operations") = [] { - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 3 }, Note { .base = 6 } } }), - "c#47 - 1"); - - evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 }, Note { .base = 6 }, Note { .base = 9 } } }), - "c47 + 2"); - - evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 }, Note { .base = 6 }, Note { .base = 9 } } }), - "2 + c47"); - }; - - should("support direct chord creation") = [] { - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), - "chord [c;e;g]"); - - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), - "chord c e g"); - - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 }, Note { .base = 4 }, Note { .base = 7 } } }), - "chord [c;e] g"); - }; - - should("support eager array creation") = [] { - evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), - "flat 10 20 30"); - - evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), - "flat [10;20] 30"); - - evaluates_to(Value::from(Array { .elements = {{ Value::from(Number(10)), Value::from(Number(20)), Value::from(Number(30)) }}}), - "flat (flat 10 20) 30"); - }; - - should("support indexing") = [] { - evaluates_to(Value::from(Number(10)), "[5;10;15].1"); - evaluates_to(Value::from(Number(10)), "(flat 5 10 15).1"); - }; - - should("support size") = [] { - evaluates_to(Value::from(Number(3)), "len [5;10;15]"); - evaluates_to(Value::from(Number(3)), "len (flat 5 10 15)"); - }; - - should("support call like octave and len setting") = [] { - evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5, .length = Number(10) }}}}), "c 5 10"); - evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5, .length = Number(10) }}}}), "(c 4 8) 5 10"); - evaluates_to(Value::from(Chord { .notes = {{ Note { .base = 0, .octave = 5 }}}}), "c 5"); - }; - }; - - "Interpreter boolean operators"_test = [] { - should("Properly support 'and' truth table") = [] { - evaluates_to(Value::from(false), "false and false"); - evaluates_to(Value::from(false), "true and false"); - evaluates_to(Value::from(false), "false and true"); - evaluates_to(Value::from(true), "true and true"); - - evaluates_to(Value::from(Number(0)), "0 and 0"); - evaluates_to(Value::from(Number(0)), "0 and 1"); - evaluates_to(Value::from(Number(0)), "1 and 0"); - evaluates_to(Value::from(Number(1)), "1 and 1"); - evaluates_to(Value::from(Number(42)), "1 and 42"); - }; - - should("Properly support 'or' truth table") = [] { - evaluates_to(Value::from(false), "false or false"); - evaluates_to(Value::from(true), "true or false"); - evaluates_to(Value::from(true), "false or true"); - evaluates_to(Value::from(true), "true or true"); - - evaluates_to(Value::from(Number(0)), "0 or 0"); - evaluates_to(Value::from(Number(1)), "0 or 1"); - evaluates_to(Value::from(Number(1)), "1 or 0"); - evaluates_to(Value::from(Number(1)), "1 or 1"); - evaluates_to(Value::from(Number(1)), "1 or 42"); - evaluates_to(Value::from(Number(42)), "42 or 1"); - }; - - // TODO Implement this test - // Currently not implemented due to interpeter inability to accept - // stateful intrinsics. Probably needs to be solved with using in-language - // variable like "__called_times_" or using names that cannot be variables - // like " ". But I need some time to think about that - skip / should("Short circuit on 'and' and 'or' operators") = [] { - }; - }; - - "Interpreter's default builtins"_test = [] { - should("Conditional execution with 'if'") = [] { - evaluates_to(Value::from(Number(10)), "if true [10] [20]"); - evaluates_to(Value::from(Number(20)), "if false [10] [20]"); - evaluates_to(Value{}, "if false [10]"); - }; - - should("Permutations generation with 'permute'") = [] { - std::vector src(5); - std::iota(src.begin(), src.end(), 0); - - auto exp = Value::from(Array{}); - exp.array.elements.resize(src.size()); - - for (bool quit = false; !quit;) { - std::stringstream ss; - ss << "permute "; - std::copy(src.begin(), src.end(), std::ostream_iterator(ss, " ")); - - quit = not std::next_permutation(src.begin(), src.end()); - - std::transform(src.begin(), src.end(), exp.array.elements.begin(), [](int x) { - return Value::from(Number(x)); - }); - - auto str = ss.str(); - if (not evaluates_to(exp, str)) - quit = true; - }; - }; - - should("Update value of array with 'update'") = [] { - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), Value::from(Number(10)), Value::from(Number(3)) - }}), "update [1;2;3] 1 10"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), Value::from(Number(10)), Value::from(Number(3)) - }}), "update (flat 1 2 3) 1 10"); - }; - - should("Support 'reverse' operation") = [] { - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), Value::from(Number(2)), Value::from(Number(3)) - }}), "reverse 3 2 1"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), Value::from(Number(2)), Value::from(Number(3)) - }}), "reverse (flat 3 2) 1"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), Value::from(Number(2)), Value::from(Number(3)) - }}), "reverse [3;2] 1"); - }; - - should("Support 'min' operation") = [] { - evaluates_to(Value::from(Number(10)), "min 10 20"); - evaluates_to(Value::from(Number(10)), "min 20 10"); - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 } } }), "min c d"); - evaluates_to(Value::from(Chord { .notes = { Note { .base = 0 } } }), "min d c"); - }; - - should("Support 'max' operation") = [] { - evaluates_to(Value::from(Number(20)), "max 10 20"); - evaluates_to(Value::from(Number(20)), "max 20 10"); - evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 } } }), "max c d"); - evaluates_to(Value::from(Chord { .notes = { Note { .base = 2 } } }), "max d c"); - }; - - should("Support 'rotate' operation") = [] { - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - }}), "rotate 0 [1;2;3;4;5]"); - - should("Support 'rotate' to right via negative rotation index") = [] { - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - }}), "rotate (0-1) [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - }}), "rotate (0-2) [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - }}), "rotate (0-3) [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - }}), "rotate (0-4) [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - }}), "rotate (0-5) [1;2;3;4;5]"); - }; - }; - - should("Support 'rotate' to left via positive rotation index") = [] { - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - }}), "rotate 1 [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - }}), "rotate 2 [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(4)), - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - }}), "rotate 3 [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(5)), - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - }}), "rotate 4 [1;2;3;4;5]"); - - evaluates_to(Value::from(Array { .elements = { - Value::from(Number(1)), - Value::from(Number(2)), - Value::from(Number(3)), - Value::from(Number(4)), - Value::from(Number(5)), - }}), "rotate 5 [1;2;3;4;5]"); - }; - }; -}; diff --git a/src/tests/lex.cc b/src/tests/lex.cc deleted file mode 100644 index 0971333..0000000 --- a/src/tests/lex.cc +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include - -using namespace boost::ut; - -enum class EOF_Status : bool -{ - Reject = false, - Accept = true, -}; - -template -static auto handle_end_of_file( - std::variant token_or_eof, - reflection::source_location const& sl) -{ - if constexpr (Expect_End_Of_File == EOF_Status::Accept) { - expect(std::holds_alternative(token_or_eof), sl) << "Expected to encounter end of file"; - } else { - expect(std::holds_alternative(token_or_eof), sl) << "Expected to NOT encounter end of file"; - return std::get(std::move(token_or_eof)); - } -} - -static void expect_token_type( - Token::Type expected_type, - std::string source, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Lexer lexer{source}; - auto result = lexer.next_token(); - expect(result.has_value() >> fatal, sl) << "have not parsed any tokens"; - auto token = handle_end_of_file(*std::move(result), sl); - expect(eq(token.type, expected_type), sl) << "different token type then expected"; -} - -static void expect_token_type_and_value( - Token::Type expected_type, - std::string_view source, - std::string_view expected, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Lexer lexer{source}; - auto result = lexer.next_token(); - expect(result.has_value(), sl) << "have not parsed any tokens"; - - if (result.has_value()) { - auto token = handle_end_of_file(*std::move(result), sl); - expect(eq(token.type, expected_type), sl) << "different token type then expected"; - expect(eq(token.source, expected), sl) << "tokenized source is not equal to original"; - } -} - -static void expect_token_type_and_value( - Token::Type expected_type, - std::string_view source, - reflection::source_location const& sl = reflection::source_location::current()) -{ - expect_token_type_and_value(expected_type, source, source, sl); -} - -static void expect_token_type_and_location( - Token::Type expected_type, - std::string_view source, - Location location, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Lexer lexer{source}; - auto result = lexer.next_token(); - expect(result.has_value() >> fatal, sl) << "have not parsed any tokens"; - - auto token = handle_end_of_file(*std::move(result), sl); - expect(eq(token.type, expected_type), sl) << "different token type then expected"; - expect(eq(token.location, location), sl) << "tokenized source is at different place then expected"; -} - -static void expect_empty_file( - std::string_view source, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Lexer lexer{source}; - auto result = lexer.next_token(); - expect(result.has_value(), sl) << "Encountered error when expecting end of file"; - handle_end_of_file(*std::move(result), sl); -} - -template -static void expect_token_sequence( - std::string_view source, - std::array const& expected_tokens, - reflection::source_location const& sl = reflection::source_location::current()) -{ - Lexer lexer{source}; - - for (Token const& expected : expected_tokens) { - auto const result = lexer.next_token(); - expect(result.has_value(), sl) << "expected token, received nothing"; - - if (result.has_value()) { - auto token = handle_end_of_file(*std::move(result), sl); - expect(eq(token.type, expected.type)) << "different token type then expected"; - expect(eq(token.source, expected.source)) << "different token source then expected"; - expect(eq(token.location, expected.location)) << "different token location then expected"; - } - } - - auto const result = lexer.next_token(); - expect(result.has_value(), sl) << "expected end of file after sequence of tokens"; - if (result.has_value()) { - expect(std::holds_alternative(*result), sl) << "expected end of file after sequence of tokens"; - } -} - -suite lexer_test = [] { - "Empty file"_test = [] { - expect_empty_file(""); - }; - - "Comments"_test = [] { - expect_empty_file("#!/bin/sh"); - expect_empty_file("-- line comment"); - expect_token_type_and_value(Token::Type::Numeric, "--- block comment --- 0", "0"); - expect_empty_file(R"musique( - --- hello - multiline comment - --- - )musique"); - }; - - "Simple token types"_test = [] { - expect_token_type(Token::Type::Close_Block, "]"); - expect_token_type(Token::Type::Close_Paren, ")"); - expect_token_type(Token::Type::Open_Block, "["); - expect_token_type(Token::Type::Open_Paren, "("); - expect_token_type(Token::Type::Parameter_Separator, "|"); - expect_token_type(Token::Type::Expression_Separator, ";"); - }; - - "Numeric tokens"_test = [] { - expect_token_type_and_value(Token::Type::Numeric, "0"); - expect_token_type_and_value(Token::Type::Numeric, "123456789"); - expect_token_type_and_value(Token::Type::Numeric, "0.75"); - expect_token_type_and_value(Token::Type::Numeric, "123456789.123456789"); - expect_token_type_and_value(Token::Type::Numeric, "123.", "123"); - expect_token_type_and_value(Token::Type::Numeric, " 1 ", "1"); - expect_token_type_and_value(Token::Type::Numeric, " 123 ", "123"); - }; - - "Proper location marking"_test = [] { - expect_token_type_and_location(Token::Type::Numeric, "123", Location::at(1, 1)); - expect_token_type_and_location(Token::Type::Numeric, " 123", Location::at(1, 4)); - expect_token_type_and_location(Token::Type::Numeric, "\n123", Location::at(2, 1)); - expect_token_type_and_location(Token::Type::Numeric, "\n 123", Location::at(2, 3)); - }; - - "Chord literals"_test = [] { - expect_token_type_and_value(Token::Type::Chord, "c"); - expect_token_type_and_value(Token::Type::Chord, "c#"); - expect_token_type_and_value(Token::Type::Chord, "c1"); - expect_token_type_and_value(Token::Type::Chord, "d1257"); - expect_token_type_and_value(Token::Type::Chord, "e#5"); - expect_token_type_and_value(Token::Type::Chord, "ef5"); - expect_token_type_and_value(Token::Type::Chord, "es5"); - expect_token_type_and_value(Token::Type::Chord, "eb5"); - expect_token_type_and_value(Token::Type::Chord, "e##5"); - expect_token_type_and_value(Token::Type::Chord, "ef#5"); - expect_token_type_and_value(Token::Type::Chord, "esf5"); - expect_token_type_and_value(Token::Type::Chord, "g127"); - }; - - "Symbol literals"_test = [] { - expect_token_type_and_value(Token::Type::Symbol, "i"); - expect_token_type_and_value(Token::Type::Symbol, "i2"); - expect_token_type_and_value(Token::Type::Symbol, "example"); - expect_token_type_and_value(Token::Type::Symbol, "d1envelope"); - expect_token_type_and_value(Token::Type::Symbol, "snake_case"); - expect_token_type_and_value(Token::Type::Symbol, "camelCase"); - expect_token_type_and_value(Token::Type::Symbol, "PascalCase"); - expect_token_type_and_value(Token::Type::Symbol, "haskell'"); - expect_token_type_and_value(Token::Type::Symbol, "zażółć"); - expect_token_type_and_value(Token::Type::Symbol, "$foo"); - expect_token_type_and_value(Token::Type::Symbol, "@bar"); - }; - - "Operators"_test = [] { - expect_token_type_and_value(Token::Type::Operator, "+"); - expect_token_type_and_value(Token::Type::Operator, "&&"); - expect_token_type_and_value(Token::Type::Operator, "||"); - expect_token_type_and_value(Token::Type::Operator, "*"); - expect_token_type_and_value(Token::Type::Operator, "**"); - expect_token_type_and_value(Token::Type::Operator, "="); - expect_token_type_and_value(Token::Type::Operator, "<"); - expect_token_type_and_value(Token::Type::Operator, ":"); - expect_token_type_and_value(Token::Type::Operator, "%"); - }; - - "Multiple tokens"_test = [] { - Location l; - - expect_token_sequence("1 + foo", std::array { - Token { Token::Type::Numeric, "1", l.at(1, 1) }, - Token { Token::Type::Operator, "+", l.at(1, 3) }, - Token { Token::Type::Symbol, "foo", l.at(1, 5) } - }); - - expect_token_sequence("foo 1 2; bar 3 4", std::array { - Token { Token::Type::Symbol, "foo", l.at(1, 1) }, - Token { Token::Type::Numeric, "1", l.at(1, 5) }, - Token { Token::Type::Numeric, "2", l.at(1, 7) }, - Token { Token::Type::Expression_Separator, ";", l.at(1, 8) }, - Token { Token::Type::Symbol, "bar", l.at(1, 10) }, - Token { Token::Type::Numeric, "3", l.at(1, 14) }, - Token { Token::Type::Numeric, "4", l.at(1, 16) } - }); - }; -}; diff --git a/src/tests/main.cc b/src/tests/main.cc deleted file mode 100644 index 29b865b..0000000 --- a/src/tests/main.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include - -int main() -{ - using namespace boost::ut; - - if (!isatty(STDOUT_FILENO) || getenv("NO_COLOR") != nullptr) { - cfg = options { - .colors = colors { .none = "", .pass = "", .fail = "" } - }; - } -} diff --git a/src/tests/number.cc b/src/tests/number.cc deleted file mode 100644 index af17c56..0000000 --- a/src/tests/number.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -void test_number_from( - std::string_view source, - Number expected, - reflection::source_location sl = reflection::source_location::current()) -{ - auto result = Number::from(Token { Token::Type::Numeric, source, {} }); - expect(result.has_value(), sl) << "failed to parse number"; - if (result.has_value()) { - expect(eq(*result, expected), sl); - } -} - -suite number_test = [] { - "Number"_test = [] { - should("provide arithmetic operators") = [] { - expect(eq(Number{1, 8} + Number{3, 4}, Number{ 7, 8})) << "for expr: (1/8) + (3/4)"; - expect(eq(Number{1, 8} - Number{3, 4}, Number{-5, 8})) << "for expr: (1/8) - (3/4)"; - expect(eq(Number{1, 8} * Number{3, 4}, Number{ 3, 32})) << "for expr: (1/8) * (3/4)"; - expect(eq(Number{1, 8} / Number{3, 4}, Number{ 1, 6})) << "for expr: (1/8) / (3/4)"; - }; - - should("provide assignment operators") = [] { - Number n; - n={1,8}; n += {3,4}; expect(eq(n, Number{ 7, 8})) << "for expr: (1/8) += (3/4)"; - n={1,8}; n -= {3,4}; expect(eq(n, Number{-5, 8})) << "for expr: (1/8) -= (3/4)"; - n={1,8}; n *= {3,4}; expect(eq(n, Number{ 3, 32})) << "for expr: (1/8) *= (3/4)"; - n={1,8}; n /= {3,4}; expect(eq(n, Number{ 1, 6})) << "for expr: (1/8) /= (3/4)"; - }; - - should("be comperable") = [] { - expect(gt(Number{1, 4}, Number{1, 8})); - expect(eq(Number{2, 4}, Number{1, 2})); - }; - - should("be convertable to int") = [] { - expect(eq(Number{4, 2}.as_int(), 2)) << "for fraction 4/2"; - expect(eq(Number{2, 1}.as_int(), 2)) << "for fraction 2/1"; - expect(eq(Number{0, 1000}.as_int(), 0)) << "for fraction 0/1000"; - }; - }; - - "Number::from"_test = [] { - test_number_from("0", Number(0)); - test_number_from("100", Number(100)); - test_number_from("0.75", Number(3, 4)); - test_number_from(".75", Number(3, 4)); - test_number_from("120.", Number(120, 1)); - }; - - "Rounding"_test = [] { - should("Support floor operation") = [] { - expect(eq(Number(0), Number(1, 2).floor())); - expect(eq(Number(1), Number(3, 2).floor())); - expect(eq(Number(-1), Number(-1, 2).floor())); - expect(eq(Number(0), Number(0).floor())); - expect(eq(Number(1), Number(1).floor())); - expect(eq(Number(-1), Number(-1).floor())); - }; - - should("Support ceil operation") = [] { - expect(eq(Number(1), Number(1, 2).ceil())); - expect(eq(Number(2), Number(3, 2).ceil())); - expect(eq(Number(0), Number(-1, 2).ceil())); - expect(eq(Number(-1), Number(-3, 2).ceil())); - expect(eq(Number(0), Number(0).ceil())); - expect(eq(Number(1), Number(1).ceil())); - expect(eq(Number(-1), Number(-1).ceil())); - }; - - should("Support round operation") = [] { - expect(eq(Number(1), Number(3, 4).round())); - expect(eq(Number(0), Number(1, 4).round())); - expect(eq(Number(1), Number(5, 4).round())); - expect(eq(Number(2), Number(7, 4).round())); - - expect(eq(Number(-1), Number(-3, 4).round())); - expect(eq(Number(0), Number(-1, 4).round())); - expect(eq(Number(-1), Number(-5, 4).round())); - expect(eq(Number(-2), Number(-7, 4).round())); - }; - }; - - "Number exponantiation"_test = [] { - should("Support integer powers") = [] { - expect(eq(Number(1, 4), Number(1, 2).pow(Number(2)))); - expect(eq(Number(4, 1), Number(1, 2).pow(Number(-2)))); - - expect(eq(Number(4, 1), Number(2).pow(Number(2)))); - expect(eq(Number(1, 4), Number(2).pow(Number(-2)))); - }; - }; -}; diff --git a/src/tests/parser.cc b/src/tests/parser.cc deleted file mode 100644 index c501a6c..0000000 --- a/src/tests/parser.cc +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include - -using namespace boost::ut; - -void expect_ast( - std::string_view source, - Ast const& expected, - reflection::source_location sl = reflection::source_location::current()) -{ - auto result = Parser::parse(source, "test"); - expect(result.has_value(), sl) << "code was expect to parse, but had not"; - expect(eq(*result, expected), sl) << "parser yielded unexpected tree"; -} - -void expect_single_ast( - std::string_view source, - Ast const& expected, - reflection::source_location sl = reflection::source_location::current()) -{ - auto result = Parser::parse(source, "test"); - expect(result.has_value(), sl) << "code was expect to parse, but had not"; - - expect(eq(result->type, Ast::Type::Sequence), sl) << "parsed does not yielded sequence"; - expect(not result->arguments.empty(), sl) << "parsed yielded empty sequence"; - expect(eq(result->arguments[0], expected), sl) << "parser yielded unexpected tree"; -} - -suite parser_test = [] { - "Empty file parsing"_test = [] { - expect_ast("", Ast::sequence({})); - }; - - "Literal parsing"_test = [] { - expect_single_ast("1", Ast::literal(Token { Token::Type::Numeric, "1", {} })); - }; - - "Binary opreator parsing"_test = [] { - expect_single_ast("1 + 2", Ast::binary( - Token { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Numeric, "1", {} }), - Ast::literal({ Token::Type::Numeric, "2", {} }) - )); - - expect_single_ast("100 * 200", Ast::binary( - Token { Token::Type::Operator, "*", {} }, - Ast::literal({ Token::Type::Numeric, "100", {} }), - Ast::literal({ Token::Type::Numeric, "200", {} }) - )); - - expect_single_ast("101 + 202 * 303", Ast::binary( - Token { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Numeric, "101", {} }), - Ast::binary( - Token { Token::Type::Operator, "*", {} }, - Ast::literal({ Token::Type::Numeric, "202", {} }), - Ast::literal({ Token::Type::Numeric, "303", {} }) - ) - )); - - }; - - // This test shouldn't be skipped since language will support precedense. - // It stays here as reminder that this feature should be implemented. - "Binary operator precedense"_test = [] { - expect_single_ast("101 * 202 + 303", Ast::binary( - Token { Token::Type::Operator, "+", {} }, - Ast::binary( - Token { Token::Type::Operator, "*", {} }, - Ast::literal({ Token::Type::Numeric, "101", {} }), - Ast::literal({ Token::Type::Numeric, "202", {} }) - ), - Ast::literal({ Token::Type::Numeric, "303", {} }) - )); - }; - - "Grouping expressions in parentheses"_test = [] { - expect_single_ast("(101 + 202) * 303", Ast::binary( - Token { Token::Type::Operator, "*", {} }, - Ast::sequence({ Ast::binary( - Token { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Numeric, "101", {} }), - Ast::literal({ Token::Type::Numeric, "202", {} }) - )}), - Ast::literal({ Token::Type::Numeric, "303", {} }) - )); - - expect_single_ast("101 * (202 + 303)", Ast::binary( - Token { Token::Type::Operator, "*", {} }, - Ast::literal({ Token::Type::Numeric, "101", {} }), - Ast::sequence({ - Ast::binary( - Token { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Numeric, "202", {} }), - Ast::literal({ Token::Type::Numeric, "303", {} }) - ) - }) - )); - }; - - "Explicit function call"_test = [] { - expect_single_ast("foo 1 2", Ast::call({ - Ast::literal({ Token::Type::Symbol, "foo", {} }), - Ast::literal({ Token::Type::Numeric, "1", {} }), - Ast::literal({ Token::Type::Numeric, "2", {} }) - })); - - should("Support function call with complicated expression") = [] { - expect_single_ast("say (fib (n-1) + fib (n-2))", Ast::call({ - Ast::literal({ Token::Type::Symbol, "say", {} }), - Ast::sequence({ - Ast::binary( - { Token::Type::Operator, "+", {} }, - Ast::call({ - Ast::literal({ Token::Type::Symbol, "fib", {} }), - Ast::sequence({ - Ast::binary( - { Token::Type::Operator, "-", {} }, - Ast::literal({ Token::Type::Symbol, "n", {} }), - Ast::literal({ Token::Type::Numeric, "1", {} }) - ) - }) - }), - Ast::call({ - Ast::literal({ Token::Type::Symbol, "fib", {} }), - Ast::sequence({ - Ast::binary( - { Token::Type::Operator, "-", {} }, - Ast::literal({ Token::Type::Symbol, "n", {} }), - Ast::literal({ Token::Type::Numeric, "2", {} }) - ) - }) - }) - ) - }) - })); - }; - }; - - "Sequence"_test = [] { - expect_ast("42; 101", Ast::sequence({ - Ast::literal({ Token::Type::Numeric, "42", {} }), - Ast::literal({ Token::Type::Numeric, "101", {} }) - })); - - expect_ast("say hello; say world", Ast::sequence({ - Ast::call({ - Ast::literal({ Token::Type::Symbol, "say", {} }), - Ast::literal({ Token::Type::Symbol, "hello", {} }) - }), - Ast::call({ - Ast::literal({ Token::Type::Symbol, "say", {} }), - Ast::literal({ Token::Type::Symbol, "world", {} }) - }) - })); - }; - - "Block"_test = [] { - expect_single_ast("[]", Ast::block(Location{})); - - expect_single_ast("[ i; j; k ]", Ast::block({}, Ast::sequence({ - Ast::literal({ Token::Type::Symbol, "i", {} }), - Ast::literal({ Token::Type::Symbol, "j", {} }), - Ast::literal({ Token::Type::Symbol, "k", {} }) - }))); - - expect_single_ast("[ i j k | i + j + k ]", Ast::lambda({}, Ast::sequence({ - Ast::binary( - { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Symbol, "i", {} }), - Ast::binary( - { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Symbol, "j", {} }), - Ast::literal({ Token::Type::Symbol, "k", {} }) - ) - ) - }) - , { - Ast::literal({ Token::Type::Symbol, "i", {} }), - Ast::literal({ Token::Type::Symbol, "j", {} }), - Ast::literal({ Token::Type::Symbol, "k", {} }) - })); - - expect_single_ast("[ i; j; k | i + j + k ]", Ast::lambda({}, Ast::sequence({ - Ast::binary( - { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Symbol, "i", {} }), - Ast::binary( - { Token::Type::Operator, "+", {} }, - Ast::literal({ Token::Type::Symbol, "j", {} }), - Ast::literal({ Token::Type::Symbol, "k", {} }) - ) - )}), { - Ast::literal({ Token::Type::Symbol, "i", {} }), - Ast::literal({ Token::Type::Symbol, "j", {} }), - Ast::literal({ Token::Type::Symbol, "k", {} }) - })); - - expect_single_ast("[|1]", Ast::lambda({}, Ast::sequence({ Ast::literal({ Token::Type::Numeric, "1", {} })}), {})); - }; - - "Variable declarations"_test = [] { - should("Support variable declaration with assigment") = [] { - expect_single_ast("var x = 10", Ast::variable_declaration( - {}, - { Ast::literal({ Token::Type::Symbol, "x", {} }) }, - Ast::literal({ Token::Type::Numeric, "10", {} }))); - }; - - skip / should("Support variable declaration") = [] { - expect_ast("var x", Ast::variable_declaration( - {}, - { Ast::literal({ Token::Type::Symbol, "x", {} }) }, - std::nullopt)); - }; - - skip / should("Support multiple variables declaration") = [] { - expect_ast("var x y", Ast::variable_declaration( - {}, - {Ast::literal({ Token::Type::Symbol, "x", {} }), Ast::literal({ Token::Type::Symbol, "y", {} })}, - std::nullopt)); - }; - }; -}; diff --git a/src/tests/unicode.cc b/src/tests/unicode.cc deleted file mode 100644 index 7b83d8d..0000000 --- a/src/tests/unicode.cc +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -void test_encoding( - u32 rune, - std::string_view expected, - reflection::source_location sl = reflection::source_location::current()) -{ - std::stringstream ss; - ss << utf8::Print{rune}; - expect(eq(ss.str(), expected), sl); -} - -suite utf8_test = [] { - "UTF-8 Character length"_test = [] { - expect(utf8::length(" ") == 1_u); - expect(utf8::length("ą") == 2_u); - expect(utf8::length("\u2705") == 3_u); - expect(utf8::length("\U000132d1") == 4_u); - }; - - "UTF-8 Character decoding"_test = [] { - expect(eq(utf8::decode(" ").first, 0x20u)); - expect(eq(utf8::decode("ą").first, 0x105u)); - expect(eq(utf8::decode("\u2705").first, 0x2705u)); - expect(eq(utf8::decode("\U000132d1").first, 0x132d1u)); - }; - - "UTF-8 Character encoding"_test = [] { - test_encoding(0x20u, " "); - test_encoding(0x105u, "ą"); - test_encoding(0x2705u, "\u2705"); - test_encoding(0x132d1u, "\U000132d1"); - }; -}; diff --git a/src/tests/value.cc b/src/tests/value.cc deleted file mode 100644 index f7d91bc..0000000 --- a/src/tests/value.cc +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include - -using namespace boost::ut; -using namespace std::string_view_literals; - -static std::string str(auto const& printable) -{ - std::stringstream ss; - ss << printable; - return std::move(ss).str(); -} - -static void expect_value( - Result received, - Value expected, - reflection::source_location sl = reflection::source_location::current()) -{ - expect(received.has_value(), sl) << "Received error, instead of value"; - expect(eq(*received, expected), sl); -} - -static void either_truthy_or_falsy( - bool(Value::*desired)() const, - Value v, - reflection::source_location sl = reflection::source_location::current()) -{ - if (desired == &Value::truthy) { - expect(v.truthy(), sl) << "Value " << v << " should be" << "truthy"; - expect(!v.falsy(), sl) << "Value " << v << " should NOT be" << "falsy"; - } else { - expect(v.falsy(), sl) << "Value " << v << " should be" << "falsy"; - expect(!v.truthy(), sl) << "Value " << v << " should NOT be" << "truthy"; - } -} - -static void test_note_resolution( - std::string_view name, - i8 octave, - u8 expected, - reflection::source_location sl = reflection::source_location::current()) -{ - auto const maybe_note = Note::from(name); - expect(maybe_note.has_value(), sl) << "Note::from didn't recognized " << name << " as a note"; - if (maybe_note) { - auto note = *maybe_note; - note.octave = octave; - auto const midi_note = note.into_midi_note(); - expect(midi_note.has_value(), sl) << "Note::into_midi_note returned nullopt, but should not"; - expect(eq(int(*midi_note), int(expected)), sl) << "Note::into_midi_note returned wrong value"; - } -} - -static void test_note_resolution( - std::string_view name, - u8 expected, - reflection::source_location sl = reflection::source_location::current()) -{ - auto const maybe_note = Note::from(name); - expect(maybe_note.has_value(), sl) << "Note::from didn't recognized " << name << " as a note"; - if (maybe_note) { - auto note = *maybe_note; - note.octave = 4; - auto const midi_note = note.into_midi_note(); - expect(midi_note.has_value(), sl) << "Note::into_midi_note returned nullopt, but should not"; - expect(eq(int(*midi_note), int(expected)), sl) << "Note::into_midi_note returned wrong value"; - } -} - -static void test_note_resolution( - std::string_view name, - std::string_view expected = {}, - reflection::source_location sl = reflection::source_location::current()) -{ - if (expected.empty()) - expected = name; - - auto const maybe_note = Note::from(name); - expect(maybe_note.has_value(), sl) << "Note::from didn't recognized " << name << " as a note"; - if (maybe_note) { - auto note = *maybe_note; - std::stringstream ss; - ss << note; - expect(eq(expected, ss.str())) << "Different string representation then expected"; - } -} - -suite value_test = [] { - "Value"_test = [] { - should("be properly created using Value::from") = [] { - expect_value(Value::from({ Token::Type::Numeric, "10", {} }), Value::from(Number(10))); - expect_value(Value::from({ Token::Type::Keyword, "nil", {} }), Value{}); - expect_value(Value::from({ Token::Type::Keyword, "true", {} }), Value::from(true)); - expect_value(Value::from({ Token::Type::Keyword, "false", {} }), Value::from(false)); - expect_value(Value::from({ Token::Type::Symbol, "foobar", {} }), Value::from("foobar")); - }; - - should("have be considered truthy or falsy") = [] { - either_truthy_or_falsy(&Value::truthy, Value::from(true)); - either_truthy_or_falsy(&Value::truthy, Value::from(Number(1))); - either_truthy_or_falsy(&Value::truthy, Value::from("foo")); - either_truthy_or_falsy(&Value::truthy, Value(Intrinsic(nullptr))); - - either_truthy_or_falsy(&Value::falsy, Value{}); - either_truthy_or_falsy(&Value::falsy, Value::from(false)); - either_truthy_or_falsy(&Value::falsy, Value::from(Number(0))); - }; - }; - - "Value comparisons"_test = [] { - should("are always not equal when types differ") = [] { - expect(neq(Value::from("0"), Value::from(Number(0)))); - }; - }; - - "Value printing"_test = [] { - expect(eq("nil"sv, str(Value{}))); - expect(eq("true"sv, str(Value::from(true)))); - expect(eq("false"sv, str(Value::from(false)))); - - expect(eq("10"sv, str(Value::from(Number(10))))); - expect(eq("1/2"sv, str(Value::from(Number(2, 4))))); - - expect(eq("foo"sv, str(Value::from("foo")))); - - expect(eq(""sv, str(Value(Intrinsic(nullptr))))); - }; - - "Note"_test = [] { - should("properly resolve notes") = [] { - for (i8 i = 0; i < 9; ++i) { - test_note_resolution("c", i + -1, i * 12 + 0); - test_note_resolution("c#", i + -1, i * 12 + 1); - test_note_resolution("d", i + -1, i * 12 + 2); - test_note_resolution("d#", i + -1, i * 12 + 3); - test_note_resolution("e", i + -1, i * 12 + 4); - test_note_resolution("e#", i + -1, i * 12 + 5); - test_note_resolution("f", i + -1, i * 12 + 5); - test_note_resolution("f#", i + -1, i * 12 + 6); - test_note_resolution("g", i + -1, i * 12 + 7); - test_note_resolution("g#", i + -1, i * 12 + 8); - test_note_resolution("a", i + -1, i * 12 + 9); - test_note_resolution("a#", i + -1, i * 12 + 10); - test_note_resolution("b", i + -1, i * 12 + 11); - } - }; - - should("Support flat and sharp") = [] { - test_note_resolution("c#", 61); - test_note_resolution("cs", 61); - test_note_resolution("cf", 59); - test_note_resolution("cb", 59); - - test_note_resolution("c##", 62); - test_note_resolution("css", 62); - test_note_resolution("cff", 58); - test_note_resolution("cbb", 58); - - test_note_resolution("cs#", 62); - test_note_resolution("c#s", 62); - test_note_resolution("cbf", 58); - test_note_resolution("cfb", 58); - - test_note_resolution("c#b", 60); - test_note_resolution("cb#", 60); - test_note_resolution("cfs", 60); - test_note_resolution("csf", 60); - - test_note_resolution("c##########", 70); - test_note_resolution("cbbbbbbbbbb", 50); - }; - - should("Preserve note when printing") = [] { - test_note_resolution("c"); - test_note_resolution("c#"); - test_note_resolution("d"); - test_note_resolution("d#"); - test_note_resolution("e"); - test_note_resolution("f"); - test_note_resolution("f#"); - test_note_resolution("g"); - test_note_resolution("g#"); - test_note_resolution("a"); - test_note_resolution("a#"); - test_note_resolution("b"); - }; - }; -};