From 723ade84ea54f9d7fd4f6ea3bc0fa886428f39ce Mon Sep 17 00:00:00 2001 From: light7734 Date: Mon, 29 Sep 2025 09:47:59 +0330 Subject: [PATCH] feat(test): add option parsing && the --stop-on-fail & -h options --- modules/test/private/entrypoint.cpp | 54 ++++++++++++++++++++++++++--- modules/test/public/test.hpp | 48 ++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/modules/test/private/entrypoint.cpp b/modules/test/private/entrypoint.cpp index 5404f4a..f867ba3 100644 --- a/modules/test/private/entrypoint.cpp +++ b/modules/test/private/entrypoint.cpp @@ -1,15 +1,59 @@ #include -auto main() -> int32_t +using namespace ::lt::test; +using namespace ::lt::test::details; + +void parse_option(std::string_view option, Registry::Options &options) +{ + if (option == "--stop-on-fail") + { + options.stop_on_fail = true; + return; + } + + throw std::invalid_argument { std::format("Invalid argument: {}", option) }; +} + +void print_help() +{ + std::println("Options: "); + std::println("--stop-on-fail --> Stops executing the remaining tests on first failure"); + std::println("--stats --> Print statistics about the tests without running any"); + std::println("--help | -h --> ~You just used it! :D"); +} + +auto main(int32_t argc, char **argv) -> int32_t try { - using namespace ::lt::test; - using namespace ::lt::test::details; + auto raw_arguments = std::span(argv, argc); - return Registry::run_all(); + auto options = Registry::Options {}; + for (auto idx = 0; auto &raw_argument : raw_arguments) + { + // First argument is the "cwd' + if (idx++ == 0) + { + continue; + } + + auto argument = std::string_view(raw_argument); + + if (argument == "-h" || argument == "--help") + { + print_help(); + return 0; + } + + if (argument.starts_with("--") || argument.starts_with("-")) + { + parse_option(argument, options); + } + } + + return Registry::run_all(options); } catch (const std::exception &exp) { - std::cout << "Terminated after uncaught exception:\n"; // NOLINT + std::cout << "Terminated after uncaught exception:\n"; std::cout << "exception.what: " << exp.what(); } diff --git a/modules/test/public/test.hpp b/modules/test/public/test.hpp index c923e88..21ea19a 100644 --- a/modules/test/public/test.hpp +++ b/modules/test/public/test.hpp @@ -10,6 +10,11 @@ namespace details { class Registry { public: + struct Options + { + bool stop_on_fail = false; + }; + using FuzzFunction = int32_t (*)(const uint8_t *, size_t); using SuiteFunction = void (*)(); @@ -30,11 +35,29 @@ public: instance().m_fuzz_harness = suite; } - static auto run_all() -> int32_t + static auto run_all(Options options) -> int32_t { + instance().m_options = options; + instance().print_options(); + for (auto &test : instance().m_suites) { - test(); + try + { + test(); + } + catch (const std::exception &exp) + { + if (options.stop_on_fail) + { + std::println("Quitting due to options.stop_on_fail == true"); + break; + } + + std::println("Uncaught exception when running suite:"); + std::println("\twhat: {}", exp.what()); + break; + } } std::cout << "Ran " << instance().m_failed_count + instance().m_pasesed_count << " tests:\n" @@ -67,7 +90,17 @@ public: ++instance().m_failed_count; } + static auto should_return_on_failure() -> bool + { + return instance().m_options.stop_on_fail; + } + private: + void print_options() + { + std::println("stop-on-failure: {}", m_options.stop_on_fail); + } + Registry() { std::cout << "________________________________________________________________\n"; @@ -79,6 +112,8 @@ private: return registry; } + Options m_options {}; + std::vector m_suites; FuzzFunction m_fuzz_harness {}; @@ -92,7 +127,8 @@ private: struct Case { - auto operator=(std::invocable auto test) -> void // NOLINT + // NOLINTNEXTLINE(misc-unconventional-assign-operator) + auto operator=(std::invocable auto test) -> void { std::cout << "[Running-----------] --> "; std::cout << name << '\n'; @@ -106,7 +142,11 @@ struct Case std::cout << exp.what() << "\n"; std::cout << "[-----------FAIL !!]" << "\n\n"; details::Registry::increment_failed_count(); - return; // TODO(Light): Should we run the remaining tests after a failure? + + if (details::Registry::should_return_on_failure()) + { + throw; + } } details::Registry::increment_passed_count();