diff --git a/README.md b/README.md index 8cdaaa3c..cbd621f6 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Sounds intriguing/interesting? Learn more at: * Easy to use ([Minimal API](#api) - `expect, test, suite`) * Fast to compile/execute ([Benchmarks](#benchmarks)) * Features ([Assertions](example/expect.cpp), [Suites](example/suite.cpp), [Tests](example/skip.cpp), [Sections](example/section.cpp), [Parameterized](example/parameterized.cpp), [BDD](example/BDD.cpp), [Matchers](example/matcher.cpp), [Logging](example/log.cpp), [Runners](example/cfg/runner.cpp), [Reporters](example/cfg/reporter.cpp), [...](example)) +* Integrations ([ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp/releases/tag/v.7.0.0)) > `*` - Limitations may apply @@ -783,8 +784,8 @@ namespace cfg { public: template auto on(ut::events::test test) { test(); } template auto on(ut::events::skip) {} - template - auto on(ut::events::assertion) -> bool { return true; } + template + auto on(ut::events::assertion) -> bool { return true; } auto on(ut::events::fatal_assertion) {} template auto on(ut::events::log) {} }; @@ -812,10 +813,10 @@ namespace cfg { auto on(ut::events::test_skip) -> void {} auto on(ut::events::test_end) -> void {} template auto on(ut::events::log) -> void {} - template - auto on(ut::events::assertion_pass) -> void {} - template - auto on(ut::events::assertion_fail) -> void {} + template + auto on(ut::events::assertion_pass) -> void {} + template + auto on(ut::events::assertion_fail) -> void {} auto on(ut::events::fatal_assertion) -> void {} auto on(ut::events::exception) -> void {} auto on(ut::events::summary) -> void {} @@ -1066,7 +1067,11 @@ namespace boost::ut::inline v1_1_4 { class runner { public: /** - * @example cfg = { .filter = "test.section.*", .colors = { .none = "" }, .dry__run = true }; + * @example cfg = { + .filter = "test.section.*", + .colors = { .none = "" }, + .dry__run = true + }; * @param options.filter {default: "*"} runs all tests which names matches test.section.* filter * @param options.colors {default: { .none = "\033[0m", @@ -1106,12 +1111,12 @@ namespace boost::ut::inline v1_1_4 { /** * @example file.cpp:42: expect(42_i == 42); - * @param assertion.location { "file.cpp", 42 } * @param assertion.expr 42_i == 42 + * @param assertion.location { "file.cpp", 42 } * @return true if expr passes, false otherwise */ - template - auto on(ut::events::assertion) -> bool; + template + auto on(ut::events::assertion) -> bool; /** * @example !expect(2_i == 1) @@ -1147,9 +1152,10 @@ namespace boost::ut::inline v1_1_4 { class reporter { public: /** - * @example "name"_test = [] {}; + * @example file.cpp:42: "name"_test = [] {}; * @param test_begin.type ["test", "given", "when", "then"] * @param test_begin.name "name" + * @param test_begin.location { "file.cpp", 42 } */ auto on(ut::events::test_begin) -> void; @@ -1183,19 +1189,19 @@ namespace boost::ut::inline v1_1_4 { /** * @example file.cpp:42: expect(42_i == 42); - * @param assertion_pass.location { "file.cpp", 42 } * @param assertion_pass.expr 42_i == 42 + * @param assertion_pass.location { "file.cpp", 42 } */ - template - auto on(ut::events::assertion_pass) -> void; + template + auto on(ut::events::assertion_pass) -> void; /** * @example file.cpp:42: expect(42_i != 42); - * @param assertion_fail.location { "file.cpp", 42 } * @param assertion_fail.expr 42_i != 42 + * @param assertion_fail.location { "file.cpp", 42 } */ - template - auto on(ut::events::assertion_fail) -> void; + template + auto on(ut::events::assertion_fail) -> void; /** * @example !expect(2_i == 1) @@ -1464,6 +1470,34 @@ All tests passed (4 asserts in 3 tests)

+
    Standardization? +

+ +Personally, I believe that C++ standard could benefit from common testing primitives (`expect`, `""_test`) because: + +* It lowers the entry-level to the language (no need for third-party libraries) +* It improves the education aspect (one standard way of doing it) +* It makes the language more coherent/stable (consistent design with other features, stable API) +* It makes the testing a first class citizen (shows that the community cares about this aspect of the language) +* It allows to publish tests for the Standard Library (STL) in the standard way (coherency, easier to extend) +* It allows to act as additional documentation as a way to verify whether a particular implementation is conforming (quality, self-verification) +* It helps with establishing standard vocabulary for testing (common across STL and other projects) + +

+
+ +
    Mocks/Stubs/Fakes? +

+ +Consider integrating one of the following frameworks: + +* https://github.com/cpp-testing/GUnit/blob/master/docs/GMock.md +* https://github.com/eranpeer/FakeIt +* https://github.com/dascandy/hippomocks + +

+
+

diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index f5600323..73b5b7ed 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -53,10 +53,12 @@ example(test _test) example(tmp tmp) example(using using) -set(CMAKE_CXX_STANDARD 17) -example(expect expect_cpp17) -example(test test_cpp17) -example(suite suite_cpp17) -example(section section_cpp17) -example(should should_cpp17) -example(skip skip_cpp17) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_STANDARD 17) + example(expect expect_cpp17) + example(test test_cpp17) + example(suite suite_cpp17) + example(section section_cpp17) + example(should should_cpp17) + example(skip skip_cpp17) +endif() diff --git a/example/cfg/reporter.cpp b/example/cfg/reporter.cpp index 9761c16d..e0dbb5e7 100644 --- a/example/cfg/reporter.cpp +++ b/example/cfg/reporter.cpp @@ -19,10 +19,10 @@ class reporter { auto on(ut::events::test_end) -> void {} template auto on(ut::events::log) -> void {} - template - auto on(ut::events::assertion_pass) -> void {} - template - auto on(ut::events::assertion_fail) -> void {} + template + auto on(ut::events::assertion_pass) -> void {} + template + auto on(ut::events::assertion_fail) -> void {} auto on(ut::events::fatal_assertion) -> void {} auto on(ut::events::exception) -> void {} auto on(ut::events::summary) -> void {} diff --git a/example/cfg/runner.cpp b/example/cfg/runner.cpp index 2a65c21c..ed8d2c77 100644 --- a/example/cfg/runner.cpp +++ b/example/cfg/runner.cpp @@ -18,8 +18,8 @@ class runner { } template auto on(ut::events::skip) {} - template - auto on(ut::events::assertion) -> bool { + template + auto on(ut::events::assertion) -> bool { return true; } auto on(ut::events::fatal_assertion) {} diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp index ae5765f9..8b2c60e9 100644 --- a/include/boost/ut.hpp +++ b/include/boost/ut.hpp @@ -202,18 +202,18 @@ class function { namespace reflection { class source_location { public: -#if (__GNUC__ >= 9 or __clang_major__ >= 9) [[nodiscard]] static constexpr auto current( - const char* file = __builtin_FILE(), - int line = __builtin_LINE()) noexcept { +#if (__GNUC__ >= 9 or __clang_major__ >= 9) + const char* file = __builtin_FILE(), int line = __builtin_LINE() +#else + const char* file = {}, int line = {} +#endif + ) noexcept { source_location sl{}; sl.file_ = file; sl.line_ = line; return sl; } -#else - [[nodiscard]] static constexpr auto current() { return source_location{}; } -#endif [[nodiscard]] constexpr auto file_name() const noexcept { return file_; } [[nodiscard]] constexpr auto line() const noexcept { return line_; } @@ -861,9 +861,9 @@ template struct test { utility::string_view type{}; utility::string_view name{}; + reflection::source_location location{}; TArg arg{}; Test run{}; - reflection::source_location location{}; constexpr auto operator()() { run_impl(static_cast(run), arg); } constexpr auto operator()() const { run_impl(static_cast(run), arg); } @@ -884,8 +884,8 @@ struct test { } }; template -test(utility::string_view, utility::string_view, TArg, Test, - reflection::source_location) +test(utility::string_view, utility::string_view, reflection::source_location, + TArg, Test) ->test; template struct suite { @@ -911,33 +911,33 @@ struct test_skip { utility::string_view type{}; utility::string_view name{}; }; -template +template struct assertion { - TLocation location{}; TExpr expr{}; + reflection::source_location location{}; }; -template -assertion(TLocation, TExpr)->assertion; +template +assertion(TExpr, reflection::source_location)->assertion; #if defined(BOOST_UT_FORWARD) or defined(BOOST_UT_IMPLEMENTATION) struct expr { bool result; utility::function out; }; #endif -template +template struct assertion_pass { - TLocation location{}; TExpr expr{}; + reflection::source_location location{}; }; -template -assertion_pass(TLocation, TExpr)->assertion_pass; -template +template +assertion_pass(TExpr)->assertion_pass; +template struct assertion_fail { - TLocation location{}; TExpr expr{}; + reflection::source_location location{}; }; -template -assertion_fail(TLocation, TExpr)->assertion_fail; +template +assertion_fail(TExpr)->assertion_fail; struct test_end { utility::string_view type{}; utility::string_view name{}; @@ -946,7 +946,7 @@ template struct log { TMsg msg{}; }; -template +template log(TMsg)->log; struct fatal_assertion {}; struct exception { @@ -1145,13 +1145,13 @@ class reporter { ++tests_.except; } - template - auto on(events::assertion_pass) -> void { + template + auto on(events::assertion_pass) -> void { ++asserts_.pass; } - template - auto on(events::assertion_fail assertion) -> void { + template + auto on(events::assertion_fail assertion) -> void { constexpr auto short_name = [](std::string_view name) { return name.rfind('/') != std::string_view::npos ? name.substr(name.rfind('/') + 1) @@ -1287,9 +1287,10 @@ class runner { if (filter_(level_, path_)) { if (not level_++) { - reporter_.on(events::test_begin{test.type, test.name, test.location}); + reporter_.on(events::test_begin{ + .type = test.type, .name = test.name, .location = test.location}); } else { - reporter_.on(events::test_run{test.type, test.name}); + reporter_.on(events::test_run{.type = test.type, .name = test.name}); } if (dry_run_) { @@ -1316,50 +1317,52 @@ class runner { #endif if (not--level_) { - reporter_.on(events::test_end{test.type, test.name}); + reporter_.on(events::test_end{.type = test.type, .name = test.name}); } } } template auto on(events::skip test) { - reporter_.on(events::test_skip{test.type, test.name}); + reporter_.on(events::test_skip{.type = test.type, .name = test.name}); } - template - [[nodiscard]] auto on(events::assertion assertion) -> bool { + template + [[nodiscard]] auto on(events::assertion assertion) -> bool { if (dry_run_) { return true; } if (static_cast(assertion.expr)) { - reporter_.on(events::assertion_pass{assertion.location, assertion.expr}); + reporter_.on(events::assertion_pass{ + .expr = assertion.expr, .location = assertion.location}); return true; } else { ++fails_; - reporter_.on(events::assertion_fail{assertion.location, assertion.expr}); + reporter_.on(events::assertion_fail{ + .expr = assertion.expr, .location = assertion.location}); return false; } } #if defined(BOOST_UT_IMPLEMENTATION) - [[nodiscard]] auto on( - events::assertion assertion) - -> bool { + [[nodiscard]] auto on(events::assertion assertion) -> bool { if (dry_run_) { return true; } if (assertion.expr.result) { - reporter_.on(events::assertion_pass{ - assertion.location, - static_cast(assertion.expr.out)}); + reporter_.on(events::assertion_pass{ + .expr = + static_cast(assertion.expr.out), + .location = assertion.location}); return true; } else { ++fails_; - reporter_.on(events::assertion_fail{ - assertion.location, - static_cast(assertion.expr.out)}); + reporter_.on(events::assertion_fail{ + .expr = + static_cast(assertion.expr.out), + .location = assertion.location}); return false; } } @@ -1425,8 +1428,7 @@ extern void on(events::suite); extern void on(events::test); extern void on(events::test>); extern void on(events::skip<>); -[[nodiscard]] extern auto on( - events::assertion) -> bool; +[[nodiscard]] extern auto on(events::assertion) -> bool; extern void on(events::fatal_assertion); extern void on(events::log); #endif @@ -1438,9 +1440,7 @@ void on(events::test> test) { cfg.on(static_cast(test)); } void on(events::skip<> skip) { cfg.on(skip); } -[[nodiscard]] auto on( - events::assertion assertion) - -> bool { +[[nodiscard]] auto on(events::assertion assertion) -> bool { return cfg.on(static_cast(assertion)); } void on(events::fatal_assertion assertion) { cfg.on(assertion); } @@ -1459,17 +1459,19 @@ constexpr auto on(const TEvent& event) { template auto on(events::test test) -> void { - link::on(events::test>{test.type, test.name, TArg{}, - test.run}); + link::on(events::test, TArg>{ + .type = test.type, .name = test.name, .arg = TArg{}, .run = test.run}); } -template -[[nodiscard]] auto on(events::assertion assertion) -> bool { - return link::on(events::assertion{ - assertion.location, - {assertion.expr, [assertion](io::ostream& os) -> io::ostream& { - return (os << assertion.expr); - }}}); +template +[[nodiscard]] auto on(events::assertion assertion) -> bool { + return link::on(events::assertion{ + .expr = {assertion.expr, + [assertion](io::ostream& os) -> io::ostream& { + return (os << assertion.expr); + }}, + .location = assertion.location, + }); } #else template @@ -1496,7 +1498,11 @@ struct test { template constexpr auto operator=(test_location test) { - on(events::test{type, name, none{}, test.test, test.location}); + on(events::test{.type = type, + .name = name, + .location = test.location, + .arg = none{}, + .run = test.test}); return test; } @@ -1505,7 +1511,8 @@ struct test { not type_traits::is_convertible_v> = 0> constexpr auto operator=(Test test) -> typename type_traits::identity::type { - on(events::test{type, name, none{}, test, {}}); + on(events::test{ + .type = type, .name = name, .location = {}, .arg = {}, .run = test}); return test; } @@ -1529,7 +1536,7 @@ class test_skip { template constexpr auto operator=(void (*test)()) { - on(events::skip{t_.type, t_.name, none{}}); + on(events::skip<>{.type = t_.type, .name = t_.name, .arg = none{}}); return test; } @@ -1538,7 +1545,7 @@ class test_skip { not type_traits::is_convertible_v> = 0> constexpr auto operator=(Test test) -> typename type_traits::identity::type { - on(events::skip{t_.type, t_.name, none{}}); + on(events::skip{.type = t_.type, .name = t_.name, .arg = none{}}); return test; } @@ -1826,7 +1833,8 @@ template (events::test{"test", name, arg, f, {}}); + detail::on(events::test{ + .type = "test", .name = name, .location = {}, .arg = arg, .run = f}); } }; } @@ -1838,7 +1846,12 @@ template < return [f, t](auto name) { apply( [f, name](const auto&... args) { - (detail::on(events::test{"test", name, args, f, {}}), ...); + (detail::on(events::test{.type = "test", + .name = name, + .location = {}, + .arg = args, + .run = f}), + ...); }, t); }; @@ -1851,7 +1864,8 @@ template {detail::on(events::assertion{sl, expr})}; + return detail::expect_{detail::on( + events::assertion{.expr = expr, .location = sl})}; } #if defined(__cpp_nontype_template_parameter_class) @@ -1913,7 +1927,8 @@ struct suite { template constexpr /*explicit(false)*/ suite(TSuite suite) { static_assert(1 == sizeof(suite)); - detail::on(events::suite{+suite}); + detail::on( + events::suite{.run = +suite}); } }; diff --git a/test/ut/ut.cpp b/test/ut/ut.cpp index 94a15919..55756ea9 100644 --- a/test/ut/ut.cpp +++ b/test/ut/ut.cpp @@ -50,7 +50,7 @@ struct fake_cfg { run_calls.push_back({test.type, test.name, test.arg}); try { test(); - } catch (boost::ut::events::exception exception) { + } catch (const ut::events::exception& exception) { exception_calls.push_back(exception.what()); } catch (const std::exception& exception) { exception_calls.push_back(exception.what()); @@ -61,12 +61,14 @@ struct fake_cfg { } template auto on(ut::events::skip test) { - skip_calls.push_back({test.type, test.name, test.arg}); + skip_calls.push_back( + {.type = test.type, .name = test.name, .arg = test.arg}); } - template - auto on(ut::events::assertion assertion) -> bool { - assertion_calls.push_back( - {assertion.location, to_string(assertion.expr), assertion.expr}); + template + auto on(ut::events::assertion assertion) -> bool { + assertion_calls.push_back({.location = assertion.location, + .str = to_string(assertion.expr), + .result = assertion.expr}); return assertion.expr; } auto on(ut::events::fatal_assertion) { ++fatal_assertion_calls; } @@ -322,12 +324,6 @@ int main() { }; { - struct fake_source_location { - [[nodiscard]] constexpr auto file_name() const noexcept { - return "file/name"; - } - [[nodiscard]] constexpr auto line() const noexcept { return 42; } - }; std::stringstream out{}; std::stringstream err{}; auto old_cout = std::cout.rdbuf(out.rdbuf()); @@ -337,8 +333,12 @@ int main() { reporter.on(events::test_begin{}); reporter.on(events::test_run{}); - reporter.on(events::assertion_pass{fake_source_location{}, true}); - reporter.on(events::assertion_fail{fake_source_location{}, false}); + reporter.on(events::assertion_pass{ + .expr = true, + .location = reflection::source_location::current("file/name.cpp", 42)}); + reporter.on(events::assertion_fail{ + .expr = false, + .location = reflection::source_location::current("file/name.cpp", 42)}); reporter.on(events::fatal_assertion{}); reporter.on(events::test_end{}); @@ -370,166 +370,187 @@ int main() { auto& reporter = run.reporter_; run.run_ = true; - run.on(events::test{"test", "run", none{}, [] {}, {}}); + const auto test_empty = [] {}; + run.on(events::test{.type = "test", + .name = "run", + .location = {}, + .arg = none{}, + .run = test_empty}); test_assert(1 == reporter.tests_.pass); test_assert(0 == reporter.tests_.fail); test_assert(0 == reporter.tests_.skip); - run.on(events::skip{"test", "skip", none{}}); + run.on(events::skip<>{.type = "test", .name = "skip", .arg = none{}}); test_assert(1 == reporter.tests_.pass); test_assert(0 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); - run = {"unknown"}; - run.on(events::test{"test", "filter", none{}, [] {}, {}}); + run = {.filter = "unknown"}; + run.on(events::test{.type = "test", + .name = "filter", + .location = {}, + .arg = none{}, + .run = test_empty}); test_assert(1 == reporter.tests_.pass); test_assert(0 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run = {"filter"}; - run.on(events::test{"test", "filter", none{}, [] {}, {}}); + run = {.filter = "filter"}; + run.on(events::test{.type = "test", + .name = "filter", + .location = {}, + .arg = none{}, + .run = test_empty}); test_assert(2 == reporter.tests_.pass); test_assert(0 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run.on(events::test{ - "test", - "pass", - none{}, - [&run] { - void(run.on(events::assertion{reflection::source_location{}, true})); - }, - {}}); + const auto test_assertion_true = [&run] { + void(run.on(events::assertion{.expr = true, .location = {}})); + }; + run.on(events::test{ + .type = "test", + .name = "pass", + .location = {}, + .arg = none{}, + .run = test_assertion_true}); test_assert(3 == reporter.tests_.pass); test_assert(0 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); - run.on(events::test{ - "test", - "fail", - none{}, - [&run] { - void(run.on(events::assertion{reflection::source_location{}, false})); - }, - {}}); + const auto test_assertion_false = [&run] { + void(run.on(events::assertion{.expr = false, .location = {}})); + }; + run.on(events::test{ + .type = "test", + .name = "fail", + .location = {}, + .arg = none{}, + .run = test_assertion_false}); test_assert(3 == reporter.tests_.pass); test_assert(1 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); - run.on(events::test{"test", "exception", none{}, [] { throw 42; }, {}}); + const auto test_throw = [] { throw 42; }; + run.on(events::test{.type = "test", + .name = "exception", + .location = {}, + .arg = none{}, + .run = test_throw}); test_assert(3 == reporter.tests_.pass); test_assert(2 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); test_assert(1 == reporter.tests_.except); - run.on(events::test{"test", "exception", none{}, - [] { throw std::runtime_error("exception"); }, {}}); + const auto test_throw_runtime_error = [] { + throw std::runtime_error("exception"); + }; + run.on(events::test{ + .type = "test", + .name = "exception", + .location = {}, + .arg = none{}, + .run = test_throw_runtime_error}); test_assert(3 == reporter.tests_.pass); test_assert(3 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); test_assert(2 == reporter.tests_.except); - run.on(events::test{ - "test", - "section", - none{}, - [&run] { - run.on(events::test{"test", "sub-section", none{}, [] {}, {}}); - }, - {}}); + const auto test_sub_section = [&run, test_empty] { + run.on(events::test{.type = "test", + .name = "sub-section", + .location = {}, + .arg = none{}, + .run = test_empty}); + }; + run.on(events::test{.type = "test", + .name = "section", + .location = {}, + .arg = none{}, + .run = test_sub_section}); test_assert(4 == reporter.tests_.pass); test_assert(3 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); - run = {"section"}; - run.on(events::test{ - "test", - "section", - none{}, - [&run] { - run.on(events::test{"test", "sub-section-1", none{}, [] {}, {}}); - run.on(events::test{ - "test", "sub-section-2", none{}, [] { throw 0; }, {}}); - }, - {}}); + const auto test_sub_sections = [&run, test_empty, test_throw] { + run.on(events::test{.type = "test", + .name = "sub-section-1", + .location = {}, + .arg = none{}, + .run = test_empty}); + run.on(events::test{.type = "test", + .name = "sub-section-2", + .location = {}, + .arg = none{}, + .run = test_throw}); + }; + run = {.filter = "section"}; + run.on(events::test{.type = "test", + .name = "section", + .location = {}, + .arg = none{}, + .run = test_sub_sections}); test_assert(4 == reporter.tests_.pass); test_assert(4 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run = {"section.sub-section-1"}; - run.on(events::test{ - "test", - "section", - none{}, - [&run] { - run.on(events::test{"test", "sub-section-1", none{}, [] {}, {}}); - run.on(events::test{ - "test", "sub-section-2", none{}, [] { throw 0; }, {}}); - }, - {}}); + run = {.filter = "section.sub-section-1"}; + run.on(events::test{.type = "test", + .name = "section", + .location = {}, + .arg = none{}, + .run = test_sub_sections}); test_assert(5 == reporter.tests_.pass); test_assert(4 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run = {"section.sub-section-2"}; - run.on(events::test{ - "test", - "section", - none{}, - [&run] { - run.on(events::test{"test", "sub-section-1", none{}, [] {}, {}}); - run.on(events::test{ - "test", "sub-section-2", none{}, [] { throw 0; }, {}}); - }, - {}}); + run = {.filter = "section.sub-section-2"}; + run.on(events::test{.type = "test", + .name = "section", + .location = {}, + .arg = none{}, + .run = test_sub_sections}); test_assert(5 == reporter.tests_.pass); test_assert(5 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run = {"section.sub-section-*"}; - run.on(events::test{ - "test", - "section", - none{}, - [&run] { - run.on(events::test{"test", "sub-section-1", none{}, [] {}, {}}); - run.on(events::test{ - "test", "sub-section-2", none{}, [] { throw 0; }, {}}); - }, - {}}); + run = {.filter = "section.sub-section-*"}; + run.on(events::test{.type = "test", + .name = "section", + .location = {}, + .arg = none{}, + .run = test_sub_sections}); test_assert(5 == reporter.tests_.pass); test_assert(6 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); run = options{}; - run.on(events::test{ - "test", - "fatal", - none{}, - [&run] { - void(run.on(events::assertion{reflection::source_location{}, false})); - run.on(events::fatal_assertion{}); - void(run.on(events::assertion{reflection::source_location{}, true})); - }, - {}}); + const auto test_assertions = [&run] { + void(run.on(events::assertion{.expr = false, .location = {}})); + run.on(events::fatal_assertion{}); + void(run.on(events::assertion{.expr = true, .location = {}})); + }; + run.on(events::test{.type = "test", + .name = "fatal", + .location = {}, + .arg = none{}, + .run = test_assertions}); test_assert(5 == reporter.tests_.pass); test_assert(7 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); - run.on(events::test{ - "test", - "normal", - none{}, - [&run] { - void(run.on(events::assertion{reflection::source_location{}, true})); - }, - {}}); + run.on(events::test{ + .type = "test", + .name = "normal", + .location = {}, + .arg = none{}, + .run = test_assertion_true}); test_assert(6 == reporter.tests_.pass); test_assert(7 == reporter.tests_.fail); test_assert(1 == reporter.tests_.skip); @@ -1009,7 +1030,7 @@ int main() { }; "events::exception throw"_test = [] { - throw events::exception{"events::exception message"}; + throw events::exception{.msg = "events::exception message"}; }; "generic throw"_test = [] { throw 0; };