Commit b423b86d authored by Dominik Charousset's avatar Dominik Charousset Committed by Marian Triebe

Improve testing DSL

parent 9a74609e
......@@ -24,6 +24,7 @@
#include <deque>
#include <chrono>
#include <limits>
#include <cstddef>
#include <algorithm>
......@@ -105,7 +106,7 @@ public:
/// Executes events until the job queue is empty and no pending timeouts are
/// left. Returns the number of processed events.
size_t run();
size_t run(size_t max_count = std::numeric_limits<size_t>::max());
/// Tries to dispatch a single delayed message.
bool dispatch_once();
......
......@@ -124,6 +124,32 @@ public:
return *reinterpret_cast<const T*>(get(pos));
}
template <class T, size_t Pos>
struct typed_index {};
template <class T, size_t Pos>
static constexpr typed_index<T, Pos> make_typed_index() {
return {};
}
template <class T, size_t Pos>
const T& get_as(typed_index<T, Pos>) const {
return *reinterpret_cast<const T*>(get(Pos));
}
template <class... Ts, long... Is>
std::tuple<const Ts&...> get_as_tuple(detail::type_list<Ts...>,
detail::int_list<Is...>) const {
return std::tuple<const Ts&...>{get_as<Ts>(Is)...};
//return get_as<Ts>(Is)...;//(make_typed_index<Ts, Is>())...;
}
template <class... Ts>
std::tuple<const Ts&...> get_as_tuple() const {
return get_as_tuple(detail::type_list<Ts...>{},
typename detail::il_range<0, sizeof...(Ts)>::type{});
}
/// Convenience function for `*reinterpret_cast<T*>(get_mutable())`.
template <class T>
T& get_mutable_as(size_t pos) {
......
......@@ -19,6 +19,8 @@
#include "caf/scheduler/test_coordinator.hpp"
#include <limits>
#include "caf/resumable.hpp"
#include "caf/monitorable_actor.hpp"
......@@ -131,9 +133,9 @@ bool test_coordinator::run_once() {
return true;
}
size_t test_coordinator::run() {
size_t test_coordinator::run(size_t max_count) {
size_t res = 0;
while (run_once())
while (res < max_count && run_once())
++res;
return res;
}
......
......@@ -21,9 +21,7 @@
// this suite tests whether actors terminate as expect in several use cases
#define CAF_SUITE actor_termination
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
......@@ -36,27 +34,23 @@ behavior mirror_impl(event_based_actor* self) {
};
}
struct fixture {
actor_system_config cfg;
actor_system system;
scoped_actor scoped_self;
struct fixture : test_coordinator_fixture<> {
actor mirror;
actor testee;
fixture()
: system(cfg),
scoped_self(system),
mirror(system.spawn(mirror_impl)) {
// nop
fixture() {
mirror = sys.spawn(mirror_impl);
// run initialization code or mirror
sched.run_once();
}
template <class... Ts>
void spawn(Ts&&... xs) {
testee = scoped_self->spawn(std::forward<Ts>(xs)...);
testee = self->spawn(std::forward<Ts>(xs)...);
}
~fixture() {
scoped_self->wait_for(testee);
self->wait_for(testee);
}
};
......@@ -73,6 +67,10 @@ CAF_TEST(single_multiplexed_request) {
);
};
spawn(f, mirror);
// run initialization code of testee
sched.run_once();
expect((int), from(testee).to(mirror).with(42));
expect((int), from(mirror).to(testee).with(42));
}
CAF_TEST(multiple_multiplexed_requests) {
......@@ -85,6 +83,14 @@ CAF_TEST(multiple_multiplexed_requests) {
);
};
spawn(f, mirror);
// run initialization code of testee
sched.run_once();
expect((int), from(testee).to(mirror).with(42));
expect((int), from(testee).to(mirror).with(42));
expect((int), from(testee).to(mirror).with(42));
expect((int), from(mirror).to(testee).with(42));
expect((int), from(mirror).to(testee).with(42));
expect((int), from(mirror).to(testee).with(42));
}
CAF_TEST(single_awaited_request) {
......@@ -96,19 +102,33 @@ CAF_TEST(single_awaited_request) {
);
};
spawn(f, mirror);
// run initialization code of testee
sched.run_once();
expect((int), from(testee).to(mirror).with(42));
expect((int), from(mirror).to(testee).with(42));
}
CAF_TEST(multiple_awaited_requests) {
auto f = [&](event_based_actor* self, actor server) {
for (int i = 0; i < 3; ++i)
self->request(server, infinite, 42).await(
self->request(server, infinite, i).await(
[=](int x) {
CAF_MESSAGE("received response #" << (i + 1));
CAF_REQUIRE_EQUAL(x, 42);
CAF_REQUIRE_EQUAL(x, i);
}
);
};
spawn(f, mirror);
// run initialization code of testee
sched.run_once();
self->monitor(testee);
expect((int), from(testee).to(mirror).with(0));
expect((int), from(testee).to(mirror).with(1));
expect((int), from(testee).to(mirror).with(2));
// request().await() processes messages out-of-order,
// which means we cannot check using expect()
sched.run();
expect((down_msg), from(testee).to(self).with(_));
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -29,6 +29,9 @@ behavior multiplier(int x) {
return {
[=](int y) {
return x * y;
},
[=](int y1, int y2) {
return x * y1 * y2;
}
};
}
......@@ -37,6 +40,17 @@ behavior adder(int x) {
return {
[=](int y) {
return x + y;
},
[=](int y1, int y2) {
return x + y1 + y2;
}
};
}
behavior float_adder(float x) {
return {
[=](float y) {
return x + y;
}
};
}
......@@ -52,9 +66,9 @@ CAF_TEST(depth2) {
auto stage2 = sys.spawn(adder, 10);
auto testee = stage2 * stage1;
self->send(testee, 1);
expect(int, from(self).to(stage1).with(1));
expect(int, from(self).to(stage2).with(4));
CAF_CHECK_EQUAL(fetch_result<int>(), 14);
expect((int), from(self).to(stage1).with(1));
expect((int), from(self).to(stage2).with(4));
expect((int), from(stage2).to(self).with(14));
}
CAF_TEST(depth3) {
......@@ -62,11 +76,20 @@ CAF_TEST(depth3) {
auto stage2 = sys.spawn(adder, 10);
auto testee = stage1 * stage2 * stage1;
self->send(testee, 1);
expect(int, from(self).to(stage1).with(1));
expect(int, from(self).to(stage2).with(4));
expect(int, from(self).to(stage1).with(14));
CAF_CHECK_EQUAL(fetch_result<int>(), 56);
expect((int), from(self).to(stage1).with(1));
expect((int), from(self).to(stage2).with(4));
expect((int), from(self).to(stage1).with(14));
expect((int), from(stage1).to(self).with(56));
}
CAF_TEST_FIXTURE_SCOPE_END()
CAF_TEST(depth2_type_mismatch) {
auto stage1 = sys.spawn(multiplier, 4);
auto stage2 = sys.spawn(float_adder, 10);
auto testee = stage2 * stage1;
self->send(testee, 1);
expect((int), from(self).to(stage1).with(1));
expect((int), from(self).to(stage2).with(4));
expect((error), from(stage2).to(self).with(sec::unexpected_message));
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE type_erased_tuple
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/make_type_erased_tuple_view.hpp"
using namespace std;
using namespace caf;
CAF_TEST(get_as_tuple) {
int x = 1;
int y = 2;
int z = 3;
auto tup = make_type_erased_tuple_view(x, y, z);
auto xs = tup.get_as_tuple<int, int, int>();
CAF_CHECK_EQUAL(xs, std::make_tuple(1, 2, 3));
}
......@@ -109,57 +109,156 @@ private:
};
template <class T>
class expect_clause {
struct match_helper {
T& ref;
template <class... Fs>
void operator()(Fs... fs) {
struct impl : Fs... {
using result_type = void;
impl(Fs... xs) : Fs(xs)... {
// nop
}
};
impl visitor{std::move(fs)...};
apply_visitor(ref, visitor);
}
};
template <class T>
match_helper<T> match(T& x) {
return {x};
}
template <class Derived>
class expect_clause_base {
public:
expect_clause(caf::scheduler::test_coordinator& sched) : sched_(sched) {
expect_clause_base(caf::scheduler::test_coordinator& sched)
: sched_(sched),
mock_dest_(false) {
// nop
}
expect_clause(expect_clause&& other)
expect_clause_base(expect_clause_base&& other)
: sched_(other.sched_),
src_(std::move(other.src_)) {
// nop
}
Derived& from(const wildcard&) {
return dref();
}
template <class Handle>
expect_clause& from(const Handle& whom) {
Derived& from(const Handle& whom) {
src_ = caf::actor_cast<caf::strong_actor_ptr>(whom);
return *this;
return dref();
}
template <class Handle>
expect_clause& to(const Handle& whom) {
Derived& to(const Handle& whom) {
CAF_REQUIRE(sched_.prioritize(whom));
auto ptr = sched_.next_job<caf::scheduled_actor>().mailbox().peek();
dest_ = &sched_.next_job<caf::scheduled_actor>();
auto ptr = dest_->mailbox().peek();
CAF_REQUIRE(ptr != nullptr);
if (src_)
CAF_REQUIRE_EQUAL(ptr->sender, src_);
return *this;
return dref();
}
Derived& to(const wildcard& whom) {
CAF_REQUIRE(sched_.prioritize(whom));
dest_ = &sched_.next_job<caf::scheduled_actor>();
return dref();
}
Derived& to(const caf::scoped_actor& whom) {
mock_dest_ = true;
dest_ = whom.ptr();
return dref();
}
template <class... Ts>
void with(Ts&&... xs) {
std::integral_constant<bool, has_outer_type<T>::value> token;
std::tuple<const Ts&...> peek() {
CAF_REQUIRE(dest_ != nullptr);
auto ptr = dest_->mailbox().peek();
CAF_REQUIRE(ptr->content().match_elements<Ts...>());
return ptr->content().get_as_tuple<Ts...>();
}
protected:
void run_once() {
if (dynamic_cast<caf::blocking_actor*>(dest_) == nullptr)
sched_.run_once();
else // remove message from mailbox
delete dest_->mailbox().try_pop();
}
Derived& dref() {
return *static_cast<Derived*>(this);
}
// denotes whether destination is a mock actor, i.e., a scoped_actor without
// functionality other than checking outputs of other actors
caf::scheduler::test_coordinator& sched_;
bool mock_dest_;
caf::strong_actor_ptr src_;
caf::local_actor* dest_;
};
template <class... Ts>
class expect_clause : public expect_clause_base<expect_clause<Ts...>> {
public:
template <class... Us>
expect_clause(Us&&... xs)
: expect_clause_base<expect_clause<Ts...>>(std::forward<Us>(xs)...) {
// nop
}
template <class... Us>
void with(Us&&... xs) {
auto tmp = std::make_tuple(std::forward<Ts>(xs)...);
elementwise_compare_inspector<decltype(tmp)> inspector{tmp};
auto ys = this->template peek<Ts...>();
CAF_CHECK(inspector(get<0>(ys)));
this->run_once();
}
};
/// The single-argument expect-clause allows to automagically unwrap T
/// if it's a variant-like wrapper.
template <class T>
class expect_clause<T> : public expect_clause_base<expect_clause<T>> {
public:
template <class... Us>
expect_clause(Us&&... xs)
: expect_clause_base<expect_clause<T>>(std::forward<Us>(xs)...) {
// nop
}
template <class... Us>
void with(Us&&... xs) {
std::integral_constant<bool, has_outer_type<T>::value> token;
auto tmp = std::make_tuple(std::forward<Us>(xs)...);
with_content(token, tmp);
sched_.run_once();
this->run_once();
}
private:
template <class U>
void with_content(std::integral_constant<bool, false>, const U& x) {
elementwise_compare_inspector<U> inspector{x};
CAF_CHECK(inspector(const_cast<T&>(sched_.peek<T>())));
auto xs = this->template peek<T>();
CAF_CHECK(inspector(get<0>(xs)));
}
template <class U>
void with_content(std::integral_constant<bool, true>, const U& x) {
elementwise_compare_inspector<U> inspector{x};
auto& y = sched_.peek<typename T::outer_type>();
CAF_CHECK(inspector(const_cast<T&>(get<T>(y))));
auto xs = this->template peek<typename T::outer_type>();
CAF_CHECK(inspector(get<0>(xs)));
}
caf::scheduler::test_coordinator& sched_;
caf::strong_actor_ptr src_;
};
template <class Config = caf::actor_system_config>
......@@ -207,15 +306,18 @@ struct test_coordinator_fixture {
return dynamic_cast<T&>(*ptr);
}
template <class T>
expect_clause<T> expect() {
template <class... Ts>
expect_clause<Ts...> expect() {
return {sched};
}
};
} // namespace <anonymous>
#define expect(type, fields) \
CAF_MESSAGE("expect(" << #type << ")." << #fields); \
expect<type>().fields
#define CAF_EXPAND(x) x
#define CAF_DSL_LIST(...) __VA_ARGS__
#define expect(types, fields) \
CAF_MESSAGE("expect" << #types << "." << #fields); \
expect< CAF_EXPAND(CAF_DSL_LIST types) >().fields
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