Commit 9a74609e authored by Dominik Charousset's avatar Dominik Charousset Committed by Marian Triebe

Add new testing DSL based on test_coordinator

parent 19a9d0d2
......@@ -169,6 +169,12 @@ public:
return res;
}
pointer peek() {
if (head_ != nullptr || fetch_new_data())
return head_;
return nullptr;
}
// note: the cache is intended to be used by the owner, the queue itself
// never accesses the cache other than for counting;
// the first partition of the cache is meant to be used to store and
......
......@@ -25,21 +25,25 @@
#include <deque>
#include <chrono>
#include <cstddef>
#include <algorithm>
#include "caf/scheduled_actor.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
namespace caf {
namespace scheduler {
/// Coordinator for testing purposes.
/// A schedule coordinator for testing purposes.
class test_coordinator : public abstract_coordinator {
public:
using super = abstract_coordinator;
test_coordinator(actor_system& sys);
/// A double-ended queue representing our current job queue.
std::deque<resumable*> jobs;
/// A scheduled message or timeout.
struct delayed_msg {
strong_actor_ptr from;
strong_actor_ptr to;
......@@ -47,17 +51,55 @@ public:
message msg;
};
/// A clock type using the highest available precision.
using hrc = std::chrono::high_resolution_clock;
/// A map type for storing scheduled messages and timeouts.
std::multimap<hrc::time_point, delayed_msg> delayed_messages;
/// Returns whether at least one job is in the queue.
inline bool has_job() const {
return !jobs.empty();
}
/// Returns the next element from the job queue as type `T`.
template <class T = resumable>
T& next_job() {
if (jobs.empty())
CAF_RAISE_ERROR("jobs.empty())");
CAF_RAISE_ERROR("jobs.empty()");
return dynamic_cast<T&>(*jobs.front());
}
/// Peeks into the mailbox of `next_job<scheduled_actor>()`.
template <class T>
const T& peek() {
auto ptr = next_job<scheduled_actor>().mailbox().peek();
CAF_ASSERT(ptr != nullptr);
if (!ptr->content().match_elements<T>())
CAF_RAISE_ERROR("Mailbox element does not match T.");
return ptr->content().get_as<T>(0);
}
/// Puts `x` at the front of the queue unless it cannot be found in the queue.
/// Returns `true` if `x` exists in the queue and was put in front, `false`
/// otherwise.
template <class Handle>
bool prioritize(const Handle& x) {
auto ptr = dynamic_cast<resumable*>(actor_cast<abstract_actor*>(x));
if (!ptr)
return false;
auto b = jobs.begin();
auto e = jobs.end();
auto i = std::find(b, e, ptr);
if (i == e)
return false;
if (i == b)
return true;
std::rotate(b, i, i + 1);
return true;
}
/// Tries to execute a single event.
bool run_once();
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
namespace caf {
namespace tag {
/// Allows the testing DSL to recognize that subtypes are boxing content types.
struct boxing_type {};
} // namespace tag
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE composition
#include "caf/test/dsl.hpp"
using namespace std;
using namespace caf;
namespace {
behavior multiplier(int x) {
return {
[=](int y) {
return x * y;
}
};
}
behavior adder(int x) {
return {
[=](int y) {
return x + y;
}
};
}
using fixture = test_coordinator_fixture<>;
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(composition_tests, fixture)
CAF_TEST(depth2) {
auto stage1 = sys.spawn(multiplier, 4);
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);
}
CAF_TEST(depth3) {
auto stage1 = sys.spawn(multiplier, 4);
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);
}
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/all.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/test/unit_test.hpp"
// allow comparing arbitrary `T`s to `message` objects
namespace caf {
template <class T>
bool operator==(const message& x, const T& y) {
return x.match_elements<T>() && x.get_as<T>(0) == y;
}
} // namespace caf
namespace {
template <class T>
struct has_outer_type {
template <class U>
static auto sfinae(U* x) -> typename U::outer_type*;
template <class U>
static auto sfinae(...) -> std::false_type;
using type = decltype(sfinae<T>(nullptr));
static constexpr bool value = !std::is_same<type, std::false_type>::value;
};
// enables ADL in `with_content`
template <class T, class U>
T get(const U&);
struct wildcard { };
static constexpr wildcard _ = wildcard{};
template <class Tup>
class elementwise_compare_inspector {
public:
using result_type = bool;
template <size_t X>
using pos = std::integral_constant<size_t, X>;
elementwise_compare_inspector(const Tup& xs) : xs_(xs) {
// nop
}
template <class... Ts>
bool operator()(const Ts&... xs) {
return iterate(pos<0>{}, xs...);
}
private:
template <size_t X>
bool iterate(pos<X>) {
// end of recursion
return true;
}
template <size_t X, class T, class... Ts>
typename std::enable_if<
caf::meta::is_annotation<T>::value,
bool
>::type
iterate(pos<X> pos, const T&, const Ts&... ys) {
return iterate(pos, ys...);
}
template <size_t X, class T, class... Ts>
typename std::enable_if<
!caf::meta::is_annotation<T>::value,
bool
>::type
iterate(pos<X>, const T& y, const Ts&... ys) {
std::integral_constant<size_t, X + 1> next;
check(y, get<X>(xs_));
return iterate(next, ys...);
}
template <class T, class U>
static void check(const T& x, const U& y) {
CAF_CHECK_EQUAL(x, y);
}
template <class T>
static void check(const T&, const wildcard&) {
// nop
}
const Tup& xs_;
};
template <class T>
class expect_clause {
public:
expect_clause(caf::scheduler::test_coordinator& sched) : sched_(sched) {
// nop
}
expect_clause(expect_clause&& other)
: sched_(other.sched_),
src_(std::move(other.src_)) {
// nop
}
template <class Handle>
expect_clause& from(const Handle& whom) {
src_ = caf::actor_cast<caf::strong_actor_ptr>(whom);
return *this;
}
template <class Handle>
expect_clause& to(const Handle& whom) {
CAF_REQUIRE(sched_.prioritize(whom));
auto ptr = sched_.next_job<caf::scheduled_actor>().mailbox().peek();
CAF_REQUIRE(ptr != nullptr);
CAF_REQUIRE_EQUAL(ptr->sender, src_);
return *this;
}
template <class... Ts>
void with(Ts&&... xs) {
std::integral_constant<bool, has_outer_type<T>::value> token;
auto tmp = std::make_tuple(std::forward<Ts>(xs)...);
with_content(token, tmp);
sched_.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>())));
}
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))));
}
caf::scheduler::test_coordinator& sched_;
caf::strong_actor_ptr src_;
};
template <class Config = caf::actor_system_config>
struct test_coordinator_fixture {
using scheduler_type = caf::scheduler::test_coordinator;
Config cfg;
caf::actor_system sys;
caf::scoped_actor self;
scheduler_type& sched;
test_coordinator_fixture()
: sys((cfg.scheduler_policy = caf::atom("testing"), cfg)),
self(sys),
sched(dynamic_cast<scheduler_type&>(sys.scheduler())) {
//sys.await_actors_before_shutdown(false);
}
template <class T = int>
caf::expected<T> fetch_result() {
caf::expected<T> result = caf::error{};
self->receive(
[&](T& x) {
result = std::move(x);
},
[&](caf::error& x) {
result = std::move(x);
},
caf::after(std::chrono::seconds(0)) >> [&] {
result = caf::sec::request_timeout;
}
);
return result;
}
template <class T>
const T& peek() {
return sched.template peek<T>();
}
template <class T = caf::scheduled_actor, class Handle = caf::actor>
T& deref(const Handle& hdl) {
auto ptr = caf::actor_cast<caf::abstract_actor*>(hdl);
CAF_REQUIRE(ptr != nullptr);
return dynamic_cast<T&>(*ptr);
}
template <class T>
expect_clause<T> expect() {
return {sched};
}
};
} // namespace <anonymous>
#define expect(type, fields) \
CAF_MESSAGE("expect(" << #type << ")." << #fields); \
expect<type>().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