Implementing an actor using a class requires the following:
\begin{itemize}
\item Provide a constructor taking a reference of type
\lstinline^actor_config&^ as first argument, which is forwarded to the base
class. The config is passed implicitly to the constructor when calling
\lstinline^spawn^, which also forwards any number of additional arguments
to the constructor.
\item Override \lstinline^make_behavior^ for event-based actors and
\lstinline^act^ for blocking actors.
\end{itemize}
* Provide a constructor taking a reference of type ``actor_config&`` as first argument, which is forwarded to the base class. The config is passed implicitly to the constructor when calling ``spawn``, which also forwards any number of additional arguments to the constructor.
* Override ``make_behavior`` for event-based actors and ``act`` for blocking actors.
Implementing actors with classes works for all kinds of actors and allows
simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help. For
statically typed actors, CAF also provides an API for composable
behaviors~\see{composable-behavior} that works well with inheritance. The
behaviors composable-behavior_ that works well with inheritance. The
following three examples implement the forward declarations shown in
Console output is disabled per default. Setting ``logger-console`` to
either ``"uncolored"`` or ``"colored"`` prints log events to
``std::clog``. Using the ``"colored"`` option will print the
log events in different colors depending on the severity level.
\subsubsection{Format Strings}
\label{log-output-format-strings}
.. _log-output-format-strings:
Format Strings
~~~~~~~~~~~~~~
CAF uses log4j-like format strings for configuring printing of individual
events via \lstinline^logger-file-format^ and
\lstinline^logger-console-format^. Note that format modifiers are not supported
events via ``logger-file-format`` and
``logger-console-format``. Note that format modifiers are not supported
at the moment. The recognized field identifiers are:
\begin{tabular}{ll}
\hline
\textbf{Character} & \textbf{Output} \\
\hline
\texttt{c} & The category/component. \\
\hline
\texttt{C} & The full qualifier of the current function. For example, the qualifier of \lstinline^void ns::foo::bar()^ is printed as \lstinline^ns.foo^. \\
\hline
\texttt{d} & The date in ISO 8601 format, i.e., \lstinline^"YYYY-MM-DDThh:mm:ss"^. \\
\hline
\texttt{F} & The file name. \\
\hline
\texttt{L} & The line number. \\
\hline
\texttt{m} & The user-defined log message. \\
\hline
\texttt{M} & The name of the current function. For example, the name of \lstinline^void ns::foo::bar()^ is printed as \lstinline^bar^. \\
\hline
\texttt{n} & A newline. \\
\hline
\texttt{p} & The priority (severity level). \\
\hline
\texttt{r} & Elapsed time since starting the application in milliseconds. \\
\hline
\texttt{t} & ID of the current thread. \\
\hline
\texttt{a} & ID of the current actor (or \lstinline^actor0^ when not logging inside an actor). \\
\hline
\texttt{\%} & A single percent sign. \\
\hline
\end{tabular}
\subsubsection{Filtering}
\label{log-output-filtering}
The two configuration options \lstinline^logger-component-filter^ and
\lstinline^logger-verbosity^ reduce the amount of generated log events. The
Message passing in CAF is always asynchronous. Further, CAF neither guarantees
message delivery nor message ordering in a distributed setting. CAF uses TCP
...
...
@@ -9,29 +11,34 @@ intermediate nodes and can get lost if one of the forwarding nodes fails.
Likewise, forwarding paths can change dynamically and thus cause messages to
arrive out of order.
The messaging layer of CAF has three primitives for sending messages:
\lstinline^send^, \lstinline^request^, and \lstinline^delegate^. The former
simply enqueues a message to the mailbox the receiver. The latter two are
discussed in more detail in \sref{request} and \sref{delegate}.
The messaging layer of CAF has three primitives for sending messages: ``send``,
``request``, and ``delegate``. The former simply enqueues a message to the
mailbox the receiver. The latter two are discussed in more detail in
:ref:`request` and :ref:`delegate`.
.. _mailbox-element:
\subsection{Structure of Mailbox Elements}
\label{mailbox-element}
Structure of Mailbox Elements
-----------------------------
When enqueuing a message to the mailbox of an actor, CAF wraps the content of
the message into a \lstinline^mailbox_element^ (shown below) to add meta data
the message into a ``mailbox_element`` (shown below) to add meta data
and processing paths.
\singlefig{mailbox_element}{UML class diagram for \lstinline^mailbox_element^}{mailbox_element}
.. _mailbox_element:
.. image:: mailbox_element.png
:alt: UML class diagram for ``mailbox_element``
The sender is stored as a \lstinline^strong_actor_ptr^ \see{actor-pointer} and
The sender is stored as a ``strong_actor_ptr`` (see :ref:`actor-pointer`) and
denotes the origin of the message. The message ID is either 0---invalid---or a
positive integer value that allows the sender to match a response to its
request. The \lstinline^stages^ vector stores the path of the message. Response
request. The ``stages`` vector stores the path of the message. Response
messages, i.e., the returned values of a message handler, are sent to
\lstinline^stages.back()^ after calling \lstinline^stages.pop_back()^. This
allows CAF to build pipelines of arbitrary size. If no more stage is left, the
response reaches the sender. Finally, \lstinline^content()^ grants access to
the type-erased tuple storing the message itself.
``stages.back()`` after calling ``stages.pop_back()``. This allows CAF to build
pipelines of arbitrary size. If no more stage is left, the response reaches the
sender. Finally, ``content()`` grants access to the type-erased tuple storing
the message itself.
Mailbox elements are created by CAF automatically and are usually invisible to
the programmer. However, understanding how messages are processed internally
...
...
@@ -41,214 +48,278 @@ It is worth mentioning that CAF usually wraps the mailbox element and its
content into a single object in order to reduce the number of memory
allocations.
\subsection{Copy on Write}
\label{copy-on-write}
.. _copy-on-write:
Copy on Write
-------------
CAF allows multiple actors to implicitly share message contents, as long as no
actor performs writes. This allows groups~\see{groups} to send the same content
to all subscribed actors without any copying overhead.
actor performs writes. This allows groups (see :ref:`groups`) to send the same
content to all subscribed actors without any copying overhead.
Actors copy message contents whenever other actors hold references to it and if
one or more arguments of a message handler take a mutable reference.
\subsection{Requirements for Message Types}
Requirements for Message Types
------------------------------
Message types in CAF must meet the following requirements:
\begin{enumerate}
\item Serializable or inspectable \see{type-inspection}
\item Default constructible
\item Copy constructible
\end{enumerate}
1. Serializable or inspectable (see :ref:`type-inspection`)
2. Default constructible
3. Copy constructible
A type is serializable if it provides free function
``serialize(Serializer&, T&)`` or
``serialize(Serializer&, T&, const unsigned int)``. Accordingly, a type is
inspectable if it provides a free function ``inspect(Inspector&, T&)``.
A type is serializable if it provides free function \lstinline^serialize(Serializer&, T&)^ or \lstinline^serialize(Serializer&, T&, const unsigned int)^. Accordingly, a type is inspectable if it provides a free function \lstinline^inspect(Inspector&, T&)^.
Requirement 2 is a consequence of requirement 1, because CAF needs to be able to
create an object of a type before it can call ``serialize`` or ``inspect`` on
it. Requirement 3 allows CAF to implement Copy on Write (see
:ref:`copy-on-write`).
Requirement 2 is a consequence of requirement 1, because CAF needs to be able
to create an object of a type before it can call \lstinline^serialize^ or
\lstinline^inspect^ on it. Requirement 3 allows CAF to implement Copy on
Write~\see{copy-on-write}.
.. _special-handler:
\subsection{Default and System Message Handlers}
\label{special-handler}
Default and System Message Handlers
-----------------------------------
CAF has three system-level message types (\lstinline^down_msg^,
\lstinline^exit_msg^, and \lstinline^error^) that all actor should handle
regardless of there current state. Consequently, event-based actors handle such
messages in special-purpose message handlers. Additionally, event-based actors
have a fallback handler for unmatched messages. Note that blocking actors have
neither of those special-purpose handlers \see{blocking-actor}.
CAF has three system-level message types (``down_msg``, ``exit_msg``, and
``error``) that all actor should handle regardless of there current state.
Consequently, event-based actors handle such messages in special-purpose message
handlers. Additionally, event-based actors have a fallback handler for unmatched
messages. Note that blocking actors have neither of those special-purpose
handlers (see :ref:`blocking-actor`).
\subsubsection{Down Handler}
\label{down-message}
.. _down-message:
Actors can monitor the lifetime of other actors by calling \lstinline^self->monitor(other)^. This will cause the runtime system of CAF to send a \lstinline^down_msg^ for \lstinline^other^ if it dies. Actors drop down messages unless they provide a custom handler via \lstinline^set_down_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (down_msg&)^ or \lstinline^void (scheduled_actor*, down_msg&)^. The latter signature allows users to implement down message handlers as free function.
Down Handler
~~~~~~~~~~~~~
\subsubsection{Exit Handler}
\label{exit-message}
Actors can monitor the lifetime of other actors by calling
``self->monitor(other)``. This will cause the runtime system of CAF to send a
``down_msg`` for ``other`` if it dies. Actors drop down messages unless they
provide a custom handler via ``set_down_handler(f)``, where ``f`` is a function
object with signature ``void (down_msg&)`` or
``void (scheduled_actor*, down_msg&)``. The latter signature allows users to
implement down message handlers as free function.
Bidirectional monitoring with a strong lifetime coupling is established by calling \lstinline^self->link_to(other)^. This will cause the runtime to send an \lstinline^exit_msg^ if either \lstinline^this^ or \lstinline^other^ dies. Per default, actors terminate after receiving an \lstinline^exit_msg^ unless the exit reason is \lstinline^exit_reason::normal^. This mechanism propagates failure states in an actor system. Linked actors form a sub system in which an error causes all actors to fail collectively. Actors can override the default handler via \lstinline^set_exit_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (exit_message&)^ or \lstinline^void (scheduled_actor*, exit_message&)^.
.. _exit-message:
\subsubsection{Error Handler}
\label{error-message}
Exit Handler
~~~~~~~~~~~~
Actors send error messages to others by returning an \lstinline^error^ \see{error} from a message handler. Similar to exit messages, error messages usually cause the receiving actor to terminate, unless a custom handler was installed via \lstinline^set_error_handler(f)^, where \lstinline^f^ is a function object with signature \lstinline^void (error&)^ or \lstinline^void (scheduled_actor*, error&)^. Additionally, \lstinline^request^ accepts an error handler as second argument to handle errors for a particular request~\see{error-response}. The default handler is used as fallback if \lstinline^request^ is used without error handler.
Bidirectional monitoring with a strong lifetime coupling is established by
calling ``self->link_to(other)``. This will cause the runtime to send an
``exit_msg`` if either ``this`` or ``other`` dies. Per default, actors terminate
after receiving an ``exit_msg`` unless the exit reason is
``exit_reason::normal``. This mechanism propagates failure states in an actor
system. Linked actors form a sub system in which an error causes all actors to
fail collectively. Actors can override the default handler via
``set_exit_handler(f)``, where ``f`` is a function object with signature
``void (exit_message&)`` or ``void (scheduled_actor*, exit_message&)``.
\subsubsection{Default Handler}
\label{default-handler}
.. _error-message:
Error Handler
~~~~~~~~~~~~~
Actors send error messages to others by returning an ``error`` (see
:ref:`error`) from a message handler. Similar to exit messages, error messages
usually cause the receiving actor to terminate, unless a custom handler was
installed via ``set_error_handler(f)``, where ``f`` is a function object with
signature ``void (error&)`` or ``void (scheduled_actor*, error&)``.
Additionally, ``request`` accepts an error handler as second argument to handle
errors for a particular request (see :ref:`error-response`). The default handler
is used as fallback if ``request`` is used without error handler.
.. _default-handler:
Default Handler
~~~~~~~~~~~~~~~
The default handler is called whenever the behavior of an actor did not match
the input. Actors can change the default handler by calling
\lstinline^set_default_handler^. The expected signature of the function object
is \lstinline^result<message> (scheduled_actor*, message_view&)^, whereas the
\lstinline^self^ pointer can again be omitted. The default handler can return a
response message or cause the runtime to \emph{skip} the input message to allow
``set_default_handler``. The expected signature of the function object
is ``result<message> (scheduled_actor*, message_view&)``, whereas the
``self`` pointer can again be omitted. The default handler can return a
response message or cause the runtime to *skip* the input message to allow
an actor to handle it in a later state. CAF provides the following built-in
``print_and_drop``, ``drop``, and ``skip``. The former
two are meant for debugging and testing purposes and allow an actor to simply
return an input. The next two functions drop unexpected messages with or
without printing a warning beforehand. Finally, \lstinline^skip^ leaves the
input message in the mailbox. The default is \lstinline^print_and_drop^.
without printing a warning beforehand. Finally, ``skip`` leaves the
input message in the mailbox. The default is ``print_and_drop``.
.. _request:
\subsection{Requests}
\label{request}
Requests
--------
A main feature of CAF is its ability to couple input and output types via the
type system. For example, a \lstinline^typed_actor<replies_to<int>::with<int>>^
essentially behaves like a function. It receives a single \lstinline^int^ as
input and responds with another \lstinline^int^. CAF embraces this functional
type system. For example, a ``typed_actor<replies_to<int>::with<int>>``
essentially behaves like a function. It receives a single ``int`` as
input and responds with another ``int``. CAF embraces this functional
take on actors by simply creating response messages from the result of message
handlers. This allows CAF to match \emph{request} to \emph{response} messages
handlers. This allows CAF to match *request* to *response* messages
and to provide a convenient API for this style of communication.
\subsubsection{Sending Requests and Handling Responses}
\label{handling-response}
.. _handling-response:
Actors send request messages by calling \lstinline^request(receiver, timeout, content...)^. This function returns an intermediate object that allows an actor to set a one-shot handler for the response message. Event-based actors can use either \lstinline^request(...).then^ or \lstinline^request(...).await^. The former multiplexes the one-shot handler with the regular actor behavior and handles requests as they arrive. The latter suspends the regular actor behavior until all awaited responses arrive and handles requests in LIFO order. Blocking actors always use \lstinline^request(...).receive^, which blocks until the one-shot handler was called. Actors receive a \lstinline^sec::request_timeout^ \see{sec} error message~\see{error-message} if a timeout occurs. Users can set the timeout to \lstinline^infinite^ for unbound operations. This is only recommended if the receiver is running locally.
Sending Requests and Handling Responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Actors send request messages by calling ``request(receiver, timeout,
content...)``. This function returns an intermediate object that allows an actor
to set a one-shot handler for the response message. Event-based actors can use
either ``request(...).then`` or ``request(...).await``. The former multiplexes
the one-shot handler with the regular actor behavior and handles requests as
they arrive. The latter suspends the regular actor behavior until all awaited
responses arrive and handles requests in LIFO order. Blocking actors always use
``request(...).receive``, which blocks until the one-shot handler was called.
Actors receive a ``sec::request_timeout`` (see :ref:`sec`) error message (see
:ref:`error-message`) if a timeout occurs. Users can set the timeout to
``infinite`` for unbound operations. This is only recommended if the receiver is
running locally.
In our following example, we use the simple cell actors shown below as
\item Synchronous send blocks no longer support \lstinline^continue_with^. This
feature has been removed without substitution.
\end{itemize}
* Type names are no longer demangled automatically. Hence, users must explicitly pass the type name as first argument when using ``announce``, i.e., ``announce<my_class>(...)`` becomes ``announce<my_class>("my_class", ...)``.
* Synchronous send blocks no longer support ``continue_with``. This feature has been removed without substitution.
\subsection{0.12 to 0.13}
0.12 to 0.13
------------
This release removes the (since 0.9 deprecated) \lstinline^cppa^ headers and
deprecates all \lstinline^*_send_tuple^ versions (simply use the function
without \lstinline^_tuple^ suffix). \lstinline^local_actor::on_exit^ once again
This release removes the (since 0.9 deprecated) ``cppa`` headers and
deprecates all ``*_send_tuple`` versions (simply use the function
without ``_tuple`` suffix). ``local_actor::on_exit`` once again
became virtual.
In case you were using the old \lstinline^cppa::options_description^ API, you
can migrate to the new API based on \lstinline^extract^ \see{extract-opts}.
In case you were using the old ``cppa::options_description`` API, you can
migrate to the new API based on ``extract`` (see :ref:`extract-opts`).
Most importantly, version 0.13 slightly changes \lstinline^last_dequeued^ and
\lstinline^last_sender^. Both functions will now cause undefined behavior
(dereferencing a \lstinline^nullptr^) instead of returning dummy values when
accessed from outside a callback or after forwarding the current message.
Besides, these function names were not a good choice in the first place, since
``last'' implies accessing data received in the past. As a result, both
functions are now deprecated. Their replacements are named
\lstinline^current_message^ and \lstinline^current_sender^ \see{interface}.
Most importantly, version 0.13 slightly changes ``last_dequeued`` and
``last_sender``. Both functions will now cause undefined behavior (dereferencing
a ``nullptr``) instead of returning dummy values when accessed from outside a
callback or after forwarding the current message. Besides, these function names
were not a good choice in the first place, since "last" implies accessing data
received in the past. As a result, both functions are now deprecated. Their
replacements are named ``current_message`` and ``current_sender`` (see
:ref:`interface`).
\subsection{0.13 to 0.14}
0.13 to 0.14
------------
The function \lstinline^timed_sync_send^ has been removed. It offered an
The function ``timed_sync_send`` has been removed. It offered an
alternative way of defining message handlers, which is inconsistent with the
rest of the API.
The policy classes \lstinline^broadcast^, \lstinline^random^, and
\lstinline^round_robin^ in \lstinline^actor_pool^ were removed and replaced by
The policy classes ``broadcast``, ``random``, and
``round_robin`` in ``actor_pool`` were removed and replaced by
factory functions using the same name.
\clearpage
\subsection{0.14 to 0.15}
Version 0.15 replaces the singleton-based architecture with
\lstinline^actor_system^. Most of the free functions in namespace
\lstinline^caf^ are now member functions of \lstinline^actor_system^
\see{actor-system}. Likewise, most functions in namespace \lstinline^caf::io^
are now member functions of \lstinline^middleman^ \see{middleman}. The
structure of CAF applications has changed fundamentally with a focus on
configurability. Setting and fine-tuning the scheduler, changing parameters of
the middleman, etc. is now bundled in the class
\lstinline^actor_system_config^. The new configuration mechanism is also easily
0.14 to 0.15
------------
Version 0.15 replaces the singleton-based architecture with ``actor_system``.
Most of the free functions in namespace ``caf`` are now member functions of
``actor_system`` (see :ref:`actor-system`). Likewise, most functions in
namespace ``caf::io`` are now member functions of ``middleman`` (see
:ref:`middleman`). The structure of CAF applications has changed fundamentally
with a focus on configurability. Setting and fine-tuning the scheduler, changing
parameters of the middleman, etc. is now bundled in the class
``actor_system_config``. The new configuration mechanism is also easily
extensible.
Patterns are now limited to the simple notation, because the advanced features
...
...
@@ -179,5 +175,5 @@ Patterns are now limited to the simple notation, because the advanced features
Windows/MSVC, and (3) drastically impact compile times. Dropping this
functionality also simplifies the implementation and improves performance.
The \lstinline^blocking_api^ flag has been removed. All variants of
\emph{spawn} now auto-detect blocking actors.
The ``blocking_api`` flag has been removed. All variants of
CAF comes with built-in support for writing unit tests in a domain-specific
language (DSL). The API looks similar to well-known testing frameworks such as
...
...
@@ -8,65 +10,65 @@ actors.
Our design leverages four main concepts:
\begin{itemize}
\item \textbf{Checks} represent single boolean expressions.
\item \textbf{Tests} contain one or more checks.
\item \textbf{Fixtures} equip tests with a fixed data environment.
\item \textbf{Suites} group tests together.
\end{itemize}
* **Checks** represent single boolean expressions.
* **Tests** contain one or more checks.
* **Fixtures** equip tests with a fixed data environment.
* **Suites** group tests together.
The following code illustrates a very basic test case that captures the four
main concepts described above.
\begin{lstlisting}
// Adds all tests in this compilation unit to the suite "math".
#define CAF_SUITE math
.. code-block:: C++
// Adds all tests in this compilation unit to the suite "math".
#define CAF_SUITE math
// Pulls in all the necessary macros.
#include "caf/test/dsl.hpp"
// Pulls in all the necessary macros.
#include "caf/test/dsl.hpp"
namespace {
namespace {
struct fixture {};
struct fixture {};
} // namespace
} // namespace
// Makes all members of `fixture` available to tests in the scope.
CAF_TEST_FIXTURE_SCOPE(math_tests, fixture)
// Makes all members of `fixture` available to tests in the scope.
CAF_TEST_FIXTURE_SCOPE(math_tests, fixture)
// Implements our first test.
CAF_TEST(divide) {
// Implements our first test.
CAF_TEST(divide) {
CAF_CHECK(1 / 1 == 0); // fails
CAF_CHECK(2 / 2 == 1); // passes
CAF_REQUIRE(3 + 3 == 5); // fails and aborts test execution
CAF_CHECK(4 - 4 == 0); // unreachable due to previous requirement error
}
}
CAF_TEST_FIXTURE_SCOPE_END()
\end{lstlisting}
CAF_TEST_FIXTURE_SCOPE_END()
The code above highlights the two basic macros \lstinline^CAF_CHECK^ and
\lstinline^CAF_REQUIRE^. The former reports failed checks, but allows the test
The code above highlights the two basic macros ``CAF_CHECK`` and
``CAF_REQUIRE``. The former reports failed checks, but allows the test
to continue on error. The latter stops test execution if the boolean expression
evaluates to false.
The third macro worth mentioning is \lstinline^CAF_FAIL^. It unconditionally
The third macro worth mentioning is ``CAF_FAIL``. It unconditionally
stops test execution with an error message. This is particularly useful for
stopping program execution after receiving unexpected messages, as we will see
later.
\subsection{Testing Actors}
Testing Actors
--------------
The following example illustrates how to add an actor system as well as a
scoped actor to fixtures. This allows spawning of and interacting with actors
in a similar way regular programs would. Except that we are using macros such
as \lstinline^CAF_CHECK^ and provide tests rather than implementing a
\lstinline^caf_main^.
as ``CAF_CHECK`` and provide tests rather than implementing a
``caf_main``.
\begin{lstlisting}
namespace {
.. code-block:: C++
struct fixture {
namespace {
struct fixture {
caf::actor_system_config cfg;
caf::actor_system sys;
caf::scoped_actor self;
...
...
@@ -74,21 +76,21 @@ struct fixture {
fixture() : sys(cfg), self(sys) {
// nop
}
};
};
caf::behavior adder() {
caf::behavior adder() {
return {
[=](int x, int y) {
return x + y;
}
};
}
}
} // namespace
} // namespace
CAF_TEST_FIXTURE_SCOPE(actor_tests, fixture)
CAF_TEST_FIXTURE_SCOPE(actor_tests, fixture)
CAF_TEST(simple actor test) {
CAF_TEST(simple actor test) {
// Our Actor-Under-Test.
auto aut = self->spawn(adder);
self->request(aut, caf::infinite, 3, 4).receive(
...
...
@@ -99,75 +101,44 @@ CAF_TEST(simple actor test) {
// Must not happen, stop test.
CAF_FAIL(sys.render(err));
});
}
}
CAF_TEST_FIXTURE_SCOPE_END()
\end{lstlisting}
CAF_TEST_FIXTURE_SCOPE_END()
The example above works, but suffers from several issues:
\begin{itemize}
\item
Significant amount of boilerplate code.
\item
* Significant amount of boilerplate code.
* Using a scoped actor as illustrated above can only test one actor at a time. However, messages between other actors are invisible to us.
* CAF runs actors in a thread pool by default. The resulting nondeterminism makes triggering reliable ordering of messages near impossible. Further, forcing timeouts to test error handling code is even harder.
Using a scoped actor as illustrated above can only test one actor at a
time. However, messages between other actors are invisible to us.
\item
CAF runs actors in a thread pool by default. The resulting nondeterminism
makes triggering reliable ordering of messages near impossible. Further,
forcing timeouts to test error handling code is even harder.
\end{itemize}
\subsection{Deterministic Testing}
Deterministic Testing
---------------------
CAF provides a scheduler implementation specifically tailored for writing unit
tests called \lstinline^test_coordinator^. It does not start any threads and
tests called ``test_coordinator``. It does not start any threads and
instead gives unit tests full control over message dispatching and timeout
management.
To reduce boilerplate code, CAF also provides a fixture template called
\lstinline^test_coordinator_fixture^ that comes with ready-to-use actor system
(\lstinline^sys^) and testing scheduler (\lstinline^sched^). The optional
``test_coordinator_fixture`` that comes with ready-to-use actor system
(``sys``) and testing scheduler (``sched``). The optional
template parameter allows unit tests to plugin custom actor system
configuration classes.
Using this fixture unlocks three additional macros:
\begin{itemize}
\item
\lstinline^expect^ checks for a single message. The macro verifies the
content types of the message and invokes the necessary member functions on
the test coordinator. Optionally, the macro checks the receiver of the
message and its content. If the expected message does not exist, the test
aborts.
\item
\lstinline^allow^ is similar to \lstinline^expect^, but it does not abort
the test if the expected message is missing. This macro returns
\lstinline^true^ if the allowed message was delivered, \lstinline^false^
otherwise.
\item
* ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts.
* ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise.
* ``disallow`` aborts the test if a particular message was delivered to an actor.
\lstinline^disallow^ aborts the test if a particular message was delivered
to an actor.
The following example implements two actors, ``ping`` and
``pong``, that exchange a configurable amount of messages. The test
*three pings* then checks the contents of each message with
``expect`` and verifies that no additional messages exist using