Fuzzer usage

The fuzzer works in a standalone manner, by calling it to fuzz a full interface structure via CLI, or by designing custom tests that fuzz or exclude different fields of the interfaces.

CLI Usage

The fuzzer can be directly invoked from the command line. Ensure that the ROS2 workspace is sourced before proceeding. Interfaces types follow the ROS2 naming scheme.

For ROS2 Message Fuzzing:

$ source /opt/ros/dashing/setup.bash
$ ros2_fuzzer message <ros2_message_type> <topic_name>

For ROS2 Service Fuzzing:

$ source /opt/ros/dashing/setup.bash
$ ros2_fuzzer service <ros2_service_type> <service_name>

Here is an usage example for performing a Log message fuzzing over the /listener topic:

$ source /opt/ros/dashing/setup.bash
$ ros2_fuzzer.py message rcl_interfaces/Log /listener

Here is an usage example for performing an AddTwoInts service fuzzing over the service ‘add_two_ints’:

$ source /opt/ros/dashing/setup.bash
$ ros2_fuzzer.py service example_interfaces/AddTwoInts add_two_ints

Usage as Unit Testing counterpart

Test cases can use the provided Hypothesis strategy generation functions to get fuzzed messages that can be modified and used for different purposes. Fuzzed test cases follow the same mechanisms as standard data test cases, obtaining the fuzzed message as a parameter to the test case. The following example shows a simple test case that makes use of a fuzzed Log message, that is then modified before being sent.

Example unittest test case
def test_fuzz_log_message_exclude(self, log):
    log.name = 'Fixed name'

The ros2_fuzzer.ros_commons.map_ros_types() function provides a dynamic strategy for the defined ROS Message class, that correctly sets up each of the elements of the message with the corresponding data type fuzzers. Examples can be extended to even fuzz different message types or sub-elements independently. Built in hypothesis hypothesis.strategies can be used as well. The hypothesis.given() decorator runs the decorated function with all the defined fuzz cases.

Example unittest with multiple parameters.
@given(log=map_ros_types(Log), header=map_ros_types(Header), name=st.text(min_length=1, max_length=20))
def test_fuzz_log_message_parameters(log, header, name):
    log.name = name
    log.header = header

Usage of node health checkers

A health checker for detecting node process crashes has been implemented as well. This way, assertions on the node process status can be performed. The health checker is instantiated prior to starting the tests, passing the node name as an argument. Currently, a local process ros2_fuzzer.process_handling.FuzzedLocalProcessHandler health checker is supported.

Example TestSuite setup function with node health check initialization.
def setUp(self):
    self.process_handler = FuzzedLocalProcessHandler('/example_node')

The health checker can then be part of assert clauses on tests, by calling the ros2_fuzzer.process_handling.FuzzedLocalProcessHandler.check_if_alive() function.

Example test case with node health checking.
@given(array(elements=float64(), min_size=6, max_size=6))
def test_fuzz_message_jointstate_effort(process_handler, fuzzed_fields):
    joint_state_message.effort = fuzzed_fields
    assert self.process_handler.check_if_alive() is True