Skip to content

Commit

Permalink
Add end-to-end CLI output tests for ros2:
Browse files Browse the repository at this point in the history
- action command
- service command
- topic command
- interface command
- node command
- msg command
- srv command

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
  • Loading branch information
hidmic committed Jul 26, 2019
1 parent 8841312 commit 07a0883
Show file tree
Hide file tree
Showing 18 changed files with 1,766 additions and 347 deletions.
2 changes: 1 addition & 1 deletion ros2node/ros2node/verb/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ def main(self, *, args):
if args.count_nodes:
print(len(node_names))
elif node_names:
print(*[n.full_name for n in node_names], sep='\n')
print(*sorted(n.full_name for n in node_names), sep='\n')
8 changes: 4 additions & 4 deletions ros2topic/ros2topic/verb/bw.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ def print_bw(self):
# min/max and even mean are likely to be much smaller,
# but for now I prefer unit consistency
if bytes_per_s < 1000:
bw, mean, min_s, max_s = ['%.2fB' % v for v in [bytes_per_s, mean, min_s, max_s]]
bw, mean, min_s, max_s = ['%.2fB/s' % v for v in [bytes_per_s, mean, min_s, max_s]]
elif bytes_per_s < 1000000:
bw, mean, min_s, max_s = \
['%.2fKB' % (v / 1000) for v in [bytes_per_s, mean, min_s, max_s]]
['%.2fKB/s' % (v / 1000) for v in [bytes_per_s, mean, min_s, max_s]]
else:
bw, mean, min_s, max_s = \
['%.2fMB' % (v / 1000000) for v in [bytes_per_s, mean, min_s, max_s]]
['%.2fMB/s' % (v / 1000000) for v in [bytes_per_s, mean, min_s, max_s]]

print('average: %s/s\n\tmean: %s min: %s max: %s window: %s' % (bw, mean, min_s, max_s, n))
print('average: %s\n\tmean: %s min: %s max: %s window: %s' % (bw, mean, min_s, max_s, n))


def _rostopic_bw(node, topic, window_size=DEFAULT_WINDOW_SIZE):
Expand Down
78 changes: 61 additions & 17 deletions test_ros2cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,35 @@ if(BUILD_TESTING)
find_package(ros_testing REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)

macro(custom_test_process_output test_name config_file_name timeout delay)
macro(add_custom_cli_test test_name)
# Set variables for configuring the test files.
set(TEST_NAME ${test_name}) # test name used in the test cases
set(TEST_CONFIGURATION_FILE ${config_file_name}) # config file to use
set(DELAY ${delay}) # Delay between the actions are launch and the ros2 command is run
cmake_parse_arguments(TEST "" "CONFIG_MODULE;SETUP_DELAY;TIMEOUT" "" ${ARGN})
# Name for test cases
set(TEST_NAME ${test_name})
# RMW implementation for test cases
set(TEST_RMW_IMPLEMENTATION ${rmw_implementation})
# Module carrying test configuration
if(NOT DEFINED TEST_CONFIG_MODULE)
set(TEST_CONFIG_MODULE ${test_name}_config)
endif()
# Setup delay for test fixture actions
if(NOT DEFINED TEST_SETUP_DELAY)
set(TEST_SETUP_DELAY 2.0)
if("${TEST_RMW_IMPLEMENTATION}" STREQUAL "rmw_connext_cpp")
# Connext startup is too slow. It needs a few extra seconds till
# discovery completes.
set(TEST_SETUP_DELAY 5.0)
endif()
endif()
# Timeout for test itself
if(NOT DEFINED TEST_TIMEOUT)
set(TEST_TIMEOUT 0)
endif()



