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

Add should_fail_with_exception and improve output

parent 641ab130
...@@ -10,13 +10,23 @@ ...@@ -10,13 +10,23 @@
#include "caf/test/scope.hpp" #include "caf/test/scope.hpp"
#include "caf/test/test.hpp" #include "caf/test/test.hpp"
#include "caf/detail/scope_guard.hpp"
namespace caf::test { namespace caf::test {
namespace {
thread_local runnable* current_runnable;
} // namespace
runnable::~runnable() { runnable::~runnable() {
// nop // nop
} }
void runnable::run() { void runnable::run() {
current_runnable = this;
auto guard = detail::make_scope_guard([] { current_runnable = nullptr; });
switch (root_type_) { switch (root_type_) {
case block_type::scenario: case block_type::scenario:
if (auto guard = ctx_->get<scenario>(0, description_, loc_)->commit()) { if (auto guard = ctx_->get<scenario>(0, description_, loc_)->commit()) {
...@@ -48,6 +58,13 @@ bool runnable::check(bool value, const detail::source_location& location) { ...@@ -48,6 +58,13 @@ bool runnable::check(bool value, const detail::source_location& location) {
return value; 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() { block& runnable::current_block() {
if (ctx_->call_stack.empty()) if (ctx_->call_stack.empty())
CAF_RAISE_ERROR(std::logic_error, "no current block"); CAF_RAISE_ERROR(std::logic_error, "no current block");
......
...@@ -12,8 +12,10 @@ ...@@ -12,8 +12,10 @@
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp" #include "caf/detail/format.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/source_location.hpp" #include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp" #include "caf/detail/test_export.hpp"
#include "caf/raise_error.hpp"
#include <string_view> #include <string_view>
...@@ -42,6 +44,18 @@ public: ...@@ -42,6 +44,18 @@ public:
/// Runs the next branch of the test. /// Runs the next branch of the test.
void run(); 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. /// Generates a message with the INFO severity level.
template <class... Ts> template <class... Ts>
void info(detail::format_string_with_location fwl, Ts&&... xs) { void info(detail::format_string_with_location fwl, Ts&&... xs) {
...@@ -143,18 +157,46 @@ public: ...@@ -143,18 +157,46 @@ public:
return false; return false;
} }
/// Checks whether `value` is `true`.
bool check(bool value, const detail::source_location& location bool check(bool value, const detail::source_location& location
= detail::source_location::current()); = detail::source_location::current());
/// Returns the `runnable` instance that is currently running.
static runnable& current();
block& current_block(); 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 #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`.
= detail::source_location::current()) { 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>) { if constexpr (std::is_same_v<Exception, void>) {
try { try {
fn(); expr();
} catch (...) { } catch (...) {
reporter::instance->pass(location); reporter::instance->pass(location);
return; return;
...@@ -162,7 +204,7 @@ public: ...@@ -162,7 +204,7 @@ public:
reporter::instance->fail("throws", location); reporter::instance->fail("throws", location);
} else { } else {
try { try {
fn(); expr();
} catch (const Exception&) { } catch (const Exception&) {
reporter::instance->pass(location); reporter::instance->pass(location);
return; return;
...@@ -173,20 +215,52 @@ public: ...@@ -173,20 +215,52 @@ public:
reporter::instance->fail("throws Exception", location); reporter::instance->fail("throws Exception", location);
} }
} }
#endif
template <class Expr> /// Checks whether `expr()` throws an exception of type `Exception` and
void should_fail(Expr&& expr, const caf::detail::source_location& location /// increases the failure count.
= caf::detail::source_location::current()) { 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* rep = reporter::instance;
auto before = rep->test_stats(); auto before = rep->test_stats();
expr(); 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(); auto after = rep->test_stats();
check_eq(before.passed, after.passed, location); auto passed_count_ok = before.passed == after.passed;
if (check_eq(before.failed + 1, after.failed, location)) 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}); 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: protected:
context_ptr ctx_; context_ptr ctx_;
std::string_view description_; 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