Unverified Commit 9ebf640e authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #1532

Re-implement inject in the deterministic fixture
parents d6c99643 5571c169
...@@ -71,6 +71,7 @@ caf_add_component( ...@@ -71,6 +71,7 @@ caf_add_component(
caf/abstract_group.cpp caf/abstract_group.cpp
caf/abstract_mailbox.cpp caf/abstract_mailbox.cpp
caf/action.cpp caf/action.cpp
caf/action.test.cpp
caf/actor.cpp caf/actor.cpp
caf/actor_addr.cpp caf/actor_addr.cpp
caf/actor_clock.cpp caf/actor_clock.cpp
...@@ -232,7 +233,6 @@ caf_add_component( ...@@ -232,7 +233,6 @@ caf_add_component(
tests/legacy/core-test.cpp tests/legacy/core-test.cpp
tests/legacy/nasty.cpp tests/legacy/nasty.cpp
LEGACY_TEST_SUITES LEGACY_TEST_SUITES
action
actor_clock actor_clock
actor_factory actor_factory
actor_lifetime actor_lifetime
......
...@@ -118,6 +118,16 @@ private: ...@@ -118,6 +118,16 @@ private:
impl_ptr pimpl_; impl_ptr pimpl_;
}; };
/// Checks whether two actions are equal by comparing their pointers.
inline bool operator==(const action& lhs, const action& rhs) noexcept {
return lhs.ptr() == rhs.ptr();
}
/// Checks whether two actions are not equal by comparing their pointers.
inline bool operator!=(const action& lhs, const action& rhs) noexcept {
return !(lhs == rhs);
}
} // namespace caf } // namespace caf
namespace caf::detail { namespace caf::detail {
......
...@@ -2,21 +2,15 @@ ...@@ -2,21 +2,15 @@
// the main distribution directory for license terms and copyright or visit // the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE. // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE action
#include "caf/action.hpp" #include "caf/action.hpp"
#include "core-test.hpp" #include "caf/test/caf_test_main.hpp"
#include "caf/test/fixture/deterministic.hpp"
using namespace caf; #include "caf/test/scenario.hpp"
namespace {
using fixture = test_coordinator_fixture<>; #include "caf/event_based_actor.hpp"
} // namespace using namespace caf;
BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO("actions wrap function calls") { SCENARIO("actions wrap function calls") {
GIVEN("an action wrapping a lambda") { GIVEN("an action wrapping a lambda") {
...@@ -24,21 +18,21 @@ SCENARIO("actions wrap function calls") { ...@@ -24,21 +18,21 @@ SCENARIO("actions wrap function calls") {
THEN("it calls the lambda and transitions from scheduled to invoked") { THEN("it calls the lambda and transitions from scheduled to invoked") {
auto called = false; auto called = false;
auto uut = make_action([&called] { called = true; }); auto uut = make_action([&called] { called = true; });
CHECK(uut.scheduled()); check(uut.scheduled());
uut.run(); uut.run();
CHECK(called); check(called);
} }
} }
WHEN("disposing the action") { WHEN("disposing the action") {
THEN("it transitions to disposed and run no longer calls the lambda") { THEN("it transitions to disposed and run no longer calls the lambda") {
auto called = false; auto called = false;
auto uut = make_action([&called] { called = true; }); auto uut = make_action([&called] { called = true; });
CHECK(uut.scheduled()); check(uut.scheduled());
uut.dispose(); uut.dispose();
CHECK(uut.disposed()); check(uut.disposed());
uut.run(); uut.run();
CHECK(!called); check(!called);
CHECK(uut.disposed()); check(uut.disposed());
} }
} }
WHEN("running the action multiple times") { WHEN("running the action multiple times") {
...@@ -48,7 +42,7 @@ SCENARIO("actions wrap function calls") { ...@@ -48,7 +42,7 @@ SCENARIO("actions wrap function calls") {
uut.run(); uut.run();
uut.run(); uut.run();
uut.run(); uut.run();
CHECK_EQ(n, 3); check_eq(n, 3);
} }
} }
WHEN("converting an action to a disposable") { WHEN("converting an action to a disposable") {
...@@ -56,13 +50,15 @@ SCENARIO("actions wrap function calls") { ...@@ -56,13 +50,15 @@ SCENARIO("actions wrap function calls") {
auto uut = make_action([] {}); auto uut = make_action([] {});
auto d1 = uut.as_disposable(); // const& overload auto d1 = uut.as_disposable(); // const& overload
auto d2 = action{uut}.as_disposable(); // && overload auto d2 = action{uut}.as_disposable(); // && overload
CHECK_EQ(uut.ptr(), d1.ptr()); check_eq(uut.ptr(), d1.ptr());
CHECK_EQ(uut.ptr(), d2.ptr()); check_eq(uut.ptr(), d2.ptr());
} }
} }
} }
} }
WITH_FIXTURE(test::fixture::deterministic) {
SCENARIO("actors run actions that they receive") { SCENARIO("actors run actions that they receive") {
GIVEN("a scheduled actor") { GIVEN("a scheduled actor") {
WHEN("sending it an action") { WHEN("sending it an action") {
...@@ -73,11 +69,13 @@ SCENARIO("actors run actions that they receive") { ...@@ -73,11 +69,13 @@ SCENARIO("actors run actions that they receive") {
}; };
}); });
auto n = 0; auto n = 0;
inject((action), to(aut).with(make_action([&n] { ++n; }))); inject().with(make_action([&n] { ++n; })).to(aut);
CHECK_EQ(n, 1); check_eq(n, 1);
} }
} }
} }
} }
END_FIXTURE_SCOPE() } // WITH_FIXTURE(test::fixture::deterministic)
CAF_TEST_MAIN()
...@@ -318,6 +318,73 @@ public: ...@@ -318,6 +318,73 @@ public:
message_predicate<Ts...> with_; message_predicate<Ts...> with_;
}; };
/// Utility class for injecting messages into the mailbox of an actor and then
/// checking whether the actor handles the message as expected.
template <class... Ts>
class injector {
public:
injector(deterministic* fix, detail::source_location loc, Ts... values)
: fix_(fix), loc_(loc), values_(std::move(values)...) {
// nop
}
injector() = delete;
injector(injector&&) noexcept = default;
injector& operator=(injector&&) noexcept = default;
injector(const injector&) = delete;
injector& operator=(const injector&) = delete;
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
injector&& from(const strong_actor_ptr& src) && {
from_ = src;
return std::move(*this);
}
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
injector&& from(const actor& src) && {
from_ = actor_cast<strong_actor_ptr>(src);
return std::move(*this);
}
/// Adds a predicate for the sender of the next message that matches only if
/// the sender is `src`.
template <class... Us>
injector&& from(const typed_actor<Us...>& src) && {
from_ = actor_cast<strong_actor_ptr>(src);
return std::move(*this);
}
/// Sets the target actor for this injector, sends the message, and then
/// checks whether the actor handles the message as expected.
template <class T>
void to(const T& dst) && {
to_impl(dst, std::make_index_sequence<sizeof...(Ts)>{});
}
private:
template <class T, size_t... Is>
void to_impl(const T& dst, std::index_sequence<Is...>) {
auto ptr = actor_cast<abstract_actor*>(dst);
ptr->eq_impl(make_message_id(), from_, nullptr,
make_message(std::get<Is>(values_)...));
fix_->expect<Ts...>(loc_)
.from(from_)
.with(std::get<Is>(values_)...)
.to(dst);
}
deterministic* fix_;
detail::source_location loc_;
strong_actor_ptr from_;
std::tuple<Ts...> values_;
};
// -- friends ---------------------------------------------------------------- // -- friends ----------------------------------------------------------------
friend class mailbox_impl; friend class mailbox_impl;
...@@ -379,6 +446,25 @@ public: ...@@ -379,6 +446,25 @@ public:
return evaluator<Ts...>{this, loc, evaluator_algorithm::allow}; return evaluator<Ts...>{this, loc, evaluator_algorithm::allow};
} }
/// Helper class for `inject` that only provides `with`.
struct inject_helper {
deterministic* fix;
detail::source_location loc;
template <class... Ts>
auto with(Ts... values) {
return injector<Ts...>{fix, loc, std::move(values)...};
}
};
/// Starts an `inject` clause. Inject clauses provide a shortcut for sending a
/// message to an actor and then calling `expect` with the same arguments to
/// check whether the actor handles the message as expected.
auto inject(const detail::source_location& loc
= detail::source_location::current()) {
return inject_helper{this, loc};
}
/// Tries to prepone a message, i.e., reorders the messages in the mailbox of /// Tries to prepone a message, i.e., reorders the messages in the mailbox of
/// the receiver such that the next call to `dispatch_message` will run it. /// the receiver such that the next call to `dispatch_message` will run it.
/// @returns `true` if the message could be preponed, `false` otherwise. /// @returns `true` if the message could be preponed, `false` otherwise.
......
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