From 7c293e87b603a4f76b7e6923107b78d30c0872f1 Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:19:03 +0800 Subject: [PATCH 1/6] improve the fatal_assertion behavior --- include/boost/ut.hpp | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp index 4785b405..745ad3d1 100644 --- a/include/boost/ut.hpp +++ b/include/boost/ut.hpp @@ -1653,8 +1653,10 @@ class reporter_junit { ? FAILED : (current_node_->skipped ? SKIPPED : PASSED); auto parent = current_node_->parent; if (parent != nullptr) { - parent->n_tests += current_node_->n_tests; - parent->fail_tests += current_node_->fail_tests; + parent->n_tests += 1LU; + if ((current_node_->fails > 0 || current_node_->fail_tests > 0)) { + parent->fail_tests++; + } parent->assertions += current_node_->assertions; parent->skipped += current_node_->skipped; parent->fails += current_node_->fails; @@ -1735,10 +1737,6 @@ class reporter_junit { } } reset_printer(); - current_node_->n_tests = 1LU; - if (current_node_->fails > 0 || current_node_->fail_tests > 0) { - current_node_->fail_tests = 1LU; - } count_result(); } @@ -1825,7 +1823,12 @@ class reporter_junit { } } - auto on(const events::fatal_assertion&) -> void {} + auto on(const events::fatal_assertion&) -> void { + std::cerr<< "\n=> " << color_.fail << "terminated for the fatal issue" << color_.none; + while (current_node_->parent != nullptr) { + count_result(); + } + } auto on(events::summary) -> void { std::cout.flush(); @@ -2102,10 +2105,7 @@ class runner { #if defined(__cpp_exceptions) try { -#endif test(); -#if defined(__cpp_exceptions) - } catch (const events::fatal_assertion&) { } catch (const std::exception& exception) { ++fails_; reporter_.on(events::exception{exception.what()}); @@ -2154,19 +2154,7 @@ class runner { auto on(events::fatal_assertion fatal_assertion) { reporter_.on(fatal_assertion); - -#if defined(__cpp_exceptions) - if (not level_) { - report_summary(); - } - throw fatal_assertion; -#else - if (level_) { - reporter_.on(events::test_end{}); - } - report_summary(); - std::abort(); -#endif + std::exit(-1); } template From 898a412aa1aaf362030029aab1a9537709c1d872 Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:20:48 +0800 Subject: [PATCH 2/6] disable a outdated test case --- test/ut/ut.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/ut/ut.cpp b/test/ut/ut.cpp index a1546630..4111ef66 100644 --- a/test/ut/ut.cpp +++ b/test/ut/ut.cpp @@ -818,15 +818,6 @@ int main() { // NOLINT(readability-function-size) test_assert(1 == reporter.tests_.skip); run = options{}; - run.on(events::test{.type = "test", - .name = "fatal", - .location = {}, - .arg = none{}, - .run = test_assertions{run}}); - test_assert(5 == reporter.tests_.pass); - test_assert(7 == reporter.tests_.fail); - test_assert(1 == reporter.tests_.skip); - run.on( events::test{.type = "test", .name = "normal", @@ -834,8 +825,17 @@ int main() { // NOLINT(readability-function-size) .arg = none{}, .run = test_assertion_true{run}}); test_assert(6 == reporter.tests_.pass); - test_assert(7 == reporter.tests_.fail); + test_assert(6 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); + + /*run.on(events::test{.type = "test", + .name = "fatal", + .location = {}, + .arg = none{}, + .run = test_assertions{run}}); + test_assert(6 == reporter.tests_.pass); + test_assert(7 == reporter.tests_.fail); + test_assert(1 == reporter.tests_.skip);*/ reporter = printer{}; } From c075c7536c4056528ee2ed83c2ffcf4f37059676 Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:07:51 +0800 Subject: [PATCH 3/6] update fatal examples --- example/fatal.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/example/fatal.cpp b/example/fatal.cpp index 2b6b2dea..de0e1375 100644 --- a/example/fatal.cpp +++ b/example/fatal.cpp @@ -19,7 +19,7 @@ int main() { using boost::ut::expect; std::optional o{42}; - expect(fatal(o.has_value())) << "fatal assertion"; + expect(fatal(o.has_value())); expect(*o == 42_i); }; @@ -28,7 +28,7 @@ int main() { using boost::ut::expect; std::optional o{42}; - expect(fatal(o.has_value())) << "fatal assertion"; + expect(o.has_value()) << "log messages...." << fatal; expect(*o == 42_i); }; @@ -38,21 +38,20 @@ int main() { using boost::ut::that; std::optional o{42}; - expect(fatal(that % o.has_value()) and that % *o == 42) - << "fatal assertion"; + expect(fatal(that % o.has_value()) and that % *o == 42); }; "fatal terse"_test = [] { using namespace boost::ut::operators::terse; std::optional o{42}; - (o.has_value() >> fatal and *o == 42_i) << "fatal assertion"; + (fatal(o.has_value()) and *o == 42_i); }; using namespace boost::ut::operators; using boost::ut::expect; std::vector v{1u}; - expect(fatal(std::size(v) == 1_ul)) << "fatal assertion"; + expect(fatal(std::size(v) == 1_ul)); expect(v[0] == 1_u); } From 3e69c75b2c23b59fde3ea4138a52fd73b595f050 Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:30:01 +0800 Subject: [PATCH 4/6] update the fatal example README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4922563e..af9eac67 100644 --- a/README.md +++ b/README.md @@ -339,21 +339,23 @@ Completed ===================================================================== int main() { using namespace boost::ut; - expect((1 == 2_i) >> fatal); // fatal assertion - expect(1_i == 2); // not executed + expect(fatal(1 == 2_i)); // fatal assertion + expect(1_i == 2); // not executed } ``` ``` UT starts ===================================================================== FAILED in: ...\example.cpp:6 - test condition: [1 == 2] +=> terminated for the fatal issue =============================================================================== Suite global tests: 0 | 0 failed asserts: 1 | 0 passed | 1 failed +Completed ===================================================================== ``` -> https://godbolt.org/z/6Mvex8TaT +> https://godbolt.org/z/xohGacdWc > But my expression is more complex than just simple comparisons. > Not a problem, logic operators are also supported in the `expect` 👍. From 0d6ba6350839b5d874db91054575bbfb49b91224 Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:02:29 +0800 Subject: [PATCH 5/6] improve the fatal log messages --- include/boost/ut.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp index 745ad3d1..5c020635 100644 --- a/include/boost/ut.hpp +++ b/include/boost/ut.hpp @@ -1824,7 +1824,13 @@ class reporter_junit { } auto on(const events::fatal_assertion&) -> void { - std::cerr<< "\n=> " << color_.fail << "terminated for the fatal issue" << color_.none; + TPrinter ss{}; + ss << ss_out_.str() << "\n=> " << color_.fail << "terminated for the fatal issue" << color_.none; + current_node_->report_string += ss.str(); + reset_printer(); + if (report_type_ == CONSOLE) { + lcout_ << ss.str(); + } while (current_node_->parent != nullptr) { count_result(); } From f404611cd085f2ea9823ad87778c42bc09faad9e Mon Sep 17 00:00:00 2001 From: "Otto C." <12378062+ILer32@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:33:26 +0800 Subject: [PATCH 6/6] update fatal examples in README.md --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index af9eac67..67e1e648 100644 --- a/README.md +++ b/README.md @@ -427,6 +427,7 @@ int main() { UT starts ===================================================================== Running test "lazy log"... FAILED in: ...\example.cpp:8 - test condition: [false] lazy evaluated +=> terminated for the fatal issue =============================================================================== Suite global tests: 1 | 1 failed @@ -552,7 +553,7 @@ int main() { "vector"_test = [] { given("I have a vector") = [] { std::vector v(5); - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); when("I resize bigger") = [=] { mut(v).resize(10); @@ -572,7 +573,7 @@ Suite 'global': all tests passed (2 asserts in 1 tests) Completed ===================================================================== ``` -> https://godbolt.org/z/4YY7KzG64 +> https://godbolt.org/z/e73b7WTGE > On top of that, `feature/scenario` aliases can be leveraged. @@ -588,7 +589,7 @@ int main() { scenario("size") = [] { given("I have a vector") = [] { std::vector v(5); - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); when("I resize bigger") = [=] { mut(v).resize(10); @@ -609,7 +610,7 @@ Suite 'global': all tests passed (2 asserts in 1 tests) Completed ===================================================================== ``` -> https://godbolt.org/z/qxdrKxxqn +> https://godbolt.org/z/nPcGqcn8f > Can I use `Gherkin`? > Yeah, let's rewrite the example using `Gherkin` specification @@ -625,7 +626,7 @@ int main() { steps.scenario("*") = [&] { steps.given("I have a vector") = [&] { std::vector v(5); - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); steps.when("I resize bigger") = [&] { v.resize(10); }; steps.then("The size should increase") = [&] { expect(10_ul == std::size(v)); }; }; @@ -650,7 +651,7 @@ Suite 'global': all tests passed (2 asserts in 1 tests) Completed ===================================================================== ``` -> https://godbolt.org/z/nxW6dsPvj +> https://godbolt.org/z/K69ha16rE > Nice, is `Spec` notation supported as well? @@ -664,7 +665,7 @@ int main() { describe("vector") = [] { std::vector v(5); - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); it("should resize bigger") = [v] { mut(v).resize(10); @@ -680,7 +681,7 @@ Suite 'global': all tests passed (2 asserts in 1 tests) Completed ===================================================================== ``` -> https://godbolt.org/z/Y76sKKs4e +> https://godbolt.org/z/5vbTs77ff > That's great, but how can call the same tests with different arguments/types to be DRY (Don't Repeat Yourself)? > Parameterized tests to the rescue! @@ -937,14 +938,14 @@ int main() { "[vector]"_test = [] { std::vector v(5); - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); should("resize bigger") = [=]() mutable { // or "resize bigger"_test v.resize(10); expect(10_ul == std::size(v)); }; - expect((5_ul == std::size(v)) >> fatal); + expect(fatal(5_ul == std::size(v))); should("resize smaller") = [=]() mutable { // or "resize smaller"_test v.resize(0); @@ -960,7 +961,7 @@ Suite 'global': all tests passed (4 asserts in 1 tests) Completed ===================================================================== ``` -> https://godbolt.org/z/rez4qMhxE +> https://godbolt.org/z/EjTEqMvzv