SpECTRE  v2024.03.19
ActionTesting Namespace Reference

Structures used for mocking the parallel components framework in order to test actions. More...

Classes

struct  GlobalCoreId
 Wraps a size_t representing the global core number. More...
 
struct  InitializeDataBox< tmpl::list< SimpleTags... >, ComputeTagsList >
 
struct  LocalCoreId
 Wraps a size_t representing the local core number. This is so the user can write things like emplace_array_component(NodeId{3},LocalCoreId{2},...) instead of emplace_array_component(3,2,...). More...
 
struct  MockArrayChare
 A mock class for the CMake-generated Parallel::Algorithms::Array More...
 
class  MockDistributedObject
 MockDistributedObject mocks the DistributedObject class. It should not be considered as part of the user interface. More...
 
struct  MockGroupChare
 A mock class for the CMake-generated Parallel::Algorithms::Group More...
 
struct  MockNodeGroupChare
 A mock class for the CMake-generated Parallel::Algorithms::NodeGroup More...
 
class  MockRuntimeSystem
 A class that mocks the infrastructure needed to run actions. It simulates message passing using the inbox infrastructure and handles most of the arguments to the apply method. This mocks the Charm++ runtime system as well as the layer built on top of it as part of SpECTRE. More...
 
struct  MockSingletonChare
 A mock class for the CMake-generated Parallel::Algorithms::Singleton More...
 
struct  NodeId
 Wraps a size_t representing the node number. This is so the user can write things like emplace_array_component(NodeId{3},...) instead of emplace_array_component(3,...). More...
 

Functions

bool operator== (const NodeId &lhs, const NodeId &rhs)
 
bool operator!= (const NodeId &lhs, const NodeId &rhs)
 
bool operator== (const LocalCoreId &lhs, const LocalCoreId &rhs)
 
bool operator!= (const LocalCoreId &lhs, const LocalCoreId &rhs)
 
bool operator== (const GlobalCoreId &lhs, const GlobalCoreId &rhs)
 
bool operator!= (const GlobalCoreId &lhs, const GlobalCoreId &rhs)
 
template<typename Metavariables >
void set_phase (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const Parallel::Phase &phase)
 Set the phase of all parallel components to phase
 
template<typename Component , typename... Options>
void emplace_component (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename Component::array_index &array_index, Options &&... opts)
 Emplaces a distributed object with index array_index into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply.
 
template<typename Component , typename... Options>
void emplace_array_component (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const NodeId node_id, const LocalCoreId local_core_id, const typename Component::array_index &array_index, Options &&... opts)
 Emplaces a distributed array object with index array_index into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply.
 
template<typename Component , typename... Options>
void emplace_singleton_component (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const NodeId node_id, const LocalCoreId local_core_id, Options &&... opts)
 Emplaces a distributed singleton object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply.
 
template<typename Component , typename... Options>
void emplace_group_component (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, Options &&... opts)
 Emplaces a distributed group object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply.
 
template<typename Component , typename... Options>
void emplace_nodegroup_component (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, Options &&... opts)
 Emplaces a distributed nodegroup object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply.
 
