diff --git a/README.md b/README.md index 22b3dde14d..b35c35d85f 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,97 @@ Topic information: Topic: /chatter | Type: std_msgs/String | Count: 9 | Serializ Topic: /my_chatter | Type: std_msgs/String | Count: 18 | Serialization Format: cdr ``` +### Converting bags + +Rosbag2 provides a tool `ros2 bag convert` (or, `rosbag2_transport::bag_rewrite` in the C++ API). +This allows the user to take one or more input bags, and write them out to one or more output bags with new settings. +This flexible feature enables the following features: +* Merge (multiple input bags, one output bag) +* Split top-level bags (one input bag, multiple output bags) +* Split internal files (by time or size - one input bag with fewer internal files, one output bag with more, smaller, internal files) +* Compress/Decompress (output bag(s) with different compression settings than the input(s)) +* Serialization format conversion +* ... and more! + +Here is an example command: + +``` +ros2 bag convert --input /path/to/bag1 --input /path/to/bag2 storage_id --output-options output_options.yaml +``` + +The `--input` argument may be specified any number of times, and takes 1 or 2 values. +The first value is the URI of the input bag. +If a second value is supplied, it specifies the storage implementation of the bag. +If no storage implementation is specified, rosbag2 will try to determine it automatically from the bag. + +The `--output-options` argument must point to the URI of a YAML file specifying the full recording configuration for each bag to output (`StorageOptions` + `RecordOptions`). +This file must contain a top-level key `output_bags`, which contains a list of these objects. + +The only required value in the output bags is `uri` and `storage_id`. All other values are options (however, if no topic selection is specified, this output bag will be empty!). + +This example notes all fields that can have an effect, with a comment on the required ones. + +``` +output_bags +- uri: /output/bag1 # required + storage_id: sqlite3 # required + max_bagfile_size: 0 + max_bagfile_duration: 0 + storage_preset_profile: "" + storage_config_uri: "" + all: false + topics: [] + rmw_serialization_format: "" # defaults to using the format of the input topic + regex: "" + exclude: "" + compression_mode: "" + compression_format: "" + compression_queue_size: 1 + compression_threads: 0 + include_hidden_topics: false +``` + +Example merge: + +``` +$ ros2 bag convert -i bag1 -i bag2 -o out.yaml + +# out.yaml +output_bags: +- uri: merged_bag + storage_id: sqlite3 + all: true +``` + +Example split: + +``` +$ ros2 bag convert -i bag1 -o out.yaml + +# out.yaml +output_bags: +- uri: split1 + storage_id: sqlite3 + topics: [/topic1, /topic2] +- uri: split2 + storage_id: sqlite3 + topics: [/topic3] +``` + +Example compress: + +``` +$ ros2 bag convert -i bag1 -o out.yaml + +# out.yaml +output_bags: +- uri: compressed + storage_id: sqlite3 + all: true + compression_mode: file + compression_format: zstd +``` + ### Overriding QoS Profiles When starting a recording or playback workflow, you can pass a YAML file that contains QoS profile settings for a specific topic. diff --git a/ros2bag/ros2bag/verb/convert.py b/ros2bag/ros2bag/verb/convert.py new file mode 100644 index 0000000000..080bb80c9d --- /dev/null +++ b/ros2bag/ros2bag/verb/convert.py @@ -0,0 +1,50 @@ +# Copyright 2021 Amazon.com Inc or its Affiliates +# +# 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 ros2bag.verb import VerbExtension +from rosbag2_py import bag_rewrite +from rosbag2_py import StorageOptions + + +class ConvertVerb(VerbExtension): + """Given an input bag, write out a new bag with different settings.""" + + def add_arguments(self, parser, cli_name): + parser.add_argument( + '-i', '--input', + required=True, + action='append', nargs='+', + metavar=('uri', 'storage_id'), + help='URI (and optional storage ID) of an input bag. May be provided more than once') + parser.add_argument( + '-o', '--output-options', + type=str, required=True, + help='YAML file with options for output bags. Must have one top-level key ' + '"output_bags", which contains a sequence of StorageOptions/RecordOptions ' + 'objects. See README.md for some examples.') + + def main(self, *, args): + input_options = [] + for input_bag in args.input: + if len(input_bag) > 2: + raise argparse.ArgumentTypeError( + f'--input expects 1 or 2 arguments, {len(input_bag)} provided') + storage_options = StorageOptions(uri=input_bag[0]) + if len(input_bag) > 1: + storage_options.storage_id = input_bag[1] + input_options.append(storage_options) + + bag_rewrite(input_options, args.output_options) diff --git a/ros2bag/setup.py b/ros2bag/setup.py index 0cc3b672fb..49db1a453f 100644 --- a/ros2bag/setup.py +++ b/ros2bag/setup.py @@ -38,6 +38,7 @@ 'ros2bag.verb = ros2bag.verb:VerbExtension', ], 'ros2bag.verb': [ + 'convert = ros2bag.verb.convert:ConvertVerb', 'info = ros2bag.verb.info:InfoVerb', 'list = ros2bag.verb.list:ListVerb', 'play = ros2bag.verb.play:PlayVerb', diff --git a/rosbag2_py/CMakeLists.txt b/rosbag2_py/CMakeLists.txt index 7646b8ea75..0f6e1bada0 100644 --- a/rosbag2_py/CMakeLists.txt +++ b/rosbag2_py/CMakeLists.txt @@ -148,38 +148,48 @@ if(BUILD_TESTING) if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE_DEBUG}") endif() - set(other_environment_vars) + set(append_env_vars "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}") + set(set_env_vars "ROSBAG2_PY_TEST_RESOURCES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test/resources") if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Work around a rtti bug when using class_loader from a # python extension built with libc++. - set(other_environment_vars "ROSBAG2_PY_TEST_WITH_RTLD_GLOBAL=True") + set(set_env_vars "${set_env_vars} ROSBAG2_PY_TEST_WITH_RTLD_GLOBAL=True") endif() + + # message(FATAL_ERROR "HELP I AM THE PATH ${CMAKE_CURRENT_SOURCE_DIR}") ament_add_pytest_test(test_sequential_reader_py "test/test_sequential_reader.py" PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" - APPEND_ENV - PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} - ${other_environment_vars} + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" ) ament_add_pytest_test(test_sequential_reader_multiple_files_py "test/test_sequential_reader_multiple_files.py" PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" - APPEND_ENV - PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} - ${other_environment_vars} + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" ) ament_add_pytest_test(test_sequential_writer_py "test/test_sequential_writer.py" PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" - APPEND_ENV "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${other_environment_vars} + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" ) ament_add_pytest_test(test_transport_py "test/test_transport.py" PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" - APPEND_ENV "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${other_environment_vars} + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" ) ament_add_pytest_test(test_reindexer_py "test/test_reindexer.py" PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" - APPEND_ENV "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" ${other_environment_vars} + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" + ) + ament_add_pytest_test(test_convert_py + "test/test_convert.py" + PYTHON_EXECUTABLE "${_PYTHON_EXECUTABLE}" + APPEND_ENV "${append_env_vars}" + ENV "${set_env_vars}" ) endif() diff --git a/rosbag2_py/rosbag2_py/__init__.py b/rosbag2_py/rosbag2_py/__init__.py index 72ef3de376..82da7d3147 100644 --- a/rosbag2_py/rosbag2_py/__init__.py +++ b/rosbag2_py/rosbag2_py/__init__.py @@ -42,6 +42,7 @@ Info, ) from rosbag2_py._transport import ( + bag_rewrite, Player, PlayOptions, Recorder, @@ -52,6 +53,7 @@ ) __all__ = [ + 'bag_rewrite', 'ConverterOptions', 'get_registered_readers', 'get_registered_writers', diff --git a/rosbag2_py/src/rosbag2_py/_storage.cpp b/rosbag2_py/src/rosbag2_py/_storage.cpp index e2807699b6..48d04e619e 100644 --- a/rosbag2_py/src/rosbag2_py/_storage.cpp +++ b/rosbag2_py/src/rosbag2_py/_storage.cpp @@ -45,7 +45,7 @@ PYBIND11_MODULE(_storage, m) { pybind11::init< std::string, std::string, uint64_t, uint64_t, uint64_t, std::string, std::string, bool>(), pybind11::arg("uri"), - pybind11::arg("storage_id"), + pybind11::arg("storage_id") = "", pybind11::arg("max_bagfile_size") = 0, pybind11::arg("max_bagfile_duration") = 0, pybind11::arg("max_cache_size") = 0, diff --git a/rosbag2_py/src/rosbag2_py/_transport.cpp b/rosbag2_py/src/rosbag2_py/_transport.cpp index 91beec78aa..c8ab6f38f5 100644 --- a/rosbag2_py/src/rosbag2_py/_transport.cpp +++ b/rosbag2_py/src/rosbag2_py/_transport.cpp @@ -21,6 +21,8 @@ #include #include "rosbag2_storage/storage_options.hpp" +#include "rosbag2_storage/yaml.hpp" +#include "rosbag2_transport/bag_rewrite.hpp" #include "rosbag2_transport/play_options.hpp" #include "rosbag2_transport/player.hpp" #include "rosbag2_transport/reader_writer_factory.hpp" @@ -175,6 +177,32 @@ class Recorder } }; +// Simple wrapper to read the output config YAML into structs +void bag_rewrite( + const std::vector & input_options, + std::string output_config_file) +{ + YAML::Node yaml_file = YAML::LoadFile(output_config_file); + auto bag_nodes = yaml_file["output_bags"]; + if (!bag_nodes) { + throw std::runtime_error("Output bag config YAML file must have top-level key 'output_bags'"); + } + if (!bag_nodes.IsSequence()) { + throw std::runtime_error( + "Top-level key 'output_bags' must contain a list of " + "StorageOptions/RecordOptions dicts."); + } + + std::vector< + std::pair> output_options; + for (const auto & bag_node : bag_nodes) { + auto storage_options = bag_node.as(); + auto record_options = bag_node.as(); + output_options.push_back(std::make_pair(storage_options, record_options)); + } + rosbag2_transport::bag_rewrite(input_options, output_options); +} + } // namespace rosbag2_py PYBIND11_MODULE(_transport, m) { @@ -240,4 +268,9 @@ PYBIND11_MODULE(_transport, m) { .def("record", &rosbag2_py::Recorder::record) .def("cancel", &rosbag2_py::Recorder::cancel) ; + + m.def( + "bag_rewrite", + &rosbag2_py::bag_rewrite, + "Given one or more input bags, output one or more bags with new settings."); } diff --git a/rosbag2_py/test/resources/convert_a/metadata.yaml b/rosbag2_py/test/resources/convert_a/metadata.yaml new file mode 100644 index 0000000000..3c0b8ae7c8 --- /dev/null +++ b/rosbag2_py/test/resources/convert_a/metadata.yaml @@ -0,0 +1,32 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 100000000 + starting_time: + nanoseconds_since_epoch: 0 + message_count: 150 + topics_with_message_count: + - topic_metadata: + name: a_empty + type: test_msgs/msg/Empty + serialization_format: cdr + offered_qos_profiles: "- history: 1\n depth: 1\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false\n- history: 1\n depth: 2\n reliability: 2\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 100 + - topic_metadata: + name: b_basictypes + type: test_msgs/msg/BasicTypes + serialization_format: cdr + offered_qos_profiles: "- history: 1\n depth: 1\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 50 + compression_format: "" + compression_mode: "" + relative_file_paths: + - rewriter_a_0.db3 + files: + - path: rewriter_a_0.db3 + starting_time: + nanoseconds_since_epoch: 0 + duration: + nanoseconds: 100000000 + message_count: 150 diff --git a/rosbag2_py/test/resources/convert_a/rewriter_a_0.db3 b/rosbag2_py/test/resources/convert_a/rewriter_a_0.db3 new file mode 100644 index 0000000000..baaa015852 Binary files /dev/null and b/rosbag2_py/test/resources/convert_a/rewriter_a_0.db3 differ diff --git a/rosbag2_py/test/resources/convert_b/metadata.yaml b/rosbag2_py/test/resources/convert_b/metadata.yaml new file mode 100644 index 0000000000..333882eb8a --- /dev/null +++ b/rosbag2_py/test/resources/convert_b/metadata.yaml @@ -0,0 +1,32 @@ +rosbag2_bagfile_information: + version: 5 + storage_identifier: sqlite3 + duration: + nanoseconds: 222000000 + starting_time: + nanoseconds_since_epoch: 0 + message_count: 75 + topics_with_message_count: + - topic_metadata: + name: c_strings + type: test_msgs/msg/Strings + serialization_format: cdr + offered_qos_profiles: "- history: 1\n depth: 1\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 50 + - topic_metadata: + name: a_empty + type: test_msgs/msg/Empty + serialization_format: cdr + offered_qos_profiles: "- history: 1\n depth: 1\n reliability: 1\n durability: 1\n deadline:\n sec: 9223372036\n nsec: 854775807\n lifespan:\n sec: 9223372036\n nsec: 854775807\n liveliness: 1\n liveliness_lease_duration:\n sec: 9223372036\n nsec: 854775807\n avoid_ros_namespace_conventions: false" + message_count: 25 + compression_format: "" + compression_mode: "" + relative_file_paths: + - rewriter_b_0.db3 + files: + - path: rewriter_b_0.db3 + starting_time: + nanoseconds_since_epoch: 0 + duration: + nanoseconds: 222000000 + message_count: 75 diff --git a/rosbag2_py/test/resources/convert_b/rewriter_b_0.db3 b/rosbag2_py/test/resources/convert_b/rewriter_b_0.db3 new file mode 100644 index 0000000000..2fedfc6a58 Binary files /dev/null and b/rosbag2_py/test/resources/convert_b/rewriter_b_0.db3 differ diff --git a/rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_0.db3 b/rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_0.db3 similarity index 100% rename from rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_0.db3 rename to rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_0.db3 diff --git a/rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_1.db3 b/rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_1.db3 similarity index 100% rename from rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_1.db3 rename to rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_1.db3 diff --git a/rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_2.db3 b/rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_2.db3 similarity index 100% rename from rosbag2_py/resources/reindex_test_bags/multiple_files/multiple_files_2.db3 rename to rosbag2_py/test/resources/reindex_test_bags/multiple_files/multiple_files_2.db3 diff --git a/rosbag2_py/resources/talker/metadata.yaml b/rosbag2_py/test/resources/talker/metadata.yaml similarity index 100% rename from rosbag2_py/resources/talker/metadata.yaml rename to rosbag2_py/test/resources/talker/metadata.yaml diff --git a/rosbag2_py/resources/talker/talker.db3 b/rosbag2_py/test/resources/talker/talker.db3 similarity index 100% rename from rosbag2_py/resources/talker/talker.db3 rename to rosbag2_py/test/resources/talker/talker.db3 diff --git a/rosbag2_py/resources/talker/talker.db3-shm b/rosbag2_py/test/resources/talker/talker.db3-shm similarity index 100% rename from rosbag2_py/resources/talker/talker.db3-shm rename to rosbag2_py/test/resources/talker/talker.db3-shm diff --git a/rosbag2_py/resources/talker/talker.db3-wal b/rosbag2_py/test/resources/talker/talker.db3-wal similarity index 100% rename from rosbag2_py/resources/talker/talker.db3-wal rename to rosbag2_py/test/resources/talker/talker.db3-wal diff --git a/rosbag2_py/resources/wbag/metadata.yaml b/rosbag2_py/test/resources/wbag/metadata.yaml similarity index 100% rename from rosbag2_py/resources/wbag/metadata.yaml rename to rosbag2_py/test/resources/wbag/metadata.yaml diff --git a/rosbag2_py/resources/wbag/wbag_0.db3 b/rosbag2_py/test/resources/wbag/wbag_0.db3 similarity index 100% rename from rosbag2_py/resources/wbag/wbag_0.db3 rename to rosbag2_py/test/resources/wbag/wbag_0.db3 diff --git a/rosbag2_py/resources/wbag/wbag_1.db3 b/rosbag2_py/test/resources/wbag/wbag_1.db3 similarity index 100% rename from rosbag2_py/resources/wbag/wbag_1.db3 rename to rosbag2_py/test/resources/wbag/wbag_1.db3 diff --git a/rosbag2_py/resources/wbag/wbag_2.db3 b/rosbag2_py/test/resources/wbag/wbag_2.db3 similarity index 100% rename from rosbag2_py/resources/wbag/wbag_2.db3 rename to rosbag2_py/test/resources/wbag/wbag_2.db3 diff --git a/rosbag2_py/resources/wbag/wbag_3.db3 b/rosbag2_py/test/resources/wbag/wbag_3.db3 similarity index 100% rename from rosbag2_py/resources/wbag/wbag_3.db3 rename to rosbag2_py/test/resources/wbag/wbag_3.db3 diff --git a/rosbag2_py/resources/wbag/wbag_4.db3 b/rosbag2_py/test/resources/wbag/wbag_4.db3 similarity index 100% rename from rosbag2_py/resources/wbag/wbag_4.db3 rename to rosbag2_py/test/resources/wbag/wbag_4.db3 diff --git a/rosbag2_py/test/test_convert.py b/rosbag2_py/test/test_convert.py new file mode 100644 index 0000000000..03757cc51d --- /dev/null +++ b/rosbag2_py/test/test_convert.py @@ -0,0 +1,100 @@ +# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# 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 os +from pathlib import Path +import sys +import tempfile +import unittest + +if os.environ.get('ROSBAG2_PY_TEST_WITH_RTLD_GLOBAL', None) is not None: + # This is needed on Linux when compiling with clang/libc++. + # TL;DR This makes class_loader work when using a python extension compiled with libc++. + # + # For the fun RTTI ABI details, see https://whatofhow.wordpress.com/2015/03/17/odr-rtti-dso/. + sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_LAZY) + +from common import get_rosbag_options # noqa +import rosbag2_py # noqa +from rosbag2_py import ( + bag_rewrite, + StorageOptions, +) # noqa + +RESOURCES_PATH = Path(os.environ['ROSBAG2_PY_TEST_RESOURCES_DIR']) + + +class TestConvert(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.tmpdir = tempfile.TemporaryDirectory() + cls.tmp_path = Path(cls.tmpdir.name) + + @classmethod + def tearDownClass(cls): + try: + cls.tmpdir.cleanup() + except OSError: + pass + + def test_no_toplevel_key(self): + output_options_path = self.tmp_path / 'no_toplevel_key.yml' + output_options_content = """ +- key: value +""" + with output_options_path.open('w') as f: + f.write(output_options_content) + with self.assertRaises(RuntimeError): + bag_rewrite([], str(output_options_path)) + + def test_output_bags_not_a_list(self): + output_options_path = self.tmp_path / 'not_a_list.yml' + output_options_content = """ +output_bags: + key: value +""" + with output_options_path.open('w') as f: + f.write(output_options_content) + with self.assertRaises(RuntimeError): + bag_rewrite([], str(output_options_path)) + + def test_basic_convert(self): + # This test is just to test that the rosbag2_py wrapper parses input + # It is not a comprehensive test of bag_rewrite. + bag_a_path = RESOURCES_PATH / 'convert_a' + bag_b_path = RESOURCES_PATH / 'convert_b' + output_uri_1 = self.tmp_path / 'converted_1' + output_uri_2 = self.tmp_path / 'converted_2' + input_options = [ + StorageOptions(uri=str(bag_a_path)), + StorageOptions(uri=str(bag_b_path), storage_id='sqlite3'), + ] + output_options_path = self.tmp_path / 'simple_convert.yml' + output_options_content = f""" +output_bags: +- uri: {output_uri_1} + storage_id: sqlite3 + topics: [a_empty] +- uri: {output_uri_2} + storage_id: sqlite3 + exclude: ".*empty.*" +""" + with output_options_path.open('w') as f: + f.write(output_options_content) + bag_rewrite(input_options, str(output_options_path)) + self.assertTrue(output_uri_1.exists() and output_uri_1.is_dir()) + self.assertTrue((output_uri_1 / 'metadata.yaml').exists()) + self.assertTrue(output_uri_2.exists() and output_uri_2.is_dir()) + self.assertTrue((output_uri_2 / 'metadata.yaml').exists()) diff --git a/rosbag2_py/test/test_reindexer.py b/rosbag2_py/test/test_reindexer.py index 296fb4e367..9ed386d307 100644 --- a/rosbag2_py/test/test_reindexer.py +++ b/rosbag2_py/test/test_reindexer.py @@ -34,9 +34,11 @@ from common import get_rosbag_options # noqa import rosbag2_py # noqa +RESOURCES_PATH = Path(os.environ['ROSBAG2_PY_TEST_RESOURCES_DIR']) + def test_reindexer_multiple_files(): - bag_path = Path(__file__).parent.parent / 'resources' / 'reindex_test_bags' / 'multiple_files' + bag_path = RESOURCES_PATH / 'reindex_test_bags' / 'multiple_files' result_path = bag_path / 'metadata.yaml' storage_options, converter_options = get_rosbag_options(str(bag_path)) diff --git a/rosbag2_py/test/test_sequential_reader.py b/rosbag2_py/test/test_sequential_reader.py index 2caa0416a7..3dc5d1f4b2 100644 --- a/rosbag2_py/test/test_sequential_reader.py +++ b/rosbag2_py/test/test_sequential_reader.py @@ -31,9 +31,11 @@ from common import get_rosbag_options # noqa import rosbag2_py # noqa +RESOURCES_PATH = Path(os.environ['ROSBAG2_PY_TEST_RESOURCES_DIR']) + def test_sequential_reader(): - bag_path = str(Path(__file__).parent.parent / 'resources' / 'talker') + bag_path = str(RESOURCES_PATH / 'talker') storage_options, converter_options = get_rosbag_options(bag_path) reader = rosbag2_py.SequentialReader() @@ -81,7 +83,7 @@ def test_sequential_reader(): def test_sequential_reader_seek(): - bag_path = str(Path(__file__).parent.parent / 'resources' / 'talker') + bag_path = str(RESOURCES_PATH / 'talker') storage_options, converter_options = get_rosbag_options(bag_path) reader = rosbag2_py.SequentialReader() diff --git a/rosbag2_py/test/test_sequential_reader_multiple_files.py b/rosbag2_py/test/test_sequential_reader_multiple_files.py index f75a0da6e2..a478878498 100644 --- a/rosbag2_py/test/test_sequential_reader_multiple_files.py +++ b/rosbag2_py/test/test_sequential_reader_multiple_files.py @@ -27,9 +27,11 @@ from common import get_rosbag_options # noqa import rosbag2_py # noqa +RESOURCES_PATH = Path(os.environ['ROSBAG2_PY_TEST_RESOURCES_DIR']) + def test_reset_filter(): - bag_path = str(Path(__file__).parent.parent / 'resources' / 'wbag') + bag_path = str(RESOURCES_PATH / 'wbag') storage_options, converter_options = get_rosbag_options(bag_path) reader = rosbag2_py.SequentialReader() @@ -74,7 +76,7 @@ def test_reset_filter(): def test_seek_forward(): - bag_path = str(Path(__file__).parent.parent / 'resources' / 'wbag') + bag_path = str(RESOURCES_PATH / 'wbag') storage_options, converter_options = get_rosbag_options(bag_path) reader = rosbag2_py.SequentialReader() @@ -109,7 +111,7 @@ def test_seek_forward(): def test_seek_backward(): - bag_path = str(Path(__file__).parent.parent / 'resources' / 'wbag') + bag_path = str(RESOURCES_PATH / 'wbag') storage_options, converter_options = get_rosbag_options(bag_path) reader = rosbag2_py.SequentialReader()