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) {
#define BATCH(first, last) make_batch(first, last)
#define AND <<
#define AND_RECEIVED <<
// -- unit tests ---------------------------------------------------------------
......@@ -367,7 +367,7 @@ CAF_TEST(one_path_force) {
}
// Give 11 credit (more than 10).
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;
}
// Drain all elements except the last 5.
......@@ -444,15 +444,15 @@ CAF_TEST(two_paths_different_sizes_force) {
// Give exactly 10 credit.
AFTER ENTITY alice TRIED FORCE_SENDING 10 ELEMENTS {
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 carl;
ENTITY alice HAS 0u CREDIT TOTAL;
}
// Give 11 credit (more than 10).
AFTER ENTITY alice TRIED FORCE_SENDING 11 ELEMENTS {
ENTITY bob RECEIVED BATCH(14, 23) AND BATCH(24, 24);
ENTITY carl RECEIVED BATCH(14, 20) AND BATCH(21, 24);
ENTITY bob RECEIVED BATCH(14, 23) AND_RECEIVED BATCH(24, 24);
ENTITY carl RECEIVED BATCH(14, 20) AND_RECEIVED BATCH(21, 24);
ENTITY alice HAS 0u CREDIT TOTAL;
}
// Drain all elements except the last 5.
......@@ -511,7 +511,7 @@ CAF_TEST(two_paths_different_sizes_without_force) {
// Give 11 credit.
AFTER ENTITY alice TRIED SENDING 11 ELEMENTS {
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 0u CREDIT FOR carl;
ENTITY alice HAS 1u CREDIT TOTAL;
......
#include "caf/fwd.hpp"
#include "caf/test/dsl.hpp"
#include "caf/test/bdd_dsl.hpp"
#include "caf/type_id.hpp"
#include "caf/typed_actor.hpp"
......
......@@ -20,7 +20,7 @@
#include "caf/detail/group_tunnel.hpp"
#include "caf/test/dsl.hpp"
#include "core-test.hpp"
using namespace caf;
......@@ -136,140 +136,144 @@ struct fixture : test_coordinator_fixture<> {
CAF_TEST_FIXTURE_SCOPE(group_tunnel_tests, fixture)
CAF_TEST(tunnels automatically subscribe to their origin on first subscribe) {
CAF_MESSAGE("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);
{ // Subtest.
CAF_MESSAGE("When an actors joins the tunnel.");
sys.spawn_in_group<lazy_init>(proxy, testee_impl);
CAF_MESSAGE("Then the tunnel worker joins the origin group.");
expect((sys_atom, join_atom), to(worker));
expect((join_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
CAF_CHECK(!sched.has_job());
}
{ // Subtest.
CAF_MESSAGE("When a second actor joins the tunnel.");
sys.spawn_in_group<lazy_init>(proxy, testee_impl);
CAF_MESSAGE("Then no messaging occurs.");
CAF_CHECK(!sched.has_job());
SCENARIO("tunnels automatically subscribe to their origin on first subscribe") {
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);
WHEN("an actors joins the tunnel") {
sys.spawn_in_group<lazy_init>(proxy, testee_impl);
THEN("the tunnel worker joins the origin group") {
expect((sys_atom, join_atom), to(worker));
expect((join_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
CHECK(!sched.has_job());
}
}
WHEN("a second actor joins the tunnel") {
sys.spawn_in_group<lazy_init>(proxy, testee_impl);
THEN("no messaging occurs") {
CHECK(!sched.has_job());
}
}
}
}
CAF_TEST("tunnels dispatch published messages") {
CAF_MESSAGE("Given a group with two local subscribers locally and tunneled.");
auto t1 = 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 t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run();
{ // Subtest.
CAF_MESSAGE("When an actors sends to the group.");
self->send(origin, put_atom_v, 42);
CAF_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(t2).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(t4).with(_, 42));
CAF_CHECK(!sched.has_job());
}
{ // Subtest.
CAF_MESSAGE("When an actors sends to the tunnel.");
self->send(proxy, put_atom_v, 42);
CAF_MESSAGE("Then the message travels to the origin.");
CAF_MESSAGE("And tunnel subscribers get the forwarded message eventually.");
expect((sys_atom, forward_atom, message), from(self).to(worker));
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(t2).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(t4).with(_, 42));
CAF_CHECK(!sched.has_job());
SCENARIO("tunnels dispatch published messages") {
GIVEN("a group with two local subscribers locally and tunneled") {
auto t1 = 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 t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run();
WHEN("an actors sends to the group") {
self->send(origin, put_atom_v, 42);
THEN("tunnel subscribers receive the forwarded message") {
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(worker).with(_, 42));
expect((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42));
CHECK(!sched.has_job());
}
}
WHEN("an actors sends to the tunnel") {
self->send(proxy, put_atom_v, 42);
THEN("the message travels to the origin")
AND("tunnel subscribers get the forwarded message eventually") {
expect((sys_atom, forward_atom, message), from(self).to(worker));
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(t2).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(t4).with(_, 42));
CHECK(!sched.has_job());
}
}
}
}
CAF_TEST(tunnels automatically unsubscribe from their origin) {
CAF_MESSAGE("Given a group with two local subscribers locally and tunneled.");
auto t1 = 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 t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run();
{ // Subtest.
CAF_MESSAGE("When the first actor leaves the tunnel.");
proxy.unsubscribe(actor_cast<actor_control_block*>(t3));
CAF_MESSAGE("Then no messaging occurs.");
CAF_CHECK(!sched.has_job());
}
{ // Subtest.
CAF_MESSAGE("When an actors sends to the group after the unsubscribe.");
self->send(origin, put_atom_v, 42);
CAF_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(t2).with(_, 42));
expect((put_atom, int), from(self).to(worker).with(_, 42));
disallow((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42));
CAF_CHECK(!sched.has_job());
}
{ // Subtest.
CAF_MESSAGE("When the second actor also unsubscribes from the tunnel.");
proxy.unsubscribe(actor_cast<actor_control_block*>(t4));
CAF_MESSAGE("Then the tunnel unsubscribes from its origin.");
expect((sys_atom, leave_atom), to(worker));
expect((leave_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
}
{ // Subtest.
CAF_MESSAGE("When an actors sends to the group after the tunnel left.");
self->send(origin, put_atom_v, 42);
CAF_MESSAGE("Then no message arrives at the tunnel.");
expect((put_atom, int), from(self).to(t1).with(_, 42));
expect((put_atom, int), from(self).to(t2).with(_, 42));
disallow((put_atom, int), from(self).to(worker).with(_, 42));
CAF_CHECK(!sched.has_job());
SCENARIO("tunnels automatically unsubscribe from their origin") {
GIVEN("a group with two local subscribers locally and tunneled") {
auto t1 = 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 t4 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
run();
WHEN("the first actor leaves the tunnel") {
proxy.unsubscribe(actor_cast<actor_control_block*>(t3));
THEN("no messaging occurs") {
CHECK(!sched.has_job());
}
}
WHEN("an actors sends to the group after the unsubscribe") {
self->send(origin, put_atom_v, 42);
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(t2).with(_, 42));
expect((put_atom, int), from(self).to(worker).with(_, 42));
disallow((put_atom, int), from(self).to(t3).with(_, 42));
expect((put_atom, int), from(self).to(t4).with(_, 42));
CHECK(!sched.has_job());
}
}
WHEN("the second actor also unsubscribes from the tunnel") {
proxy.unsubscribe(actor_cast<actor_control_block*>(t4));
THEN("the tunnel unsubscribes from its origin") {
expect((sys_atom, leave_atom), to(worker));
expect((leave_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
}
}
WHEN("an actors sends to the group after the tunnel left") {
self->send(origin, put_atom_v, 42);
THEN("no message arrives at the tunnel") {
expect((put_atom, int), from(self).to(t1).with(_, 42));
expect((put_atom, int), from(self).to(t2).with(_, 42));
disallow((put_atom, int), from(self).to(worker).with(_, 42));
CHECK(!sched.has_job());
}
}
}
}
CAF_TEST(tunnels cache messages until connected) {
CAF_MESSAGE("Given an unconnected tunnel with two subscribers.");
make_unconnected();
auto t1 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
auto t2 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
{ // Subtest.
CAF_MESSAGE("When an actors sends to the group.");
self->send(proxy, put_atom_v, 1);
self->send(proxy, put_atom_v, 2);
self->send(proxy, put_atom_v, 3);
CAF_MESSAGE("Then unconnected tunnel caches the messages.");
CAF_CHECK(!sched.has_job());
}
{ // Subtest.
CAF_MESSAGE("When the tunnel becomes connected.");
connect_proxy();
CAF_MESSAGE("Then tunnel subscribes upstream and flushes its cache.");
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((join_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
expect((forward_atom, message), from(self).to(intermediary));
expect((forward_atom, message), from(self).to(intermediary));
expect((forward_atom, message), from(self).to(intermediary));
expect((put_atom, int), from(self).to(worker).with(_, 1));
expect((put_atom, int), from(self).to(worker).with(_, 2));
expect((put_atom, int), from(self).to(worker).with(_, 3));
expect((put_atom, int), from(self).to(t1).with(_, 1));
expect((put_atom, int), from(self).to(t1).with(_, 2));
expect((put_atom, int), from(self).to(t1).with(_, 3));
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(_, 3));
CAF_CHECK(!sched.has_job());
SCENARIO("tunnels cache messages until connected") {
GIVEN("an unconnected tunnel with two subscribers") {
make_unconnected();
auto t1 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
auto t2 = sys.spawn_in_group<lazy_init>(proxy, testee_impl);
WHEN("an actors sends to the group") {
self->send(proxy, put_atom_v, 1);
self->send(proxy, put_atom_v, 2);
self->send(proxy, put_atom_v, 3);
THEN("unconnected tunnel caches the messages") {
CHECK(!sched.has_job());
}
}
WHEN("the tunnel becomes connected") {
connect_proxy();
THEN("tunnel subscribes upstream and flushes its cache") {
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((join_atom, strong_actor_ptr),
from(worker).to(intermediary).with(_, worker));
expect((forward_atom, message), from(self).to(intermediary));
expect((forward_atom, message), from(self).to(intermediary));
expect((forward_atom, message), from(self).to(intermediary));
expect((put_atom, int), from(self).to(worker).with(_, 1));
expect((put_atom, int), from(self).to(worker).with(_, 2));
expect((put_atom, int), from(self).to(worker).with(_, 3));
expect((put_atom, int), from(self).to(t1).with(_, 1));
expect((put_atom, int), from(self).to(t1).with(_, 2));
expect((put_atom, int), from(self).to(t1).with(_, 3));
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(_, 3));
CHECK(!sched.has_job());
}
}
}
}
......
......@@ -26,16 +26,6 @@
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 {
using e_int = expected<int>;
......@@ -58,9 +48,9 @@ CAF_TEST(both_engaged_not_equal) {
e_int y{24};
CHECK(x);
CHECK(y);
CHECK_NEQ(x, y);
CHECK_NEQ(x, sec::unexpected_message);
CHECK_NEQ(y, sec::unexpected_message);
CHECK_NE(x, y);
CHECK_NE(x, sec::unexpected_message);
CHECK_NE(y, sec::unexpected_message);
CHECK_EQ(x, 42);
CHECK_EQ(y, 24);
}
......@@ -72,10 +62,10 @@ CAF_TEST(engaged_plus_not_engaged) {
CHECK(!y);
CHECK_EQ(x, 42);
CHECK_EQ(y, sec::unexpected_message);
CHECK_NEQ(x, sec::unexpected_message);
CHECK_NEQ(x, y);
CHECK_NEQ(y, 42);
CHECK_NEQ(y, sec::unsupported_sys_key);
CHECK_NE(x, sec::unexpected_message);
CHECK_NE(x, y);
CHECK_NE(y, 42);
CHECK_NE(y, sec::unsupported_sys_key);
}
CAF_TEST(both_not_engaged) {
......@@ -87,15 +77,15 @@ CAF_TEST(both_not_engaged) {
CHECK_EQ(x, sec::unexpected_message);
CHECK_EQ(y, sec::unexpected_message);
CHECK_EQ(x.error(), y.error());
CHECK_NEQ(x, sec::unsupported_sys_key);
CHECK_NEQ(y, sec::unsupported_sys_key);
CHECK_NE(x, sec::unsupported_sys_key);
CHECK_NE(y, sec::unsupported_sys_key);
}
CAF_TEST(move_and_copy) {
e_str x{sec::unexpected_message};
e_str y{"hello"};
x = "hello";
CHECK_NEQ(x, sec::unexpected_message);
CHECK_NE(x, sec::unexpected_message);
CHECK_EQ(x, "hello");
CHECK_EQ(x, y);
y = "world";
......@@ -107,7 +97,7 @@ CAF_TEST(move_and_copy) {
CHECK_EQ(z_cpy, "world");
CHECK_EQ(z, z_cpy);
z = e_str{sec::unsupported_sys_key};
CHECK_NEQ(z, z_cpy);
CHECK_NE(z, z_cpy);
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