@@ -19,10 +19,12 @@ class TestROS2(SyncSmokeTest):
1919 does not crash when publishing large-payload sensor data (Image, PointCloud2)
2020 over DDS.
2121
22- No ROS2 subscriber is needed: the tests exercise the full server-side
23- publish path (PublisherImpl -> FastDDSPublisherMiddleware ->
22+ No ROS2 subscriber is needed for most tests: they exercise the full
23+ server-side publish path (PublisherImpl -> FastDDSPublisherMiddleware ->
2424 GenericCdrPubSubType::serialize()) without verifying that data arrives at
25- an external ROS2 node.
25+ an external ROS2 node. The exception is
26+ test_ros2_traffic_light_decode_with_carla_msgs, which subscribes with the
27+ real ros-carla-msgs package when rclpy is importable and skips otherwise.
2628 """
2729
2830 def test_ros2_api (self ):
@@ -295,6 +297,75 @@ def test_ros2_traffic_lights_lifecycle(self):
295297 for _ in range (5 ):
296298 self .world .tick ()
297299
300+ def test_ros2_traffic_light_decode_with_carla_msgs (self ):
301+ """Traffic light topics decode with the real ros-carla-msgs package.
302+
303+ The type-hash unit tests pin the REP-2011 hashes, but only a real
304+ subscriber proves the CDR payload decodes with the canonical
305+ carla_msgs definitions, catching wire-format drift a hash test alone
306+ cannot. Subscribes to both latched traffic light topics, decodes one
307+ sample of each, and cross-checks the published ids against the
308+ client-visible traffic light actors. Skips when rclpy or carla_msgs
309+ is not importable (both come from sourcing a ROS2 environment with
310+ ros-carla-msgs built), so plain smoke runs are unaffected.
311+ """
312+ try :
313+ import rclpy
314+ from rclpy .qos import (DurabilityPolicy , HistoryPolicy ,
315+ QoSProfile , ReliabilityPolicy )
316+ from carla_msgs .msg import (CarlaTrafficLightInfoList ,
317+ CarlaTrafficLightStatus ,
318+ CarlaTrafficLightStatusList )
319+ except ImportError :
320+ self .skipTest ('rclpy and ros-carla-msgs are required '
321+ 'for the decode test' )
322+
323+ lights = self .world .get_actors ().filter ('traffic.traffic_light' )
324+ self .assertTrue (len (lights ) > 0 ,
325+ 'expected traffic lights in the default map' )
326+ expected_ids = {light .id for light in lights }
327+
328+ rclpy .init ()
329+ node = rclpy .create_node ('carla_smoke_traffic_light_decode' )
330+ latched = QoSProfile (
331+ depth = 1 ,
332+ history = HistoryPolicy .KEEP_LAST ,
333+ reliability = ReliabilityPolicy .RELIABLE ,
334+ durability = DurabilityPolicy .TRANSIENT_LOCAL )
335+ received = {}
336+ node .create_subscription (
337+ CarlaTrafficLightInfoList , '/carla/traffic_lights/info' ,
338+ lambda msg : received .__setitem__ ('info' , msg ), latched )
339+ node .create_subscription (
340+ CarlaTrafficLightStatusList , '/carla/traffic_lights/status' ,
341+ lambda msg : received .__setitem__ ('status' , msg ), latched )
342+ try :
343+ deadline = time .time () + 30.0
344+ while time .time () < deadline and len (received ) < 2 :
345+ self .world .tick ()
346+ rclpy .spin_once (node , timeout_sec = 0.1 )
347+
348+ self .assertIn ('info' , received ,
349+ 'no decodable CarlaTrafficLightInfoList arrived '
350+ 'on /carla/traffic_lights/info' )
351+ self .assertIn ('status' , received ,
352+ 'no decodable CarlaTrafficLightStatusList arrived '
353+ 'on /carla/traffic_lights/status' )
354+
355+ info = received ['info' ].traffic_lights
356+ status = received ['status' ].traffic_lights
357+ self .assertEqual ({entry .id for entry in info }, expected_ids ,
358+ 'info ids should match the traffic light actors' )
359+ self .assertEqual ({entry .id for entry in status }, expected_ids ,
360+ 'status ids should match the traffic light actors' )
361+ for entry in status :
362+ self .assertLessEqual (entry .state , CarlaTrafficLightStatus .UNKNOWN )
363+ for entry in info :
364+ self .assertGreater (entry .trigger_volume .size .x , 0.0 )
365+ finally :
366+ node .destroy_node ()
367+ rclpy .shutdown ()
368+
298369 def test_ros2_multi_sensor_publish (self ):
299370 """4 sensors + hero vehicle: 100-tick stress run then sequential teardown.
300371
0 commit comments