@@ -85,8 +85,10 @@ class TestSubscriptionTopicStatistics : public SubscriptionTopicStatistics<Callb
8585public:
8686 TestSubscriptionTopicStatistics (
8787 const std::string & node_name,
88+ const std::string & topic_name,
8889 rclcpp::Publisher<statistics_msgs::msg::MetricsMessage>::SharedPtr publisher)
89- : SubscriptionTopicStatistics<CallbackMessageT>(node_name, publisher)
90+ : SubscriptionTopicStatistics<CallbackMessageT>(
91+ node_name, topic_name, publisher)
9092 {
9193 }
9294
@@ -391,6 +393,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_manual_construction)
391393 // Construct a separate instance
392394 auto sub_topic_stats = std::make_unique<TestSubscriptionTopicStatistics<Empty>>(
393395 empty_subscriber->get_name (),
396+ " /test_topic" ,
394397 topic_stats_publisher);
395398
396399 // Expect no data has been collected / no samples received
@@ -586,3 +589,81 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_include_window
586589 }
587590 }
588591}
592+ /* *
593+ * Test that multiple subscriptions on the same node produce differentiated
594+ * measurement_source_name values in their statistics messages.
595+ * Regression test for #2756.
596+ */
597+ TEST_F (TestSubscriptionTopicStatisticsFixture, test_multiple_subscriptions_differentiated)
598+ {
599+ constexpr const char kTopicA []{" /test_topic_a" };
600+ constexpr const char kTopicB []{" /test_topic_b" };
601+ constexpr const uint64_t kNumStatsMessages {4 };
602+
603+ auto pub_a = std::make_shared<EmptyPublisher>(" pub_node_a" , kTopicA );
604+ auto pub_b = std::make_shared<EmptyPublisher>(" pub_node_b" , kTopicB );
605+
606+ auto multi_sub_node = std::make_shared<rclcpp::Node>(" multi_sub_node" );
607+
608+ auto options = rclcpp::SubscriptionOptions ();
609+ options.topic_stats_options .state = rclcpp::TopicStatisticsState::Enable;
610+ options.topic_stats_options .publish_period = defaultStatisticsPublishPeriod;
611+
612+ auto cb = [](Empty::UniquePtr msg) {(void ) msg;};
613+
614+ auto sub_a = multi_sub_node->create_subscription <Empty>(
615+ kTopicA ,
616+ rclcpp::QoS (rclcpp::KeepAll ()),
617+ std::function<void (Empty::UniquePtr)>(cb),
618+ options);
619+
620+ auto sub_b = multi_sub_node->create_subscription <Empty>(
621+ kTopicB ,
622+ rclcpp::QoS (rclcpp::KeepAll ()),
623+ std::function<void (Empty::UniquePtr)>(cb),
624+ options);
625+
626+ auto stats_listener =
627+ std::make_shared<rclcpp::topic_statistics::MetricsMessageSubscriber>(
628+ " multi_sub_stats_listener" ,
629+ " /statistics" ,
630+ kNumStatsMessages );
631+
632+ rclcpp::executors::SingleThreadedExecutor ex;
633+ ex.add_node (pub_a);
634+ ex.add_node (pub_b);
635+ ex.add_node (multi_sub_node);
636+ ex.add_node (stats_listener);
637+
638+ ex.spin_until_future_complete (stats_listener->GetFuture (), kTestTimeout );
639+
640+ const auto received_messages = stats_listener->GetReceivedMessages ();
641+ ASSERT_GE (received_messages.size (), kNumStatsMessages );
642+
643+ std::set<std::string> source_names;
644+ for (const auto & msg : received_messages) {
645+ source_names.insert (msg.measurement_source_name );
646+ }
647+
648+ bool found_topic_a = false ;
649+ bool found_topic_b = false ;
650+
651+ for (const auto & name : source_names) {
652+ if (name.find (" test_topic_a" ) != std::string::npos) {
653+ found_topic_a = true ;
654+ }
655+
656+ if (name.find (" test_topic_b" ) != std::string::npos) {
657+ found_topic_b = true ;
658+ }
659+ }
660+
661+ EXPECT_TRUE (found_topic_a)
662+ << " No stats found containing topic_a in source name" ;
663+
664+ EXPECT_TRUE (found_topic_b)
665+ << " No stats found containing topic_b in source name" ;
666+
667+ EXPECT_GT (source_names.size (), 1u )
668+ << " Both subscriptions produced the same measurement_source_name" ;
669+ }
0 commit comments