Mock ROS action servers, send goals, and assert action activity.
mock_action/3 records incoming goals, feedback, and results. Use send_goal/3
to invoke an action server, and use the expect_* and refute_goal assertions
to verify the recorded action lifecycle.
Options
Every function in this module accepts an optional trailing keyword list:
:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".:goal_callbackdecides whether an incoming goal is accepted. Arity 1 (the goal struct), must return:acceptor:reject. Defaults to accepting every goal.:cancel_callbackdecides whether a cancel request is honoured. Arity 1 (the goal handle), must return:acceptor:reject. Defaults to rejecting every cancellation.:handle_accepted_callbackruns once a goal has been accepted. Arity 5 (goal info, action type, action name, node name, namespace). Defaults toRclex.execute_goal/5, which runs the handler passed tomock_action/4.:optionsaRclex.ActionServerOptionsstruct (per-service QoS,:clock_type,:result_timeout). Defaults toRclex.ActionServerOptions.default/0
:optionsaRclex.ActionClientOptionsstruct. Defaults to the Rclex default.:feedback_callback,:accepted_callbackand:goal_uuidare forwarded toRclex.send_goal_async/4.:timeouthow long to wait, in milliseconds. Defaults to5000.
Any option other than :timeout is forwarded verbatim to the underlying Rclex call, so options not listed here are supported too.
Summary
Functions
Assert that the mocked action produced a new feedback message.
expect_feedback/2 with either a predicate or options.
expect_feedback/3 with both a predicate and options.
Assert that the mocked action server received a new goal.
expect_goal/2 with either a predicate or options.
expect_goal/3 with both a predicate and options.
Assert that the mocked action server rejected a goal.
expect_rejected_goal/2 with either a predicate or options.
expect_rejected_goal/3 with both a predicate and options.
Assert that the mocked action produced a result.
expect_result/2 with either a predicate or options.
expect_result/3 with both a predicate and options.
Register a test action server implementation on the scenario node.
Assert that no new goal is submitted to the mocked action server.
refute_goal/2 with either a predicate or options.
refute_goal/3 with both a predicate and options.
Assert that the mocked action server rejected no goal.
refute_rejected_goal/2 with either a predicate or options.
refute_rejected_goal/3 with both a predicate and options.
Send a goal to an action server and return the goal UUID.
Functions
Assert that the mocked action produced a new feedback message.
expect_feedback "/navigate", Navigate
expect_feedback/2 with either a predicate or options.
expect_feedback "/navigate", Navigate, fn fb -> fb.progress == 100 end
expect_feedback "/navigate", Navigate, timeout: 10_000opts
:timeouthow long to wait, in milliseconds. Defaults to5000.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
expect_feedback/3 with both a predicate and options.
expect_feedback "/navigate", Navigate, fn fb -> fb.progress == 100 end, timeout: 10_000
Assert that the mocked action server received a new goal.
expect_goal "/navigate", Navigate
expect_goal/2 with either a predicate or options.
expect_goal "/navigate", Navigate, fn goal -> goal.x == 10 end
expect_goal "/navigate", Navigate, timeout: 10_000opts
:timeouthow long to wait, in milliseconds. Defaults to5000.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
expect_goal/3 with both a predicate and options.
expect_goal "/navigate", Navigate, fn goal -> goal.x == 10 end, timeout: 10_000
Assert that the mocked action server rejected a goal.
A goal is rejected when the :goal_callback given to mock_action/4 returns
:reject. Such a goal never reaches the handler, so expect_goal/2 will not
match it — use this assertion instead.
mock_action "/navigate", Navigate, handler,
goal_callback: fn goal -> if goal.x >= 0, do: :accept, else: :reject end
send_goal "/navigate", Navigate, %Navigate.Goal{x: -1}
expect_rejected_goal "/navigate", Navigate
expect_rejected_goal/2 with either a predicate or options.
expect_rejected_goal "/navigate", Navigate, fn goal -> goal.x < 0 end
expect_rejected_goal "/navigate", Navigate, timeout: 10_000opts
:timeouthow long to wait, in milliseconds. Defaults to5000.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
expect_rejected_goal/3 with both a predicate and options.
expect_rejected_goal "/navigate", Navigate, fn goal -> goal.x < 0 end, timeout: 10_000
Assert that the mocked action produced a result.
expect_result "/navigate", Navigate
expect_result/2 with either a predicate or options.
expect_result "/navigate", Navigate, fn result -> result.success end
expect_result "/navigate", Navigate, timeout: 10_000opts
:timeouthow long to wait, in milliseconds. Defaults to5000.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
expect_result/3 with both a predicate and options.
expect_result "/navigate", Navigate, fn result -> result.success end, timeout: 10_000
Register a test action server implementation on the scenario node.
Goals, feedback, and results are all recorded in the collector. The
handler receives (goal, publish_feedback) and must return a result struct.
Cleanup is automatic when the scenario ends.
mock_action "/navigate", Navigate, fn goal, publish_feedback ->
publish_feedback.(%Navigate.Feedback{progress: 50})
publish_feedback.(%Navigate.Feedback{progress: 100})
%Navigate.Result{success: true}
endhandler is the execute callback. The three remaining action-server
callbacks are available as options:
mock_action "/navigate", Navigate, handler,
goal_callback: fn goal -> if goal.x >= 0, do: :accept, else: :reject end,
cancel_callback: fn _goal_handle -> :accept end,
options: %Rclex.ActionServerOptions{result_timeout: 30.0}A goal rejected by :goal_callback never reaches handler, so it is not
recorded as a goal and expect_goal/2 will not match it. Assert it with
expect_rejected_goal/2 instead.
opts
:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".:goal_callbackdecides whether an incoming goal is accepted. Arity 1 (the goal struct), must return:acceptor:reject. Defaults to accepting every goal.:cancel_callbackdecides whether a cancel request is honoured. Arity 1 (the goal handle), must return:acceptor:reject. Defaults to rejecting every cancellation.:handle_accepted_callbackruns once a goal has been accepted. Arity 5 (goal info, action type, action name, node name, namespace). Defaults toRclex.execute_goal/5, which runs the handler passed tomock_action/4.:optionsaRclex.ActionServerOptionsstruct (per-service QoS,:clock_type,:result_timeout). Defaults toRclex.ActionServerOptions.default/0
Assert that no new goal is submitted to the mocked action server.
refute_goal "/navigate", Navigate
refute_goal/2 with either a predicate or options.
refute_goal "/navigate", Navigate, fn goal -> goal.x < 0 end
refute_goal "/navigate", Navigate, timeout: 500opts
:timeouthow long to wait, in milliseconds. Defaults to200.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
refute_goal/3 with both a predicate and options.
refute_goal "/navigate", Navigate, fn goal -> goal.x < 0 end, timeout: 500
Assert that the mocked action server rejected no goal.
refute_rejected_goal "/navigate", Navigate
refute_rejected_goal/2 with either a predicate or options.
refute_rejected_goal "/navigate", Navigate, fn goal -> goal.x > 0 end
refute_rejected_goal "/navigate", Navigate, timeout: 500opts
:timeouthow long to wait, in milliseconds. Defaults to200.:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".
refute_rejected_goal/3 with both a predicate and options.
refute_rejected_goal "/navigate", Navigate, fn goal -> goal.x > 0 end, timeout: 500
Send a goal to an action server and return the goal UUID.
Starts an action client on the scenario node on first use.
goal_uuid = send_goal "/navigate", Navigate, %Navigate.Goal{x: 10, y: 20}
goal_uuid = send_goal "/navigate", Navigate, goal, feedback_callback: &IO.inspect/1Use :accepted_callback to observe whether the server accepted the goal,
which is the client-side counterpart to mock_action/4's :goal_callback:
test_pid = self()
send_goal "/navigate", Navigate, goal,
accepted_callback: fn _uuid, accepted, _stamp -> send(test_pid, {:accepted, accepted}) end
assert_receive {:accepted, false}opts
:namespacethe namespace of the scenario node. Defaults to the namespace given toscenario/3, or"/".:optionsaRclex.ActionClientOptionsstruct. Defaults to the Rclex default.:feedback_callback,:accepted_callbackand:goal_uuidare forwarded toRclex.send_goal_async/4.