diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f54cef0..461f8b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: shell: bash run: | shopt -s globstar extglob - g++ -Og -Isrc tests/unit/**/*.cpp src/!(main).cpp + g++ -Og -Isrc -std=c++17 tests/unit/**/*.cpp src/!(main).cpp ./a.out cli-tests-unix: strategy: diff --git a/README.md b/README.md index a5b177e..5265da4 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ Optionally, you can control the behavior of the date/time instructions with `TRI If the macro `NO_BUFFER` is defined, output to stdout will not be buffered. -C++17 (`201304L`) or later is required to compile this project. Certain features from C++20 and C++23 will be used if available, such as the `[[unlikely]]` atribute. As such, it is recommended to use C++20 or later if performance is a concern. (If it is, why? How are you possibly using this language in a performance-sensitive environment?) +C++17 or later is required to compile this project. Certain features from C++20 and C++23 will be used if available, such as the `[[unlikely]]` atribute. As such, it is recommended to use C++20 or later if performance is a concern. (If it is, why? How are you possibly using this language in a performance-sensitive environment?) ### Compiling for the web diff --git a/tests/unit/README.md b/tests/unit/README.md index 5682bc8..d6ad2fe 100644 --- a/tests/unit/README.md +++ b/tests/unit/README.md @@ -8,7 +8,7 @@ Running the tests is simple: ```bash shopt -s globstar extglob -g++ -Og -Isrc tests/unit/**/*.cpp src/!(main).cpp +g++ -Og -Isrc -std=c++17 tests/unit/**/*.cpp src/!(main).cpp ./a.out ``` @@ -17,6 +17,6 @@ g++ -Og -Isrc tests/unit/**/*.cpp src/!(main).cpp Avoid compiling with the MSVC CLI if possible, as it's more involved than the alternatives. However, in Visual Studio developer PowerShell, this should work: ```pwsh -cl tests\unit\main.cpp tests\unit\test-framework\*\*.cpp (ls src\*.cpp -Exclude *main.cpp | % FullName) /Od /EHs /Isrc /MTd /link /out:test.exe +cl tests\unit\main.cpp tests\unit\test-framework\*\*.cpp (ls src\*.cpp -Exclude *main.cpp | % FullName) /Od /EHs /Isrc /std:c++17 /MTd /link /out:test.exe .\test.exe ``` diff --git a/tests/unit/disassemble.hh b/tests/unit/disassemble.hh index b1a8b50..bbeb464 100644 --- a/tests/unit/disassemble.hh +++ b/tests/unit/disassemble.hh @@ -8,11 +8,11 @@ class test_disassemble : public instruction_scanner { public: inline test_disassemble(NONNULL_PTR(const program) p) : instruction_scanner(p) {} - inline NONNULL_PTR(const std::vector*>) get_fragments() noexcept { - if (m_fragments == nullptr) { + inline const std::vector>>& get_fragments() noexcept { + if (!m_fragments.has_value()) { build_state(); } - return m_fragments; + return *m_fragments; } inline void write_state(std::ostream& os) {} @@ -24,9 +24,9 @@ testgroup (disassemble) { program p("@"); test_disassemble td(&p); - auto* fragments = td.get_fragments(); - test_assert(fragments->size() == 1, "Program without branches or threads should have 1 fragment"); - const std::vector* fragment = fragments->at(0); + const auto& fragments = td.get_fragments(); + test_assert(fragments.size() == 1, "Program without branches or threads should have 1 fragment"); + const auto& fragment = fragments.at(0); test_assert(fragment->size() == 1, "Program of 1 character should be 1 instruction"); test_assert(fragment->back().is_exit(), "Program must end in EXT"); } @@ -34,9 +34,9 @@ testgroup (disassemble) { program p("."); test_disassemble td(&p); - auto* fragments = td.get_fragments(); - test_assert(fragments->size() == 1, "Program without branches or threads should have 1 fragment"); - const std::vector* fragment = fragments->at(0); + const auto& fragments = td.get_fragments(); + test_assert(fragments.size() == 1, "Program without branches or threads should have 1 fragment"); + const auto& fragment = fragments.at(0); // 1 for the instruction itself and one for the JMP // In the general case, this has to be two (e.g. GTC). I'm just using NOP here because it's detectable. test_assert(fragment->size() == 2); @@ -57,10 +57,10 @@ testgroup (disassemble) { test_disassemble td(&p); - auto* fragments = td.get_fragments(); - test_assert(fragments->size() == 3, "Program with one branch should have 3 fragments"); - test_assert(fragments->at(0)->back().first_if_branch(), "First fragment should end in branch"); - test_assert(fragments->at(1)->back().is_exit(), "Second fragment should end in EXT"); - test_assert(fragments->at(2)->back().is_exit(), "Third fragment should end in EXT"); + const auto& fragments = td.get_fragments(); + test_assert(fragments.size() == 3, "Program with one branch should have 3 fragments"); + test_assert(fragments.at(0)->back().first_if_branch(), "First fragment should end in branch"); + test_assert(fragments.at(1)->back().is_exit(), "Second fragment should end in EXT"); + test_assert(fragments.at(2)->back().is_exit(), "Third fragment should end in EXT"); } }; diff --git a/tests/unit/ip_movement.hh b/tests/unit/ip_movement.hh index 7e6803f..3bae588 100644 --- a/tests/unit/ip_movement.hh +++ b/tests/unit/ip_movement.hh @@ -16,7 +16,7 @@ using std::pair; // Each test program consists of every digit 0-9 exactly once, in the order they are hit if the IP is traveling in the // given direction from the top corner. -std::initializer_list>> test_programs = { +CONSTEXPR_ALG std::initializer_list>> test_programs = { TEST_ITEM(southwest, "0142573689"), TEST_ITEM(west, "0215439876"), TEST_ITEM(northwest, "0395286417"), @@ -35,7 +35,7 @@ std::initializer_list>> test_programs } // Each item is a 4-tuple of starting direction, instruction, should go left, ending direction. -std::initializer_list>> branch_tests = { +constexpr std::initializer_list>> branch_tests = { LR_PAIR(southwest, '7', west, '>'), LR_PAIR(west, '>', northwest, 'v'), LR_PAIR(northwest, 'v', northeast, 'L'), LR_PAIR(northeast, 'L', east, '<'), LR_PAIR(east, '<', southeast, '^'), LR_PAIR(southeast, '^', southwest, '7'), }; @@ -54,7 +54,7 @@ std::initializer_list>> mirror_tests = { +constexpr std::initializer_list>> mirror_tests = { MIRROR('|', northwest, west, southwest, southeast, east, northeast), MIRROR('_', southeast, east, northeast, northwest, west, southwest), MIRROR('/', northeast, northwest, west, southwest, southeast, east), diff --git a/tests/unit/utf8.hh b/tests/unit/utf8.hh index b5523c0..df13a6c 100644 --- a/tests/unit/utf8.hh +++ b/tests/unit/utf8.hh @@ -6,64 +6,60 @@ #include #include "test-framework/test_framework.hh" -using std::ostringstream; -using std::string; -using std::vector; - testgroup (utf8_parsing) { testcase (eof_immediately) { int24_t parsed = parse_unichar([]() noexcept { return EOF; }); test_assert(parsed == INT24_C(-1), "EOF should be -1"); } , testcase (ascii_string) { - vector parsed = parse_utf8("abcd", false); - vector expected = { 'a', 'b', 'c', 'd' }; + std::vector parsed = parse_utf8("abcd", false); + std::vector expected = { 'a', 'b', 'c', 'd' }; test_assert(parsed == expected, "ASCII characters should maintain their values"); } , testcase (shebangs) { - vector parsed1 = parse_utf8("#!/bin/trilangle\nabc", true); - vector expected1 = { 'a', 'b', 'c' }; + std::vector parsed1 = parse_utf8("#!/bin/trilangle\nabc", true); + std::vector expected1 = { 'a', 'b', 'c' }; test_assert(parsed1 == expected1, "Shebang should be removed"); - vector parsed2 = parse_utf8("#!@", false); - vector expected2 = { '#', '!', '@' }; + std::vector parsed2 = parse_utf8("#!@", false); + std::vector expected2 = { '#', '!', '@' }; test_assert(parsed2 == expected2, "Shebang should be kept"); } , testcase (multibyte_characters) { - vector parsed = parse_utf8(".\xC3\xA9.e\xCC\x81.\xF0\x9F\x8E\x88", false); - vector expected = { '.', INT24_C(0x00e9), '.', 'e', INT24_C(0x0301), '.', INT24_C(0x1f388) }; + std::vector parsed = parse_utf8(".\xC3\xA9.e\xCC\x81.\xF0\x9F\x8E\x88", false); + std::vector expected = { '.', INT24_C(0x00e9), '.', 'e', INT24_C(0x0301), '.', INT24_C(0x1f388) }; test_assert(parsed == expected); } , testcase (invalid_char) { - vector parsed1 = parse_utf8("\xC0@", false); - vector expected1 = { INT24_C(0xfffd) }; + std::vector parsed1 = parse_utf8("\xC0@", false); + std::vector expected1 = { INT24_C(0xfffd) }; test_assert(parsed1 == expected1, "Ill-formed UTF-8 should produce U+FFFD"); - vector parsed2 = parse_utf8("\xF9.", false); - vector expected2 = { INT24_C(0xfffd), '.' }; + std::vector parsed2 = parse_utf8("\xF9.", false); + std::vector expected2 = { INT24_C(0xfffd), '.' }; test_assert(parsed2 == expected2, "Characters after invalid byte should be left as-is"); } }; testgroup (utf8_output) { testcase (ascii_char) { - ostringstream oss; + std::ostringstream oss; print_unichar('a', oss); - string result = oss.str(); + std::string result = oss.str(); test_assert(result == "a", "ASCII character should maintain its value"); } , testcase (high_byte) { - ostringstream oss; + std::ostringstream oss; print_unichar(INT24_C(0x00e9), oss); - string result = oss.str(); + std::string result = oss.str(); test_assert(result == "\xC3\xA9"); } , testcase (multibyte_char) { - ostringstream oss; + std::ostringstream oss; print_unichar(INT24_C(0x1f388), oss); - string result = oss.str(); + std::string result = oss.str(); test_assert(result == "\xF0\x9F\x8E\x88"); }