Commit 6b59bb87 authored by Dominik Charousset's avatar Dominik Charousset

Add should_fail_with_exception and improve output

parent 641ab130
......@@ -10,13 +10,23 @@
#include "caf/test/scope.hpp"
#include "caf/test/test.hpp"
#include "caf/detail/scope_guard.hpp"
namespace caf::test {
namespace {
thread_local runnable* current_runnable;
} // namespace
runnable::~runnable() {
// nop
}
void runnable::run() {
current_runnable = this;
auto guard = detail::make_scope_guard([] { current_runnable = nullptr; });
switch (root_type_) {
case block_type::scenario:
if (auto guard = ctx_->get<scenario>(0, description_, loc_)->commit()) {
......@@ -48,6 +58,13 @@ bool runnable::check(bool value, const detail::source_location& location) {
return value;
}
runnable& runnable::current() {
auto ptr = current_runnable;
if (!ptr)
CAF_RAISE_ERROR(std::logic_error, "no current runnable");
return *ptr;
}
block& runnable::current_block() {
if (ctx_->call_stack.empty())
CAF_RAISE_ERROR(std::logic_error, "no current block");
......
......@@ -12,8 +12,10 @@
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/raise_error.hpp"
#include <string_view>
......@@ -42,6 +44,18 @@ public:
/// Runs the next branch of the test.
void run();
/// Generates a message with the INFO severity level.
template <class... Ts>
[[noreturn]] void fail(detail::format_string_with_location fwl, Ts&&... xs) {
if constexpr (sizeof...(Ts) > 0) {
auto msg = detail::format(fwl.value, std::forward<Ts>(xs)...);
reporter::instance->fail(msg, fwl.location);
} else {
reporter::instance->fail(fwl.value, fwl.location);
}
CAF_RAISE_ERROR(std::logic_error, "requirement failed: abort test");
}
/// Generates a message with the INFO severity level.
template <class... Ts>
void info(detail::format_string_with_location fwl, Ts&&... xs) {
......@@ -143,18 +157,46 @@ public:
return false;
}
/// Checks whether `value` is `true`.
bool check(bool value, const detail::source_location& location
= detail::source_location::current());
/// Returns the `runnable` instance that is currently running.
static runnable& current();
block& current_block();
template <class Expr>
void should_fail(Expr&& expr, const caf::detail::source_location& location
= caf::detail::source_location::current()) {
auto* rep = reporter::instance;
auto lvl = rep->verbosity(CAF_LOG_LEVEL_QUIET);
auto before = rep->test_stats();
{
auto lvl_guard = detail::make_scope_guard([&] { rep->verbosity(lvl); });
expr();
}
auto after = rep->test_stats();
auto passed_count_ok = before.passed == after.passed;
auto failed_count_ok = before.failed + 1 == after.failed;
if (passed_count_ok && failed_count_ok) {
reporter::instance->pass(location);
rep->test_stats({before.passed + 1, before.failed});
} else {
reporter::instance->fail("nested check should fail", location);
rep->test_stats({before.passed, before.failed + 1});
}
}
#ifdef CAF_ENABLE_EXCEPTIONS
template <class Exception = void, class CodeBlock>
void check_throws(CodeBlock&& fn, const detail::source_location& location
/// Checks whether `expr()` throws an exception of type `Exception`.
template <class Exception = void, class Expr>
void check_throws(Expr&& expr, const detail::source_location& location
= detail::source_location::current()) {
if constexpr (std::is_same_v<Exception, void>) {
try {
fn();
expr();
} catch (...) {
reporter::instance->pass(location);
return;
......@@ -162,7 +204,7 @@ public:
reporter::instance->fail("throws", location);
} else {
try {
fn();
expr();
} catch (const Exception&) {
reporter::instance->pass(location);
return;
......@@ -173,20 +215,52 @@ public:
reporter::instance->fail("throws Exception", location);
}
}
#endif
template <class Expr>
void should_fail(Expr&& expr, const caf::detail::source_location& location
/// Checks whether `expr()` throws an exception of type `Exception` and
/// increases the failure count.
template <class Exception = void, class Expr>
void should_fail_with_exception(Expr&& expr,
const caf::detail::source_location& location
= caf::detail::source_location::current()) {
auto* rep = reporter::instance;
auto before = rep->test_stats();
auto lvl = rep->verbosity(CAF_LOG_LEVEL_QUIET);
auto caught = false;
if constexpr (std::is_same_v<Exception, void>) {
try {
expr();
} catch (...) {
caught = true;
}
} else {
try {
expr();
} catch (const Exception&) {
caught = true;
} catch (...) {
// TODO: print error message
}
}
rep->verbosity(lvl);
auto after = rep->test_stats();
check_eq(before.passed, after.passed, location);
if (check_eq(before.failed + 1, after.failed, location))
auto passed_count_ok = before.passed == after.passed;
auto failed_count_ok = before.failed + 1 == after.failed;
if (caught && passed_count_ok && failed_count_ok) {
reporter::instance->pass(location);
rep->test_stats({before.passed + 1, before.failed});
} else {
if (!caught) {
reporter::instance->fail("nested check should throw an Exception",
location);
} else if (!passed_count_ok || !failed_count_ok) {
reporter::instance->fail("nested check should fail", location);
}
rep->test_stats({before.passed, before.failed + 1});
}
}
#endif
protected:
context_ptr ctx_;
std::string_view description_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment