diff --git a/ros2bag/pytest.ini b/ros2bag/pytest.ini index fe55d2ed64..c3b31e9d2c 100644 --- a/ros2bag/pytest.ini +++ b/ros2bag/pytest.ini @@ -1,2 +1,4 @@ [pytest] junit_family=xunit2 +# flake8 in Ubuntu 22.04 prints this warning; we ignore it for now +filterwarnings=ignore:SelectableGroups:DeprecationWarning diff --git a/ros2bag/ros2bag/verb/record.py b/ros2bag/ros2bag/verb/record.py index 797f77e2e4..1793f6ddb0 100644 --- a/ros2bag/ros2bag/verb/record.py +++ b/ros2bag/ros2bag/verb/record.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from argparse import FileType +from argparse import ArgumentParser, FileType import datetime import os @@ -34,168 +34,178 @@ import yaml +def add_recorder_arguments(parser: ArgumentParser) -> None: + parser.formatter_class = SplitLineFormatter + writer_choices = get_registered_writers() + default_storage_id = get_default_storage_id() + default_writer = default_storage_id if default_storage_id in writer_choices else \ + next(iter(writer_choices)) + + serialization_choices = get_registered_serializers() + converter_suffix = '_converter' + serialization_choices = { + f[:-len(converter_suffix)] + for f in serialization_choices + if f.endswith(converter_suffix) + } + + # Base output + parser.add_argument( + '-o', '--output', + help='Destination of the bagfile to create, ' + 'defaults to a timestamped folder in the current directory.') + parser.add_argument( + '-s', '--storage', default=default_writer, choices=writer_choices, + help="Storage identifier to be used, defaults to '%(default)s'.") + + # Topic filter arguments + topics_args_group = parser.add_mutually_exclusive_group() + topics_args_group.add_argument( + 'topics_positional', type=str, default=[], metavar='[Topic ...]', nargs='*', + help='Space-delimited list of topics to record. (deprecated)') + topics_args_group.add_argument( + '--topics', type=str, default=[], metavar='Topic', nargs='+', + help='Space-delimited list of topics to record.') + parser.add_argument( + '--services', type=str, metavar='ServiceName', nargs='+', + help='Space-delimited list of services to record.') + parser.add_argument( + '--topic-types', nargs='+', default=[], metavar='TopicType', + help='Space-delimited list of topic types to record.') + parser.add_argument( + '-a', '--all', action='store_true', + help='Record all topics and services (Exclude hidden topic).') + parser.add_argument( + '--all-topics', action='store_true', + help='Record all topics (Exclude hidden topic).') + parser.add_argument( + '--all-services', action='store_true', + help='Record all services via service event topics.') + parser.add_argument( + '-e', '--regex', default='', + help='Record only topics and services containing provided regular expression. ' + 'Overrides --all, --all-topics and --all-services, applies on top of ' + 'topics list and service list.') + parser.add_argument( + '--exclude-regex', default='', + help='Exclude topics and services containing provided regular expression. ' + 'Works on top of ' + '--all, --all-topics, --all-services, --topics, --services or --regex.') + parser.add_argument( + '--exclude-topic-types', type=str, default=[], metavar='ExcludeTopicTypes', nargs='+', + help='Space-delimited list of topic types not being recorded. ' + 'Works on top of --all, --all-topics, --topics or --regex.') + parser.add_argument( + '--exclude-topics', type=str, metavar='Topic', nargs='+', + help='Space-delimited list of topics not being recorded. ' + 'Works on top of --all, --all-topics, or --regex.') + parser.add_argument( + '--exclude-services', type=str, metavar='ServiceName', nargs='+', + help='Space-delimited list of services not being recorded. ' + 'Works on top of --all, --all-services, or --regex.') + + # Discovery behavior + parser.add_argument( + '--include-unpublished-topics', action='store_true', + help='Discover and record topics which have no publisher. ' + 'Subscriptions on such topics will be made with default QoS unless otherwise ' + 'specified in a QoS overrides file.') + parser.add_argument( + '--include-hidden-topics', action='store_true', + help='Discover and record hidden topics as well. ' + 'These are topics used internally by ROS 2 implementation.') + parser.add_argument( + '--no-discovery', action='store_true', + help='Disables topic auto discovery during recording: only topics present at ' + 'startup will be recorded.') + parser.add_argument( + '-p', '--polling-interval', type=int, default=100, + help='Time in ms to wait between querying available topics for recording. ' + 'It has no effect if --no-discovery is enabled.') + parser.add_argument( + '--ignore-leaf-topics', action='store_true', + help='Ignore topics without a subscription.') + parser.add_argument( + '--qos-profile-overrides-path', type=FileType('r'), + help='Path to a yaml file defining overrides of the QoS profile for specific topics.') + + # Core config + parser.add_argument( + '-f', '--serialization-format', default='', choices=serialization_choices, + help='The rmw serialization format in which the messages are saved, defaults to the ' + 'rmw currently in use.') + parser.add_argument( + '-b', '--max-bag-size', type=int, default=0, + help='Maximum size in bytes before the bagfile will be split. ' + 'Default: %(default)d, recording written in single bagfile and splitting ' + 'is disabled.') + parser.add_argument( + '-d', '--max-bag-duration', type=int, default=0, + help='Maximum duration in seconds before the bagfile will be split. ' + 'Default: %(default)d, recording written in single bagfile and splitting ' + 'is disabled. If both splitting by size and duration are enabled, ' + 'the bag will split at whichever threshold is reached first.') + parser.add_argument( + '--max-cache-size', type=int, default=100 * 1024 * 1024, + help='Maximum size (in bytes) of messages to hold in each buffer of cache. ' + 'Default: %(default)d. The cache is handled through double buffering, ' + 'which means that in pessimistic case up to twice the parameter value of memory ' + 'is needed. A rule of thumb is to cache an order of magnitude corresponding to ' + 'about one second of total recorded data volume. ' + 'If the value specified is 0, then every message is directly written to disk.') + parser.add_argument( + '--disable-keyboard-controls', action='store_true', default=False, + help='disables keyboard controls for recorder') + parser.add_argument( + '--start-paused', action='store_true', default=False, + help='Start the recorder in a paused state.') + parser.add_argument( + '--use-sim-time', action='store_true', default=False, + help='Use simulation time for message timestamps by subscribing to the /clock topic. ' + 'Until first /clock message is received, no messages will be written to bag.') + parser.add_argument( + '--node-name', type=str, default='rosbag2_recorder', + help='Specify the recorder node name. Default is %(default)s.') + parser.add_argument( + '--custom-data', type=str, metavar='KEY=VALUE', nargs='*', + help='Space-delimited list of key=value pairs. Store the custom data in metadata ' + 'under the "rosbag2_bagfile_information/custom_data". The key=value pair can ' + 'appear more than once. The last value will override the former ones.') + parser.add_argument( + '--snapshot-mode', action='store_true', + help='Enable snapshot mode. Messages will not be written to the bagfile until ' + 'the "/rosbag2_recorder/snapshot" service is called. e.g. \n ' + 'ros2 service call /rosbag2_recorder/snapshot rosbag2_interfaces/Snapshot') + + # Storage configuration + add_writer_storage_plugin_extensions(parser) + + # Core compression configuration + # TODO(emersonknapp) this configuration will be moved down to implementing plugins + parser.add_argument( + '--compression-queue-size', type=int, default=1, + help='Number of files or messages that may be queued for compression ' + 'before being dropped. Default is %(default)d.') + parser.add_argument( + '--compression-threads', type=int, default=0, + help='Number of files or messages that may be compressed in parallel. ' + 'Default is %(default)d, which will be interpreted as the number of CPU cores.') + parser.add_argument( + '--compression-mode', type=str, default='none', + choices=['none', 'file', 'message'], + help='Choose mode of compression for the storage. Default: %(default)s.') + parser.add_argument( + '--compression-format', type=str, default='', + choices=get_registered_compressors(), + help='Choose the compression format/algorithm. ' + 'Has no effect if no compression mode is chosen. Default: %(default)s.') + + class RecordVerb(VerbExtension): """Record ROS data to a bag.""" def add_arguments(self, parser, cli_name): # noqa: D102 - parser.formatter_class = SplitLineFormatter - writer_choices = get_registered_writers() - default_storage_id = get_default_storage_id() - default_writer = default_storage_id if default_storage_id in writer_choices else \ - next(iter(writer_choices)) - - serialization_choices = get_registered_serializers() - converter_suffix = '_converter' - serialization_choices = { - f[:-len(converter_suffix)] - for f in serialization_choices - if f.endswith(converter_suffix) - } - - # Base output - parser.add_argument( - '-o', '--output', - help='Destination of the bagfile to create, ' - 'defaults to a timestamped folder in the current directory.') - parser.add_argument( - '-s', '--storage', default=default_writer, choices=writer_choices, - help="Storage identifier to be used, defaults to '%(default)s'.") - - # Topic filter arguments - parser.add_argument( - 'topics', nargs='*', default=None, help='List of topics to record.') - parser.add_argument( - '--topic-types', nargs='+', default=[], help='List of topic types to record.') - parser.add_argument( - '-a', '--all', action='store_true', - help='Record all topics and services (Exclude hidden topic).') - parser.add_argument( - '--all-topics', action='store_true', - help='Record all topics (Exclude hidden topic).') - parser.add_argument( - '--all-services', action='store_true', - help='Record all services via service event topics.') - parser.add_argument( - '-e', '--regex', default='', - help='Record only topics and services containing provided regular expression. ' - 'Overrides --all, --all-topics and --all-services, applies on top of ' - 'topics list and service list.') - parser.add_argument( - '--exclude-regex', default='', - help='Exclude topics and services containing provided regular expression. ' - 'Works on top of --all, --all-topics, or --regex.') - parser.add_argument( - '--exclude-topic-types', type=str, default=[], metavar='ExcludeTypes', nargs='+', - help='List of topic types not being recorded. ' - 'Works on top of --all, --all-topics, or --regex.') - parser.add_argument( - '--exclude-topics', type=str, metavar='Topic', nargs='+', - help='List of topics not being recorded. ' - 'Works on top of --all, --all-topics, or --regex.') - parser.add_argument( - '--exclude-services', type=str, metavar='ServiceName', nargs='+', - help='List of services not being recorded. ' - 'Works on top of --all, --all-services, or --regex.') - - # Enable to record service - parser.add_argument( - '--services', type=str, metavar='ServiceName', nargs='+', - help='List of services to record.') - - # Discovery behavior - parser.add_argument( - '--include-unpublished-topics', action='store_true', - help='Discover and record topics which have no publisher. ' - 'Subscriptions on such topics will be made with default QoS unless otherwise ' - 'specified in a QoS overrides file.') - parser.add_argument( - '--include-hidden-topics', action='store_true', - help='Discover and record hidden topics as well. ' - 'These are topics used internally by ROS 2 implementation.') - parser.add_argument( - '--no-discovery', action='store_true', - help='Disables topic auto discovery during recording: only topics present at ' - 'startup will be recorded.') - parser.add_argument( - '-p', '--polling-interval', type=int, default=100, - help='Time in ms to wait between querying available topics for recording. ' - 'It has no effect if --no-discovery is enabled.') - parser.add_argument( - '--ignore-leaf-topics', action='store_true', - help='Ignore topics without a subscription.') - parser.add_argument( - '--qos-profile-overrides-path', type=FileType('r'), - help='Path to a yaml file defining overrides of the QoS profile for specific topics.') - - # Core config - parser.add_argument( - '-f', '--serialization-format', default='', choices=serialization_choices, - help='The rmw serialization format in which the messages are saved, defaults to the ' - 'rmw currently in use.') - parser.add_argument( - '-b', '--max-bag-size', type=int, default=0, - help='Maximum size in bytes before the bagfile will be split. ' - 'Default: %(default)d, recording written in single bagfile and splitting ' - 'is disabled.') - parser.add_argument( - '-d', '--max-bag-duration', type=int, default=0, - help='Maximum duration in seconds before the bagfile will be split. ' - 'Default: %(default)d, recording written in single bagfile and splitting ' - 'is disabled. If both splitting by size and duration are enabled, ' - 'the bag will split at whichever threshold is reached first.') - parser.add_argument( - '--max-cache-size', type=int, default=100*1024*1024, - help='Maximum size (in bytes) of messages to hold in each buffer of cache. ' - 'Default: %(default)d. The cache is handled through double buffering, ' - 'which means that in pessimistic case up to twice the parameter value of memory ' - 'is needed. A rule of thumb is to cache an order of magnitude corresponding to ' - 'about one second of total recorded data volume. ' - 'If the value specified is 0, then every message is directly written to disk.') - parser.add_argument( - '--disable-keyboard-controls', action='store_true', default=False, - help='disables keyboard controls for recorder') - parser.add_argument( - '--start-paused', action='store_true', default=False, - help='Start the recorder in a paused state.') - parser.add_argument( - '--use-sim-time', action='store_true', default=False, - help='Use simulation time for message timestamps by subscribing to the /clock topic. ' - 'Until first /clock message is received, no messages will be written to bag.') - parser.add_argument( - '--node-name', type=str, default='rosbag2_recorder', - help='Specify the recorder node name. Default is %(default)s.') - parser.add_argument( - '--custom-data', type=str, metavar='KEY=VALUE', nargs='*', - help='Store the custom data in metadata.yaml ' - 'under "rosbag2_bagfile_information/custom_data". The key=value pair can ' - 'appear more than once. The last value will override the former ones.') - parser.add_argument( - '--snapshot-mode', action='store_true', - help='Enable snapshot mode. Messages will not be written to the bagfile until ' - 'the "/rosbag2_recorder/snapshot" service is called.') - - # Storage configuration - add_writer_storage_plugin_extensions(parser) - - # Core compression configuration - # TODO(emersonknapp) this configuration will be moved down to implementing plugins - parser.add_argument( - '--compression-queue-size', type=int, default=1, - help='Number of files or messages that may be queued for compression ' - 'before being dropped. Default is %(default)d.') - parser.add_argument( - '--compression-threads', type=int, default=0, - help='Number of files or messages that may be compressed in parallel. ' - 'Default is %(default)d, which will be interpreted as the number of CPU cores.') - parser.add_argument( - '--compression-mode', type=str, default='none', - choices=['none', 'file', 'message'], - help='Choose mode of compression for the storage. Default: %(default)s.') - parser.add_argument( - '--compression-format', type=str, default='', - choices=get_registered_compressors(), - help='Choose the compression format/algorithm. ' - 'Has no effect if no compression mode is chosen. Default: %(default)s.') + add_recorder_arguments(parser) def _check_necessary_argument(self, args): # At least one options out of --all, --all-topics, --all-services, --services, --topics, @@ -208,6 +218,9 @@ def _check_necessary_argument(self, args): def main(self, *, args): # noqa: D102 + if args.topics_positional: + args.topics = args.topics_positional + if not self._check_necessary_argument(args): return print_error('Need to specify one option out of --all, --all-topics, ' '--all-services, --services, --topics, --topic-types and --regex') diff --git a/ros2bag/test/test_recorder_args_parser.py b/ros2bag/test/test_recorder_args_parser.py new file mode 100644 index 0000000000..d195626534 --- /dev/null +++ b/ros2bag/test/test_recorder_args_parser.py @@ -0,0 +1,130 @@ +# Copyright 2024 Apex.AI, 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. + +import argparse +from pathlib import Path + +import pytest + +from ros2bag.verb.record import add_recorder_arguments + +RESOURCES_PATH = Path(__file__).parent / 'resources' + + +@pytest.fixture(scope='function') +def test_arguments_parser(): + parser = argparse.ArgumentParser() + add_recorder_arguments(parser) + return parser + + +def test_recorder_positional_topics_list_argument(test_arguments_parser): + """Test recorder positional topics list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['topic1', 'topic2', '--output', output_path.as_posix()] + ) + assert ['topic1', 'topic2'] == args.topics_positional + assert output_path.as_posix() == args.output + + +def test_recorder_optional_topics_list_argument(test_arguments_parser): + """Test recorder optional --topics list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--topics', 'topic1', 'topic2', '--output', output_path.as_posix()] + ) + assert ['topic1', 'topic2'] == args.topics + assert output_path.as_posix() == args.output + + +def test_recorder_services_list_argument(test_arguments_parser): + """Test recorder --services list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--services', 'service1', 'service2', '--output', output_path.as_posix()] + ) + assert ['service1', 'service2'] == args.services + assert output_path.as_posix() == args.output + + +def test_recorder_services_and_positional_topics_list_arguments(test_arguments_parser): + """Test recorder --services list and positional topics list arguments parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--output', output_path.as_posix(), + '--services', 'service1', 'service2', '--', 'topic1', 'topic2']) + assert ['service1', 'service2'] == args.services + assert ['topic1', 'topic2'] == args.topics_positional + assert output_path.as_posix() == args.output + + +def test_recorder_services_and_optional_topics_list_arguments(test_arguments_parser): + """Test recorder --services list and optional --topics list arguments parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--output', output_path.as_posix(), + '--services', 'service1', 'service2', '--topics', 'topic1', 'topic2']) + assert ['service1', 'service2'] == args.services + assert ['topic1', 'topic2'] == args.topics + assert output_path.as_posix() == args.output + + +def test_recorder_topic_types_list_argument(test_arguments_parser): + """Test recorder --topic-types list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--topic-types', 'topic_type1', 'topic_type2', '--output', output_path.as_posix()]) + assert ['topic_type1', 'topic_type2'] == args.topic_types + assert output_path.as_posix() == args.output + + +def test_recorder_exclude_topic_types_list_argument(test_arguments_parser): + """Test recorder --exclude-topic-types list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--exclude-topic-types', 'topic_type1', 'topic_type2', '--output', + output_path.as_posix()]) + assert ['topic_type1', 'topic_type2'] == args.exclude_topic_types + assert output_path.as_posix() == args.output + + +def test_recorder_exclude_topics_list_argument(test_arguments_parser): + """Test recorder --exclude-topics list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--exclude-topics', 'topic1', 'topic2', '--output', output_path.as_posix()] + ) + assert ['topic1', 'topic2'] == args.exclude_topics + assert output_path.as_posix() == args.output + + +def test_recorder_exclude_services_list_argument(test_arguments_parser): + """Test recorder --exclude-services list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--exclude-services', 'service1', 'service2', '--output', output_path.as_posix()] + ) + assert ['service1', 'service2'] == args.exclude_services + assert output_path.as_posix() == args.output + + +def test_recorder_custom_data_list_argument(test_arguments_parser): + """Test recorder --custom-data list argument parser.""" + output_path = RESOURCES_PATH / 'ros2bag_tmp_file' + args = test_arguments_parser.parse_args( + ['--custom-data', 'Key1=Value1', 'key2=value2', '--output', output_path.as_posix()] + ) + assert ['Key1=Value1', 'key2=value2'] == args.custom_data + assert output_path.as_posix() == args.output diff --git a/rosbag2_tests/test/rosbag2_tests/test_rosbag2_record_end_to_end.cpp b/rosbag2_tests/test/rosbag2_tests/test_rosbag2_record_end_to_end.cpp index 5d059cf4b4..d93bec9d1a 100644 --- a/rosbag2_tests/test/rosbag2_tests/test_rosbag2_record_end_to_end.cpp +++ b/rosbag2_tests/test/rosbag2_tests/test_rosbag2_record_end_to_end.cpp @@ -76,7 +76,7 @@ TEST_P(RecordFixture, record_end_to_end_test_with_zstd_file_compression) { " --compression-mode file" << " --compression-format " << compression_format << " --max-cache-size 0" << - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(cmd.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( @@ -135,7 +135,7 @@ TEST_P(RecordFixture, record_end_to_end_test) { pub_manager.setup_publisher("/unrecorded_topic", unrecorded_message, 3); auto process_handle = start_execution( - get_base_record_command() + " --max-cache-size 0 /test_topic"); + get_base_record_command() + " --max-cache-size 0 --topics /test_topic"); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { stop_execution(process_handle); @@ -177,7 +177,7 @@ TEST_P(RecordFixture, record_end_to_end_test_start_paused) { pub_manager.setup_publisher("/test_topic", message, 10); auto process_handle = start_execution( - get_base_record_command() + " --max-cache-size 0 --start-paused /test_topic"); + get_base_record_command() + " --max-cache-size 0 --start-paused --topics /test_topic"); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { stop_execution(process_handle); @@ -206,7 +206,7 @@ TEST_P(RecordFixture, record_end_to_end_exits_gracefully_on_sigterm) { rosbag2_test_common::PublicationManager pub_manager; pub_manager.setup_publisher(topic_name, message, 10); auto process_handle = start_execution( - get_base_record_command() + " " + topic_name); + get_base_record_command() + " --topics " + topic_name); ASSERT_TRUE(pub_manager.wait_for_matched(topic_name.c_str())) << "Expected find rosbag subscription"; @@ -292,7 +292,7 @@ TEST_P(RecordFixture, record_end_to_end_with_splitting_bagsize_split_is_at_least std::stringstream command; command << get_base_record_command() << " --max-bag-size " << bagfile_split_size << - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(command.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { @@ -347,7 +347,7 @@ TEST_P(RecordFixture, record_end_to_end_with_splitting_max_size_not_reached) { std::stringstream command; command << get_base_record_command() << " --max-bag-size " << bagfile_split_size << - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(command.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { @@ -397,7 +397,7 @@ TEST_P(RecordFixture, record_end_to_end_with_splitting_splits_bagfile) { std::stringstream command; command << get_base_record_command() << " --max-bag-size " << bagfile_split_size << - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(command.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { @@ -444,7 +444,7 @@ TEST_P(RecordFixture, record_end_to_end_with_duration_splitting_splits_bagfile) std::stringstream command; command << get_base_record_command() << " -d " << bagfile_split_duration << - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(command.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { @@ -490,7 +490,7 @@ TEST_P(RecordFixture, record_end_to_end_test_with_zstd_file_compression_compress " --max-bag-size " << bagfile_split_size << " --compression-mode file" << " --compression-format zstd" - " " << topic_name; + " --topics " << topic_name; auto process_handle = start_execution(command.str()); auto cleanup_process_handle = rcpputils::make_scope_exit( @@ -538,7 +538,7 @@ TEST_P(RecordFixture, record_fails_gracefully_if_bag_already_exists) { TEST_P(RecordFixture, record_fails_if_both_all_and_topic_list_is_specified) { internal::CaptureStderr(); auto exit_code = execute_and_wait_until_completion( - get_base_record_command() + " -a /some_topic", temporary_dir_path_); + get_base_record_command() + " -a --topics /some_topic", temporary_dir_path_); auto error_output = internal::GetCapturedStderr(); EXPECT_THAT(exit_code, Eq(EXIT_FAILURE)); @@ -579,8 +579,8 @@ TEST_P(RecordFixture, record_end_to_end_test_with_cache) { auto process_handle = start_execution( get_base_record_command() + - " " + topic_name + " " + - "--max-cache-size " + std::to_string(max_cache_size)); + " --topics " + topic_name + + " --max-cache-size " + std::to_string(max_cache_size)); auto cleanup_process_handle = rcpputils::make_scope_exit( [process_handle]() { stop_execution(process_handle);