Commit 95554bc0 authored by Dominik Charousset's avatar Dominik Charousset

Fix disallow in the testing DSL

parent 4dda7d9a
......@@ -152,16 +152,21 @@ private:
// -- unified access to all actor handles in CAF -------------------------------
/// Reduces any of CAF's handle types to an `abstract_actor` pointer.
class caf_handle {
class caf_handle : caf::detail::comparable<caf_handle>,
caf::detail::comparable<caf_handle, std::nullptr_t> {
public:
using pointer = caf::abstract_actor*;
constexpr caf_handle() : ptr_(nullptr) {
// nop
}
template <class T>
caf_handle(const T& x) {
*this = x;
}
caf_handle& operator=(caf::abstract_actor* x) {
inline caf_handle& operator=(caf::abstract_actor* x) {
ptr_ = x;
return *this;
}
......@@ -175,15 +180,20 @@ public:
caf_handle& operator=(const caf_handle&) = default;
pointer get() const {
inline pointer get() const {
return ptr_;
}
private:
caf_handle() : ptr_(nullptr) {
// nop
inline ptrdiff_t compare(const caf_handle& other) const {
return reinterpret_cast<ptrdiff_t>(ptr_)
- reinterpret_cast<ptrdiff_t>(other.ptr_);
}
inline ptrdiff_t compare(std::nullptr_t) const {
return reinterpret_cast<ptrdiff_t>(ptr_);
}
private:
caf::abstract_actor* ptr_;
};
......@@ -192,7 +202,7 @@ private:
/// Returns a pointer to the next element in an actor's mailbox without taking
/// it out of the mailbox.
/// @pre `ptr` is alive and either a `scheduled_actor` or `blocking_actor`
caf::mailbox_element* next_mailbox_element(caf_handle x) {
inline caf::mailbox_element* next_mailbox_element(caf_handle x) {
CAF_ASSERT(x.get() != nullptr);
auto sptr = dynamic_cast<caf::scheduled_actor*>(x.get());
if (sptr != nullptr)
......@@ -227,26 +237,33 @@ caf::optional<std::tuple<T>> unboxing_extract(caf_handle x) {
/// `default_extract`.
/// @private
template <class T, bool HasOuterType, class... Ts>
struct extract_impl {
struct try_extract_impl {
caf::optional<std::tuple<T, Ts...>> operator()(caf_handle x) {
return default_extract<T, Ts...>(x);
}
};
template <class T>
struct extract_impl<T, true> {
struct try_extract_impl<T, true> {
caf::optional<std::tuple<T>> operator()(caf_handle x) {
return unboxing_extract<T>(x);
}
};
/// Returns the content of the next mailbox element as `tuple<T, Ts...>` on a
/// match. Returns `none` otherwise.
template <class T, class... Ts>
caf::optional<std::tuple<T, Ts...>> try_extract(caf_handle x) {
try_extract_impl<T, has_outer_type<T>::value, Ts...> f;
return f(x);
}
/// Returns the content of the next mailbox element without taking it out of
/// the mailbox. Fails on an empty mailbox or if the content of the next
/// element does not match `<T, Ts...>`.
template <class T, class... Ts>
std::tuple<T, Ts...> extract(caf_handle x) {
extract_impl<T, has_outer_type<T>::value, Ts...> f;
auto result = f(x);
auto result = try_extract<T, Ts...>(x);
if (result == caf::none) {
auto ptr = next_mailbox_element(x);
if (ptr == nullptr)
......@@ -259,11 +276,9 @@ std::tuple<T, Ts...> extract(caf_handle x) {
template <class T, class... Ts>
bool received(caf_handle x) {
extract_impl<T, has_outer_type<T>::value, Ts...> f;
return f(x) != caf::none;
return try_extract<T, Ts...>(x) != caf::none;
}
template <class... Ts>
class expect_clause {
public:
......@@ -340,149 +355,69 @@ protected:
std::function<void ()> peek_;
};
template <class Derived>
class disallow_clause_base {
template <class... Ts>
class disallow_clause {
public:
disallow_clause_base(caf::scheduler::test_coordinator& sched)
: sched_(sched),
dest_(nullptr) {
// nop
}
disallow_clause_base(disallow_clause_base&& other)
: sched_(other.sched_),
src_(std::move(other.src_)) {
// nop
}
Derived& from(const wildcard&) {
return dref();
}
template <class Handle>
Derived& from(const Handle& whom) {
src_ = caf::actor_cast<caf::strong_actor_ptr>(whom);
return dref();
}
template <class Handle>
Derived& to(const Handle& whom) {
// not setting dest_ causes the content checking to succeed immediately
if (sched_.prioritize(whom)) {
dest_ = &sched_.next_job<caf::scheduled_actor>();
}
return dref();
}
Derived& to(const wildcard& whom) {
if (sched_.prioritize(whom))
dest_ = &sched_.next_job<caf::scheduled_actor>();
disallow_clause() {
check_ = [=] {
auto ptr = next_mailbox_element(dest_);
if (ptr == nullptr)
return;
if (src_ != nullptr && ptr->sender != src_)
return;
auto res = try_extract<Ts...>(dest_);
if (res != caf::none)
CAF_FAIL("received disallowed message: " << CAF_ARG(*res));
};
}
Derived& to(const caf::scoped_actor& whom) {
dest_ = whom.ptr();
return dref();
}
disallow_clause(disallow_clause&& other) = default;
template <class... Ts>
caf::optional<std::tuple<const Ts&...>> peek() {
CAF_REQUIRE(dest_ != nullptr);
auto ptr = peek(dest_);
if (!ptr->content().template match_elements<Ts...>())
return caf::none;
return ptr->content().template get_as_tuple<Ts...>();
~disallow_clause() {
if (check_ != nullptr)
check_();
}
protected:
Derived& dref() {
return *static_cast<Derived*>(this);
}
caf::scheduler::test_coordinator& sched_;
caf::strong_actor_ptr src_;
caf::local_actor* dest_;
};
template <class... Ts>
class disallow_clause : public disallow_clause_base<disallow_clause<Ts...>> {
public:
template <class... Us>
disallow_clause(Us&&... xs)
: disallow_clause_base<disallow_clause<Ts...>>(std::forward<Us>(xs)...) {
// nop
disallow_clause& from(const wildcard&) {
return *this;
}
template <class... Us>
void with(Us&&... xs) {
// succeed immediately if dest_ is empty
if (this->dest_ == nullptr)
return;
auto tmp = std::make_tuple(std::forward<Us>(xs)...);
elementwise_compare_inspector<decltype(tmp)> inspector{tmp};
auto ys = this->template peek<Ts...>();
if (ys && inspector(get<0>(*ys)))
CAF_FAIL("disallowed message found: " << caf::deep_to_string(ys));
disallow_clause& from(caf_handle x) {
src_ = x;
return *this;
}
};
/// The single-argument disallow-clause allows to automagically unwrap T
/// if it's a variant-like wrapper.
template <class T>
class disallow_clause<T> : public disallow_clause_base<disallow_clause<T>> {
public:
template <class... Us>
disallow_clause(Us&&... xs)
: disallow_clause_base<disallow_clause<T>>(std::forward<Us>(xs)...) {
// nop
disallow_clause& to(caf_handle x) {
dest_ = x;
return *this;
}
template <class... Us>
void with(Us&&... xs) {
if (this->dest_ == nullptr)
return;
std::integral_constant<bool, has_outer_type<T>::value> token;
// TODO: move tmp into lambda when switching to C++14
auto tmp = std::make_tuple(std::forward<Us>(xs)...);
with_content(token, tmp);
}
private:
template <class U>
void with_content(std::integral_constant<bool, false>, const U& x) {
elementwise_compare_inspector<U> inspector{x};
auto xs = this->template peek<T>();
if (xs && inspector(get<0>(*xs)))
CAF_FAIL("disallowed message found: " << caf::deep_to_string(*xs));
}
template <class U>
void with_content(std::integral_constant<bool, true>, const U& x) {
elementwise_compare_inspector<U> inspector{x};
auto xs = this->template peek<typename T::outer_type>();
if (!xs)
return;
auto& x0 = get<0>(*xs);
if (is<T>(x0) && inspect(inspector, const_cast<T&>(get<T>(x0))))
CAF_FAIL("disallowed message found: " << caf::deep_to_string(x0));
}
};
template <>
class disallow_clause<void>
: public disallow_clause_base<disallow_clause<void>> {
public:
template <class... Us>
disallow_clause(Us&&... xs)
: disallow_clause_base<disallow_clause<void>>(std::forward<Us>(xs)...) {
// nop
check_ = [=] {
auto ptr = next_mailbox_element(dest_);
if (ptr == nullptr)
return;
if (src_ != nullptr && ptr->sender != src_)
return;
auto res = try_extract<Ts...>(dest_);
if (res != caf::none) {
using namespace caf::detail;
elementwise_compare_inspector<decltype(tmp)> inspector{tmp};
auto& ys = *res;
auto ys_indices = get_indices(ys);
if (apply_args(inspector, ys_indices, ys))
CAF_FAIL("received disallowed message: " << CAF_ARG(*res));
}
};
}
void with() {
if (dest_ == nullptr)
return;
auto ptr = next_mailbox_element(dest_);
CAF_REQUIRE(!ptr->content().empty());
}
protected:
caf_handle src_;
caf_handle dest_;
std::function<void ()> check_;
};
template <class Config = caf::actor_system_config>
......@@ -547,4 +482,4 @@ struct test_coordinator_fixture {
#define disallow(types, fields) \
CAF_MESSAGE("disallow" << #types << "." << #fields); \
disallow_clause< CAF_EXPAND(CAF_DSL_LIST types) >{sched} . fields
disallow_clause< 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