Commit 60fa5004 authored by Dominik Charousset's avatar Dominik Charousset

Improve testing DSL

parent b627fd8d
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <deque> #include <deque>
#include <chrono> #include <chrono>
#include <limits>
#include <cstddef> #include <cstddef>
#include <algorithm> #include <algorithm>
...@@ -105,7 +106,7 @@ public: ...@@ -105,7 +106,7 @@ public:
/// Executes events until the job queue is empty and no pending timeouts are /// Executes events until the job queue is empty and no pending timeouts are
/// left. Returns the number of processed events. /// 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. /// Tries to dispatch a single delayed message.
bool dispatch_once(); bool dispatch_once();
......
...@@ -124,6 +124,32 @@ public: ...@@ -124,6 +124,32 @@ public:
return *reinterpret_cast<const T*>(get(pos)); 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())`. /// Convenience function for `*reinterpret_cast<T*>(get_mutable())`.
template <class T> template <class T>
T& get_mutable_as(size_t pos) { T& get_mutable_as(size_t pos) {
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "caf/scheduler/test_coordinator.hpp" #include "caf/scheduler/test_coordinator.hpp"
#include <limits>
#include "caf/resumable.hpp" #include "caf/resumable.hpp"
#include "caf/monitorable_actor.hpp" #include "caf/monitorable_actor.hpp"
...@@ -131,9 +133,9 @@ bool test_coordinator::run_once() { ...@@ -131,9 +133,9 @@ bool test_coordinator::run_once() {
return true; return true;
} }
size_t test_coordinator::run() { size_t test_coordinator::run(size_t max_count) {
size_t res = 0; size_t res = 0;
while (run_once()) while (res < max_count && run_once())
++res; ++res;
return res; return res;
} }
......
...@@ -21,9 +21,7 @@ ...@@ -21,9 +21,7 @@
// this suite tests whether actors terminate as expect in several use cases // this suite tests whether actors terminate as expect in several use cases
#define CAF_SUITE actor_termination #define CAF_SUITE actor_termination
#include "caf/test/unit_test.hpp" #include "caf/test/dsl.hpp"
#include "caf/all.hpp"
using namespace caf; using namespace caf;
...@@ -36,27 +34,23 @@ behavior mirror_impl(event_based_actor* self) { ...@@ -36,27 +34,23 @@ behavior mirror_impl(event_based_actor* self) {
}; };
} }
struct fixture { struct fixture : test_coordinator_fixture<> {
actor_system_config cfg;
actor_system system;
scoped_actor scoped_self;
actor mirror; actor mirror;
actor testee; actor testee;
fixture() fixture() {
: system(cfg), mirror = sys.spawn(mirror_impl);
scoped_self(system), // run initialization code or mirror
mirror(system.spawn(mirror_impl)) { sched.run_once();
// nop
} }
template <class... Ts> template <class... Ts>
void spawn(Ts&&... xs) { void spawn(Ts&&... xs) {
testee = scoped_self->spawn(std::forward<Ts>(xs)...); testee = self->spawn(std::forward<Ts>(xs)...);
} }
~fixture() { ~fixture() {
scoped_self->wait_for(testee); self->wait_for(testee);
} }
}; };
...@@ -73,6 +67,10 @@ CAF_TEST(single_multiplexed_request) { ...@@ -73,6 +67,10 @@ CAF_TEST(single_multiplexed_request) {
); );
}; };
spawn(f, mirror); 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) { CAF_TEST(multiple_multiplexed_requests) {
...@@ -85,6 +83,14 @@ CAF_TEST(multiple_multiplexed_requests) { ...@@ -85,6 +83,14 @@ CAF_TEST(multiple_multiplexed_requests) {
); );
}; };
spawn(f, mirror); 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) { CAF_TEST(single_awaited_request) {
...@@ -96,19 +102,33 @@ CAF_TEST(single_awaited_request) { ...@@ -96,19 +102,33 @@ CAF_TEST(single_awaited_request) {
); );
}; };
spawn(f, mirror); 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) { CAF_TEST(multiple_awaited_requests) {
auto f = [&](event_based_actor* self, actor server) { auto f = [&](event_based_actor* self, actor server) {
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
self->request(server, infinite, 42).await( self->request(server, infinite, i).await(
[=](int x) { [=](int x) {
CAF_MESSAGE("received response #" << (i + 1)); CAF_MESSAGE("received response #" << (i + 1));
CAF_REQUIRE_EQUAL(x, 42); CAF_REQUIRE_EQUAL(x, i);
} }
); );
}; };
spawn(f, mirror); 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() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -29,6 +29,9 @@ behavior multiplier(int x) { ...@@ -29,6 +29,9 @@ behavior multiplier(int x) {
return { return {
[=](int y) { [=](int y) {
return x * y; return x * y;
},
[=](int y1, int y2) {
return x * y1 * y2;
} }
}; };
} }
...@@ -37,6 +40,17 @@ behavior adder(int x) { ...@@ -37,6 +40,17 @@ behavior adder(int x) {
return { return {
[=](int y) { [=](int y) {
return x + 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) { ...@@ -52,9 +66,9 @@ CAF_TEST(depth2) {
auto stage2 = sys.spawn(adder, 10); auto stage2 = sys.spawn(adder, 10);
auto testee = stage2 * stage1; auto testee = stage2 * stage1;
self->send(testee, 1); self->send(testee, 1);
expect(int, from(self).to(stage1).with(1)); expect((int), from(self).to(stage1).with(1));
expect(int, from(self).to(stage2).with(4)); expect((int), from(self).to(stage2).with(4));
CAF_CHECK_EQUAL(fetch_result<int>(), 14); expect((int), from(stage2).to(self).with(14));
} }
CAF_TEST(depth3) { CAF_TEST(depth3) {
...@@ -62,11 +76,20 @@ CAF_TEST(depth3) { ...@@ -62,11 +76,20 @@ CAF_TEST(depth3) {
auto stage2 = sys.spawn(adder, 10); auto stage2 = sys.spawn(adder, 10);
auto testee = stage1 * stage2 * stage1; auto testee = stage1 * stage2 * stage1;
self->send(testee, 1); self->send(testee, 1);
expect(int, from(self).to(stage1).with(1)); expect((int), from(self).to(stage1).with(1));
expect(int, from(self).to(stage2).with(4)); expect((int), from(self).to(stage2).with(4));
expect(int, from(self).to(stage1).with(14)); expect((int), from(self).to(stage1).with(14));
CAF_CHECK_EQUAL(fetch_result<int>(), 56); 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: ...@@ -109,57 +109,156 @@ private:
}; };
template <class T> 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: public:
expect_clause(caf::scheduler::test_coordinator& sched) : sched_(sched) { expect_clause_base(caf::scheduler::test_coordinator& sched)
: sched_(sched),
mock_dest_(false) {
// nop // nop
} }
expect_clause(expect_clause&& other) expect_clause_base(expect_clause_base&& other)
: sched_(other.sched_), : sched_(other.sched_),
src_(std::move(other.src_)) { src_(std::move(other.src_)) {
// nop // nop
} }
Derived& from(const wildcard&) {
return dref();
}
template <class Handle> template <class Handle>
expect_clause& from(const Handle& whom) { Derived& from(const Handle& whom) {
src_ = caf::actor_cast<caf::strong_actor_ptr>(whom); src_ = caf::actor_cast<caf::strong_actor_ptr>(whom);
return *this; return dref();
} }
template <class Handle> template <class Handle>
expect_clause& to(const Handle& whom) { Derived& to(const Handle& whom) {
CAF_REQUIRE(sched_.prioritize(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); CAF_REQUIRE(ptr != nullptr);
CAF_REQUIRE_EQUAL(ptr->sender, src_); if (src_)
return *this; CAF_REQUIRE_EQUAL(ptr->sender, src_);
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> template <class... Ts>
void with(Ts&&... xs) { std::tuple<const Ts&...> peek() {
std::integral_constant<bool, has_outer_type<T>::value> token; 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)...); 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); with_content(token, tmp);
sched_.run_once(); this->run_once();
} }
private: private:
template <class U> template <class U>
void with_content(std::integral_constant<bool, false>, const U& x) { void with_content(std::integral_constant<bool, false>, const U& x) {
elementwise_compare_inspector<U> inspector{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> template <class U>
void with_content(std::integral_constant<bool, true>, const U& x) { void with_content(std::integral_constant<bool, true>, const U& x) {
elementwise_compare_inspector<U> inspector{x}; elementwise_compare_inspector<U> inspector{x};
auto& y = sched_.peek<typename T::outer_type>(); auto xs = this->template peek<typename T::outer_type>();
CAF_CHECK(inspector(const_cast<T&>(get<T>(y)))); CAF_CHECK(inspector(get<0>(xs)));
} }
caf::scheduler::test_coordinator& sched_;
caf::strong_actor_ptr src_;
}; };
template <class Config = caf::actor_system_config> template <class Config = caf::actor_system_config>
...@@ -207,15 +306,18 @@ struct test_coordinator_fixture { ...@@ -207,15 +306,18 @@ struct test_coordinator_fixture {
return dynamic_cast<T&>(*ptr); return dynamic_cast<T&>(*ptr);
} }
template <class T> template <class... Ts>
expect_clause<T> expect() { expect_clause<Ts...> expect() {
return {sched}; return {sched};
} }
}; };
} // namespace <anonymous> } // namespace <anonymous>
#define expect(type, fields) \ #define CAF_EXPAND(x) x
CAF_MESSAGE("expect(" << #type << ")." << #fields); \ #define CAF_DSL_LIST(...) __VA_ARGS__
expect<type>().fields
#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