configure_file(
test/test_process_output_customizable.py.in
test/test_cli.py.in
${test_name}${target_suffix}.py.genexp
@ONLY
)
Expand All @@ -35,15 +56,15 @@ if(BUILD_TESTING)
INPUT "${CMAKE_CURRENT_BINARY_DIR}/${test_name}${target_suffix}.py.genexp"
)
install(
FILES test/${config_file_name}.py
FILES test/${TEST_CONFIG_MODULE}.py
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
)
add_ros_test(
"${CMAKE_CURRENT_BINARY_DIR}/${test_name}${target_suffix}_$<CONFIG>.py"
TARGET ${test_name}${target_suffix}
ENV RMW_IMPLEMENTATION=${rmw_implementation}
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT ${timeout}
TIMEOUT ${TEST_TIMEOUT}
)
list(
APPEND generated_python_files
Expand All @@ -52,17 +73,40 @@ if(BUILD_TESTING)
endmacro()

set(generated_python_files)
macro(tests)
set(TEST_ROS2ACTION_DELAY 0.0)
if("${rmw_implementation}" STREQUAL "rmw_connext_cpp")
# Connext startup is too slow. It needs a few seconds until discovery starts working.
set(TEST_ROS2ACTION_DELAY 5.0)
endif()
custom_test_process_output(test_ros2action config_ros2action_test 180 ${TEST_ROS2ACTION_DELAY})
custom_test_process_output(test_ros2msg config_ros2msg_test 60 0.0)
macro(custom_cli_tests)
add_custom_cli_test(
test_ros2action
CONFIG_MODULE ros2action_test_configuration
TIMEOUT 300)
add_custom_cli_test(
test_ros2topic
CONFIG_MODULE ros2topic_test_configuration
TIMEOUT 300)
add_custom_cli_test(
test_ros2service
CONFIG_MODULE ros2service_test_configuration
TIMEOUT 300)
add_custom_cli_test(
test_ros2node
CONFIG_MODULE ros2node_test_configuration
TIMEOUT 240)
add_custom_cli_test(
test_ros2msg
CONFIG_MODULE ros2msg_test_configuration
TIMEOUT 240)
add_custom_cli_test(
test_ros2srv
CONFIG_MODULE ros2srv_test_configuration
TIMEOUT 240)
add_custom_cli_test(
test_ros2interface
CONFIG_MODULE ros2interface_test_configuration
TIMEOUT 240)
endmacro()
install(
FILES test/test_config.py
FILES
test/cli_test_configuration.py
test/fixture_actions.py
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
)

Expand All @@ -71,7 +115,7 @@ if(BUILD_TESTING)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()

call_for_each_rmw_implementation(tests)
call_for_each_rmw_implementation(custom_cli_tests)

find_package(ament_cmake_flake8 REQUIRED)
ament_flake8(
Expand Down
10 changes: 10 additions & 0 deletions test_ros2cli/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@
<test_depend>action_tutorials</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>demo_nodes_py</test_depend>
<test_depend>geometry_msgs</test_depend>
<test_depend>ros_testing</test_depend>
<test_depend>ros2action</test_depend>
<test_depend>ros2cli</test_depend>
<test_depend>ros2interface</test_depend>
<test_depend>ros2msg</test_depend>
<test_depend>ros2node</test_depend>
<test_depend>ros2param</test_depend>
<test_depend>ros2service</test_depend>
<test_depend>ros2srv</test_depend>
<test_depend>ros2topic</test_depend>
<test_depend>std_msgs</test_depend>
<test_depend>std_srvs</test_depend>
<test_depend>test_msgs</test_depend>

<export>
<build_type>ament_cmake</build_type>
Expand Down
102 changes: 102 additions & 0 deletions test_ros2cli/test/cli_test_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Callable
from typing import Iterable
from typing import Mapping
from typing import Optional
from typing import Pattern
from typing import Union

from launch import Action
from launch import SomeSubstitutionsType
from launch.utilities import normalize_to_list_of_substitutions

import launch_testing.asserts

ExpectedOutputType = Union[
Iterable[Union[str, Pattern[str], Callable[[str], bool]]],
Callable[[Iterable[str]], bool]
]
SomeExpectedOutputType = Union[ExpectedOutputType, Mapping[str, ExpectedOutputType]]


class CLITestConfiguration(object):
"""A class for configuring the `test_process_output_customizable.py.in` test."""

def __init__(
self,
*,
command: str,
arguments: Iterable[SomeSubstitutionsType],
description: Optional[str] = None,
fixture_actions: Optional[Iterable[Action]] = None,
setup_delay: Optional[int] = None,
expected_output: Optional[SomeExpectedOutputType] = None,
output_filter: Optional[Callable[[str], str]] = None,
exit_codes: Optional[Iterable[int]] = None,
self_terminates: bool = True,
timeout: int = 30.
):
"""
Constructor.
:param command: `ros2` command to be tested.
:param arguments: A list of `SomeSubstitutionsType`, which are passed
as arguments to the command.
:param description: description of the test being done. command to be tested.
It usually contains the verb and arguments being tested.
:param fixture_actions: A list of actions, which are launched before the `ros2`
command under test.
:param setup_delay: A period in seconds to wait for fixture actions to start up
before launching the `ros2` command under test. If none is given, an appropriate
default is automatically provided.
:param expected_output: Output expectations for the `ros2` command under test and/or
fixture actions. In its most explicit form it is a mapping from process names to
either a list of expected lines, which may be expressed as plain strings, compiled
regexes, or a callable for custom line assertion, or a callable that takes all lines
for custom output assertions. Empty lists express that no output is expected. The
`ros2` command under test can be referenced as 'cli'. Alternatively, this mapping
can be elided if expectations are only defined for the `ros2` command under test.
In that case, only the corresponding mapping value has to be provided.
:param output_filter: A callable to filter output before testing it against expectations.
:param exit_codes: A list of allowed exit codes for the `ros2` command under test.
:param self_terminates: A flag for command termination policy. Non-self terminating
commands are forced to terminate after the specified timeout has elapsed.
:param timeout: A finite test timeout in seconds.
"""
self.command = command
self.arguments = [normalize_to_list_of_substitutions(arg) for arg in arguments]
if description is None:
description = 'ros2 {} {}'.format(
command, ' '.join([
''.join([sub.describe() for sub in some_subs]) for some_subs in self.arguments
])
)
self.description = description
if fixture_actions is None:
fixture_actions = []
self.fixture_actions = fixture_actions
self.expected_output = expected_output
self.output_filter = output_filter
if exit_codes is None:
exit_codes = [launch_testing.asserts.EXIT_OK]
self.exit_codes = exit_codes
self.self_terminates = self_terminates
self.setup_delay = setup_delay
self.timeout = timeout

def __repr__(self):
"""Return the description."""
return self.description
130 changes: 0 additions & 130 deletions test_ros2cli/test/config_ros2action_test.py

This file was deleted.

Loading

0 comments on commit 07a0883

Please sign in to comment.