Removed unused tests
This commit is contained in:
parent
1384e5f96e
commit
34eb56ac50
@ -55,8 +55,6 @@ $ make bin/musique
|
|||||||
### Testowanie
|
### Testowanie
|
||||||
|
|
||||||
- `make test` - Uruchom wszystkie dostępne testy automatyczne
|
- `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 test examples` - Uruchamia testy zachowań przykładów
|
||||||
- `scripts/test.py record examples` - Nagrywa 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
|
├── bin Miejsce produkcji plików wykonywalnych
|
||||||
├── coverage
|
|
||||||
├── doc Dokumentacja języka, interpretera
|
├── doc Dokumentacja języka, interpretera
|
||||||
│ └── build Miejsce produkcji dokumentacji
|
│ └── build Miejsce produkcji dokumentacji
|
||||||
├── editor Pluginy do edytorów dodające wsparcie dla języka
|
├── 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
|
└── include Główny katalog z plikami nagłówkowymi
|
||||||
├── scripts Skrypty wspierające budowanie i tworzenie
|
├── scripts Skrypty wspierające budowanie i tworzenie
|
||||||
└── src Główny katalog z plikami źródłowymi
|
└── src Główny katalog z plikami źródłowymi
|
||||||
└── tests Katalog z testami jednostkowymi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Kolorowanie składni
|
## Kolorowanie składni
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"returncode": 0,
|
"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": "",
|
"stderr": "",
|
||||||
"flags": []
|
"flags": []
|
||||||
}
|
}
|
@ -11,23 +11,6 @@ Tests= \
|
|||||||
|
|
||||||
Test_Obj=$(addprefix bin/debug/tests/,$(Tests))
|
Test_Obj=$(addprefix bin/debug/tests/,$(Tests))
|
||||||
|
|
||||||
test: unit-tests
|
test: bin/debug/musique
|
||||||
scripts/test.py test examples
|
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
|
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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<decltype(dur)::period, std::ratio<1, 1>>,
|
|
||||||
"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) }));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,61 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,423 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <numeric>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
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<void> {
|
|
||||||
Interpreter interpreter;
|
|
||||||
auto result = Try(interpreter.eval(Try(Parser::parse(source_code, "test"))));
|
|
||||||
expect(eq(result, value), sl);
|
|
||||||
return {};
|
|
||||||
}, sl)();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
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<T>(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<errors::Not_Callable>(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<errors::Not_Callable>(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<int> 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<int>(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]");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
216
src/tests/lex.cc
216
src/tests/lex.cc
@ -1,216 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
enum class EOF_Status : bool
|
|
||||||
{
|
|
||||||
Reject = false,
|
|
||||||
Accept = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
template<EOF_Status Expect_End_Of_File = EOF_Status::Reject>
|
|
||||||
static auto handle_end_of_file(
|
|
||||||
std::variant<Token, End_Of_File> token_or_eof,
|
|
||||||
reflection::source_location const& sl)
|
|
||||||
{
|
|
||||||
if constexpr (Expect_End_Of_File == EOF_Status::Accept) {
|
|
||||||
expect(std::holds_alternative<End_Of_File>(token_or_eof), sl) << "Expected to encounter end of file";
|
|
||||||
} else {
|
|
||||||
expect(std::holds_alternative<Token>(token_or_eof), sl) << "Expected to NOT encounter end of file";
|
|
||||||
return std::get<Token>(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<EOF_Status::Accept>(*std::move(result), sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<auto N>
|
|
||||||
static void expect_token_sequence(
|
|
||||||
std::string_view source,
|
|
||||||
std::array<Token, N> 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<End_Of_File>(*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) }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,12 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
if (!isatty(STDOUT_FILENO) || getenv("NO_COLOR") != nullptr) {
|
|
||||||
cfg<override> = options {
|
|
||||||
.colors = colors { .none = "", .pass = "", .fail = "" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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))));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,224 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,38 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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");
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,188 +0,0 @@
|
|||||||
#include <boost/ut.hpp>
|
|
||||||
#include <musique.hh>
|
|
||||||
|
|
||||||
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<Value> 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("<intrinsic>"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");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user