From 91faa2423ecbc3093bbc66e49438da8eada6a6d5 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 25 Nov 2020 13:17:24 +0300 Subject: [PATCH] Add testing context as test case argument & support simple profiling in tests --- tools/test/include/psemek/test/test.hpp | 46 +++++++++++++++++++++++-- tools/test/source/main.cpp | 21 +++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/tools/test/include/psemek/test/test.hpp b/tools/test/include/psemek/test/test.hpp index 9c5f1df8..40da501a 100644 --- a/tools/test/include/psemek/test/test.hpp +++ b/tools/test/include/psemek/test/test.hpp @@ -1,13 +1,27 @@ #pragma once #include +#include +#include #include namespace psemek::test { - void add_test_case(char const * name, void(*f)()); + struct context + { + struct profile_data + { + std::string name; + std::chrono::high_resolution_clock::duration duration; + bool ended_with_exception; + }; + + std::vector profile; + }; + + void add_test_case(char const * name, void(*f)(context &)); struct failure : std::exception @@ -27,12 +41,35 @@ namespace psemek::test std::string location_; }; + struct profiler + { + using clock = std::chrono::high_resolution_clock; + + profiler(std::string name, context & ctx) + : name_{std::move(name)} + , ctx_{ctx} + { + start_ = clock::now(); + } + + ~profiler() + { + auto end = clock::now(); + ctx_.profile.push_back({name_, end - start_, std::uncaught_exceptions() > 0}); + } + + private: + clock::time_point start_; + std::string name_; + context & ctx_; + }; + } #define test_case(name) \ -void name ## _test_case (); \ +void name ## _test_case (::psemek::test::context &); \ static const auto name ## _test_case_registrator = []{ ::psemek::test::add_test_case(#name, &(name ## _test_case)); return 0; }(); \ -void name ## _test_case () +void name ## _test_case ([[maybe_unused]] ::psemek::test::context & _ctx) #define fail(...) throw ::psemek::test::failure(::psemek::util::to_string(__VA_ARGS__), ::psemek::util::to_string(__FILE__, ":", __LINE__)) @@ -52,3 +89,6 @@ void name ## _test_case () #define expect_gequal(expr1, expr2) if ((expr1) < (expr2)) fail(#expr1, " (", (expr1), ") < ", #expr2, " (", (expr2), ")") #define expect_throw(expr, type) do { bool thrown = false; try { (void)(expr); } catch (type const &) { thrown = true; } if (!thrown) fail(#expr, " didn't throw ", #type); } while (false) + +#define test_profile(name) \ + if (::psemek::test::profiler name ## _profiler(#name, _ctx); true) diff --git a/tools/test/source/main.cpp b/tools/test/source/main.cpp index 2c49edd6..b0558848 100644 --- a/tools/test/source/main.cpp +++ b/tools/test/source/main.cpp @@ -13,7 +13,7 @@ namespace psemek::test { - static std::map tests; + static std::map tests; static std::string normalize(std::string name) { @@ -31,7 +31,7 @@ namespace psemek::test return name; } - void add_test_case(char const * name, void(*f)()) + void add_test_case(char const * name, void(*f)(context &)) { std::string pname = normalize(name); @@ -99,10 +99,12 @@ int main(int argc, char ** argv) << '[' << std::setfill(' ') << std::setw(test_index_len) << std::right << i << '/' << test_count << "] " << std::left << std::setfill('.') << std::setw(max_name_length + 5) << name; + psemek::test::context ctx; + try { auto start = clock::now(); - psemek::test::tests[name](); + psemek::test::tests[name](ctx); auto end = clock::now(); std::cout << "ok (" << psemek::util::pretty(end - start, std::chrono::milliseconds{1}) << ")" << std::endl; ++success; @@ -121,6 +123,19 @@ int main(int argc, char ** argv) { std::cout << "failure: (unknown exception)" << std::endl; } + + std::size_t max_profile_name_len = 0; + for (auto const & p : ctx.profile) + max_profile_name_len = std::max(max_profile_name_len, p.name.size()); + + for (auto const & p : ctx.profile) + { + std::cout << indent + << std::setfill(' ') << std::setw(max_profile_name_len + 5) << p.name + << psemek::util::pretty(p.duration, std::chrono::milliseconds{1}) + << (p.ended_with_exception ? " (exception)" : "") + << std::endl; + } } auto all_end = clock::now();