Add testing context as test case argument & support simple profiling in tests

This commit is contained in:
Nikita Lisitsa 2020-11-25 13:17:24 +03:00
parent bc18b03a53
commit 91faa2423e
2 changed files with 61 additions and 6 deletions

View file

@ -1,13 +1,27 @@
#pragma once
#include <exception>
#include <chrono>
#include <vector>
#include <psemek/util/to_string.hpp>
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_data> 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)

View file

@ -13,7 +13,7 @@
namespace psemek::test
{
static std::map<std::string, void(*)()> tests;
static std::map<std::string, void(*)(context &)> 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();