Commit f39e3fea authored by Dominik Charousset's avatar Dominik Charousset

Fix build on GCC/Linux

parent 7a77a40e
......@@ -30,7 +30,7 @@ IndentCaseLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: "^BEGIN_STATE$|CAF_BEGIN_TYPE_ID_BLOCK"
MacroBlockEnd: "^END_STATE$|CAF_END_TYPE_ID_BLOCK"
......
......@@ -90,6 +90,7 @@ config = [
tags: ['docker', 'LeakSanitizer'],
builds: ['debug'],
extraBuildFlags: [
'BUILD_SHARED_LIBS:BOOL=OFF',
'CAF_LOG_LEVEL:STRING=TRACE',
'CAF_ENABLE_ROBOT_TESTS:BOOL=ON',
'CAF_SANITIZERS:STRING=address',
......@@ -104,9 +105,9 @@ config = [
tags: ['docker', 'UBSanitizer'],
builds: ['debug'],
extraBuildFlags: [
'BUILD_SHARED_LIBS:BOOL=OFF',
'CAF_LOG_LEVEL:STRING=TRACE',
'CAF_ENABLE_ROBOT_TESTS:BOOL=ON',
'BUILD_SHARED_LIBS:BOOL=OFF',
'CAF_SANITIZERS:STRING=address,undefined',
],
extraBuildEnv: [
......
......@@ -17,7 +17,9 @@ namespace caf::test {
/// structure.
class CAF_TEST_EXPORT block {
public:
block(context_ptr ctx, int id, std::string_view description,
// Note: the context owns the block. Hence, we can use a raw pointer for
// pointing back to the parent object here.
block(context* ctx, int id, std::string_view description,
const detail::source_location& loc);
virtual ~block();
......@@ -100,7 +102,7 @@ public:
protected:
std::unique_ptr<block>& get_nested_or_construct(int id);
context_ptr ctx_;
context* ctx_;
int id_ = 0;
std::string_view description_;
bool active_ = false;
......
......@@ -16,10 +16,10 @@
namespace caf::test {
/// Represents the exeuction context of a test. The context stores all steps of
/// Represents the execution context of a test. The context stores all steps of
/// the test and the current execution stack. The context persists across
/// multiple runs of the test in order to select one execution path per run.
class CAF_TEST_EXPORT context : public std::enable_shared_from_this<context> {
class CAF_TEST_EXPORT context {
public:
/// Returns whether the test is still active. A test is active as long as
/// no unwinding is in progress.
......@@ -42,9 +42,7 @@ public:
/// Checks whether `ptr` has been activated this run, i.e., whether we can
/// find it in `unwind_stack`.
bool activated(block* ptr) {
return std::find(path.begin(), path.end(), ptr) != path.end();
}
bool activated(block* ptr) const noexcept;
/// Stores the current execution stack for the run.
std::vector<block*> call_stack;
......@@ -55,7 +53,6 @@ public:
/// Stores all steps that we have reached at least once during the run.
std::vector<block*> path;
/// Stores all steps of the test with their run-time ID.
std::map<int, std::unique_ptr<block>> steps;
......@@ -64,7 +61,7 @@ public:
const detail::source_location& loc) {
auto& result = steps[id];
if (!result) {
result = std::make_unique<T>(shared_from_this(), id, description, loc);
result = std::make_unique<T>(this, id, description, loc);
}
return static_cast<T*>(result.get());
}
......
......@@ -87,4 +87,4 @@ private:
detail::source_location loc_;
};
} // namespace caf::test
} // namespace caf::test
......@@ -18,7 +18,6 @@ namespace caf::test {
/// A registry for our factories.
class registry {
public:
/// Maps test names to factories. Elements are sorted by the order of their
/// registration.
using tests_map = unordered_flat_map<std::string_view, factory*>;
......
......@@ -16,7 +16,7 @@ public:
static constexpr block_type type_token = block_type::when;
then* get_then(int id, std::string_view description,
const detail::source_location& loc) override ;
const detail::source_location& loc) override;
and_then* get_and_then(int id, std::string_view description,
const detail::source_location& loc) override;
......
......@@ -7,9 +7,11 @@
#include "caf/test/context.hpp"
#include "caf/test/nesting_error.hpp"
#include <algorithm>
namespace caf::test {
block::block(context_ptr ctx, int id, std::string_view description,
block::block(context* ctx, int id, std::string_view description,
const detail::source_location& loc)
: ctx_(ctx), id_(id), description_(description), loc_(loc) {
// nop
......
......@@ -7,6 +7,8 @@
#include "caf/test/block.hpp"
#include "caf/test/reporter.hpp"
#include <algorithm>
namespace caf::test {
void context::on_enter(block* ptr) {
......@@ -22,6 +24,10 @@ void context::on_leave(block* ptr) {
reporter::instance->end_step(ptr);
}
bool context::activated(block* ptr) const noexcept {
return std::find(path.begin(), path.end(), ptr) != path.end();
}
bool context::can_run() {
auto pred = [](auto& kvp) { return kvp.second->can_run(); };
return !steps.empty() && std::any_of(steps.begin(), steps.end(), pred);
......
......@@ -39,4 +39,4 @@ scope given::commit() {
return scope{this};
}
} // namespace caf::test
} // namespace caf::test
......@@ -32,9 +32,9 @@ nesting_error::raise_impl(nesting_error::code what, block_type parent,
#ifdef CAF_ENABLE_EXCEPTIONS
throw nesting_error{what, parent, child, loc};
#else
auto msg = nesting_error{code::not_allowed, parent, child}.message();
fprintf(stderr, "[FATAL] critical error (%s:%d): %s\n", msg.c_str(),
loc.file_name, static_cast<int>(loc.line));
auto msg = nesting_error{what, parent, child, loc}.message();
fprintf(stderr, "[FATAL] critical error (%s:%d): %s\n", loc.file_name(),
static_cast<int>(loc.line()), msg.c_str());
abort();
#endif
}
......
......@@ -34,6 +34,12 @@ namespace {
/// - $C(cyan text)
/// - $0 turns off coloring completely (enter verbatim mode)
struct colorizing_iterator {
using difference_type = void;
using value_type = void;
using pointer = void;
using reference = void;
using iterator_category = std::output_iterator_tag;
enum mode_t {
normal,
read_color,
......@@ -187,7 +193,7 @@ public:
if (!failed_suites_.empty()) {
format_to(colored(), " $B(Failed Suites):\n");
for (auto name : failed_suites_)
format_to(colored(), " - $R({})\n", name);
format_to(colored(), " - $R({})\n", name);
}
std::cout << std::endl;
}
......@@ -223,7 +229,7 @@ public:
if (!failed_tests_.empty()) {
format_to(colored(), " $B(Failed tests):\n");
for (auto name : failed_tests_)
format_to(colored(), " - $R({})\n", name);
format_to(colored(), " - $R({})\n", name);
}
std::cout << std::endl;
}
......@@ -240,7 +246,7 @@ public:
failed_tests_.push_back(current_test_);
suite_stats_ += test_stats_;
current_ctx_.reset();
if(live_)
if (live_)
std::cout << std::endl;
}
......@@ -252,7 +258,7 @@ public:
CAF_RAISE_ERROR(std::logic_error, "begin_test was not called");
format_to(colored(), "$C(Suite): $0{}\n", current_suite_);
indent_ = 2;
live_= true;
live_ = true;
for (auto* frame : current_ctx_->call_stack)
begin_step(frame);
}
......
......@@ -22,127 +22,142 @@ namespace caf::test {
namespace {
config_option_set make_option_set() {
config_option_set result;
config_option_adder{result, "global"}
.add<bool>("help,h?", "print this help text")
.add<bool>("available-suites,a", "print all available suites")
.add<std::string>("available-tests,A", "print tests for a suite")
.add<std::string>("verbosity,v", "set verbosity level of the reporter");
return result;
}
config_option_set make_option_set() {
config_option_set result;
config_option_adder{result, "global"}
.add<bool>("available-suites,a", "print all available suites")
.add<bool>("help,h?", "print this help text")
.add<bool>("no-colors,n", "disable coloring (ignored on Windows)")
.add<size_t>("max-runtime,r", "set a maximum runtime in seconds")
.add<std::string>("suites,s", "regex for selecting suites")
.add<std::string>("available-tests,A", "print tests for a suite")
.add<std::string>("verbosity,v", "set verbosity level of the reporter");
return result;
}
std::optional<unsigned> parse_log_level(std::string_view x) {
if (x == "quiet")
return CAF_LOG_LEVEL_QUIET;
if (x == "error")
return CAF_LOG_LEVEL_ERROR;
if (x == "warning")
return CAF_LOG_LEVEL_WARNING;
if (x == "info")
return CAF_LOG_LEVEL_INFO;
if (x == "debug")
return CAF_LOG_LEVEL_DEBUG;
if (x == "trace")
return CAF_LOG_LEVEL_TRACE;
return {};
}
std::optional<unsigned> parse_log_level(std::string_view x) {
// Note: the 0-5 aliases are for compatibility with the old unit testing
// framework.
if (x == "quiet" || x == "0")
return CAF_LOG_LEVEL_QUIET;
if (x == "error" || x == "1")
return CAF_LOG_LEVEL_ERROR;
if (x == "warning" || x == "2")
return CAF_LOG_LEVEL_WARNING;
if (x == "info" || x == "3")
return CAF_LOG_LEVEL_INFO;
if (x == "debug" || x == "4")
return CAF_LOG_LEVEL_DEBUG;
if (x == "trace" || x == "5")
return CAF_LOG_LEVEL_TRACE;
return {};
}
}//
} // namespace
runner::runner() : suites_(caf::test::registry::suites()) {
// nop
// nop
}
int runner::run(int argc, char** argv) {
auto default_reporter = reporter::make_default();
reporter::instance = default_reporter.get();
if (auto [ok, help_printed] = parse_cli(argc, argv); !ok) {
return EXIT_FAILURE;
} else if (help_printed) {
return EXIT_SUCCESS;
}
default_reporter->start();
for (auto& [suite_name, suite] : suites_) {
default_reporter->begin_suite(suite_name);
for (auto [test_name, factory_instance] : suite) {
auto state = std::make_shared<context>();
try {
do {
default_reporter->begin_test(state, test_name);
auto def = factory_instance->make(state);
def->run();
default_reporter->end_test();
state->clear_stacks();
} while (state->can_run());
} catch (const nesting_error& ex) {
default_reporter->unhandled_exception(ex.message(), ex.location());
default_reporter->end_test();
} catch (const std::exception& ex) {
default_reporter->unhandled_exception(ex.what());
default_reporter->end_test();
} catch (...) {
default_reporter->unhandled_exception("unknown exception type");
auto default_reporter = reporter::make_default();
reporter::instance = default_reporter.get();
if (auto [ok, help_printed] = parse_cli(argc, argv); !ok) {
return EXIT_FAILURE;
} else if (help_printed) {
return EXIT_SUCCESS;
}
default_reporter->start();
for (auto& [suite_name, suite] : suites_) {
default_reporter->begin_suite(suite_name);
for (auto [test_name, factory_instance] : suite) {
auto state = std::make_shared<context>();
#ifdef CAF_ENABLE_EXCEPTIONS
try {
do {
default_reporter->begin_test(state, test_name);
auto def = factory_instance->make(state);
def->run();
default_reporter->end_test();
}
state->clear_stacks();
} while (state->can_run());
} catch (const nesting_error& ex) {
default_reporter->unhandled_exception(ex.message(), ex.location());
default_reporter->end_test();
} catch (const std::exception& ex) {
default_reporter->unhandled_exception(ex.what());
default_reporter->end_test();
} catch (...) {
default_reporter->unhandled_exception("unknown exception type");
default_reporter->end_test();
}
default_reporter->end_suite(suite_name);
#else
do {
default_reporter->begin_test(state, test_name);
auto def = factory_instance->make(state);
def->run();
default_reporter->end_test();
state->clear_stacks();
} while (state->can_run());
#endif
}
default_reporter->stop();
return default_reporter->success() > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
default_reporter->end_suite(suite_name);
}
default_reporter->stop();
return default_reporter->success() > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
runner::parse_cli_result runner::parse_cli(int argc, char** argv) {
using detail::format_to;
caf::settings cfg;
std::vector<std::string> args_cpy{argv + 1, argv + argc};
auto options = make_option_set();
auto res = options.parse(cfg, args_cpy);
auto err = std::ostream_iterator<char>{std::cerr};
if (res.first != caf::pec::success) {
format_to(err, "error while parsing argument '{}': {}\n\n{}\n",
*res.second, to_string(res.first), options.help_text());
using detail::format_to;
caf::settings cfg;
std::vector<std::string> args_cpy{argv + 1, argv + argc};
auto options = make_option_set();
auto res = options.parse(cfg, args_cpy);
auto err = std::ostream_iterator<char>{std::cerr};
if (res.first != caf::pec::success) {
format_to(err, "error while parsing argument '{}': {}\n\n{}\n", *res.second,
to_string(res.first), options.help_text());
return {false, true};
}
if (get_or(cfg, "help", false)) {
format_to(err, "{}\n", options.help_text());
return {true, true};
}
if (get_or(cfg, "available-suites", false)) {
format_to(err, "available suites:\n");
for (auto& [suite_name, suite] : suites_)
format_to(err, "- {}\n", suite_name);
return {true, true};
}
if (auto suite_name = get_as<std::string>(cfg, "available-tests")) {
auto i = suites_.find(*suite_name);
if (i == suites_.end()) {
format_to(err, "no such suite: {}\n", *suite_name);
return {false, true};
}
if (get_or(cfg, "help", false)) {
format_to(err, "{}\n", options.help_text());
return {true, true};
}
if (get_or(cfg, "available-suites", false)) {
format_to(err, "available suites:\n");
for (auto& [suite_name, suite] : suites_)
format_to(err, "- {}\n", suite_name);
return {true, true};
}
if (auto suite_name = get_as<std::string>(cfg, "available-tests")) {
auto i = suites_.find(*suite_name);
if (i == suites_.end()) {
format_to(err, "no such suite: {}\n", *suite_name);
return {false, true};
}
format_to(err, "available tests in suite {}:\n", i->first);
for (const auto& [test_name, factory_instance] : i->second)
format_to(err, "- {}\n", test_name);
return {true, true};
}
if (auto verbosity = get_as<std::string>(cfg, "verbosity")) {
auto level = parse_log_level(*verbosity);
if (!level) {
format_to(err,
"unrecognized verbosity level: '{}'\n"
"expected one of:\n"
"- quiet\n"
"- error\n"
"- warning\n"
"- info\n"
"- debug\n"
"- trace\n",
*verbosity);
return {false, true};
}
reporter::instance->verbosity(*level);
format_to(err, "available tests in suite {}:\n", i->first);
for (const auto& [test_name, factory_instance] : i->second)
format_to(err, "- {}\n", test_name);
return {true, true};
}
if (auto verbosity = get_as<std::string>(cfg, "verbosity")) {
auto level = parse_log_level(*verbosity);
if (!level) {
format_to(err,
"unrecognized verbosity level: '{}'\n"
"expected one of:\n"
"- quiet\n"
"- error\n"
"- warning\n"
"- info\n"
"- debug\n"
"- trace\n",
*verbosity);
return {false, true};
}
return {true, false};
reporter::instance->verbosity(*level);
}
return {true, false};
}
} // namespace caf::test
......@@ -14,35 +14,35 @@
namespace caf::test {
block_type scenario::type() const noexcept {
return block_type::scenario;
}
block_type scenario::type() const noexcept {
return block_type::scenario;
}
given* scenario::get_given(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<given>(id, description, loc);
}
and_given* scenario::get_and_given(int id, std::string_view description,
given* scenario::get_given(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<and_given>(id, description, loc);
}
return get_nested<given>(id, description, loc);
}
when* scenario::get_when(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<when>(id, description, loc);
}
and_given* scenario::get_and_given(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<and_given>(id, description, loc);
}
and_when* scenario::get_and_when(int id, std::string_view description,
when* scenario::get_when(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<and_when>(id, description, loc);
}
scope scenario::commit() {
if (!ctx_->active() || !can_run())
return {};
enter();
return scope{this};
}
return get_nested<when>(id, description, loc);
}
and_when* scenario::get_and_when(int id, std::string_view description,
const detail::source_location& loc) {
return get_nested<and_when>(id, description, loc);
}
scope scenario::commit() {
if (!ctx_->active() || !can_run())
return {};
enter();
return scope{this};
}
} // namespace caf::test
......@@ -44,7 +44,6 @@ SCENARIO("each run starts with fresh local variables") {
}
}
struct int_fixture {
int my_int = 0;
};
......@@ -105,12 +104,12 @@ SCENARIO("scenario-1") {
}
}
AND_WHEN("and-when-1-1") {
check_eq(++counter, 6);
check_eq(render(), "scenario-1/given-1/and-when-1-1");
check_eq(++counter, 6);
check_eq(render(), "scenario-1/given-1/and-when-1-1");
}
AND_WHEN("and-when-1-2") {
check_eq(++counter, 7);
check_eq(render(), "scenario-1/given-1/and-when-1-2");
check_eq(++counter, 7);
check_eq(render(), "scenario-1/given-1/and-when-1-2");
}
WHEN("when-2") {
check_eq(++counter, 2);
......@@ -129,12 +128,12 @@ SCENARIO("scenario-1") {
}
}
AND_WHEN("and-when-2-1") {
check_eq(++counter, 6);
check_eq(render(), "scenario-1/given-1/and-when-2-1");
check_eq(++counter, 6);
check_eq(render(), "scenario-1/given-1/and-when-2-1");
}
AND_WHEN("and-when-2-2") {
check_eq(++counter, 7);
check_eq(render(), "scenario-1/given-1/and-when-2-2");
check_eq(++counter, 7);
check_eq(render(), "scenario-1/given-1/and-when-2-2");
}
}
}
......
......@@ -8,7 +8,7 @@ using caf::test::block_type;
SUITE("caf.test.test") {
TEST("tests can contain checks") {
auto* rep = caf::test::reporter::instance;
auto* rep = caf::test::reporter::instance;
for (int i = 0; i < 3; ++i)
check_eq(i, i);
auto stats = rep->test_stats();
......
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