Commit ff54e90a authored by Dominik Charousset's avatar Dominik Charousset

Use fixtures in unit tests

parent a79b8628
...@@ -31,9 +31,9 @@ using namespace caf; ...@@ -31,9 +31,9 @@ using namespace caf;
using check_atom = caf::atom_constant<caf::atom("check")>; using check_atom = caf::atom_constant<caf::atom("check")>;
namespace { namespace {
std::atomic<long> s_testees; std::atomic<long> s_testees;
std::atomic<long> s_pending_on_exits; std::atomic<long> s_pending_on_exits;
} // namespace <anonymous>
class testee : public event_based_actor { class testee : public event_based_actor {
public: public:
...@@ -67,16 +67,13 @@ behavior testee::make_behavior() { ...@@ -67,16 +67,13 @@ behavior testee::make_behavior() {
template <class ExitMsgType> template <class ExitMsgType>
behavior tester(event_based_actor* self, const actor& aut) { behavior tester(event_based_actor* self, const actor& aut) {
CAF_MESSAGE("tester behaivor entered");
if (std::is_same<ExitMsgType, exit_msg>::value) { if (std::is_same<ExitMsgType, exit_msg>::value) {
self->trap_exit(true); self->trap_exit(true);
self->link_to(aut); self->link_to(aut);
} else { } else {
self->monitor(aut); self->monitor(aut);
} }
CAF_MESSAGE("tester before `anon_send_exit`");
anon_send_exit(aut, exit_reason::user_shutdown); anon_send_exit(aut, exit_reason::user_shutdown);
CAF_MESSAGE("tester after `anon_send_exit`");
return { return {
[self](const ExitMsgType& msg) { [self](const ExitMsgType& msg) {
// must be still alive at this point // must be still alive at this point
...@@ -100,32 +97,47 @@ behavior tester(event_based_actor* self, const actor& aut) { ...@@ -100,32 +97,47 @@ behavior tester(event_based_actor* self, const actor& aut) {
}; };
} }
CAF_TEST(no_spawn_options) { struct fixture {
~fixture() {
await_all_actors_done();
shutdown();
}
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(actor_lifetime_tests, fixture)
CAF_TEST(no_spawn_options_and_exit_msg) {
spawn<no_spawn_options>(tester<exit_msg>, spawn<testee, no_spawn_options>()); spawn<no_spawn_options>(tester<exit_msg>, spawn<testee, no_spawn_options>());
await_all_actors_done(); }
CAF_TEST(no_spawn_options_and_down_msg) {
spawn<no_spawn_options>(tester<down_msg>, spawn<testee, no_spawn_options>()); spawn<no_spawn_options>(tester<down_msg>, spawn<testee, no_spawn_options>());
await_all_actors_done();
} }
CAF_TEST(mixed_spawn_options) { CAF_TEST(mixed_spawn_options_and_exit_msg) {
spawn<detached>(tester<exit_msg>, spawn<testee, no_spawn_options>()); spawn<detached>(tester<exit_msg>, spawn<testee, no_spawn_options>());
await_all_actors_done(); }
CAF_TEST(mixed_spawn_options_and_down_msg) {
spawn<detached>(tester<down_msg>, spawn<testee, no_spawn_options>()); spawn<detached>(tester<down_msg>, spawn<testee, no_spawn_options>());
await_all_actors_done();
} }
CAF_TEST(mixed_spawn_options2) { CAF_TEST(mixed_spawn_options2_and_exit_msg) {
spawn<no_spawn_options>(tester<exit_msg>, spawn<testee, detached>()); spawn<no_spawn_options>(tester<exit_msg>, spawn<testee, detached>());
await_all_actors_done(); }
CAF_TEST(mixed_spawn_options2_and_down_msg) {
spawn<no_spawn_options>(tester<down_msg>, spawn<testee, detached>()); spawn<no_spawn_options>(tester<down_msg>, spawn<testee, detached>());
await_all_actors_done();
} }
CAF_TEST(detached_spawn_options) { CAF_TEST(detached_and_exit_msg) {
spawn<detached>(tester<exit_msg>, spawn<testee, detached>()); spawn<detached>(tester<exit_msg>, spawn<testee, detached>());
await_all_actors_done(); }
CAF_TEST(detached_and_down_msg) {
spawn<detached>(tester<down_msg>, spawn<testee, detached>()); spawn<detached>(tester<down_msg>, spawn<testee, detached>());
await_all_actors_done();
shutdown();
} }
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -29,8 +29,6 @@ namespace { ...@@ -29,8 +29,6 @@ namespace {
std::atomic<size_t> s_ctors; std::atomic<size_t> s_ctors;
std::atomic<size_t> s_dtors; std::atomic<size_t> s_dtors;
} // namespace <anonymous>
class worker : public event_based_actor { class worker : public event_based_actor {
public: public:
worker(); worker();
...@@ -58,124 +56,145 @@ actor spawn_worker() { ...@@ -58,124 +56,145 @@ actor spawn_worker() {
return spawn<worker>(); return spawn<worker>();
} }
CAF_TEST(test_actor_pool) { struct fixture {
announce<std::vector<int>>("vector<int>"); fixture() {
scoped_actor self; announce<std::vector<int>>("vector<int>");
auto w = actor_pool::make(5, spawn_worker, actor_pool::round_robin{});
self->monitor(w);
self->send(w, sys_atom::value, put_atom::value, spawn_worker());
std::vector<actor_addr> workers;
for (int i = 0; i < 6; ++i) {
self->sync_send(w, i, i).await(
[&](int res) {
CAF_CHECK_EQUAL(res, i + i);
auto sender = self->current_sender();
self->monitor(sender);
workers.push_back(sender);
}
);
} }
CAF_CHECK(workers.size() == 6); };
CAF_CHECK(std::unique(workers.begin(), workers.end()) == workers.end());
auto is_invalid = [](const actor_addr& addr) { } // namespace <anonymous>
return addr == invalid_actor_addr;
}; CAF_TEST_FIXTURE_SCOPE(actor_pool_tests, fixture)
CAF_CHECK(std::none_of(workers.begin(), workers.end(), is_invalid));
self->sync_send(w, sys_atom::value, get_atom::value).await( CAF_TEST(round_robin_actor_pool) {
[&](std::vector<actor>& ws) { { // lifetime scope of self
std::sort(workers.begin(), workers.end()); scoped_actor self;
std::sort(ws.begin(), ws.end()); auto w = actor_pool::make(5, spawn_worker, actor_pool::round_robin{});
CAF_CHECK(workers.size() == ws.size() self->monitor(w);
&& std::equal(workers.begin(), workers.end(), ws.begin())); self->send(w, sys_atom::value, put_atom::value, spawn_worker());
} std::vector<actor_addr> workers;
); for (int i = 0; i < 6; ++i) {
anon_send_exit(workers.back(), exit_reason::user_shutdown); self->sync_send(w, i, i).await(
self->receive( [&](int res) {
after(std::chrono::milliseconds(25)) >> [] { CAF_CHECK_EQUAL(res, i + i);
// wait some time to give the pool time to remove the failed worker auto sender = self->current_sender();
} self->monitor(sender);
); workers.push_back(sender);
self->receive(
[&](const down_msg& dm) {
CAF_CHECK(dm.source == workers.back());
workers.pop_back();
// check whether actor pool removed failed worker
self->sync_send(w, sys_atom::value, get_atom::value).await(
[&](std::vector<actor>& ws) {
std::sort(ws.begin(), ws.end());
CAF_CHECK(workers.size() == ws.size()
&& std::equal(workers.begin(), workers.end(), ws.begin()));
} }
); );
},
after(std::chrono::milliseconds(250)) >> [] {
CAF_TEST_ERROR("didn't receive a down message");
} }
); CAF_CHECK(workers.size() == 6);
CAF_MESSAGE("about to send exit to workers"); CAF_CHECK(std::unique(workers.begin(), workers.end()) == workers.end());
self->send_exit(w, exit_reason::user_shutdown); auto is_invalid = [](const actor_addr& addr) {
for (int i = 0; i < 6; ++i) { return addr == invalid_actor_addr;
};
CAF_CHECK(std::none_of(workers.begin(), workers.end(), is_invalid));
self->sync_send(w, sys_atom::value, get_atom::value).await(
[&](std::vector<actor>& ws) {
std::sort(workers.begin(), workers.end());
std::sort(ws.begin(), ws.end());
CAF_CHECK(workers.size() == ws.size()
&& std::equal(workers.begin(), workers.end(), ws.begin()));
}
);
anon_send_exit(workers.back(), exit_reason::user_shutdown);
self->receive(
after(std::chrono::milliseconds(25)) >> [] {
// wait some time to give the pool time to remove the failed worker
}
);
self->receive( self->receive(
[&](const down_msg& dm) { [&](const down_msg& dm) {
auto last = workers.end(); CAF_CHECK(dm.source == workers.back());
auto src = dm.source; workers.pop_back();
CAF_CHECK(src != invalid_actor_addr); // check whether actor pool removed failed worker
auto pos = std::find(workers.begin(), last, src); self->sync_send(w, sys_atom::value, get_atom::value).await(
//CAF_CHECK(pos != last || src == w); fail? [&](std::vector<actor>& ws) {
if (pos != last) { std::sort(ws.begin(), ws.end());
workers.erase(pos); CAF_CHECK(workers.size() == ws.size()
} && std::equal(workers.begin(), workers.end(), ws.begin()));
}
);
}, },
after(std::chrono::milliseconds(250)) >> [] { after(std::chrono::milliseconds(250)) >> [] {
CAF_TEST_ERROR("didn't receive a down message"); CAF_TEST_ERROR("didn't receive a down message");
} }
); );
} CAF_MESSAGE("about to send exit to workers");
} self->send_exit(w, exit_reason::user_shutdown);
for (int i = 0; i < 6; ++i) {
CAF_TEST(test_broadcast_actor_pool) { self->receive(
scoped_actor self; [&](const down_msg& dm) {
auto spawn5 = []() { auto last = workers.end();
return actor_pool::make(5, spawn_worker, actor_pool::broadcast{}); auto src = dm.source;
}; CAF_CHECK(src != invalid_actor_addr);
auto w = actor_pool::make(5, spawn5, actor_pool::broadcast{}); auto pos = std::find(workers.begin(), last, src);
self->send(w, 1, 2); //CAF_CHECK(pos != last || src == w); fail?
std::vector<int> results; if (pos != last) {
int i = 0; workers.erase(pos);
self->receive_for(i, 25)( }
[&](int res) { },
results.push_back(res); after(std::chrono::milliseconds(250)) >> [] {
}, CAF_TEST_ERROR("didn't receive a down message");
after(std::chrono::milliseconds(250)) >> [] { }
CAF_TEST_ERROR("didn't receive a result"); );
} }
); }
CAF_CHECK_EQUAL(results.size(), 25); await_all_actors_done();
CAF_CHECK(std::all_of(results.begin(), results.end(), shutdown();
[](int res) { return res == 3; })); CAF_CHECK_EQUAL(s_dtors.load(), s_ctors.load());
self->send_exit(w, exit_reason::user_shutdown);
self->await_all_other_actors_done();
} }
CAF_TEST(test_random_actor_pool) { CAF_TEST(broadcast_actor_pool) {
scoped_actor self; { // lifetime scope of self
auto w = actor_pool::make(5, spawn_worker, actor_pool::random{}); scoped_actor self;
for (int i = 0; i < 5; ++i) { auto spawn5 = []() {
self->sync_send(w, 1, 2).await( return actor_pool::make(5, spawn_worker, actor_pool::broadcast{});
};
auto w = actor_pool::make(5, spawn5, actor_pool::broadcast{});
self->send(w, 1, 2);
std::vector<int> results;
int i = 0;
self->receive_for(i, 25)(
[&](int res) { [&](int res) {
CAF_CHECK_EQUAL(res, 3); results.push_back(res);
}, },
after(std::chrono::milliseconds(250)) >> [] { after(std::chrono::milliseconds(250)) >> [] {
CAF_TEST_ERROR("didn't receive a down message"); CAF_TEST_ERROR("didn't receive a result");
} }
); );
CAF_CHECK_EQUAL(results.size(), 25);
CAF_CHECK(std::all_of(results.begin(), results.end(),
[](int res) { return res == 3; }));
self->send_exit(w, exit_reason::user_shutdown);
} }
self->send_exit(w, exit_reason::user_shutdown); await_all_actors_done();
self->await_all_other_actors_done(); shutdown();
CAF_CHECK_EQUAL(s_dtors.load(), s_ctors.load());
}
CAF_TEST(random_actor_pool) {
{ // lifetime scope of self
scoped_actor self;
auto w = actor_pool::make(5, spawn_worker, actor_pool::random{});
for (int i = 0; i < 5; ++i) {
self->sync_send(w, 1, 2).await(
[&](int res) {
CAF_CHECK_EQUAL(res, 3);
},
after(std::chrono::milliseconds(250)) >> [] {
CAF_TEST_ERROR("didn't receive a down message");
}
);
}
self->send_exit(w, exit_reason::user_shutdown);
}
await_all_actors_done();
shutdown();
CAF_CHECK_EQUAL(s_dtors.load(), s_ctors.load());
} }
CAF_TEST(test_split_join_actor_pool) { CAF_TEST(split_join_actor_pool) {
announce<std::vector<int>>("vector<int>");
auto spawn_split_worker = [] { auto spawn_split_worker = [] {
return spawn<lazy_init>([]() -> behavior { return spawn<lazy_init>([]() -> behavior {
return { return {
...@@ -195,25 +214,25 @@ CAF_TEST(test_split_join_actor_pool) { ...@@ -195,25 +214,25 @@ CAF_TEST(test_split_join_actor_pool) {
res += x; res += x;
}); });
}; };
scoped_actor self; { // lifetime scope of self
auto w = actor_pool::make(5, spawn_split_worker, scoped_actor self;
actor_pool::split_join<int>(join_fun, split_fun)); auto w = actor_pool::make(5, spawn_split_worker,
self->sync_send(w, std::vector<int>{1, 2, 3, 4, 5}).await( actor_pool::split_join<int>(join_fun, split_fun));
[&](int res) { self->sync_send(w, std::vector<int>{1, 2, 3, 4, 5}).await(
CAF_CHECK_EQUAL(res, 15); [&](int res) {
} CAF_CHECK_EQUAL(res, 15);
); }
self->sync_send(w, std::vector<int>{6, 7, 8, 9, 10}).await( );
[&](int res) { self->sync_send(w, std::vector<int>{6, 7, 8, 9, 10}).await(
CAF_CHECK_EQUAL(res, 40); [&](int res) {
} CAF_CHECK_EQUAL(res, 40);
); }
self->send_exit(w, exit_reason::user_shutdown); );
self->await_all_other_actors_done(); self->send_exit(w, exit_reason::user_shutdown);
} }
CAF_TEST(test_dtors) {
await_all_actors_done(); await_all_actors_done();
shutdown(); shutdown();
CAF_CHECK_EQUAL(s_dtors.load(), s_ctors.load()); CAF_CHECK_EQUAL(s_dtors.load(), s_ctors.load());
} }
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -30,18 +30,28 @@ using namespace caf; ...@@ -30,18 +30,28 @@ using namespace caf;
namespace { namespace {
constexpr auto s_foo = atom("FooBar"); constexpr auto s_foo = atom("FooBar");
using abc_atom = atom_constant<atom("abc")>; using abc_atom = atom_constant<atom("abc")>;
struct fixture {
~fixture() {
await_all_actors_done();
shutdown();
}
};
} // namespace <anonymous> } // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(atom_tests, fixture)
CAF_TEST(basics) { CAF_TEST(basics) {
// check if there are leading bits that distinguish "zzz" and "000 " // check if there are leading bits that distinguish "zzz" and "000 "
CAF_CHECK(atom("zzz") != atom("000 ")); CAF_CHECK(atom("zzz") != atom("000 "));
// check if there are leading bits that distinguish "abc" and " abc" // check if there are leading bits that distinguish "abc" and " abc"
CAF_CHECK(atom("abc") != atom(" abc")); CAF_CHECK(atom("abc") != atom(" abc"));
// 'illegal' characters are mapped to whitespaces // 'illegal' characters are mapped to whitespaces
CAF_CHECK_EQUAL(atom(" "), atom("@!?")); CAF_CHECK(atom(" ") == atom("@!?"));
// check to_string impl. // check to_string impl
CAF_CHECK_EQUAL(to_string(s_foo), "FooBar"); CAF_CHECK_EQUAL(to_string(s_foo), "FooBar");
} }
...@@ -111,7 +121,6 @@ using testee = typed_actor<replies_to<abc_atom>::with<int>>; ...@@ -111,7 +121,6 @@ using testee = typed_actor<replies_to<abc_atom>::with<int>>;
testee::behavior_type testee_impl(testee::pointer self) { testee::behavior_type testee_impl(testee::pointer self) {
return { return {
[=](abc_atom) { [=](abc_atom) {
CAF_MESSAGE("received abc_atom");
self->quit(); self->quit();
return 42; return 42;
} }
...@@ -119,15 +128,13 @@ testee::behavior_type testee_impl(testee::pointer self) { ...@@ -119,15 +128,13 @@ testee::behavior_type testee_impl(testee::pointer self) {
} }
CAF_TEST(sync_send_atom_constants) { CAF_TEST(sync_send_atom_constants) {
{ scoped_actor self;
scoped_actor self; auto tst = spawn_typed(testee_impl);
auto tst = spawn_typed(testee_impl); self->sync_send(tst, abc_atom::value).await(
self->sync_send(tst, abc_atom::value).await( [](int i) {
[](int i) { CAF_CHECK_EQUAL(i, 42);
CAF_CHECK_EQUAL(i, 42); }
} );
);
self->await_all_other_actors_done();
}
shutdown();
} }
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -31,6 +31,13 @@ using namespace caf; ...@@ -31,6 +31,13 @@ using namespace caf;
namespace { namespace {
struct fixture {
~fixture() {
await_all_actors_done();
shutdown();
}
};
class testee : public event_based_actor { class testee : public event_based_actor {
public: public:
testee(); testee();
...@@ -81,13 +88,13 @@ behavior tester::make_behavior() { ...@@ -81,13 +88,13 @@ behavior tester::make_behavior() {
send(m_aut, m_msg); send(m_aut, m_msg);
return { return {
on(1, 2, 3) >> [=] { on(1, 2, 3) >> [=] {
CAF_CHECK_EQUAL(current_message().cvals()->get_reference_count(), 2); CAF_CHECK(current_message().cvals()->get_reference_count() == 2
CAF_CHECK(current_message().cvals().get() == m_msg.cvals().get()); && current_message().cvals().get() == m_msg.cvals().get());
}, },
[=](const down_msg& dm) { [=](const down_msg& dm) {
CAF_CHECK(dm.source == m_aut); CAF_CHECK(dm.source == m_aut
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal); && dm.reason == exit_reason::normal
CAF_CHECK_EQUAL(current_message().cvals()->get_reference_count(), 1); && current_message().cvals()->get_reference_count() == 1);
quit(); quit();
}, },
others >> [&] { others >> [&] {
...@@ -96,7 +103,19 @@ behavior tester::make_behavior() { ...@@ -96,7 +103,19 @@ behavior tester::make_behavior() {
}; };
} }
void message_lifetime_in_scoped_actor() { template <spawn_options Os>
void test_message_lifetime() {
// put some preassure on the scheduler (check for thread safety)
for (size_t i = 0; i < 100; ++i) {
spawn<tester>(spawn<testee, Os>());
}
}
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(message_lifetime_tests, fixture)
CAF_TEST(message_lifetime_in_scoped_actor) {
auto msg = make_message(1, 2, 3); auto msg = make_message(1, 2, 3);
scoped_actor self; scoped_actor self;
self->send(self, msg); self->send(self, msg);
...@@ -121,30 +140,12 @@ void message_lifetime_in_scoped_actor() { ...@@ -121,30 +140,12 @@ void message_lifetime_in_scoped_actor() {
CAF_CHECK_EQUAL(msg.get_as<int>(0), 42); CAF_CHECK_EQUAL(msg.get_as<int>(0), 42);
} }
template <spawn_options Os> CAF_TEST(message_lifetime_no_spawn_options) {
void test_message_lifetime() {
message_lifetime_in_scoped_actor();
// put some preassure on the scheduler (check for thread safety)
for (size_t i = 0; i < 100; ++i) {
spawn<tester>(spawn<testee, Os>());
}
}
} // namespace <anonymous>
CAF_TEST(test_message_lifetime_in_scoped_actor) {
message_lifetime_in_scoped_actor();
}
CAF_TEST(test_message_lifetime_no_spawn_options) {
CAF_MESSAGE("test_message_lifetime<no_spawn_options>");
test_message_lifetime<no_spawn_options>(); test_message_lifetime<no_spawn_options>();
} }
CAF_TEST(test_message_lifetime_priority_aware) { CAF_TEST(message_lifetime_priority_aware) {
CAF_MESSAGE("test_message_lifetime<priority_aware>");
test_message_lifetime<priority_aware>(); test_message_lifetime<priority_aware>();
await_all_actors_done();
shutdown();
} }
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -122,21 +122,21 @@ enum class test_enum { ...@@ -122,21 +122,21 @@ enum class test_enum {
c c
}; };
struct common_fixture { struct fixture {
int32_t i32 = -345; int32_t i32 = -345;
test_enum te = test_enum::b; test_enum te = test_enum::b;
string str = "Lorem ipsum dolor sit amet."; string str = "Lorem ipsum dolor sit amet.";
raw_struct rs; raw_struct rs;
message msg; message msg;
common_fixture() { fixture() {
announce<test_enum>("test_enum"); announce<test_enum>("test_enum");
announce(typeid(raw_struct), uniform_type_info_ptr{new raw_struct_type_info}); announce(typeid(raw_struct), uniform_type_info_ptr{new raw_struct_type_info});
rs.str.assign(string(str.rbegin(), str.rend())); rs.str.assign(string(str.rbegin(), str.rend()));
msg = make_message(i32, te, str, rs); msg = make_message(i32, te, str, rs);
} }
~common_fixture() { ~fixture() {
await_all_actors_done(); await_all_actors_done();
shutdown(); shutdown();
} }
...@@ -211,7 +211,7 @@ struct is_message { ...@@ -211,7 +211,7 @@ struct is_message {
} // namespace <anonymous> } // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(serialization_tests, common_fixture) CAF_TEST_FIXTURE_SCOPE(serialization_tests, fixture)
CAF_TEST(test_serialization) { CAF_TEST(test_serialization) {
using token = std::integral_constant<int, detail::impl_id<strmap>()>; using token = std::integral_constant<int, detail::impl_id<strmap>()>;
......
...@@ -925,6 +925,36 @@ CAF_TEST(test_exit_reason_scoped_actor) { ...@@ -925,6 +925,36 @@ CAF_TEST(test_exit_reason_scoped_actor) {
shutdown(); shutdown();
} }
CAF_TEST(test_spawn_fun) {
class my_actor : public event_based_actor {
public:
behavior make_behavior() override {
return {
[=](sys_atom, std::function<behavior (my_actor*)>& fun) {
become(fun(this));
}
};
}
};
auto ma = spawn<my_actor>();
std::function<behavior (my_actor*)> fun = [](my_actor*) -> behavior {
return {
[](int x, int y) {
return x + y;
}
};
};
scoped_actor self;
self->send(ma, sys_atom::value, fun);
self->sync_send(ma, 10, 20).await(
[](int res) {
CAF_CHECK_EQUAL(res, 10 + 20);
}
);
self->send_exit(ma, exit_reason::kill);
self->await_all_other_actors_done();
}
CAF_TEST(test_number_of_instances) { CAF_TEST(test_number_of_instances) {
CAF_CHECK_EQUAL(s_actor_instances.load(), 0); CAF_CHECK_EQUAL(s_actor_instances.load(), 0);
CAF_MESSAGE("max. nr. of actor instances: " << s_max_actor_instances.load()); CAF_MESSAGE("max. nr. of actor instances: " << s_max_actor_instances.load());
......
...@@ -36,6 +36,14 @@ namespace caf { ...@@ -36,6 +36,14 @@ namespace caf {
class message; class message;
namespace test { namespace test {
/**
* A sequence of *checks*.
*/
class check_sequence {
virtual ~check_sequence();
virtual void run() = 0;
};
/** /**
* A sequence of *checks*. * A sequence of *checks*.
*/ */
...@@ -61,7 +69,9 @@ class test { ...@@ -61,7 +69,9 @@ class test {
return m_bad; return m_bad;
} }
virtual void run() = 0; void run();
virtual std::unique_ptr<check_sequence> make_sequence() = 0;
private: private:
size_t m_expected_failures; size_t m_expected_failures;
...@@ -219,14 +229,12 @@ class engine { ...@@ -219,14 +229,12 @@ class engine {
*/ */
static char* path(); static char* path();
using test_generator = std::function<std::unique_ptr<test> ()>;
/** /**
* Adds a test to the engine. * Adds a test to the engine.
* @param name The name of the suite. * @param name The name of the suite.
* @param t The test to register. * @param t The test to register.
*/ */
static void add(const char* name, test_generator generator); static void add(const char* name, std::unique_ptr<test> ptr);
/** /**
* Invokes tests in all suites. * Invokes tests in all suites.
...@@ -296,7 +304,7 @@ class engine { ...@@ -296,7 +304,7 @@ class engine {
const char* m_check_file = "<none>"; const char* m_check_file = "<none>";
size_t m_check_line = 0; size_t m_check_line = 0;
test* m_current_test = nullptr; test* m_current_test = nullptr;
std::map<std::string, std::vector<test_generator>> m_suites; std::map<std::string, std::vector<std::unique_ptr<test>>> m_suites;
}; };
namespace detail { namespace detail {
...@@ -598,8 +606,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture; ...@@ -598,8 +606,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
#define CAF_TEST(name) \ #define CAF_TEST(name) \
namespace { \ namespace { \
struct CAF_UNIQUE(test) : public ::caf::test::test, \ struct CAF_UNIQUE(test) : public ::caf::test::test { \
public caf_test_case_auto_fixture { \
CAF_UNIQUE(test)() : test{CAF_XSTR(name)} { } \ CAF_UNIQUE(test)() : test{CAF_XSTR(name)} { } \
void run() final; \ void run() final; \
}; \ }; \
......
...@@ -81,6 +81,10 @@ void watchdog::stop() { ...@@ -81,6 +81,10 @@ void watchdog::stop() {
delete s_watchdog; delete s_watchdog;
} }
check_sequence::~check_sequence() {
// nop
}
test::test(std::string test_name) test::test(std::string test_name)
: m_expected_failures(0), : m_expected_failures(0),
m_name(std::move(test_name)), m_name(std::move(test_name)),
...@@ -114,6 +118,11 @@ const std::string& test::name() const { ...@@ -114,6 +118,11 @@ const std::string& test::name() const {
return m_name; return m_name;
} }
void test::run() {
auto runner = make_sequence();
runner->run();
}
namespace detail { namespace detail {
require_error::require_error(const std::string& msg) : std::logic_error(msg) { require_error::require_error(const std::string& msg) : std::logic_error(msg) {
...@@ -241,10 +250,16 @@ char* engine::path() { ...@@ -241,10 +250,16 @@ char* engine::path() {
return instance().m_path; return instance().m_path;
} }
void engine::add(const char* cstr_name, test_generator generator) { void engine::add(const char* cstr_name, std::unique_ptr<test> ptr) {
std::string name = cstr_name ? cstr_name : ""; std::string name = cstr_name ? cstr_name : "";
auto& suite = instance().m_suites[name]; auto& suite = instance().m_suites[name];
suite.emplace_back(std::move(generator)); for (auto& x : suite) {
if (x->name() == ptr->name()) {
std::cout << "duplicate test name: " << ptr->name() << '\n';
std::abort();
}
}
suite.emplace_back(std::move(ptr));
} }
bool engine::run(bool colorize, bool engine::run(bool colorize,
......
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