From 7227af20c7e308799e86c734f43aafc52bbaeb73 Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sat, 1 Nov 2025 14:38:48 +0000 Subject: [PATCH 1/8] pre-commit modifications --- .gitignore | 2 +- .pre-commit-config.yaml | 10 +--------- README.md | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 6622204..be50f60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ iris.log -build/ +build/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d928b2..1fee693 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,5 @@ --- +exclude: ^include|^build repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -16,19 +17,10 @@ repos: hooks: - id: clang-format exclude: ".json" - - repo: local - hooks: - - id: clang-tidy - name: clang-tidy - language: system - entry: clang-tidy -p build/compile_commands.json -extra-arg=-std=c++23 - files: cpp - exclude: "^build/" - repo: https://github.com/cmake-lint/cmake-lint rev: 1.4.3 hooks: - id: cmakelint - exclude: "^include/" - repo: https://github.com/asottile/reorder-python-imports rev: v3.16.0 hooks: diff --git a/README.md b/README.md index 46be4a5..cc91cfb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Willow -A unit testing library for modern c++23 +A unit testing library for modern c++23 From 342ec32537c7263a33737fd7db0ac32538db1c0d Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sat, 1 Nov 2025 14:39:09 +0000 Subject: [PATCH 2/8] initial --- CMakeLists.txt | 14 +++++++++++++ src/main.cpp | 25 ++++++++++++++++++++++ src/willow/CMakeLists.txt | 7 +++++++ src/willow/reporters.h | 29 ++++++++++++++++++++++++++ src/willow/test.h | 44 +++++++++++++++++++++++++++++++++++++++ src/willow/willow.cpp | 29 ++++++++++++++++++++++++++ src/willow/willow.h | 14 +++++++++++++ 7 files changed, 162 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/main.cpp create mode 100644 src/willow/CMakeLists.txt create mode 100644 src/willow/reporters.h create mode 100644 src/willow/test.h create mode 100644 src/willow/willow.cpp create mode 100644 src/willow/willow.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ae263a3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.30) +project(willow-test LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_compile_options(-Wall) +add_compile_options(-Wextra) +add_compile_options(-Wconversion) +add_compile_options(-Wimplicit-fallthrough) + +add_subdirectory(src/willow) +add_executable(willow_test src/main.cpp) +target_link_libraries(willow_test PRIVATE willow) diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8e63b82 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#include "willow/reporters.h" +#include "willow/willow.h" + +auto test_pass() -> int { + return 0; +} + +auto test_fail() -> int { + return 1; +} + +auto test_skip() -> int { + return 1; +} + +auto main() -> int { + Willow::DefaultReporter r = {}; + return Willow::run_tests( + { + {"Pass", test_pass}, + {"Fail", test_fail}, + {"Skipped", test_skip, Willow::Status::Skip}, + }, + r); +} diff --git a/src/willow/CMakeLists.txt b/src/willow/CMakeLists.txt new file mode 100644 index 0000000..868b0c7 --- /dev/null +++ b/src/willow/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(willow STATIC + willow.cpp +) + +target_include_directories(willow PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) diff --git a/src/willow/reporters.h b/src/willow/reporters.h new file mode 100644 index 0000000..93cdaeb --- /dev/null +++ b/src/willow/reporters.h @@ -0,0 +1,29 @@ +#ifndef WILLOW_REPORTER_H +#define WILLOW_REPORTER_H + +#include + +#include "test.h" + +namespace Willow { + class Reporter { + public: + virtual auto print(const Test& test) -> void = 0; + virtual ~Reporter() = default; + }; + + class DefaultReporter : public Reporter { + private: + int test_count = 0; + + public: + inline DefaultReporter() {}; + + inline auto print(const Test& test) -> void { + std::println("[{}] {}\t{}", ++test_count, test.name, toString(test.status)); + } + }; + +} // namespace Willow + +#endif // WILLOW_REPORTER_H diff --git a/src/willow/test.h b/src/willow/test.h new file mode 100644 index 0000000..e302b31 --- /dev/null +++ b/src/willow/test.h @@ -0,0 +1,44 @@ +#ifndef WILLOW_TEST_H +#define WILLOW_TEST_H + +#include + +namespace Willow { + using TestFn = int (*)(); + + enum class Status { None, Pass, Fail, Skip }; + + inline auto toString(const Status& st) -> std::string { + switch (st) { + case Status::None: + return "None"; + break; + case Status::Pass: + return "Pass"; + break; + case Status::Fail: + return "Fail"; + break; + case Status::Skip: + return "Skip"; + break; + }; + + return ""; + } + + struct Test { + std::string name; + TestFn fn; + int retcode = 0; + Status status = Status::None; + + Test(std::string given_name, TestFn f) : name {given_name}, fn {f} {} + Test(std::string given_name, TestFn f, Status st) + : name {given_name}, fn {f}, status {st} {} + + auto operator()() -> void { retcode = fn(); } + }; +}; // namespace Willow + +#endif // WILLOW_TEST_H diff --git a/src/willow/willow.cpp b/src/willow/willow.cpp new file mode 100644 index 0000000..f73952d --- /dev/null +++ b/src/willow/willow.cpp @@ -0,0 +1,29 @@ +#include "willow.h" + +#include + +#include "reporters.h" +#include "test.h" + +namespace Willow { + auto run_tests(std::vector tests, Reporter& reporter) -> int { + for (auto&& test : tests) { + if (test.status == Status::Skip) { + continue; + } + + test(); + if (test.retcode != 0) { + test.status = Status::Fail; + continue; + } + + test.status = Status::Pass; + } + + std::for_each(tests.begin(), tests.end(), [&reporter](Test& t) { reporter.print(t); }); + + return std::count_if( + tests.begin(), tests.end(), [](Test& t) { return t.status == Status::Fail; }); + } +} // namespace Willow diff --git a/src/willow/willow.h b/src/willow/willow.h new file mode 100644 index 0000000..bacba2c --- /dev/null +++ b/src/willow/willow.h @@ -0,0 +1,14 @@ +#ifndef WILLOW_H +#define WILLOW_H + +#include +#include + +#include "reporters.h" +#include "test.h" + +namespace Willow { + auto run_tests(std::vector, Reporter&) -> int; +} + +#endif // WILLOW_H From 0651033232de3e4bfe43f1051200b30329e13e4b Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sat, 1 Nov 2025 15:47:34 +0000 Subject: [PATCH 3/8] pre-commit reporter and cleanup function --- .gitignore | 2 + CMakeLists.txt | 1 + src/main.cpp | 2 +- src/willow/reporters.h | 93 ++++++++++++++++++++++++++++++++++++++++++ src/willow/test.h | 6 +-- src/willow/willow.cpp | 1 + 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index be50f60..ec9da65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ iris.log build/ +.gdb_history +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index ae263a3..9229dd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(willow-test LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +add_compile_options(-g) add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Wconversion) diff --git a/src/main.cpp b/src/main.cpp index 8e63b82..fa6d2f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,7 @@ auto test_skip() -> int { } auto main() -> int { - Willow::DefaultReporter r = {}; + Willow::PreCommitReporter r = {}; return Willow::run_tests( { {"Pass", test_pass}, diff --git a/src/willow/reporters.h b/src/willow/reporters.h index 93cdaeb..8bb3e1f 100644 --- a/src/willow/reporters.h +++ b/src/willow/reporters.h @@ -9,6 +9,7 @@ namespace Willow { class Reporter { public: virtual auto print(const Test& test) -> void = 0; + virtual auto cleanup() -> void = 0; virtual ~Reporter() = default; }; @@ -22,6 +23,98 @@ namespace Willow { inline auto print(const Test& test) -> void { std::println("[{}] {}\t{}", ++test_count, test.name, toString(test.status)); } + + inline auto cleanup() -> void {} + }; + + // A reporter that displays based on the output from pre-commit + class PreCommitReporter : public Reporter { + struct Results { + int pass = 0; + int fail = 0; + int skip = 0; + }; + + const int screen_len = 80; + Results results = {}; + + public: + inline auto print(const Test& test) -> void { + const int name_len = test.name.size(); + const int status_len = toString(test.status).size(); + const std::string ansi = highlight(test.status); + + if (test.status == Status::Fail) { + results.fail++; + } else if (test.status == Status::Skip) { + results.skip++; + } else { + results.pass++; + } + + std::println( + "{}{}{}{}\x1b[0m", test.name, + std::string(screen_len - (name_len + status_len), '.'), ansi, + toString(test.status)); + } + + inline auto cleanup() -> void { + Status final = + (results.fail ? Status::Fail : (results.skip ? Status::Skip : Status::Pass)); + + auto make_bar = [&](std::size_t len) { return std::string((len - 2) / 2, '='); }; + + switch (final) { + case Status::Pass: { + std::string msg = "All tests passed!"; + std::println( + "\x1b[32m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg, + make_bar(screen_len - msg.size())); + break; + } + + case Status::Skip: { + std::string msg = std::to_string(results.pass) + " tests passed (" + + std::to_string(results.skip) + " skipped)"; + std::println( + "\x1b[33m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg, + make_bar(screen_len - msg.size())); + break; + } + + case Status::Fail: { + std::string msg = std::to_string(results.fail) + " tests failed, (" + + std::to_string(results.pass) + " passed)"; + std::println( + "\x1b[31m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg, + make_bar(screen_len - msg.size())); + break; + } + + case Status::None: + default: + break; + } + } + + inline constexpr auto highlight(const Status& st) -> std::string { + switch (st) { + case Status::None: + return "\x1b[0m"; + break; + case Status::Pass: + return "\x1b[42m"; + break; + case Status::Fail: + return "\x1b[41m"; + break; + case Status::Skip: + return "\x1b[43m"; + break; + } + + return ""; + } }; } // namespace Willow diff --git a/src/willow/test.h b/src/willow/test.h index e302b31..4bb339b 100644 --- a/src/willow/test.h +++ b/src/willow/test.h @@ -14,13 +14,13 @@ namespace Willow { return "None"; break; case Status::Pass: - return "Pass"; + return "Passed"; break; case Status::Fail: - return "Fail"; + return "Failed"; break; case Status::Skip: - return "Skip"; + return "Skipped"; break; }; diff --git a/src/willow/willow.cpp b/src/willow/willow.cpp index f73952d..32d9f20 100644 --- a/src/willow/willow.cpp +++ b/src/willow/willow.cpp @@ -22,6 +22,7 @@ namespace Willow { } std::for_each(tests.begin(), tests.end(), [&reporter](Test& t) { reporter.print(t); }); + reporter.cleanup(); return std::count_if( tests.begin(), tests.end(), [](Test& t) { return t.status == Status::Fail; }); From 790f04b7e128eabb16082e59686d4c3c86641339 Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sun, 2 Nov 2025 11:11:00 +0000 Subject: [PATCH 4/8] unit tests with willow --- src/main.cpp | 31 +++++++++++++------------------ src/tests/testReporter.h | 13 +++++++++++++ src/tests/test_reporters.h | 14 ++++++++++++++ src/tests/test_test.h | 24 ++++++++++++++++++++++++ src/tests/test_willow.h | 22 ++++++++++++++++++++++ src/willow/reporters.h | 21 ++++++++++----------- src/willow/willow.cpp | 24 +----------------------- src/willow/willow.h | 26 +++++++++++++++++++++++--- 8 files changed, 120 insertions(+), 55 deletions(-) create mode 100644 src/tests/testReporter.h create mode 100644 src/tests/test_reporters.h create mode 100644 src/tests/test_test.h create mode 100644 src/tests/test_willow.h diff --git a/src/main.cpp b/src/main.cpp index fa6d2f7..1ecfa34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,25 +1,20 @@ +#include "tests/test_reporters.h" +#include "tests/test_test.h" +#include "tests/test_willow.h" #include "willow/reporters.h" #include "willow/willow.h" -auto test_pass() -> int { - return 0; -} - -auto test_fail() -> int { - return 1; -} - -auto test_skip() -> int { - return 1; -} - auto main() -> int { - Willow::PreCommitReporter r = {}; - return Willow::run_tests( + Willow::PreCommitReporter reporter = {}; + + return Willow::runTests( { - {"Pass", test_pass}, - {"Fail", test_fail}, - {"Skipped", test_skip, Willow::Status::Skip}, + {"test_runTests", test_runTests}, + {"test_toString", test_toString}, + {"test_Test_Operator()", test_Test_Operator}, + {"PreCommitReporter::print", TestPreCommitReporter::test_print}, + {"PreCommitReporter::cleanup", TestPreCommitReporter::test_cleanup}, + {"PreCommitReporter::highlight", TestPreCommitReporter::test_highlight}, }, - r); + reporter); } diff --git a/src/tests/testReporter.h b/src/tests/testReporter.h new file mode 100644 index 0000000..b5abc86 --- /dev/null +++ b/src/tests/testReporter.h @@ -0,0 +1,13 @@ +#ifndef WILLOW_TEST_SILENT_REPORTER_H +#define WILLOW_TEST_SILENT_REPORTER_H + +#include "willow/reporters.h" + +class SilentReporter : public Willow::Reporter { + public: + constexpr SilentReporter() {} + constexpr inline auto print([[maybe_unused]] const Willow::Test& test) -> void {} + constexpr inline auto cleanup() -> void {} +}; + +#endif // WILLOW_TEST_SILENT_REPORTER_H diff --git a/src/tests/test_reporters.h b/src/tests/test_reporters.h new file mode 100644 index 0000000..b007000 --- /dev/null +++ b/src/tests/test_reporters.h @@ -0,0 +1,14 @@ +#include "willow/reporters.h" + +// PreCommitReporter +class TestPreCommitReporter : public Willow::PreCommitReporter { + public: + static constexpr inline auto test_print() -> int { + // NOTE: WE can test the logic and what's held in `results` + return 0; + } + + static constexpr inline auto test_cleanup() -> int { return 0; } + + static constexpr inline auto test_highlight() -> int { return 0; } +}; diff --git a/src/tests/test_test.h b/src/tests/test_test.h new file mode 100644 index 0000000..6d4aaf5 --- /dev/null +++ b/src/tests/test_test.h @@ -0,0 +1,24 @@ +#include "willow/test.h" + +constexpr inline auto test_toString() -> int { + if (Willow::toString(Willow::Status::None) != "None") { + return 1; + } + if (Willow::toString(Willow::Status::Pass) != "Passed") { + return 2; + } + if (Willow::toString(Willow::Status::Fail) != "Failed") { + return 3; + } + if (Willow::toString(Willow::Status::Skip) != "Skipped") { + return 4; + } + return 0; +} + +constexpr inline auto test_Test_Operator() -> int { + Willow::Test t = {"t", [] { return 42; }}; + t(); + + return !(t.retcode == 42); +} diff --git a/src/tests/test_willow.h b/src/tests/test_willow.h new file mode 100644 index 0000000..5cb94bd --- /dev/null +++ b/src/tests/test_willow.h @@ -0,0 +1,22 @@ +#include "testReporter.h" +#include "willow/willow.h" + +namespace FixtureFuncs { + constexpr auto pass() -> int { + return 0; + } + constexpr auto fail() -> int { + return 1; + } +}; // namespace FixtureFuncs + +constexpr inline auto test_runTests() -> int { + SilentReporter r = {}; + int ret = Willow::runTests( + {{"pass", FixtureFuncs::pass}, + {"fail", FixtureFuncs::fail}, + {"skip", FixtureFuncs::fail, Willow::Status::Skip}}, + r); + + return !(ret == 1); +} diff --git a/src/willow/reporters.h b/src/willow/reporters.h index 8bb3e1f..2a27376 100644 --- a/src/willow/reporters.h +++ b/src/willow/reporters.h @@ -8,9 +8,9 @@ namespace Willow { class Reporter { public: - virtual auto print(const Test& test) -> void = 0; - virtual auto cleanup() -> void = 0; - virtual ~Reporter() = default; + virtual constexpr auto print(const Test& test) -> void = 0; + virtual constexpr auto cleanup() -> void = 0; + virtual constexpr ~Reporter() = default; }; class DefaultReporter : public Reporter { @@ -18,17 +18,16 @@ namespace Willow { int test_count = 0; public: - inline DefaultReporter() {}; - - inline auto print(const Test& test) -> void { + inline constexpr auto print(const Test& test) -> void { std::println("[{}] {}\t{}", ++test_count, test.name, toString(test.status)); } - inline auto cleanup() -> void {} + inline constexpr auto cleanup() -> void {} }; // A reporter that displays based on the output from pre-commit class PreCommitReporter : public Reporter { + protected: struct Results { int pass = 0; int fail = 0; @@ -39,9 +38,9 @@ namespace Willow { Results results = {}; public: - inline auto print(const Test& test) -> void { - const int name_len = test.name.size(); - const int status_len = toString(test.status).size(); + inline constexpr auto print(const Test& test) -> void { + const std::size_t name_len = test.name.size(); + const std::size_t status_len = toString(test.status).size(); const std::string ansi = highlight(test.status); if (test.status == Status::Fail) { @@ -58,7 +57,7 @@ namespace Willow { toString(test.status)); } - inline auto cleanup() -> void { + inline constexpr auto cleanup() -> void { Status final = (results.fail ? Status::Fail : (results.skip ? Status::Skip : Status::Pass)); diff --git a/src/willow/willow.cpp b/src/willow/willow.cpp index 32d9f20..858d882 100644 --- a/src/willow/willow.cpp +++ b/src/willow/willow.cpp @@ -5,26 +5,4 @@ #include "reporters.h" #include "test.h" -namespace Willow { - auto run_tests(std::vector tests, Reporter& reporter) -> int { - for (auto&& test : tests) { - if (test.status == Status::Skip) { - continue; - } - - test(); - if (test.retcode != 0) { - test.status = Status::Fail; - continue; - } - - test.status = Status::Pass; - } - - std::for_each(tests.begin(), tests.end(), [&reporter](Test& t) { reporter.print(t); }); - reporter.cleanup(); - - return std::count_if( - tests.begin(), tests.end(), [](Test& t) { return t.status == Status::Fail; }); - } -} // namespace Willow +namespace Willow {} // namespace Willow diff --git a/src/willow/willow.h b/src/willow/willow.h index bacba2c..0b9e479 100644 --- a/src/willow/willow.h +++ b/src/willow/willow.h @@ -1,14 +1,34 @@ #ifndef WILLOW_H #define WILLOW_H -#include #include #include "reporters.h" #include "test.h" namespace Willow { - auto run_tests(std::vector, Reporter&) -> int; -} + constexpr auto runTests(std::vector tests, Reporter& reporter) -> int { + for (auto&& test : tests) { + if (test.status == Status::Skip) { + continue; + } + + test(); + if (test.retcode != 0) { + test.status = Status::Fail; + continue; + } + + test.status = Status::Pass; + } + + std::for_each(tests.begin(), tests.end(), [&reporter](Test& t) { reporter.print(t); }); + reporter.cleanup(); + + return int32_t(std::count_if(tests.begin(), tests.end(), [](Test& t) { + return t.status == Status::Fail; + })); + } +} // namespace Willow #endif // WILLOW_H From 190ea76601b18bf351696df4b9599684dd08cd99 Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sun, 2 Nov 2025 12:09:37 +0000 Subject: [PATCH 5/8] add message for erroring --- src/main.cpp | 6 ++++++ src/tests/test_reporters.h | 6 +++--- src/tests/test_test.h | 12 +++++++++--- src/tests/test_willow.h | 6 +++--- src/willow/reporters.h | 20 ++++++++++++++------ src/willow/test.h | 7 +++++-- src/willow/willow.h | 5 +++++ 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1ecfa34..4d73f9f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,11 +4,17 @@ #include "willow/reporters.h" #include "willow/willow.h" +constexpr auto fail_test(Willow::Test* test) -> int { + Willow::alert(test, "This test fails"); + return 1; +} + auto main() -> int { Willow::PreCommitReporter reporter = {}; return Willow::runTests( { + {"fail", fail_test}, {"test_runTests", test_runTests}, {"test_toString", test_toString}, {"test_Test_Operator()", test_Test_Operator}, diff --git a/src/tests/test_reporters.h b/src/tests/test_reporters.h index b007000..13937a5 100644 --- a/src/tests/test_reporters.h +++ b/src/tests/test_reporters.h @@ -3,12 +3,12 @@ // PreCommitReporter class TestPreCommitReporter : public Willow::PreCommitReporter { public: - static constexpr inline auto test_print() -> int { + static constexpr auto test_print([[maybe_unused]] Willow::Test* test) -> int { // NOTE: WE can test the logic and what's held in `results` return 0; } - static constexpr inline auto test_cleanup() -> int { return 0; } + static constexpr auto test_cleanup([[maybe_unused]] Willow::Test* test) -> int { return 0; } - static constexpr inline auto test_highlight() -> int { return 0; } + static constexpr auto test_highlight([[maybe_unused]] Willow::Test* test) -> int { return 0; } }; diff --git a/src/tests/test_test.h b/src/tests/test_test.h index 6d4aaf5..62e2642 100644 --- a/src/tests/test_test.h +++ b/src/tests/test_test.h @@ -1,6 +1,6 @@ #include "willow/test.h" -constexpr inline auto test_toString() -> int { +constexpr auto test_toString([[maybe_unused]] Willow::Test* test) -> int { if (Willow::toString(Willow::Status::None) != "None") { return 1; } @@ -16,8 +16,14 @@ constexpr inline auto test_toString() -> int { return 0; } -constexpr inline auto test_Test_Operator() -> int { - Willow::Test t = {"t", [] { return 42; }}; +namespace FixtureFuncs { + constexpr auto op_bracket([[maybe_unused]] Willow::Test* t) -> int { + return 42; + } +} // namespace FixtureFuncs + +constexpr auto test_Test_Operator([[maybe_unused]] Willow::Test* test) -> int { + Willow::Test t = {"t", FixtureFuncs::op_bracket}; t(); return !(t.retcode == 42); diff --git a/src/tests/test_willow.h b/src/tests/test_willow.h index 5cb94bd..5e3fdaf 100644 --- a/src/tests/test_willow.h +++ b/src/tests/test_willow.h @@ -2,15 +2,15 @@ #include "willow/willow.h" namespace FixtureFuncs { - constexpr auto pass() -> int { + constexpr auto pass([[maybe_unused]] Willow::Test* t) -> int { return 0; } - constexpr auto fail() -> int { + constexpr auto fail([[maybe_unused]] Willow::Test* t) -> int { return 1; } }; // namespace FixtureFuncs -constexpr inline auto test_runTests() -> int { +constexpr auto test_runTests([[maybe_unused]] Willow::Test* test) -> int { SilentReporter r = {}; int ret = Willow::runTests( {{"pass", FixtureFuncs::pass}, diff --git a/src/willow/reporters.h b/src/willow/reporters.h index 2a27376..0609b57 100644 --- a/src/willow/reporters.h +++ b/src/willow/reporters.h @@ -43,18 +43,26 @@ namespace Willow { const std::size_t status_len = toString(test.status).size(); const std::string ansi = highlight(test.status); + std::println( + "{}{}{}{}\x1b[0m", test.name, + std::string(screen_len - (name_len + status_len), '.'), ansi, + toString(test.status)); + if (test.status == Status::Fail) { results.fail++; + std::println("\x1b[31m\tReturn code: {}\x1b[0m", test.retcode); + if (test.msg.has_value()) { + std::println("\x1b[31m\t{}\x1b[0m", test.msg.value()); + } + } else if (test.status == Status::Skip) { results.skip++; + if (test.msg.has_value()) { + std::println("\x1b[33m\t{}\x1b[0m", test.msg.value()); + } } else { results.pass++; } - - std::println( - "{}{}{}{}\x1b[0m", test.name, - std::string(screen_len - (name_len + status_len), '.'), ansi, - toString(test.status)); } inline constexpr auto cleanup() -> void { @@ -67,7 +75,7 @@ namespace Willow { case Status::Pass: { std::string msg = "All tests passed!"; std::println( - "\x1b[32m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg, + "\x1b[32m{} {} {}\x1b[0m", make_bar((screen_len - msg.size()) + 1), msg, make_bar(screen_len - msg.size())); break; } diff --git a/src/willow/test.h b/src/willow/test.h index 4bb339b..2a517c5 100644 --- a/src/willow/test.h +++ b/src/willow/test.h @@ -4,7 +4,9 @@ #include namespace Willow { - using TestFn = int (*)(); + // forward declaration for type alias + struct Test; + using TestFn = int (*)(Test*); enum class Status { None, Pass, Fail, Skip }; @@ -31,13 +33,14 @@ namespace Willow { std::string name; TestFn fn; int retcode = 0; + std::optional msg = std::nullopt; Status status = Status::None; Test(std::string given_name, TestFn f) : name {given_name}, fn {f} {} Test(std::string given_name, TestFn f, Status st) : name {given_name}, fn {f}, status {st} {} - auto operator()() -> void { retcode = fn(); } + auto operator()() -> void { retcode = fn(this); } }; }; // namespace Willow diff --git a/src/willow/willow.h b/src/willow/willow.h index 0b9e479..452894c 100644 --- a/src/willow/willow.h +++ b/src/willow/willow.h @@ -29,6 +29,11 @@ namespace Willow { return t.status == Status::Fail; })); } + + constexpr auto alert(Test* test, std::string message) -> void { + test->msg = message; + } + } // namespace Willow #endif // WILLOW_H From aff388bdacc1e7ccd74fceee4e4d9bba1d00a9bf Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sun, 2 Nov 2025 12:18:55 +0000 Subject: [PATCH 6/8] github actions...maybe --- .github/workflows/main.yml | 18 ++++++++++++++++++ CMakeLists.txt | 1 + 2 files changed, 19 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f68e8f7 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,18 @@ +name: main +on: + pull_request: + branches: [ main ] +jobs: + pre_commit_run: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: pre-commit/action@v3.0.0 + unit_tests: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - run: sudo apt-get install clang-19 -y + - run: cmake -S . -B build && cmake --build build + - run: ./build/willow_test diff --git a/CMakeLists.txt b/CMakeLists.txt index 9229dd1..bf6718d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.30) +set(CMAKE_CXX_COMPILER "/usr/bin/clang++") project(willow-test LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) From ffccf91b638427c877460306bc893e3ce5cc9be6 Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sun, 2 Nov 2025 12:51:45 +0000 Subject: [PATCH 7/8] constexpr tostring --- src/willow/test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/willow/test.h b/src/willow/test.h index 2a517c5..754247b 100644 --- a/src/willow/test.h +++ b/src/willow/test.h @@ -10,7 +10,7 @@ namespace Willow { enum class Status { None, Pass, Fail, Skip }; - inline auto toString(const Status& st) -> std::string { + constexpr auto toString(const Status& st) -> std::string { switch (st) { case Status::None: return "None"; From 749f12156c74510ae6c7db3a2eab794075aabd4a Mon Sep 17 00:00:00 2001 From: ttibsi Date: Sun, 2 Nov 2025 13:12:13 +0000 Subject: [PATCH 8/8] alert test --- src/main.cpp | 7 +------ src/tests/test_willow.h | 10 ++++++++++ src/willow/reporters.h | 2 +- src/willow/test.h | 5 +++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4d73f9f..0e83379 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,18 +4,13 @@ #include "willow/reporters.h" #include "willow/willow.h" -constexpr auto fail_test(Willow::Test* test) -> int { - Willow::alert(test, "This test fails"); - return 1; -} - auto main() -> int { Willow::PreCommitReporter reporter = {}; return Willow::runTests( { - {"fail", fail_test}, {"test_runTests", test_runTests}, + {"test_alert", test_alert}, {"test_toString", test_toString}, {"test_Test_Operator()", test_Test_Operator}, {"PreCommitReporter::print", TestPreCommitReporter::test_print}, diff --git a/src/tests/test_willow.h b/src/tests/test_willow.h index 5e3fdaf..1ff04a4 100644 --- a/src/tests/test_willow.h +++ b/src/tests/test_willow.h @@ -20,3 +20,13 @@ constexpr auto test_runTests([[maybe_unused]] Willow::Test* test) -> int { return !(ret == 1); } + +constexpr auto test_alert([[maybe_unused]] Willow::Test* test) -> int { + Willow::Test t {}; + Willow::alert(&t, "fail"); + + if (t.msg.has_value() && t.msg.value() == "fail") { + return 0; + } + return 1; +} diff --git a/src/willow/reporters.h b/src/willow/reporters.h index 0609b57..63f8e6b 100644 --- a/src/willow/reporters.h +++ b/src/willow/reporters.h @@ -34,7 +34,7 @@ namespace Willow { int skip = 0; }; - const int screen_len = 80; + const std::size_t screen_len = 80; Results results = {}; public: diff --git a/src/willow/test.h b/src/willow/test.h index 754247b..bb8fa15 100644 --- a/src/willow/test.h +++ b/src/willow/test.h @@ -36,8 +36,9 @@ namespace Willow { std::optional msg = std::nullopt; Status status = Status::None; - Test(std::string given_name, TestFn f) : name {given_name}, fn {f} {} - Test(std::string given_name, TestFn f, Status st) + constexpr Test() : name(""), fn(NULL) {} + constexpr Test(std::string given_name, TestFn f) : name {given_name}, fn {f} {} + constexpr Test(std::string given_name, TestFn f, Status st) : name {given_name}, fn {f}, status {st} {} auto operator()() -> void { retcode = fn(this); }