8#define CARLA_ROS2_MIDDLEWARE_TESTING
9#define CARLA_ROS2_MIDDLEWARE_FASTDDS
37TEST(cdr_topic_info, type_names_are_non_empty) {
55TEST(cdr_topic_info, max_sizes_are_positive) {
72TEST(cdr_serialization, time_round_trip) {
75 original.nanosec = 123456789u;
78 ASSERT_FALSE(buf.empty());
82 EXPECT_EQ(recovered.sec, 42);
83 EXPECT_EQ(recovered.nanosec, 123456789u);
97TEST(cdr_serialization, time_golden_xcdrv1_bytes) {
100 original.nanosec = 123456789u;
104 const std::vector<uint8_t> expected{
105 0x00u, 0x01u, 0x00u, 0x00u,
106 0x2Au, 0x00u, 0x00u, 0x00u,
107 0x15u, 0xCDu, 0x5Bu, 0x07u};
110 ASSERT_EQ(buf.size(), expected.size());
111 EXPECT_EQ(buf, expected);
114 EXPECT_EQ(buf[0], 0x00u);
115 EXPECT_EQ(buf[1], 0x01u);
118TEST(cdr_serialization, header_round_trip) {
121 original.stamp.nanosec = 500000000u;
122 original.frame_id =
"base_link";
125 ASSERT_FALSE(buf.empty());
129 EXPECT_EQ(recovered.stamp.sec, 10);
130 EXPECT_EQ(recovered.stamp.nanosec, 500000000u);
131 EXPECT_EQ(recovered.frame_id,
"base_link");
134TEST(cdr_serialization, vector3_round_trip) {
141 ASSERT_FALSE(buf.empty());
145 EXPECT_DOUBLE_EQ(recovered.x, 1.5);
146 EXPECT_DOUBLE_EQ(recovered.y, -2.75);
147 EXPECT_DOUBLE_EQ(recovered.z, 3.0);
150TEST(cdr_serialization, imu_round_trip) {
153 original.header.frame_id =
"imu_link";
154 original.orientation.x = 0.1;
155 original.orientation.y = 0.2;
156 original.orientation.z = 0.3;
157 original.orientation.w = 0.9;
158 original.angular_velocity.x = 0.01;
159 original.linear_acceleration.z = 9.81;
160 original.orientation_covariance[0] = 1.0;
161 original.orientation_covariance[4] = 1.0;
162 original.orientation_covariance[8] = 1.0;
165 ASSERT_FALSE(buf.empty());
169 EXPECT_EQ(recovered.header.stamp.sec, 5);
170 EXPECT_EQ(recovered.header.frame_id,
"imu_link");
171 EXPECT_DOUBLE_EQ(recovered.orientation.x, 0.1);
172 EXPECT_DOUBLE_EQ(recovered.orientation.w, 0.9);
173 EXPECT_DOUBLE_EQ(recovered.linear_acceleration.z, 9.81);
174 EXPECT_DOUBLE_EQ(recovered.orientation_covariance[0], 1.0);
175 EXPECT_DOUBLE_EQ(recovered.orientation_covariance[4], 1.0);
178TEST(cdr_serialization, image_round_trip) {
181 original.height = 2u;
183 original.encoding =
"rgb8";
184 original.is_bigendian = 0u;
186 original.data = {1u, 2u, 3u, 4u, 5u, 6u};
189 ASSERT_FALSE(buf.empty());
193 EXPECT_EQ(recovered.header.frame_id,
"camera");
194 EXPECT_EQ(recovered.height, 2u);
195 EXPECT_EQ(recovered.width, 3u);
196 EXPECT_EQ(recovered.encoding,
"rgb8");
197 ASSERT_EQ(recovered.data.size(), 6u);
198 EXPECT_EQ(recovered.data[0], 1u);
199 EXPECT_EQ(recovered.data[5], 6u);
202TEST(cdr_serialization, pointcloud2_round_trip) {
205 original.height = 1u;
213 original.fields.push_back(pf);
215 original.is_bigendian =
false;
216 original.point_step = 4u;
217 original.row_step = 8u;
218 original.data = {0u, 0u, 128u, 63u,
220 original.is_dense =
true;
223 ASSERT_FALSE(buf.empty());
227 EXPECT_EQ(recovered.header.frame_id,
"velodyne");
228 EXPECT_EQ(recovered.height, 1u);
229 EXPECT_EQ(recovered.width, 2u);
230 ASSERT_EQ(recovered.fields.size(), 1u);
231 EXPECT_EQ(recovered.fields[0].name,
"x");
233 EXPECT_EQ(recovered.is_dense,
true);
234 ASSERT_EQ(recovered.data.size(), 8u);
237TEST(cdr_serialization, tfmessage_round_trip) {
242 ts.header.frame_id =
"world";
243 ts.child_frame_id =
"robot";
244 ts.transform.translation.x = 1.0;
245 ts.transform.translation.y = 2.0;
246 ts.transform.translation.z = 0.5;
247 ts.transform.rotation.w = 1.0;
248 original.transforms.push_back(ts);
251 ASSERT_FALSE(buf.empty());
255 ASSERT_EQ(recovered.transforms.size(), 1u);
256 EXPECT_EQ(recovered.transforms[0].header.stamp.sec, 1);
257 EXPECT_EQ(recovered.transforms[0].header.frame_id,
"world");
258 EXPECT_EQ(recovered.transforms[0].child_frame_id,
"robot");
259 EXPECT_DOUBLE_EQ(recovered.transforms[0].transform.translation.x, 1.0);
260 EXPECT_DOUBLE_EQ(recovered.transforms[0].transform.translation.y, 2.0);
261 EXPECT_DOUBLE_EQ(recovered.transforms[0].transform.rotation.w, 1.0);
264TEST(cdr_serialization, navsat_fix_round_trip) {
269 original.latitude = 48.8566;
270 original.longitude = 2.3522;
271 original.altitude = 35.0;
272 original.position_covariance[0] = 0.01;
273 original.position_covariance_type =
277 ASSERT_FALSE(buf.empty());
281 EXPECT_EQ(recovered.header.frame_id,
"gps");
283 recovered.status.status,
285 EXPECT_DOUBLE_EQ(recovered.latitude, 48.8566);
286 EXPECT_DOUBLE_EQ(recovered.longitude, 2.3522);
287 EXPECT_DOUBLE_EQ(recovered.altitude, 35.0);
288 EXPECT_DOUBLE_EQ(recovered.position_covariance[0], 0.01);
290 recovered.position_covariance_type,
294TEST(cdr_serialization, carla_ego_vehicle_control_round_trip) {
297 original.steer = -0.5f;
298 original.brake = 0.0f;
299 original.hand_brake =
false;
300 original.reverse =
true;
302 original.manual_gear_shift =
false;
305 ASSERT_FALSE(buf.empty());
309 EXPECT_FLOAT_EQ(recovered.throttle, 0.75f);
310 EXPECT_FLOAT_EQ(recovered.steer, -0.5f);
311 EXPECT_FLOAT_EQ(recovered.brake, 0.0f);
312 EXPECT_EQ(recovered.hand_brake,
false);
313 EXPECT_EQ(recovered.reverse,
true);
314 EXPECT_EQ(recovered.gear, 2);
315 EXPECT_EQ(recovered.manual_gear_shift,
false);
318TEST(cdr_serialization, carla_line_invasion_round_trip) {
321 original.crossed_lane_markings = {1, 4, 7};
324 ASSERT_FALSE(buf.empty());
328 EXPECT_EQ(recovered.header.frame_id,
"vehicle");
329 ASSERT_EQ(recovered.crossed_lane_markings.size(), 3u);
330 EXPECT_EQ(recovered.crossed_lane_markings[0], 1);
331 EXPECT_EQ(recovered.crossed_lane_markings[1], 4);
332 EXPECT_EQ(recovered.crossed_lane_markings[2], 7);
335TEST(cdr_serialization, clock_round_trip) {
338 original.clock.nanosec = 1u;
341 ASSERT_FALSE(buf.empty());
345 EXPECT_EQ(recovered.clock.sec, 999);
346 EXPECT_EQ(recovered.clock.nanosec, 1u);
349TEST(cdr_serialization, empty_tfmessage_round_trip) {
353 ASSERT_FALSE(buf.empty());
357 EXPECT_TRUE(recovered.transforms.empty());
360TEST(cdr_serialization, pointcloud2_multi_field_round_trip) {
366 original.height = 1u;
387 original.fields.push_back(fx);
388 original.fields.push_back(fy);
389 original.fields.push_back(fz);
390 original.is_bigendian =
false;
391 original.point_step = 12u;
392 original.row_step = 48u;
393 original.data.resize(48u, 0u);
394 original.is_dense =
true;
397 ASSERT_FALSE(buf.empty());
401 ASSERT_EQ(recovered.fields.size(), 3u);
402 EXPECT_EQ(recovered.fields[0].name,
"x");
403 EXPECT_EQ(recovered.fields[0].offset, 0u);
404 EXPECT_EQ(recovered.fields[1].name,
"y");
405 EXPECT_EQ(recovered.fields[1].offset, 4u);
406 EXPECT_EQ(recovered.fields[2].name,
"z");
407 EXPECT_EQ(recovered.fields[2].offset, 8u);
408 EXPECT_EQ(recovered.point_step, 12u);
409 EXPECT_EQ(recovered.row_step, 48u);
410 ASSERT_EQ(recovered.data.size(), 48u);
413TEST(cdr_serialization, tfmessage_multi_transform_round_trip) {
420 a.header.frame_id =
"world";
421 a.child_frame_id =
"robot_a";
422 a.transform.translation.x = 1.0;
423 a.transform.rotation.w = 1.0;
427 b.header.frame_id =
"world";
428 b.child_frame_id =
"robot_b";
429 b.transform.translation.y = 2.0;
430 b.transform.rotation.w = 1.0;
432 original.transforms.push_back(a);
433 original.transforms.push_back(b);
436 ASSERT_FALSE(buf.empty());
440 ASSERT_EQ(recovered.transforms.size(), 2u);
441 EXPECT_EQ(recovered.transforms[0].child_frame_id,
"robot_a");
442 EXPECT_DOUBLE_EQ(recovered.transforms[0].transform.translation.x, 1.0);
443 EXPECT_EQ(recovered.transforms[1].child_frame_id,
"robot_b");
444 EXPECT_DOUBLE_EQ(recovered.transforms[1].transform.translation.y, 2.0);
447TEST(cdr_serialization, deserialize_truncated_returns_false) {
452 original.frame_id =
"needs_more_bytes";
455 ASSERT_GT(buf.size(), 8u);
459 buf.data(), buf.size() / 2u, recovered));
462TEST(cdr_serialization, deserialize_corrupt_encapsulation_returns_false) {
465 const uint8_t bogus[2] = {0xFFu, 0xFFu};
470TEST(cdr_serialization, deserialize_pointcloud2_hostile_length_returns_false) {
474 std::vector<uint8_t> buf;
476 buf.push_back(0x00u);
477 buf.push_back(0x01u);
478 buf.push_back(0x00u);
479 buf.push_back(0x00u);
481 for (
int i = 0; i < 8; ++i) buf.push_back(0x00u);
484 buf.push_back(0x01u);
485 buf.push_back(0x00u);
486 buf.push_back(0x00u);
487 buf.push_back(0x00u);
488 buf.push_back(0x00u);
489 buf.push_back(0x00u);
490 buf.push_back(0x00u);
491 buf.push_back(0x00u);
493 for (
int i = 0; i < 8; ++i) buf.push_back(0x00u);
495 buf.push_back(0xFFu);
496 buf.push_back(0xFFu);
497 buf.push_back(0xFFu);
498 buf.push_back(0xFFu);
502 buf.data(), buf.size(), recovered));
505TEST(cdr_serialization, deserialize_tfmessage_hostile_length_returns_false) {
508 std::vector<uint8_t> buf;
509 buf.push_back(0x00u);
510 buf.push_back(0x01u);
511 buf.push_back(0x00u);
512 buf.push_back(0x00u);
513 buf.push_back(0xFFu);
514 buf.push_back(0xFFu);
515 buf.push_back(0xFFu);
516 buf.push_back(0xFFu);
520 buf.data(), buf.size(), recovered));
523TEST(cdr_serialization, cdr_serialized_size_matches_serialize_to_cdr) {
530 msg.frame_id =
"map";
538 msg.encoding =
"rgb8";
539 msg.data.assign(6u, 0xAAu);
547 msg.data.assign(48u, 0xBBu);
554 msg.transforms[0].header.frame_id =
"world";
555 msg.transforms[1].header.frame_id =
"base_link";
561TEST(cdr_serialization, cdr_serialized_size_image_exceeds_static_max) {
569 msg.encoding =
"rgb8";
570 msg.step = 800u * 3u;
571 const size_t data_bytes = 800u * 600u * 3u;
572 msg.data.assign(data_bytes, 0x7Fu);
576 static_cast<uint32_t
>(
580 ASSERT_FALSE(bytes.empty());
581 EXPECT_EQ(computed,
static_cast<uint32_t
>(bytes.size()));
585 bytes.data(), bytes.size(), recovered));
586 EXPECT_EQ(recovered.data.size(), data_bytes);
589TEST(cdr_serialization, cdr_serialized_size_pointcloud2_exceeds_static_max) {
596 msg.row_step = 22000u * 16u;
597 const size_t data_bytes = 22000u * 16u;
598 msg.data.assign(data_bytes, 0x3Cu);
604 msg.fields.push_back(pf);
608 static_cast<uint32_t
>(
612 ASSERT_FALSE(bytes.empty());
613 EXPECT_EQ(computed,
static_cast<uint32_t
>(bytes.size()));
617 bytes.data(), bytes.size(), recovered));
618 EXPECT_EQ(recovered.data.size(), data_bytes);
633struct TestPubTraits {
634 using msg_type = TestMsg;
637struct TestSubTraits {
638 using msg_type = TestMsg;
656TEST(middleware_to_string, fastdds_returns_correct_string) {
660TEST(middleware_to_string, cyclonedds_returns_correct_string) {
664TEST(middleware_to_string, zenoh_returns_correct_string) {
672TEST(middleware_from_string, fastdds_lowercase_valid) {
674 EXPECT_TRUE(result.valid);
675 EXPECT_EQ(result.middleware, Middleware::FastDDS);
678TEST(middleware_from_string, unknown_string_invalid) {
680 EXPECT_FALSE(result.valid);
683TEST(middleware_from_string, cyclonedds_lowercase_valid) {
685 EXPECT_TRUE(result.valid);
686 EXPECT_EQ(result.middleware, Middleware::CycloneDDS);
689TEST(middleware_from_string, zenoh_lowercase_valid) {
691 EXPECT_TRUE(result.valid);
692 EXPECT_EQ(result.middleware, Middleware::Zenoh);
695TEST(middleware_from_string, empty_string_invalid) {
697 EXPECT_FALSE(result.valid);
700TEST(middleware_from_string, uppercase_rejected) {
702 EXPECT_FALSE(result.valid);
705TEST(middleware_from_string, partial_match_rejected) {
707 EXPECT_FALSE(result.valid);
714TEST(middleware_available, fastdds_available) {
719TEST(middleware_available, available_string_contains_fastdds) {
721 EXPECT_NE(available.find(
"FastDDS"), std::string::npos);
724TEST(middleware_available, cyclonedds_not_available_without_macro) {
729TEST(middleware_available, zenoh_not_available_without_macro) {
738TEST(middleware_type_name, bare_name) {
742TEST(middleware_type_name, fully_qualified) {
745 "sensor_msgs::msg::dds_::Image_");
748TEST(middleware_type_name, single_namespace) {
752TEST(middleware_type_name, empty_string) {
772 EXPECT_TRUE(resolution.success);
773 EXPECT_EQ(resolution.middleware, Middleware::FastDDS);
778 EXPECT_NE(available.find(
"FastDDS"), std::string::npos);
789 EXPECT_FALSE(resolution.success);
790 EXPECT_EQ(resolution.middleware, Middleware::CycloneDDS);
795 ::testing::internal::CaptureStderr();
796 auto pub = MiddlewareFactory::CreatePublisher<TestPubTraits>();
797 ::testing::internal::GetCapturedStderr();
798 EXPECT_EQ(pub,
nullptr);
803 ::testing::internal::CaptureStderr();
804 auto sub = MiddlewareFactory::CreateSubscriber<TestSubTraits>();
805 ::testing::internal::GetCapturedStderr();
806 EXPECT_EQ(sub,
nullptr);
817 EXPECT_FALSE(resolution.success);
818 EXPECT_EQ(resolution.middleware, Middleware::Zenoh);
823 ::testing::internal::CaptureStderr();
824 auto pub = MiddlewareFactory::CreatePublisher<TestPubTraits>();
825 ::testing::internal::GetCapturedStderr();
826 EXPECT_EQ(pub,
nullptr);
831 ::testing::internal::CaptureStderr();
832 auto sub = MiddlewareFactory::CreateSubscriber<TestSubTraits>();
833 ::testing::internal::GetCapturedStderr();
834 EXPECT_EQ(sub,
nullptr);
843TEST(generic_cdr_pubsubtype, type_name_matches_cdr_topic_info) {
863TEST(generic_cdr_pubsubtype, m_typesize_is_positive) {
874TEST(generic_cdr_pubsubtype, serialize_deserialize_fixed_size_via_payload) {
877 using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t;
882 original.clock.nanosec = 500u;
884 SerializedPayload_t payload(1024u);
885 ASSERT_TRUE(pubsub_type.
serialize(
static_cast<void*
>(&original), &payload));
886 EXPECT_GT(payload.length, 0u);
889 ASSERT_TRUE(pubsub_type.
deserialize(&payload,
static_cast<void*
>(&recovered)));
890 EXPECT_EQ(recovered.clock.sec, 100);
891 EXPECT_EQ(recovered.clock.nanosec, 500u);
894TEST(generic_cdr_pubsubtype, serialize_deserialize_string_type_via_payload) {
896 using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t;
901 original.frame_id =
"test_frame";
903 SerializedPayload_t payload(4096u);
904 ASSERT_TRUE(pubsub_type.
serialize(
static_cast<void*
>(&original), &payload));
905 EXPECT_GT(payload.length, 0u);
908 ASSERT_TRUE(pubsub_type.
deserialize(&payload,
static_cast<void*
>(&recovered)));
909 EXPECT_EQ(recovered.stamp.sec, 42);
910 EXPECT_EQ(recovered.frame_id,
"test_frame");
913TEST(generic_cdr_pubsubtype, create_and_delete_data) {
917 ASSERT_NE(data,
nullptr);
928TEST(generic_cdr_pubsubtype, getkey_returns_false) {
931 EXPECT_FALSE(pubsub_type.m_isGetKeyDefined);
932 EXPECT_FALSE(pubsub_type.
getKey(
nullptr,
nullptr,
false));
944TEST(generic_cdr_pubsubtype_large_payload, image_large_payload_serialize_succeeds) {
945 using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t;
949 constexpr uint32_t width = 640u;
950 constexpr uint32_t height = 480u;
951 constexpr uint32_t channels = 4u;
952 const uint32_t pixel_bytes = width * height * channels;
956 original.header.frame_id =
"camera";
957 original.height = height;
958 original.width = width;
959 original.encoding =
"bgra8";
960 original.is_bigendian = 0u;
961 original.step = width * channels;
962 original.data.assign(pixel_bytes, 0xABu);
969 static_cast<void*
>(&original));
970 uint32_t reported_size = size_fn();
971 EXPECT_GT(reported_size, pixel_bytes)
972 <<
"getSerializedSizeProvider must exceed the raw pixel count";
974 SerializedPayload_t payload(reported_size);
975 ASSERT_TRUE(pubsub_type.
serialize(
static_cast<void*
>(&original), &payload));
976 EXPECT_GT(payload.length, pixel_bytes);
979 ASSERT_TRUE(pubsub_type.
deserialize(&payload,
static_cast<void*
>(&recovered)));
980 EXPECT_EQ(recovered.header.frame_id,
"camera");
981 EXPECT_EQ(recovered.height, height);
982 EXPECT_EQ(recovered.width, width);
983 EXPECT_EQ(recovered.encoding,
"bgra8");
984 ASSERT_EQ(recovered.data.size(), pixel_bytes);
985 EXPECT_EQ(recovered.data[0], 0xABu);
986 EXPECT_EQ(recovered.data[pixel_bytes - 1u], 0xABu);
989TEST(generic_cdr_pubsubtype_large_payload, pointcloud2_large_payload_serialize_succeeds) {
990 using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t;
994 constexpr uint32_t num_points = 10000u;
995 constexpr uint32_t point_step = 16u;
996 const uint32_t data_bytes = num_points * point_step;
1000 original.height = 1u;
1001 original.width = num_points;
1002 original.point_step = point_step;
1003 original.row_step = num_points * point_step;
1004 original.is_dense =
true;
1005 original.data.assign(data_bytes, 0x55u);
1012 original.fields.push_back(field);
1017 static_cast<void*
>(&original));
1018 uint32_t reported_size = size_fn();
1019 EXPECT_GT(reported_size, data_bytes)
1020 <<
"getSerializedSizeProvider must exceed the raw point data size";
1022 SerializedPayload_t payload(reported_size);
1023 ASSERT_TRUE(pubsub_type.
serialize(
static_cast<void*
>(&original), &payload));
1024 EXPECT_GT(payload.length, data_bytes);
1027 ASSERT_TRUE(pubsub_type.
deserialize(&payload,
static_cast<void*
>(&recovered)));
1028 EXPECT_EQ(recovered.header.frame_id,
"velodyne");
1029 EXPECT_EQ(recovered.height, 1u);
1030 EXPECT_EQ(recovered.width, num_points);
1031 EXPECT_EQ(recovered.is_dense,
true);
1032 ASSERT_EQ(recovered.data.size(), data_bytes);
1033 EXPECT_EQ(recovered.data[0], 0x55u);
1036TEST(generic_cdr_pubsubtype_large_payload, size_provider_returns_actual_size_not_max) {
1040 small_img.
data.assign(100u, 0u);
1043 large_img.
data.assign(100000u, 0u);
1048 static_cast<void*
>(&small_img));
1050 static_cast<void*
>(&large_img));
1052 uint32_t small_size = small_fn();
1053 uint32_t large_size = large_fn();
1055 EXPECT_LT(small_size, large_size)
1056 <<
"Size must grow with the data vector length";
1057 EXPECT_GT(large_size, 100000u)
1058 <<
"Size must at least cover the raw data bytes";
1080 bool Init(
const std::string &topic_name)
override {
1109 const std::string &topic_name,
1111 bool *new_message_flag)
override {
1127TEST(publisher_impl, get_message_returns_pointer) {
1130 ASSERT_NE(msg,
nullptr);
1135TEST(publisher_impl, init_delegates_to_middleware) {
1138 pub.SetMiddlewareForTesting(
1139 std::unique_ptr<IPublisherMiddleware>(mock));
1140 EXPECT_TRUE(pub.
Init(
"rt/test_topic"));
1141 EXPECT_TRUE(mock->init_called);
1142 EXPECT_EQ(mock->last_topic_name,
"rt/test_topic");
1145TEST(publisher_impl, publish_delegates_to_middleware) {
1148 pub.SetMiddlewareForTesting(
1149 std::unique_ptr<IPublisherMiddleware>(mock));
1150 pub.
Init(
"rt/test_topic");
1152 EXPECT_TRUE(mock->publish_called);
1153 EXPECT_EQ(mock->last_published_data, pub.
GetMessage());
1156TEST(publisher_impl, publish_before_init_fails) {
1158 ::testing::internal::CaptureStderr();
1160 ::testing::internal::GetCapturedStderr();
1163TEST(publisher_impl, is_alive_delegates) {
1167 pub.SetMiddlewareForTesting(
1168 std::unique_ptr<IPublisherMiddleware>(mock));
1169 pub.
Init(
"rt/test_topic");
1171 mock->alive =
false;
1175TEST(publisher_impl, topic_name_delegates) {
1178 pub.SetMiddlewareForTesting(
1179 std::unique_ptr<IPublisherMiddleware>(mock));
1180 pub.
Init(
"rt/camera/image");
1184TEST(publisher_impl, data_flows_through_publish) {
1187 pub.SetMiddlewareForTesting(
1188 std::unique_ptr<IPublisherMiddleware>(mock));
1189 pub.
Init(
"rt/test_topic");
1194 ASSERT_NE(mock->last_published_data,
nullptr);
1195 auto *published =
static_cast<TestMsg *
>(mock->last_published_data);
1196 EXPECT_EQ(published->value, 42);
1203TEST(subscriber_impl, has_new_message_initially_false) {
1208TEST(subscriber_impl, init_delegates_to_middleware) {
1211 sub.SetMiddlewareForTesting(
1212 std::unique_ptr<ISubscriberMiddleware>(mock));
1213 EXPECT_TRUE(sub.
Init(
"rt/test_topic"));
1214 EXPECT_TRUE(mock->init_called);
1215 EXPECT_EQ(mock->last_topic_name,
"rt/test_topic");
1216 EXPECT_NE(mock->stored_message_ptr,
nullptr);
1217 EXPECT_NE(mock->stored_flag_ptr,
nullptr);
1220TEST(subscriber_impl, get_message_clears_flag) {
1223 sub.SetMiddlewareForTesting(
1224 std::unique_ptr<ISubscriberMiddleware>(mock));
1225 sub.
Init(
"rt/test_topic");
1229 sub.SimulateMessageReceiptForTesting(msg);
1233 EXPECT_EQ(retrieved.value, 77);
1237TEST(subscriber_impl, is_alive_delegates) {
1241 sub.SetMiddlewareForTesting(
1242 std::unique_ptr<ISubscriberMiddleware>(mock));
1243 sub.
Init(
"rt/test_topic");
1245 mock->alive =
false;
1249TEST(subscriber_impl, topic_name_delegates) {
1252 sub.SetMiddlewareForTesting(
1253 std::unique_ptr<ISubscriberMiddleware>(mock));
1254 sub.
Init(
"rt/lidar/points");
1258TEST(subscriber_impl, simulate_message_receipt) {
1261 sub.SetMiddlewareForTesting(
1262 std::unique_ptr<ISubscriberMiddleware>(mock));
1263 sub.
Init(
"rt/test_topic");
1268 sub.SimulateMessageReceiptForTesting(msg);
1273TEST(subscriber_impl, init_failure_propagated) {
1276 mock->init_return_value =
false;
1277 sub.SetMiddlewareForTesting(
1278 std::unique_ptr<ISubscriberMiddleware>(mock));
1279 EXPECT_FALSE(sub.
Init(
"rt/test_topic"));
1309TEST(middleware_config_valid, zero_is_valid) {
1313TEST(middleware_config_valid, one_is_valid) {
1317TEST(middleware_config_valid, max_is_valid) {
1321TEST(middleware_config_valid, above_max_is_invalid) {
1325TEST(middleware_config_valid, unset_sentinel_is_invalid) {
1335TEST(domain_id_parsing, nullptr_is_rejected) {
1340TEST(domain_id_parsing, empty_string_is_rejected) {
1345TEST(domain_id_parsing, non_numeric_is_rejected) {
1350TEST(domain_id_parsing, trailing_garbage_is_rejected) {
1355TEST(domain_id_parsing, above_max_is_rejected) {
1360TEST(domain_id_parsing, negative_is_rejected) {
1365TEST(domain_id_parsing, zero_is_accepted) {
1371TEST(domain_id_parsing, mid_value_is_accepted) {
1377TEST(domain_id_parsing, max_value_is_accepted) {
1383TEST(domain_id_resolution, command_line_wins_over_environment) {
1385 EXPECT_EQ(resolved.id, 42);
1386 EXPECT_EQ(resolved.source, DomainIdSource::CommandLine);
1389TEST(domain_id_resolution, command_line_zero_wins_over_environment) {
1391 EXPECT_EQ(resolved.id, 0);
1392 EXPECT_EQ(resolved.source, DomainIdSource::CommandLine);
1395TEST(domain_id_resolution, environment_used_when_command_line_unset) {
1397 EXPECT_EQ(resolved.id, 7);
1398 EXPECT_EQ(resolved.source, DomainIdSource::Environment);
1401TEST(domain_id_resolution, default_when_both_absent) {
1404 EXPECT_EQ(resolved.source, DomainIdSource::Default);
1407TEST(domain_id_resolution, default_when_environment_empty) {
1410 EXPECT_EQ(resolved.source, DomainIdSource::Default);
1413TEST(domain_id_resolution, invalid_environment_falls_back_to_default) {
1416 EXPECT_EQ(resolved.source, DomainIdSource::Default);
1419TEST(domain_id_resolution, invalid_command_line_falls_through_to_environment) {
1421 EXPECT_EQ(resolved.id, 7);
1422 EXPECT_EQ(resolved.source, DomainIdSource::Environment);
1432TEST(zenoh_wire_format, topic_keyexpr_starts_with_domain_segment) {
1433 const std::string ke = zenoh_make_topic_keyexpr(
1434 "42",
"vehicle/imu",
"sensor_msgs::msg::dds_::Imu_",
"RIHS01_abc");
1435 EXPECT_EQ(ke.compare(0, 3,
"42/"), 0) <<
"keyexpr: " << ke;
1436 EXPECT_NE(ke.find(
"/vehicle/imu/"), std::string::npos);
1439TEST(zenoh_wire_format, node_liveliness_keyexpr_starts_with_domain_segment) {
1440 const std::string ke = zenoh_make_node_liveliness_keyexpr(
"42",
"deadbeef");
1441 EXPECT_EQ(ke.compare(0, 12,
"@ros2_lv/42/"), 0) <<
"keyexpr: " << ke;
1444TEST(zenoh_wire_format, topic_liveliness_keyexpr_has_domain_and_mangled_topic) {
1445 const std::string ke = zenoh_make_topic_liveliness_keyexpr(
1446 "42",
"deadbeef", 7u, kZenohEntityKindPub,
"vehicle/imu",
1447 "sensor_msgs::msg::dds_::Imu_",
"RIHS01_abc", kZenohDefaultQos);
1448 EXPECT_EQ(ke.compare(0, 12,
"@ros2_lv/42/"), 0) <<
"keyexpr: " << ke;
1449 EXPECT_NE(ke.find(
"%vehicle%imu"), std::string::npos);
1516 EXPECT_EQ(zenoh_ros_domain_id(),
"42");
1521 EXPECT_EQ(zenoh_ros_domain_id(),
"13");
1527 EXPECT_EQ(zenoh_ros_domain_id(),
"99");
1532 EXPECT_EQ(zenoh_ros_domain_id(),
"0");
1537 EXPECT_EQ(zenoh_ros_domain_id(),
"0");
1554 const FakeFrag* nextfrag{
nullptr};
1557 const uint8_t* payload{
nullptr};
1560const uint8_t* FakeFragPayload(
const FakeFrag* f) {
return f->payload; }
1562std::vector<uint8_t> MakePatternBytes(
size_t size) {
1563 std::vector<uint8_t> bytes(size);
1564 for (
size_t i = 0u; i < size; ++i) {
1565 bytes[i] =
static_cast<uint8_t
>((i * 31u + 7u) & 0xFFu);
1572std::vector<FakeFrag> MakeFragChain(
1573 const std::vector<uint8_t>& bytes,
1576 std::vector<FakeFrag> frags;
1577 for (
size_t off = 0u; off < bytes.size(); off += frag_size) {
1578 const size_t end = std::min(bytes.size(), off + frag_size);
1580 f.min =
static_cast<uint32_t
>(off);
1581 f.maxp1 =
static_cast<uint32_t
>(end);
1582 f.payload = bytes.data() + off;
1585 for (
size_t i = 1u; i < frags.size(); ++i) {
1586 frags[i - 1u].nextfrag = &frags[i];
1593TEST(cyclonedds_fragment_gather, single_fragment_copies_all) {
1594 const auto bytes = MakePatternBytes(256u);
1595 const auto frags = MakeFragChain(bytes, bytes.size());
1596 ASSERT_EQ(frags.size(), 1u);
1598 std::vector<uint8_t> dest(bytes.size(), 0u);
1599 const size_t gathered = carla_cdr_gather_fragments(
1600 dest.data(), dest.size(), frags.data(), FakeFragPayload);
1602 EXPECT_EQ(gathered, bytes.size());
1603 EXPECT_EQ(dest, bytes);
1606TEST(cyclonedds_fragment_gather, multi_fragment_reassembles_in_order) {
1607 const auto bytes = MakePatternBytes(5000u);
1608 const auto frags = MakeFragChain(bytes, 1400u);
1609 ASSERT_GT(frags.size(), 3u);
1611 std::vector<uint8_t> dest(bytes.size(), 0u);
1612 const size_t gathered = carla_cdr_gather_fragments(
1613 dest.data(), dest.size(), frags.data(), FakeFragPayload);
1615 EXPECT_EQ(gathered, bytes.size());
1616 EXPECT_EQ(dest, bytes);
1619TEST(cyclonedds_fragment_gather, overlapping_fragments_gather_once) {
1620 const auto bytes = MakePatternBytes(1200u);
1623 FakeFrag f2{
nullptr, 800u, 1200u, bytes.data() + 800u};
1624 FakeFrag f1{&f2, 400u, 1000u, bytes.data() + 400u};
1625 FakeFrag f0{&f1, 0u, 600u, bytes.data()};
1627 std::vector<uint8_t> dest(bytes.size(), 0u);
1628 const size_t gathered = carla_cdr_gather_fragments(
1629 dest.data(), dest.size(), &f0, FakeFragPayload);
1631 EXPECT_EQ(gathered, bytes.size());
1632 EXPECT_EQ(dest, bytes);
1635TEST(cyclonedds_fragment_gather, truncated_chain_reports_short_count) {
1636 const auto bytes = MakePatternBytes(1200u);
1639 FakeFrag f1{
nullptr, 900u, 1200u, bytes.data() + 900u};
1640 FakeFrag f0{&f1, 0u, 500u, bytes.data()};
1642 std::vector<uint8_t> dest(bytes.size(), 0u);
1643 const size_t gathered = carla_cdr_gather_fragments(
1644 dest.data(), dest.size(), &f0, FakeFragPayload);
1646 EXPECT_EQ(gathered, 500u);
1649TEST(cyclonedds_fragment_gather, oversized_fragment_is_clamped_to_size) {
1650 const auto bytes = MakePatternBytes(128u);
1652 FakeFrag f0{
nullptr, 0u, 128u, bytes.data()};
1654 std::vector<uint8_t> dest(100u, 0u);
1655 const size_t gathered = carla_cdr_gather_fragments(
1656 dest.data(), dest.size(), &f0, FakeFragPayload);
1658 EXPECT_EQ(gathered, 100u);
1659 EXPECT_TRUE(std::equal(dest.begin(), dest.end(), bytes.begin()));
1662TEST(cyclonedds_fragment_gather, large_pointcloud_round_trips_through_fragments) {
1666 constexpr uint32_t num_points = 5000u;
1667 constexpr uint32_t point_step = 16u;
1668 const uint32_t data_bytes = num_points * point_step;
1672 original.height = 1u;
1673 original.width = num_points;
1674 original.point_step = point_step;
1675 original.row_step = num_points * point_step;
1676 original.is_dense =
true;
1677 original.data.assign(data_bytes, 0u);
1678 for (uint32_t i = 0u; i < data_bytes; ++i) {
1679 original.data[i] =
static_cast<uint8_t
>((i * 13u + 5u) & 0xFFu);
1683 ASSERT_GT(wire.size(), 65536u) <<
"sample must exceed one 64KB fragment unit";
1685 const auto frags = MakeFragChain(wire, 1400u);
1686 ASSERT_GT(frags.size(), 40u);
1688 std::vector<uint8_t> dest(wire.size(), 0u);
1689 const size_t gathered = carla_cdr_gather_fragments(
1690 dest.data(), dest.size(), frags.data(), FakeFragPayload);
1691 ASSERT_EQ(gathered, wire.size());
1695 EXPECT_EQ(recovered.header.frame_id,
"velodyne");
1696 EXPECT_EQ(recovered.width, num_points);
1697 ASSERT_EQ(recovered.data.size(), data_bytes);
1698 EXPECT_EQ(recovered.data, original.data);
double min(double v1, double v2)
void * last_published_data
std::string last_topic_name
bool publish_return_value
bool Publish(void *message_data) override
Serialize and write a message to the network.
std::string GetTopicName() const override
bool Init(const std::string &topic_name) override
Initialize the underlying middleware entities.
bool IsAlive() const override
void * stored_message_ptr
bool Init(const std::string &topic_name, void *message_ptr, bool *new_message_flag) override
Initialize the underlying middleware entities.
std::string GetTopicName() const override
std::string last_topic_name
bool IsAlive() const override
Generic FastDDS TopicDataType that serializes carla::ros2::msg::* POD structs directly to CDR via Cdr...
std::function< uint32_t()> getSerializedSizeProvider(void *data) override
Return a function that gives the actual CDR-serialized size for this specific message instance.
bool deserialize(SerializedPayload_t *payload, void *data) override
Deserialize a FastDDS payload buffer into a MsgType instance.
bool serialize(void *data, SerializedPayload_t *payload) override
Serialize a MsgType instance into the FastDDS payload buffer.
void deleteData(void *data) override
Delete a MsgType previously returned by createData().
void * createData() override
Allocate a new default-initialized MsgType on the heap.
bool getKey(void *, eprosima::fastrtps::rtps::InstanceHandle_t *, bool) override
CARLA topics are not keyed — always return false.
Type-erased abstract interface for a publisher middleware.
Type-erased abstract interface for a subscriber middleware.
static int GetEffectiveDomainId()
static void SetDomainId(int id)
Configure the ROS 2 domain id for all subsequent transport contexts.
static void SetMiddleware(Middleware middleware)
Select the middleware for all subsequent publisher/subscriber creation.
static std::string GetAvailableMiddlewareString()
Return a readable list of middleware implementations compiled into this binary.
static MiddlewareResolution ResolveMiddleware(Middleware requested)
Resolve the requested middleware strictly — no fallback to other middleware.
static Middleware GetMiddleware()
static bool IsMiddlewareAvailable(Middleware middleware)
Check whether a specific middleware was compiled into this binary.
bool Init(std::string topic_name)
std::string GetTopicName()
bool Init(std::string topic_name)
std::string GetTopicName()
bool IsValidDomainId(int id)
constexpr int kDefaultDomainId
Domain id used when neither the command line nor the environment provides one.
const char * MiddlewareToString(Middleware middleware)
Convert a Middleware enum value to a readable string.
std::vector< uint8_t > serialize_to_cdr(const T &msg)
Serialize a msg::X to a CDR byte buffer including the DDS encapsulation header (Classic CDR,...
constexpr int kMaxDomainId
Highest valid ROS 2 domain id (RTPS maximum).
ResolvedDomainId ResolveDomainId(int cli_value, const char *env_value)
Resolve the effective domain id from a command line value and an environment value,...
constexpr int kUnsetDomainId
Sentinel domain id meaning "no domain id was configured on the command line"; each middleware falls b...
std::string GetAvailableMiddlewareString()
Return a readable list of middleware implementations compiled into this binary.
uint32_t cdr_serialized_size(const T &msg)
Return the exact CDR-serialized size in bytes for a message instance, including the 4-byte DDS encaps...
bool TryParseDomainId(const char *text, int &out)
Parse a ROS_DOMAIN_ID-style string into a valid domain id.
bool deserialize_from_cdr(const uint8_t *data, size_t size, T &msg)
Deserialize a msg::X from a CDR byte buffer that was produced by serialize_to_cdr() or by any ROS2-co...
std::string ToROS2TypeName(const std::string &type_name)
Mangle a type name into the ROS2-compatible format.
constexpr char kRosDomainIdEnvVar[]
Name of the standard ROS 2 environment variable that selects the domain id.
MiddlewareParseResult MiddlewareFromString(const std::string &name)
Parse a middleware name string (lowercase).
Per-type metadata needed by the middleware layer.
std::vector< uint8_t > data
static const uint8_t COVARIANCE_TYPE_DIAGONAL_KNOWN
static const uint8_t STATUS_FIX
static const uint16_t SERVICE_GPS
static const uint8_t FLOAT32
std::vector< TransformStamped > transforms
TEST(cdr_topic_info, type_names_are_non_empty)
TEST_F(MiddlewareFactoryFixture, set_and_get_middleware)