3232#include " test_msgs/msg/basic_types.hpp"
3333#include " test_msgs/message_fixtures.hpp"
3434
35+ #include " qos.hpp"
36+
3537#include " rosbag2_play_test_fixture.hpp"
38+ #include " rosbag2_transport_test_fixture.hpp"
3639
3740using namespace ::testing; // NOLINT
3841using namespace rosbag2_transport ; // NOLINT
@@ -205,35 +208,59 @@ class RosBag2PlayQosOverrideTestFixture : public RosBag2PlayTestFixture
205208 RosBag2PlayQosOverrideTestFixture ()
206209 : RosBag2PlayTestFixture()
207210 {
208- // Because this test only cares about compatibility (receiving any messages at all)
211+ }
212+
213+ void initialize (const std::vector<rosbag2_transport::Rosbag2QoS> & offered_qos)
214+ {
215+ // Because these tests only cares about compatibility (receiving any messages at all)
209216 // We publish one more message than we expect to receive, to avoid caring about
210217 // shutdown edge behaviors that are not explicitly being tested here.
211218 const size_t num_msgs_to_publish = num_msgs_to_wait_for_ + 1 ;
212- topic_timestamps_ms_ .reserve (num_msgs_to_publish);
219+ messages_ .reserve (num_msgs_to_publish);
213220 for (size_t i = 0 ; i < num_msgs_to_publish; i++) {
214- topic_timestamps_ms_.push_back (start_time_ms_ + message_spacing_ms_ * i);
221+ const auto timestamp = start_time_ms_ + message_spacing_ms_ * i;
222+ messages_.push_back (serialize_test_message (topic_name_, timestamp, basic_msg_));
215223 }
216224
217- messages_.reserve (topic_timestamps_ms_.size ());
218- for (const auto topic_timestamp : topic_timestamps_ms_) {
219- messages_.push_back (serialize_test_message (topic_name_, topic_timestamp, basic_msg_));
225+ std::string serialized_offered_qos = " " ;
226+ if (!offered_qos.empty ()) {
227+ YAML::Node offered_qos_yaml;
228+ for (const auto & profile : offered_qos) {
229+ offered_qos_yaml.push_back (profile);
230+ }
231+ serialized_offered_qos = YAML::Dump (offered_qos_yaml);
220232 }
233+ topic_types_.push_back (
234+ {topic_name_, msg_type_, " " /* serialization_format*/ , serialized_offered_qos});
221235
222236 auto prepared_mock_reader = std::make_unique<MockSequentialReader>();
223237 prepared_mock_reader->prepare (messages_, topic_types_);
224238 reader_ = std::make_unique<rosbag2_cpp::Reader>(std::move (prepared_mock_reader));
225239 }
226240
241+ template <typename Duration>
242+ void play_and_wait (Duration timeout, bool expect_timeout = false )
243+ {
244+ auto await_received_messages = sub_->spin_subscriptions ();
245+ Rosbag2Transport transport (reader_, writer_, info_);
246+ transport.play (storage_options_, play_options_);
247+ const auto result = await_received_messages.wait_for (timeout);
248+ if (expect_timeout) {
249+ ASSERT_EQ (result, std::future_status::timeout);
250+ } else {
251+ ASSERT_NE (result, std::future_status::timeout);
252+ }
253+ transport.shutdown ();
254+ }
255+
227256 const std::string topic_name_{" /test_topic" };
228257 const std::string msg_type_{" test_msgs/BasicTypes" };
229- const size_t num_msgs_to_wait_for_{3 };
258+ // Receiving _any_ messages means we've confirmed compatibility in these tests
259+ const size_t num_msgs_to_wait_for_{1 };
230260 test_msgs::msg::BasicTypes::SharedPtr basic_msg_{get_messages_basic_types ()[0 ]};
231- const std::vector<rosbag2_storage::TopicMetadata> topic_types_{
232- {topic_name_, msg_type_, " " /* serialization_format*/ , " " /* offered_qos_profiles*/ }
233- };
261+ std::vector<rosbag2_storage::TopicMetadata> topic_types_{};
234262 const int64_t start_time_ms_{500 };
235263 const int64_t message_spacing_ms_{200 };
236- std::vector<int64_t > topic_timestamps_ms_{};
237264 std::vector<std::shared_ptr<rosbag2_storage::SerializedBagMessage>> messages_;
238265};
239266
@@ -246,27 +273,19 @@ TEST_F(RosBag2PlayQosOverrideTestFixture, topic_qos_profiles_overridden)
246273 // to receive messages.
247274 const auto qos_request = rclcpp::QoS{rclcpp::KeepAll ()}.reliable ().transient_local ();
248275 const auto qos_playback_override = rclcpp::QoS{rclcpp::KeepAll ()}.reliable ().transient_local ();
249-
250276 const auto topic_qos_profile_overrides = std::unordered_map<std::string, rclcpp::QoS>{
251277 std::pair<std::string, rclcpp::QoS>{topic_name_, qos_playback_override},
252278 };
279+ // This should normally take less than 1s - just making it shorter than 60s default
280+ const auto timeout = 5s;
281+
282+ initialize ({});
253283
254284 sub_->add_subscription <test_msgs::msg::BasicTypes>(
255285 topic_name_, num_msgs_to_wait_for_, qos_request);
256- auto await_received_messages = sub_->spin_subscriptions ();
257286 play_options_.topic_qos_profile_overrides = topic_qos_profile_overrides;
258287
259- Rosbag2Transport rosbag2_transport{reader_, writer_, info_};
260- rosbag2_transport.play (storage_options_, play_options_);
261-
262- // This should normally take less than 1s - just making it shorter than 60s default
263- const auto future_result = await_received_messages.wait_for (5s);
264- EXPECT_NE (future_result, std::future_status::timeout);
265- rosbag2_transport.shutdown ();
266-
267- const auto received_messages =
268- sub_->get_received_messages <test_msgs::msg::BasicTypes>(topic_name_);
269- EXPECT_FALSE (received_messages.empty ());
288+ play_and_wait (timeout);
270289}
271290
272291TEST_F (RosBag2PlayQosOverrideTestFixture, topic_qos_profiles_overridden_incompatible)
@@ -277,25 +296,40 @@ TEST_F(RosBag2PlayQosOverrideTestFixture, topic_qos_profiles_overridden_incompat
277296 // which should not be a compatible offer and therefore we should receive no messages.
278297 const auto qos_request = rclcpp::QoS{rclcpp::KeepAll ()}.reliable ();
279298 const auto qos_playback_override = rclcpp::QoS{rclcpp::KeepAll ()}.best_effort ();
280-
281299 const auto topic_qos_profile_overrides = std::unordered_map<std::string, rclcpp::QoS>{
282300 std::pair<std::string, rclcpp::QoS>{topic_name_, qos_playback_override},
283301 };
284- play_options_.topic_qos_profile_overrides = topic_qos_profile_overrides;
302+ // If any messages were going to come in, it should happen in under 1s even in slow scenarios
303+ const auto timeout = 3s;
304+
305+ initialize ({});
285306
286307 sub_->add_subscription <test_msgs::msg::BasicTypes>(
287308 topic_name_, num_msgs_to_wait_for_, qos_request);
288- auto await_received_messages = sub_->spin_subscriptions ();
289-
290- Rosbag2Transport rosbag2_transport{reader_, writer_, info_};
291- rosbag2_transport.play (storage_options_, play_options_);
309+ play_options_.topic_qos_profile_overrides = topic_qos_profile_overrides;
292310
293- using namespace std ::chrono_literals;
294- const auto future_result = await_received_messages.wait_for (3s);
295- EXPECT_EQ (future_result, std::future_status::timeout);
311+ play_and_wait (timeout, true /* expect timeout */ );
296312
297- rosbag2_transport.shutdown ();
298313 const auto received_messages =
299314 sub_->get_received_messages <test_msgs::msg::BasicTypes>(topic_name_);
300315 EXPECT_EQ (received_messages.size (), 0u );
301316}
317+
318+ TEST_F (RosBag2PlayQosOverrideTestFixture, playback_uses_recorded_profiles)
319+ {
320+ // In this test, we subscribe requesting DURABILITY_TRANSIENT_LOCAL.
321+ // The bag metadata has this recorded for the original Publisher,
322+ // so playback's offer should be compatible (whereas the default offer would not be)
323+ const size_t arbitrary_finite_history{5 };
324+ const auto transient_local_profile = Rosbag2QoS{
325+ rclcpp::QoS{arbitrary_finite_history}.transient_local ()};
326+ // This should normally take less than 1s - just making it shorter than 60s default
327+ const auto timeout = 5s;
328+
329+ initialize ({transient_local_profile});
330+
331+ sub_->add_subscription <test_msgs::msg::BasicTypes>(
332+ topic_name_, num_msgs_to_wait_for_, transient_local_profile);
333+
334+ play_and_wait (timeout);
335+ }
0 commit comments