template<typename Component , typename... Options, typename Metavars = typename Component::metavariables>
void emplace_component_and_initialize (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename Component::array_index &array_index, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
 Emplaces a distributed object with index array_index into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply Additionally, the simple tags in the DataBox are initialized from the values set in initial_values.
 
template<typename Component , typename... Options, typename Metavars = typename Component::metavariables>
void emplace_array_component_and_initialize (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const NodeId node_id, const LocalCoreId local_core_id, const typename Component::array_index &array_index, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
 Emplaces a distributed array object with index array_index into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply Additionally, the simple tags in the DataBox are initialized from the values set in initial_values.
 
template<typename Component , typename... Options, typename Metavars = typename Component::metavariables>
void emplace_singleton_component_and_initialize (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const NodeId node_id, const LocalCoreId local_core_id, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
 Emplaces a distributed singleton object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply Additionally, the simple tags in the DataBox are initialized from the values set in initial_values.
 
template<typename Component , typename... Options, typename Metavars = typename Component::metavariables>
void emplace_group_component_and_initialize (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
 Emplaces a distributed group object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply Additionally, the simple tags in the DataBox are initialized from the values set in initial_values.
 
template<typename Component , typename... Options, typename Metavars = typename Component::metavariables>
void emplace_nodegroup_component_and_initialize (const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
 Emplaces a distributed nodegroup object into the parallel component Component. The options opts are forwarded to be used in a call to detail::ForwardAllOptionsToDataBox::apply Additionally, the simple tags in the DataBox are initialized from the values set in initial_values.
 
template<typename Component , typename Metavariables >
size_t get_next_action_index (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Get the index in the action list of the next action.
 
template<typename Component , typename Tag , typename Metavariables >
const auto & get_databox_tag (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns the Tag from the DataBox of the parallel component Component with array index array_index. If the component's current DataBox type does not contain Tag then an error is emitted.
 
template<typename Component , typename Tag , typename Metavariables >
bool box_contains (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns true if the current DataBox of Component with index array_index contains the tag Tag. If the tag is not contained, returns false.
 
template<typename Component , typename Tag , typename Metavariables >
bool tag_is_retrievable (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns true if the tag Tag can be retrieved from the current DataBox of Component with index array_index.
 
template<typename Component , typename Metavariables >
void next_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Invoke the next action in the ActionList on the parallel component Component on the component labeled by array_index, failing if it was not ready.
 
template<typename Component , typename Metavariables >
bool next_action_if_ready (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Invoke the next action in the ActionList on the parallel component Component on the component labeled by array_index, returning whether it was ready.
 
template<typename Component , typename Action , typename Metavariables , typename... Args>
void simple_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index, Args &&... args)
 Runs the simple action Action on the array_indexth element of the parallel component Component.
 
template<typename Component , typename Action , typename Metavariables , typename... Args>
void queue_simple_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index, Args &&... args)
 Queues the simple action Action on the array_indexth element of the parallel component Component.
 
template<typename Component , typename Action , typename Metavariables , typename... Args>
void threaded_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index, Args &&... args)
 Runs the simple action Action on the array_indexth element of the parallel component Component.
 
template<typename Component , typename Metavariables >
void invoke_queued_simple_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Runs the next queued simple action on the array_indexth element of the parallel component Component.
 
template<typename Component , typename Metavariables >
bool is_simple_action_queue_empty (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns true if there are no simple actions in the queue.
 
template<typename Component , typename Metavariables >
size_t number_of_queued_simple_actions (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns the number of simple actions in the queue.
 
template<typename Component , typename Metavariables >
void invoke_queued_threaded_action (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Runs the next queued threaded action on the array_indexth element of the parallel component Component.
 
template<typename Component , typename Metavariables >
bool is_threaded_action_queue_empty (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns true if there are no threaded actions in the queue.
 
template<typename Component , typename Metavariables >
size_t number_of_queued_threaded_actions (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns the number of threaded actions in the queue.
 
template<typename Component , typename Metavariables >
bool get_terminate (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns whether or not the Component with index array_index has been terminated.
 
template<typename Component , typename Metavariables , typename ArrayIndex >
Parallel::GlobalCache< Metavariables > & cache (MockRuntimeSystem< Metavariables > &runner, const ArrayIndex &array_index)
 Returns the GlobalCache of Component with index array_index.
 
template<typename ComponentList , typename MockRuntimeSystem , typename ArrayIndex >
std::vector< size_t > indices_of_components_with_queued_simple_actions (const gsl::not_null< MockRuntimeSystem * > runner, const ArrayIndex &array_index)
 Returns a vector of all the indices of the Components in the ComponentList that have queued simple actions, for a particular array_index.
 
template<typename ComponentList , typename MockRuntimeSystem >
auto array_indices_with_queued_simple_actions (const gsl::not_null< MockRuntimeSystem * > runner) -> detail::array_indices_for_each_component< ComponentList >
 Returns a vector of array_indices for each Component. The vector is filled with only those array_indices for which there are queued simple actions.
 
template<typename ComponentList >
size_t number_of_elements_with_queued_simple_actions (const detail::array_indices_for_each_component< ComponentList > &array_indices)
 Given the output of array_indices_with_queued_simple_actions, returns the total number of array indices that have queued simple actions.
 
template<typename ComponentList , typename MockRuntimeSystem , typename Generator >
void invoke_random_queued_simple_action (const gsl::not_null< MockRuntimeSystem * > runner, const gsl::not_null< Generator * > generator, const detail::array_indices_for_each_component< ComponentList > &array_indices)
 Invokes the next queued action on a random Component on a random array_index of that component. array_indices is the thing returned by array_indices_with_queued_simple_actions
 
template<typename Component , typename Metavariables >
const auto & get_databox (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Retrieves the DataBox from the parallel component Component with index array_index.
 
template<typename Component , typename Metavariables >
auto & get_databox (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Retrieves the DataBox from the parallel component Component with index array_index.
 
template<typename Component , typename InboxTag , typename Metavariables >
const auto & get_inbox_tag (const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
 Returns the InboxTag from the parallel component Component with array index array_index.
 
template<typename Component , typename InboxTag , typename Metavariables >
auto & get_inbox_tag (const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
 Returns the InboxTag from the parallel component Component with array index array_index.
 

Detailed Description

Structures used for mocking the parallel components framework in order to test actions.

The ActionTesting framework is designed to mock the parallel components so that actions and sequences of actions can be tested in a controlled environment that behaves effectively identical to the actual parallel environment.

The basics

The action testing framework (ATF) works essentially identically to the parallel infrastructure. A metavariables must be supplied which must at least list the components used (using component_list = tmpl::list<>). As a simple example, let's look at the test for the Parallel::Actions::TerminatePhase action.

The Metavariables is given by:

struct Metavariables {
using component_list = tmpl::list<Component<Metavariables>>;
};

The component list in this case just contains a single component that is described below.

The component is templated on the metavariables, which, while not always necessary, eliminates some compilation issues that may arise otherwise. The component for the TerminatePhase test is given by:

template <typename Metavariables>
struct Component {
using metavariables = Metavariables;
using chare_type = ActionTesting::MockArrayChare;
using array_index = int;
using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<
Parallel::Phase::Testing, tmpl::list<Parallel::Actions::TerminatePhase>>>;
};
@ Testing
phase in which something is tested
A mock class for the CMake-generated Parallel::Algorithms::Array
Definition: ActionTesting.hpp:737
List of all the actions to be executed in the specified phase.
Definition: PhaseDependentActionList.hpp:17

Just like with the standard parallel code, a metavariables type alias must be present. The chare type should be ActionTesting::MockArrayChare, ActionTesting::MockGroupChare, ActionTesting::MockNodeGroupChare, or ActionTesting::MockSingletonChare. Currently many groups, nodegroups, and singletons are treated during action testing as a one-element array component, but that usage is deprecated and will eventually be removed. The index_type must be whatever the actions will use to index the array. In the case of a singleton int is recommended. Finally, the phase_dependent_action_list must be specified just as in the cases for parallel executables. In this case only the Testing phase is used and only has the TerminatePhase action listed.

The SPECTRE_TEST_CASE is

SPECTRE_TEST_CASE("Unit.Parallel.Actions.TerminatePhase",
"[Unit][Parallel][Actions]") {
using component = Component<Metavariables>;
ActionTesting::emplace_component<component>(&runner, 0);
CHECK_FALSE(ActionTesting::get_terminate<component>(runner, 0));
ActionTesting::next_action<component>(make_not_null(&runner), 0);
CHECK(ActionTesting::get_terminate<component>(runner, 0));
}
A class that mocks the infrastructure needed to run actions. It simulates message passing using the i...
Definition: MockRuntimeSystem.hpp:84
void set_phase(const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const Parallel::Phase &phase)
Set the phase of all parallel components to phase
Definition: MockRuntimeSystemFreeFunctions.hpp:24

The type alias component = Component<Metavariables> is just used to reduce the amount of typing needed. The ATF provides a ActionTesting::MockRuntimeSystem class, which is the code that takes the place of the parallel runtime system (RTS) (e.g. Charm++) that manages the actions and components. The constructor of ActionTesting::MockRuntimeSystem takes a std::vector<size_t> whose length is the number of (mocked) nodes and whose values are the number of (mocked) cores on each node; the RTS runs only on a single core, but it keeps track of which components are on which (mocked) nodes and cores so that one can test some of the functionality of multiple cores/nodes. Components are added to the RTS using the functions ActionTesting::emplace_array_component(), ActionTesting::emplace_singleton_component(), ActionTesting::emplace_group_component(), and ActionTesting::emplace_nodegroup_component(). Currently there is a deprecated ActionTesting::emplace_component() function that is the same as ActionTesting::emplace_array_component() and can be used for the deprecated usage of treating singletons, groups, and nodegroups as single-element arrays. The emplace functions take the runner by not_null. The array emplace function take the array index of the component being inserted (0 in the above example, but this is arbitrary), and the array and singleton emplace functions take the (mocked) node and core on which the component lives. The emplace function for groups places its object on all (mocked) cores and the array index is the same as the global core index; the emplace function for nodegroups places its object on a single (mocked) core on each (mocked) node, and the array index is the same as the node index. All emplace functions optionally take a parameter pack of additional arguments that are forwarded to the constructor of the component. These additional arguments are how input options (set in the simple_tags_from_options type alias of the parallel component) get passed to parallel components.

With the ATF, everything is controlled explicitly, providing the ability to perform full introspection into the state of the RTS at virtually any point during the execution. This means that the phase is not automatically advanced like it is in a parallel executable, but instead must be advanced by calling the ActionTesting::set_phase function. While testing the TerminatePhase action we are only interesting in checking whether the algorithm has set the terminate flag. This is done using the ActionTesting::get_terminate() function and is done for each distributed object (i.e. per component). The next action in the action list for the current phase is invoked by using the ActionTesting::next_action() function, which takes as arguments the runner by not_null and the array index of the distributed object to invoke the next action on.

InitializeDataBox and action introspection

Having covered the very basics of ATF let us look at the test for the Actions::Goto action. We will introduce the functionality InitializeDataBox,force_next_action_to_be, get_next_action_index, and get_databox_tag. The Goto<the_label_class> action changes the next action in the algorithm to be the Actions::Label<the_label_class>. For this test the Metavariables is:

struct Metavariables {
using component_list = tmpl::list<Component<Metavariables>>;
};

You can see that this time there are two test phases, Testing and Execute.

The component for this case is quite a bit more complicated so we will go through it in detail. It is given by

template <typename Metavariables>
struct Component {
using metavariables = Metavariables;
using chare_type = ActionTesting::MockArrayChare;
using array_index = int;
using repeat_until_phase_action_list = tmpl::flatten<
tmpl::list<Actions::RepeatUntil<HasConverged, tmpl::list<Increment>>,
using phase_dependent_action_list = tmpl::list<
tmpl::list<ActionTesting::InitializeDataBox<
tmpl::list<Actions::Goto<Label1>, Actions::Label<Label2>,
repeat_until_phase_action_list>>;
};
tmpl::flatten< tmpl::list< Tags... > > AddSimpleTags
List of Tags to add to the DataBox.
Definition: DataBox.hpp:1399
tmpl::flatten< tmpl::list< Tags... > > AddComputeTags
List of Compute Item Tags to add to the DataBox.
Definition: DataBox.hpp:1406
@ Execute
generic execution phase of an executable
@ Initialization
initial phase of an executable
Jumps to a Label.
Definition: Goto.hpp:71
Labels a location in the action list that can be jumped to using Goto.
Definition: Goto.hpp:40
Terminate the algorithm to proceed to the next phase.
Definition: TerminatePhase.hpp:28

In addition to the Initialization and Exit phases, there are two additional phases, Testing and Execute. Just as before there are metavariables, chare_type, and array_index type aliases. The repeat_until_phase_action_list is the list of iterable actions that are called during the Execute phase. The Initialization phase action list now has the action ActionTesting::InitializeDataBox, which takes simple tags and compute tags that will be added to the DataBox in the Initialization phase. How the values for the simple tags are set will be made clear below when we discuss the ActionTesting::emplace_component_and_initialize() functions. The action lists for the Testing and Execute phases are fairly simple and not the focus of this discussion.

Having discussed the metavariables and component, let us now look at the test case.

SPECTRE_TEST_CASE("Unit.Parallel.GotoAction", "[Unit][Parallel][Actions]") {
using component = Component<Metavariables>;
MockRuntimeSystem runner{{}};
ActionTesting::emplace_component_and_initialize<component>(&runner, 0,
{size_t{0}});
runner.force_next_action_to_be<component, Actions::Label<Label1>>(0);
runner.next_action<component>(0);
CHECK(runner.get_next_action_index<component>(0) == 3);
runner.force_next_action_to_be<component, Actions::Goto<Label1>>(0);
runner.next_action<component>(0);
CHECK(runner.get_next_action_index<component>(0) == 2);
runner.force_next_action_to_be<component, Actions::Goto<Label2>>(0);
runner.next_action<component>(0);
CHECK(runner.get_next_action_index<component>(0) == 1);
while (not ActionTesting::get_terminate<component>(runner, 0)) {
runner.next_action<component>(0);
}
CHECK(ActionTesting::get_databox_tag<component, HasConverged>(runner, 0));
CHECK(ActionTesting::get_databox_tag<component, Counter>(runner, 0) == 2);
// Test zero iterations of the `RepeatUntil` loop
runner.next_action<component>(0);
CHECK(runner.get_next_action_index<component>(0) ==
tmpl::index_of<typename component::repeat_until_phase_action_list,
// Make sure `Increment` was not called for this situation where the
// condition is already fulfilled at the start.
CHECK(ActionTesting::get_databox_tag<component, Counter>(runner, 0) == 2);
}
constexpr T & value(T &t)
Returns t.value() if t is a std::optional otherwise returns t.
Definition: OptionalHelpers.hpp:32

Just as for the TerminatePhase test we have some type aliases to reduce the amount of typing needed and to make the test easier to read. The runner is again created with the default constructor. However, the component is now inserted using the functions ActionTesting::emplace_array_component_and_initialize(), ActionTesting::emplace_singleton_component_and_initialize(), ActionTesting::emplace_group_component_and_initialize(), and ActionTesting::emplace_nodegroup_component_and_initialize(). There is a deprecated function ActionTesting::emplace_component_and_initialize() that is used when one treats groups, nodegroups, and singletons as arrays. The third argument to the ActionTesting::emplace_component_and_initialize() functions is a tuples::TaggedTuple of the simple tags, which is to be populated with the initial values for the component. Note that there is no call to ActionTesting::set_phase (&runner, Parallel::Phase::Initialization): this and the required action invocation is handled internally by the ActionTesting::emplace_component_and_initialize() functions.

Once the phase is set the next action to be executed is set to be Actions::Label<Label1> by calling the MockRuntimeSystem::force_next_action_to_be() member function of the runner. The argument to the function is the array index of the component for which to set the next action. After the Label<Label1> action is invoked we check that the next action is the fourth (index 3 because we use zero-based indexing) by calling the MockRuntimeSystem::get_next_action_index() member function. For clarity, the indices of the actions in the Phase::Testing phase are:

DataBox introspection

Since the exact DataBox type of any component at some point in the action list may not be known, the ActionTesting::get_databox_tag() function is provided. An example usage of this function can be seen in the last line of the test snippet above. The component and tag are passed as template parameters while the runner and array index are passed as arguments. It is also possible to retrieve DataBox directly using the ActionTesting::get_databox() function if the DataBox type is known:

const auto& box =
ActionTesting::get_databox<component_for_simple_action_mock<metavars>>(
runner, 0);

There is also a gsl::not_null overload of ActionTesting::get_databox() that can be used to mutate tags in the DataBox. It is also possible to check if an item can be retrieved from the DataBox using the ActionTesting::tag_is_retrievable() function as follows:

ActionTesting::tag_is_retrievable<component, DummyTimeTag>(runner, 0)

Stub actions and invoking simple and threaded actions

A simple action can be invoked on a distributed object or component using the ActionTesting::simple_action() function:

ActionTesting::simple_action<component_for_simple_action_mock<metavars>,
simple_action_b>(make_not_null(&runner), 0, 2);

A threaded action can be invoked on a distributed object or component using the ActionTesting::threaded_action() function:

ActionTesting::threaded_action<component_for_simple_action_mock<metavars>,
threaded_action_a>(make_not_null(&runner), 0);

Sometimes an individual action calls other actions but we still want to be able to test the action and its effects in isolation. To this end the ATF supports replacing calls to actions with calls to other actions. For example, instead of calling simple_action_a we want to call simple_action_a_mock which just verifies the data received is correct and sets a flag in the DataBox that it was invoked. This can be done by setting the type aliases replace_these_simple_actions and with_these_simple_actions in the parallel component definition as follows:

using replace_these_simple_actions =
tmpl::list<simple_action_a, simple_action_c, threaded_action_b>;
using with_these_simple_actions =
tmpl::list<simple_action_a_mock, simple_action_c_mock,
threaded_action_b_mock>;

The length of the two type lists must be the same and the Nth action in replace_these_simple_actions gets replaced by the Nth action in with_these_simple_actions. Note that simple_action_a_mock will get invoked in any context that simple_action_a is called to be invoked. The same feature also exists for threaded actions:

using replace_these_threaded_actions = tmpl::list<threaded_action_b>;
using with_these_threaded_actions = tmpl::list<threaded_action_b_mock>;

Furthermore, simple actions invoked from an action are not run immediately. Instead, they are queued so that order randomization and introspection may occur. The simplest form of introspection is checking whether the simple action queue is empty:

component_for_simple_action_mock<metavars>>(runner, 0)
bool is_simple_action_queue_empty(const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
Returns true if there are no simple actions in the queue.
Definition: MockRuntimeSystemFreeFunctions.hpp:326

The ActionTesting::invoke_queued_simple_action() invokes the next queued simple action:

component_for_simple_action_mock<metavars>>(make_not_null(&runner), 0);
void invoke_queued_simple_action(const gsl::not_null< MockRuntimeSystem< Metavariables > * > runner, const typename Component::array_index &array_index)
Runs the next queued simple action on the array_indexth element of the parallel component Component.
Definition: MockRuntimeSystemFreeFunctions.hpp:318

Note that the same functionality exists for threaded actions. The functions are ActionTesting::is_threaded_action_queue_empty(), and ActionTesting::invoke_queued_threaded_action().

Reduction Actions

The ATF, in general, does not support reduction actions at the moment. Meaning that calls to Parallel::contribute_to_reduction and Parallel::threaded_action<observers::ThreadedActions::WriteReductionData> are not supported. However, the ATF does support calls using WriteReductionDataRow without having to actually write any data to disk (this avoids unnecessary IO when all we really care about is if the values are correct).

If you want to test code that has a call to Parallel::threaded_action< observers::ThreadedActions::WriteReductionDataRow>, you'll need to add the TestHelpers::observers::MockObserverWriter component, found in Helpers/IO/Observers/MockWriteReductionDataRow.hpp, to your test metavars (see next section for what mocking a component means). Initialize the component as follows:

mock_observer_writer>(make_not_null(&runner), {});
void emplace_nodegroup_component_and_initialize(const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
Emplaces a distributed nodegroup object into the parallel component Component. The options opts are f...
Definition: MockRuntimeSystemFreeFunctions.hpp:173

Then, when you want to check the data that was "written", you can do something along the lines of

auto& mock_h5_file =
ActionTesting::get_databox_tag<mock_observer_writer,
MockReductionFileTag>(runner, 0);
const auto& mock_dat_file = mock_h5_file.get_dat(subfile_path);
CHECK(mock_dat_file.get_legend() == legend);
CHECK(mock_dat_file.get_data() == Matrix{{1.0, 9.3}});
A dynamically sized matrix of doubles with column-major storage.
Definition: Matrix.hpp:19
const auto & get_databox_tag(const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index)
Returns the Tag from the DataBox of the parallel component Component with array index array_index....
Definition: MockRuntimeSystemFreeFunctions.hpp:215

The data is stored in MockH5File and MockDat objects, which have similar interfaces to h5::H5File and h5::Dat, respectively.

Mocking or replacing components with stubs

An action can invoke an action on another parallel component. In this case we need to be able to tell the mocking framework to replace the component the action is trying to invoke the other action on and instead use a different component that we have set up to mock the original component. For example, the action below invokes an action on ComponentB

struct CallActionOnComponentB {
template <typename ParallelComponent, typename DbTagsList,
typename Metavariables, typename ArrayIndex>
static void apply(db::DataBox<DbTagsList>& /*box*/, // NOLINT
const ArrayIndex& /*array_index*/) {
Parallel::simple_action<ActionCalledOnComponentB>(
Parallel::get_parallel_component<ComponentB<Metavariables>>(cache));
}
};
A Charm++ chare that caches global data once per Charm++ node.
Definition: GlobalCache.hpp:221
auto apply(F &&f, const ObservationBox< ComputeTagsList, DataBoxType > &observation_box, Args &&... args)
Apply the function object f using its nested argument_tags list of tags.
Definition: ObservationBox.hpp:238
auto get_parallel_component(GlobalCache< Metavariables > &cache) -> Parallel::proxy_from_parallel_component< GlobalCache_detail::get_component_if_mocked< typename Metavariables::component_list, ParallelComponentTag > > &
Access the Charm++ proxy associated with a ParallelComponent.
Definition: GlobalCache.hpp:755
Parallel::GlobalCache< Metavariables > & cache(MockRuntimeSystem< Metavariables > &runner, const ArrayIndex &array_index)
Returns the GlobalCache of Component with index array_index.
Definition: MockRuntimeSystemFreeFunctions.hpp:379

Let us assume we cannot use the component ComponentB in our test and that we need to mock it. We do so using the type alias component_being_mocked in the mock component:

template <typename Metavariables>
struct ComponentBMock {
using metavariables = Metavariables;
using chare_type = ActionTesting::MockArrayChare;
using array_index = size_t;
using component_being_mocked = ComponentB<Metavariables>;
using phase_dependent_action_list = tmpl::list<
tmpl::list<ActionTesting::InitializeDataBox<
};

When creating the runner ComponentBMock is emplaced:

ComponentBMock<Metavariables>>(&runner, 0, {0});
void emplace_component_and_initialize(const gsl::not_null< MockRuntimeSystem< typename Component::metavariables > * > runner, const typename Component::array_index &array_index, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts)
Emplaces a distributed object with index array_index into the parallel component Component....
Definition: MockRuntimeSystemFreeFunctions.hpp:100

Any checks and function calls into the ATF also use ComponentBMock. For example:

CHECK(not ActionTesting::is_simple_action_queue_empty<component_b_mock>(
runner, 0));
ActionTesting::invoke_queued_simple_action<component_b_mock>(
make_not_null(&runner), 0);
CHECK(ActionTesting::get_databox_tag<component_b_mock, ValueTag>(runner, 0) ==
5);

Const global cache tags

Actions sometimes need tags/items to be placed into the Parallel::GlobalCache. Once the list of tags for the global cache has been assembled, the associated objects need to be inserted. This is done using the constructor of the ActionTesting::MockRuntimeSystem. For example, consider the tags:

struct ValueTag : db::SimpleTag {
using type = int;
static std::string name() { return "ValueTag"; }
};
struct PassedToB : db::SimpleTag {
using type = double;
static std::string name() { return "PassedToB"; }
};
std::string name()
Return the result of the name() member of a class. If a class doesn't have a name() member,...
Definition: PrettyType.hpp:733
Mark a struct as a simple tag by inheriting from this.
Definition: Tag.hpp:36

These are added into the global cache by, for example, the metavariables:

using const_global_cache_tags = tmpl::list<ValueTag, PassedToB>;

A constructor of ActionTesting::MockRuntimeSystem takes a tuples::TaggedTuple of the const global cache tags. If you know the order of the tags in the tuples::TaggedTuple you can use the constructor without explicitly specifying the type of the tuples::TaggedTuple as follows:

If you do not know the order of the tags but know all the tags that are present you can use another constructor where you explicitly specify the tuples::TaggedTuple type:

An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:261

Mutable global cache tags

Similarly to const global cache tags, sometimes Actions will want to change the data of a tag in the Parallel::GlobalCache. Consider this tag:

struct CacheTag : db::SimpleTag {
using type = int;
};

To indicate that this tag in the global cache may be changed, it is added by, for example, the metavariables:

using mutable_global_cache_tags = tmpl::list<CacheTag>;

Then, exactly like the const global cache tags, the constructor of ActionTesting::MockRuntimeSystem takes a tuples::TaggedTuple of the mutable global cache tags as its second argument. The mutable global cache tags are empty by default so you need not specify a tuples::TaggedTuple if you don't have any mutable global cache tags. Here is how you would specify zero const global cache tags and one mutable global cache tag:

To mutate a tag in the mutable global cache, use the Parallel::mutate function just like you would in a charm-aware situation.

Inbox tags introspection

The inbox tags can also be retrieved from a component by using the ActionTesting::get_inbox_tag function. Both a const version:

ActionTesting::get_inbox_tag<component, Tags::ValueTag>(runner, 0).at(1)

and a non-const version exist:

ActionTesting::get_inbox_tag<component, Tags::ValueTag>(
make_not_null(&runner), 0)
.clear();

The non-const version can be used like in the above example to clear or otherwise manipulate the inbox tags.