Unverified Commit ef24fff5 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #1179

Add new capabilities for BDD-style unit tests
parents 574d4346 2382b283
...@@ -342,7 +342,7 @@ receive_checker<F> operator<<(receive_checker<F> xs, not_empty_t) { ...@@ -342,7 +342,7 @@ receive_checker<F> operator<<(receive_checker<F> xs, not_empty_t) {
#define BATCH(first, last) make_batch(first, last) #define BATCH(first, last) make_batch(first, last)
#define AND << #define AND_RECEIVED <<
// -- unit tests --------------------------------------------------------------- // -- unit tests ---------------------------------------------------------------
...@@ -367,7 +367,7 @@ CAF_TEST(one_path_force) { ...@@ -367,7 +367,7 @@ CAF_TEST(one_path_force) {
} }
// Give 11 credit (more than 10). // Give 11 credit (more than 10).
AFTER ENTITY alice TRIED FORCE_SENDING 11 ELEMENTS { AFTER ENTITY alice TRIED FORCE_SENDING 11 ELEMENTS {
ENTITY bob RECEIVED BATCH(14, 23) AND BATCH(24, 24); ENTITY bob RECEIVED BATCH(14, 23) AND_RECEIVED BATCH(24, 24);
ENTITY alice HAS 0u CREDIT FOR bob; ENTITY alice HAS 0u CREDIT FOR bob;
} }
// Drain all elements except the last 5. // Drain all elements except the last 5.
...@@ -444,15 +444,15 @@ CAF_TEST(two_paths_different_sizes_force) { ...@@ -444,15 +444,15 @@ CAF_TEST(two_paths_different_sizes_force) {
// Give exactly 10 credit. // Give exactly 10 credit.
AFTER ENTITY alice TRIED FORCE_SENDING 10 ELEMENTS { AFTER ENTITY alice TRIED FORCE_SENDING 10 ELEMENTS {
ENTITY bob RECEIVED BATCH(4, 13); ENTITY bob RECEIVED BATCH(4, 13);
ENTITY carl RECEIVED BATCH(4, 10) AND BATCH(11, 13); ENTITY carl RECEIVED BATCH(4, 10) AND_RECEIVED BATCH(11, 13);
ENTITY alice HAS 0u CREDIT FOR bob; ENTITY alice HAS 0u CREDIT FOR bob;
ENTITY alice HAS 0u CREDIT FOR carl; ENTITY alice HAS 0u CREDIT FOR carl;
ENTITY alice HAS 0u CREDIT TOTAL; ENTITY alice HAS 0u CREDIT TOTAL;
} }
// Give 11 credit (more than 10). // Give 11 credit (more than 10).
AFTER ENTITY alice TRIED FORCE_SENDING 11 ELEMENTS { AFTER ENTITY alice TRIED FORCE_SENDING 11 ELEMENTS {
ENTITY bob RECEIVED BATCH(14, 23) AND BATCH(24, 24); ENTITY bob RECEIVED BATCH(14, 23) AND_RECEIVED BATCH(24, 24);
ENTITY carl RECEIVED BATCH(14, 20) AND BATCH(21, 24); ENTITY carl RECEIVED BATCH(14, 20) AND_RECEIVED BATCH(21, 24);
ENTITY alice HAS 0u CREDIT TOTAL; ENTITY alice HAS 0u CREDIT TOTAL;
} }
// Drain all elements except the last 5. // Drain all elements except the last 5.
...@@ -511,7 +511,7 @@ CAF_TEST(two_paths_different_sizes_without_force) { ...@@ -511,7 +511,7 @@ CAF_TEST(two_paths_different_sizes_without_force) {
// Give 11 credit. // Give 11 credit.
AFTER ENTITY alice TRIED SENDING 11 ELEMENTS { AFTER ENTITY alice TRIED SENDING 11 ELEMENTS {
ENTITY bob RECEIVED BATCH(11, 20); ENTITY bob RECEIVED BATCH(11, 20);
ENTITY carl RECEIVED BATCH(8, 14) AND BATCH(15, 21); ENTITY carl RECEIVED BATCH(8, 14) AND_RECEIVED BATCH(15, 21);
ENTITY alice HAS 1u CREDIT FOR bob; ENTITY alice HAS 1u CREDIT FOR bob;
ENTITY alice HAS 0u CREDIT FOR carl; ENTITY alice HAS 0u CREDIT FOR carl;
ENTITY alice HAS 1u CREDIT TOTAL; ENTITY alice HAS 1u CREDIT TOTAL;
......
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/test/dsl.hpp" #include "caf/test/bdd_dsl.hpp"
#include "caf/type_id.hpp" #include "caf/type_id.hpp"
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "caf/detail/group_tunnel.hpp" #include "caf/detail/group_tunnel.hpp"
#include "caf/test/dsl.hpp" #include "core-test.hpp"
using namespace caf; using namespace caf;
...@@ -136,50 +136,50 @@ struct fixture : test_coordinator_fixture<> { ...@@ -136,50 +136,50 @@ struct fixture : test_coordinator_fixture<> {
CAF_TEST_FIXTURE_SCOPE(group_tunnel_tests, fixture) CAF_TEST_FIXTURE_SCOPE(group_tunnel_tests, fixture)
CAF_TEST(tunnels automatically subscribe to their origin on first subscribe) { SCENARIO("tunnels automatically subscribe to their origin on first subscribe") {
CAF_MESSAGE("Given a group with two subscribers and a tunnel."); GIVEN("a group with two subscribers and a tunnel") {
sys.spawn_in_group<lazy_init>(origin, testee_impl); sys.spawn_in_group<lazy_init>(origin, testee_impl);
sys.spawn_in_group<lazy_init>(origin, testee_impl); sys.spawn_in_group<lazy_init>(origin, testee_impl);
{ // Subtest. WHEN("an actors joins the tunnel") {
CAF_MESSAGE("When an actors joins the tunnel.");
sys.spawn_in_group<lazy_init>(proxy, testee_impl); sys.spawn_in_group<lazy_init>(proxy, testee_impl);
CAF_MESSAGE("Then the tunnel worker joins the origin group."); THEN("the tunnel worker joins the origin group") {
expect((sys_atom, join_atom), to(worker)); expect((sys_atom, join_atom), to(worker));
expect((join_atom, strong_actor_ptr), expect((join_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker)); from(worker).to(intermediary).with(_, worker));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
} }
{ // Subtest. }
CAF_MESSAGE("When a second actor joins the tunnel."); WHEN("a second actor joins the tunnel") {
sys.spawn_in_group<lazy_init>(proxy, testee_impl); sys.spawn_in_group<lazy_init>(proxy, testee_impl);
CAF_MESSAGE("Then no messaging occurs."); THEN("no messaging occurs") {
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
}
} }
} }
CAF_TEST("tunnels dispatch published messages") { SCENARIO("tunnels dispatch published messages") {
CAF_MESSAGE("Given a group with two local subscribers locally and tunneled."); GIVEN("a group with two local subscribers locally and tunneled") {
auto t1 = sys.spawn_in_group<lazy_init>(origin, testee_impl); auto t1 = sys.spawn_in_group<lazy_init>(origin, testee_impl);
auto t2 = sys.spawn_in_group<lazy_init>(origin, testee_impl); auto t2 = sys.spawn_in_group<lazy_init>(origin, testee_impl);
auto t3 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t3 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
auto t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run(); run();
{ // Subtest. WHEN("an actors sends to the group") {
CAF_MESSAGE("When an actors sends to the group.");
self->send(origin, put_atom_v, 42); self->send(origin, put_atom_v, 42);
CAF_MESSAGE("Then tunnel subscribers receive the forwarded message."); THEN("tunnel subscribers receive the forwarded message") {
expect((put_atom, int), from(self).to(t1).with(_, 42)); expect((put_atom, int), from(self).to(t1).with(_, 42));
expect((put_atom, int), from(self).to(t2).with(_, 42)); expect((put_atom, int), from(self).to(t2).with(_, 42));
expect((put_atom, int), from(self).to(worker).with(_, 42)); expect((put_atom, int), from(self).to(worker).with(_, 42));
expect((put_atom, int), from(self).to(t3).with(_, 42)); expect((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42)); expect((put_atom, int), from(self).to(t4).with(_, 42));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
} }
{ // Subtest. WHEN("an actors sends to the tunnel") {
CAF_MESSAGE("When an actors sends to the tunnel.");
self->send(proxy, put_atom_v, 42); self->send(proxy, put_atom_v, 42);
CAF_MESSAGE("Then the message travels to the origin."); THEN("the message travels to the origin")
CAF_MESSAGE("And tunnel subscribers get the forwarded message eventually."); AND("tunnel subscribers get the forwarded message eventually") {
expect((sys_atom, forward_atom, message), from(self).to(worker)); expect((sys_atom, forward_atom, message), from(self).to(worker));
expect((forward_atom, message), from(self).to(intermediary)); expect((forward_atom, message), from(self).to(intermediary));
expect((put_atom, int), from(self).to(t1).with(_, 42)); expect((put_atom, int), from(self).to(t1).with(_, 42));
...@@ -187,70 +187,72 @@ CAF_TEST("tunnels dispatch published messages") { ...@@ -187,70 +187,72 @@ CAF_TEST("tunnels dispatch published messages") {
expect((put_atom, int), from(self).to(worker).with(_, 42)); expect((put_atom, int), from(self).to(worker).with(_, 42));
expect((put_atom, int), from(self).to(t3).with(_, 42)); expect((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42)); expect((put_atom, int), from(self).to(t4).with(_, 42));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
}
} }
} }
CAF_TEST(tunnels automatically unsubscribe from their origin) { SCENARIO("tunnels automatically unsubscribe from their origin") {
CAF_MESSAGE("Given a group with two local subscribers locally and tunneled."); GIVEN("a group with two local subscribers locally and tunneled") {
auto t1 = sys.spawn_in_group<lazy_init>(origin, testee_impl); auto t1 = sys.spawn_in_group<lazy_init>(origin, testee_impl);
auto t2 = sys.spawn_in_group<lazy_init>(origin, testee_impl); auto t2 = sys.spawn_in_group<lazy_init>(origin, testee_impl);
auto t3 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t3 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
auto t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run(); run();
{ // Subtest. WHEN("the first actor leaves the tunnel") {
CAF_MESSAGE("When the first actor leaves the tunnel.");
proxy.unsubscribe(actor_cast<actor_control_block*>(t3)); proxy.unsubscribe(actor_cast<actor_control_block*>(t3));
CAF_MESSAGE("Then no messaging occurs."); THEN("no messaging occurs") {
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
} }
{ // Subtest. WHEN("an actors sends to the group after the unsubscribe") {
CAF_MESSAGE("When an actors sends to the group after the unsubscribe.");
self->send(origin, put_atom_v, 42); self->send(origin, put_atom_v, 42);
CAF_MESSAGE("Then the unsubscribed actor no longer receives the message."); THEN("the unsubscribed actor no longer receives the message") {
expect((put_atom, int), from(self).to(t1).with(_, 42)); expect((put_atom, int), from(self).to(t1).with(_, 42));
expect((put_atom, int), from(self).to(t2).with(_, 42)); expect((put_atom, int), from(self).to(t2).with(_, 42));
expect((put_atom, int), from(self).to(worker).with(_, 42)); expect((put_atom, int), from(self).to(worker).with(_, 42));
disallow((put_atom, int), from(self).to(t3).with(_, 42)); disallow((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42)); expect((put_atom, int), from(self).to(t4).with(_, 42));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
} }
{ // Subtest. }
CAF_MESSAGE("When the second actor also unsubscribes from the tunnel."); WHEN("the second actor also unsubscribes from the tunnel") {
proxy.unsubscribe(actor_cast<actor_control_block*>(t4)); proxy.unsubscribe(actor_cast<actor_control_block*>(t4));
CAF_MESSAGE("Then the tunnel unsubscribes from its origin."); THEN("the tunnel unsubscribes from its origin") {
expect((sys_atom, leave_atom), to(worker)); expect((sys_atom, leave_atom), to(worker));
expect((leave_atom, strong_actor_ptr), expect((leave_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker)); from(worker).to(intermediary).with(_, worker));
} }
{ // Subtest. }
CAF_MESSAGE("When an actors sends to the group after the tunnel left."); WHEN("an actors sends to the group after the tunnel left") {
self->send(origin, put_atom_v, 42); self->send(origin, put_atom_v, 42);
CAF_MESSAGE("Then no message arrives at the tunnel."); THEN("no message arrives at the tunnel") {
expect((put_atom, int), from(self).to(t1).with(_, 42)); expect((put_atom, int), from(self).to(t1).with(_, 42));
expect((put_atom, int), from(self).to(t2).with(_, 42)); expect((put_atom, int), from(self).to(t2).with(_, 42));
disallow((put_atom, int), from(self).to(worker).with(_, 42)); disallow((put_atom, int), from(self).to(worker).with(_, 42));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
}
} }
} }
CAF_TEST(tunnels cache messages until connected) { SCENARIO("tunnels cache messages until connected") {
CAF_MESSAGE("Given an unconnected tunnel with two subscribers."); GIVEN("an unconnected tunnel with two subscribers") {
make_unconnected(); make_unconnected();
auto t1 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t1 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
auto t2 = sys.spawn_in_group<lazy_init>(proxy, testee_impl); auto t2 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
{ // Subtest. WHEN("an actors sends to the group") {
CAF_MESSAGE("When an actors sends to the group.");
self->send(proxy, put_atom_v, 1); self->send(proxy, put_atom_v, 1);
self->send(proxy, put_atom_v, 2); self->send(proxy, put_atom_v, 2);
self->send(proxy, put_atom_v, 3); self->send(proxy, put_atom_v, 3);
CAF_MESSAGE("Then unconnected tunnel caches the messages."); THEN("unconnected tunnel caches the messages") {
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
} }
{ // Subtest. }
CAF_MESSAGE("When the tunnel becomes connected."); WHEN("the tunnel becomes connected") {
connect_proxy(); connect_proxy();
CAF_MESSAGE("Then tunnel subscribes upstream and flushes its cache."); THEN("tunnel subscribes upstream and flushes its cache") {
expect((sys_atom, join_atom), to(worker)); expect((sys_atom, join_atom), to(worker));
expect((sys_atom, forward_atom, message), from(self).to(worker)); expect((sys_atom, forward_atom, message), from(self).to(worker));
expect((sys_atom, forward_atom, message), from(self).to(worker)); expect((sys_atom, forward_atom, message), from(self).to(worker));
...@@ -269,7 +271,9 @@ CAF_TEST(tunnels cache messages until connected) { ...@@ -269,7 +271,9 @@ CAF_TEST(tunnels cache messages until connected) {
expect((put_atom, int), from(self).to(t2).with(_, 1)); expect((put_atom, int), from(self).to(t2).with(_, 1));
expect((put_atom, int), from(self).to(t2).with(_, 2)); expect((put_atom, int), from(self).to(t2).with(_, 2));
expect((put_atom, int), from(self).to(t2).with(_, 3)); expect((put_atom, int), from(self).to(t2).with(_, 3));
CAF_CHECK(!sched.has_job()); CHECK(!sched.has_job());
}
}
} }
} }
......
...@@ -26,16 +26,6 @@ ...@@ -26,16 +26,6 @@
using namespace caf; using namespace caf;
#define CHECK(x) CAF_CHECK(x);
#define CHECK_EQ(x, y) \
CAF_CHECK(x == y); \
CAF_CHECK(y == x);
#define CHECK_NEQ(x, y) \
CAF_CHECK(x != y); \
CAF_CHECK(y != x);
namespace { namespace {
using e_int = expected<int>; using e_int = expected<int>;
...@@ -58,9 +48,9 @@ CAF_TEST(both_engaged_not_equal) { ...@@ -58,9 +48,9 @@ CAF_TEST(both_engaged_not_equal) {
e_int y{24}; e_int y{24};
CHECK(x); CHECK(x);
CHECK(y); CHECK(y);
CHECK_NEQ(x, y); CHECK_NE(x, y);
CHECK_NEQ(x, sec::unexpected_message); CHECK_NE(x, sec::unexpected_message);
CHECK_NEQ(y, sec::unexpected_message); CHECK_NE(y, sec::unexpected_message);
CHECK_EQ(x, 42); CHECK_EQ(x, 42);
CHECK_EQ(y, 24); CHECK_EQ(y, 24);
} }
...@@ -72,10 +62,10 @@ CAF_TEST(engaged_plus_not_engaged) { ...@@ -72,10 +62,10 @@ CAF_TEST(engaged_plus_not_engaged) {
CHECK(!y); CHECK(!y);
CHECK_EQ(x, 42); CHECK_EQ(x, 42);
CHECK_EQ(y, sec::unexpected_message); CHECK_EQ(y, sec::unexpected_message);
CHECK_NEQ(x, sec::unexpected_message); CHECK_NE(x, sec::unexpected_message);
CHECK_NEQ(x, y); CHECK_NE(x, y);
CHECK_NEQ(y, 42); CHECK_NE(y, 42);
CHECK_NEQ(y, sec::unsupported_sys_key); CHECK_NE(y, sec::unsupported_sys_key);
} }
CAF_TEST(both_not_engaged) { CAF_TEST(both_not_engaged) {
...@@ -87,15 +77,15 @@ CAF_TEST(both_not_engaged) { ...@@ -87,15 +77,15 @@ CAF_TEST(both_not_engaged) {
CHECK_EQ(x, sec::unexpected_message); CHECK_EQ(x, sec::unexpected_message);
CHECK_EQ(y, sec::unexpected_message); CHECK_EQ(y, sec::unexpected_message);
CHECK_EQ(x.error(), y.error()); CHECK_EQ(x.error(), y.error());
CHECK_NEQ(x, sec::unsupported_sys_key); CHECK_NE(x, sec::unsupported_sys_key);
CHECK_NEQ(y, sec::unsupported_sys_key); CHECK_NE(y, sec::unsupported_sys_key);
} }
CAF_TEST(move_and_copy) { CAF_TEST(move_and_copy) {
e_str x{sec::unexpected_message}; e_str x{sec::unexpected_message};
e_str y{"hello"}; e_str y{"hello"};
x = "hello"; x = "hello";
CHECK_NEQ(x, sec::unexpected_message); CHECK_NE(x, sec::unexpected_message);
CHECK_EQ(x, "hello"); CHECK_EQ(x, "hello");
CHECK_EQ(x, y); CHECK_EQ(x, y);
y = "world"; y = "world";
...@@ -107,7 +97,7 @@ CAF_TEST(move_and_copy) { ...@@ -107,7 +97,7 @@ CAF_TEST(move_and_copy) {
CHECK_EQ(z_cpy, "world"); CHECK_EQ(z_cpy, "world");
CHECK_EQ(z, z_cpy); CHECK_EQ(z, z_cpy);
z = e_str{sec::unsupported_sys_key}; z = e_str{sec::unsupported_sys_key};
CHECK_NEQ(z, z_cpy); CHECK_NE(z, z_cpy);
CHECK_EQ(z, sec::unsupported_sys_key); CHECK_EQ(z, sec::unsupported_sys_key);
} }
......
#pragma once
#include "caf/test/dsl.hpp"
#define SCENARIO(description) \
namespace { \
struct CAF_UNIQUE(test) : caf_test_case_auto_fixture { \
void run_test_impl(); \
}; \
::caf::test::detail::adder<::caf::test::test_impl<CAF_UNIQUE(test)>> \
CAF_UNIQUE(a){CAF_XSTR(CAF_SUITE), "SCENARIO " description, false}; \
} \
void CAF_UNIQUE(test)::run_test_impl()
#define GIVEN(description) \
CAF_MESSAGE("GIVEN " description); \
if (true)
#define WHEN(description) \
CAF_MESSAGE("WHEN " description); \
if (true)
#define THEN(description) \
CAF_MESSAGE("THEN " description); \
if (true)
#define AND(description) \
{} \
CAF_MESSAGE("AND " description); \
if (true)
#define CHECK(what) CAF_CHECK(what)
#define CHECK_EQ(lhs, rhs) CAF_CHECK_EQUAL(lhs, rhs)
#define CHECK_NE(lhs, rhs) CAF_CHECK_NOT_EQUAL(lhs, rhs)
#define CHECK_LT(lhs, rhs) CAF_CHECK_LESS(lhs, rhs)
#define CHECK_LE(lhs, rhs) CAF_CHECK_LESS_OR_EQUAL(lhs, rhs)
#define CHECK_GT(lhs, rhs) CAF_CHECK_GREATER(lhs, rhs)
#define CHECK_GE(lhs, rhs) CAF_CHECK_GREATER_OR_EQUAL(lhs, rhs)
#define REQUIRE(what) CAF_REQUIRE(what)
#define REQUIRE_EQ(lhs, rhs) CAF_REQUIRE_EQUAL(lhs, rhs)
#define REQUIRE_NE(lhs, rhs) CAF_REQUIRE_NOT_EQUAL(lhs, rhs)
#define REQUIRE_LT(lhs, rhs) CAF_REQUIRE_LESS(lhs, rhs)
#define REQUIRE_LE(lhs, rhs) CAF_REQUIRE_LESS_OR_EQUAL(lhs, rhs)
#define REQUIRE_GT(lhs, rhs) CAF_REQUIRE_GREATER(lhs, rhs)
#define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs)
#define MESSAGE(what) CAF_MESSAGE(what)
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