From 1725351ec84ae2cf731fab77f65b881cc11421bd Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Wed, 16 Sep 2026 12:50:45 +0000 Subject: [PATCH 1/8] Add Test Coverage: Large Data & BEST_EFFORT --- test_suite.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test_suite.py b/test_suite.py index 5d58ac3..a50d2d9 100644 --- a/test_suite.py +++ b/test_suite.py @@ -882,6 +882,24 @@ 'the last byte sent in additional_payload_size is correctly set.\n' }, + # BEST_EFFORT variant of Test_LargeData_0: large samples must be fragmented + # (RTPS DATA_FRAG) and reassembled without the retransmission safety net + # that RELIABLE provides. + 'Test_LargeData_1' : { + 'apps' : ['-P -t Square -b --additional-payload-size 100000', + '-S -t Square -b'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_large_data, + 'title' : 'Test large data with BEST_EFFORT reliability', + 'description' : 'This test covers the interoperability scenario with large, fragmented data ' + 'sent over BEST_EFFORT reliability:\n\n' + ' * Configures the publisher / subscriber with a BEST_EFFORT reliability\n' + ' * Configures the publisher to use 100000 additional payload size (to represent large data samples)\n' + ' * Verifies the publisher and subscriber discover and match each other\n\n' + 'The tests passes if the subscriber receives samples from the publisher and ' + 'the last byte sent in additional_payload_size is correctly set.\n' + }, + 'Test_Lifespan_0' : { 'apps' : ['-P -t Square -r -k 0 -z 0 --write-period 100 --lifespan 250', '-S -t Square -r -k 0 --read-period 500'], From 893f689a76e2ab2b62a0f53e0d3071eb6d43fd20 Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Wed, 16 Sep 2026 12:59:38 +0000 Subject: [PATCH 2/8] Add Test Coverage: Partition Wildcards --- test_suite.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test_suite.py b/test_suite.py index a50d2d9..658c47e 100644 --- a/test_suite.py +++ b/test_suite.py @@ -562,6 +562,38 @@ '(first publisher)\n' }, + # Exercises the single-character "?" wildcard, a distinct partition + # matching rule from the "*" (any number of characters) case above. + 'Test_Partition_3' : { + 'apps' : ['-P -t Square -p "p1" -c BLUE', '-P -t Square -p "pxx" -c RED', '-S -t Square -p "p?"'], + 'check_function' : tsf.test_color_receivers, + 'expected_codes' : [ReturnCode.OK, ReturnCode.READER_NOT_MATCHED, ReturnCode.RECEIVING_FROM_ONE], + 'title' : 'Usage of a partition "?" wildcard to match a single character', + 'description' : 'Verifies a subscription using a partition "?" wildcard only receives data from ' + 'publishers whose partition differs by exactly one character\n\n' + ' * Configures a subscriber with a PARTITION expression "p?" that only matches ' + 'partitions consisting of "p" followed by exactly one character\n' + ' * Configures a first publisher to use PARTITION "p1" and "color" equal to "BLUE"\n' + ' * Configures a second publisher to use PARTITION "pxx" and "color" equal to "RED"\n' + ' * Verifies that only the first publisher (PARTITION "p1") discovers and matches subscriber\n' + ' * Verifies that the second publisher (PARTITION "pxx") does not match the subscriber\n\n' + f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ} samples of one color ' + '(first publisher)\n' + }, + + # The DDS spec treats the default (empty) partition as equivalent to a + # single empty-string partition, which "*" must also match. + 'Test_Partition_4' : { + 'apps' : ['-P -t Square', '-S -t Square -p "*"'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'title' : 'Communication between a publisher using the default partition and a subscriber using "*"', + 'description' : 'Verifies a subscriber using the wildcard partition "*" matches a publisher that does ' + 'not set a PARTITION (the default, empty partition)\n\n' + ' * Configures the publisher with no PARTITION (default)\n' + ' * Configures the subscriber with PARTITION expression "*"\n\n' + 'The test passes if the subscriber receives samples from the publisher\n' + }, + # DURABILITY 'Test_Durability_0' : { 'apps' : ['-P -t Square -D v', '-S -t Square -D v'], From 65e250642259b80353cffd25458a8e967c5cb8f2 Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Wed, 16 Sep 2026 13:00:26 +0000 Subject: [PATCH 3/8] Add Test Coverage: CFT Compound Expression --- test_suite.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test_suite.py b/test_suite.py index 658c47e..253e4d1 100644 --- a/test_suite.py +++ b/test_suite.py @@ -523,6 +523,25 @@ f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ/2} samples with size < 20\n' }, + # Combines a key and a non-key field in one compound expression, unlike + # Test_Cft_0 (key only) and Test_Cft_1 (non-key only). + 'Test_Cft_2': { + 'apps': ['-P -t Square -r -k 0 -c RED -z 15', '-P -t Square -r -k 0 -c RED -z 25', + '-S -t Square -r -k 0 --cft "(color = \'RED\') AND (shapesize <= 20)"'], + 'expected_codes': [ReturnCode.OK, ReturnCode.OK, ReturnCode.RECEIVING_FROM_ONE], + 'check_function': tsf.test_size_receivers, + 'title' : 'Use of a compound content filter combining a key and a non-key field', + 'description': 'Verifies a subscription using a ContentFilteredTopic compound (AND) expression only ' + 'receives data that passes both clauses\n\n' + ' * Use RELIABLE Qos in all publishers and subscriber to avoid samples losses\n' + ' * Configures the publisher / subscriber with history KEEP_ALL\n' + ' * Configures a first publisher with "color" equal to "RED" and constant "shapesize" 15\n' + ' * Configures a second publisher with "color" equal to "RED" and constant "shapesize" 25\n' + ' * Subscriber uses --cft "(color = \'RED\') AND (shapesize <= 20)"\n\n' + f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ} samples from the ' + 'first publisher only\n' + }, + # PARTITION 'Test_Partition_0' : { 'apps' : ['-P -t Square -p "p1"', '-S -t Square -p "p1"'], From 0ad16b2f2806e491e7256f5eaaa5944bca267bdb Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Wed, 16 Sep 2026 12:57:08 +0000 Subject: [PATCH 4/8] Add Test Coverage: History KEEP_LAST + Depth 1 --- test_suite.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test_suite.py b/test_suite.py index 253e4d1..f2b83c8 100644 --- a/test_suite.py +++ b/test_suite.py @@ -251,6 +251,27 @@ 'instance, without losses or duplicates, in the same order as sent\n' }, + # Unlike Test_History_0/1 (depth 5, no losses expected), depth 1 is the + # most common real-world setting and intentionally drops superseded + # samples; the writer must GAP them for the RELIABLE protocol to proceed. + 'Test_History_2' : { + 'apps' : ['-P -t Square -r -k 1 -z 0 --write-period 10', + '-S -t Square -r -k 1'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_order_w_instances, + 'title' : 'Behavior of KEEP_LAST 1 history', + 'description' : 'Verifies a RELIABLE, KEEP_LAST 1 publisher communicates with a RELIABLE, KEEP_LAST 1 ' + 'subscriber. The publisher writes much faster than the subscriber reads, so older ' + 'unread samples are expected to be superseded and never delivered.\n\n' + ' * Configures the publisher and subscriber with a RELIABLE reliability\n' + ' * Configures the publisher and subscriber with history KEEP_LAST 1\n' + ' * Configures the publisher with a writing period of 10ms\n' + ' * The publisher application sends samples with increasing value of the "size" member\n' + ' * Verifies the publisher and subscriber discover and match each other\n\n' + 'The test passes if the subscriber receives samples with a strictly increasing ' + f'"size" value for {tsf.MAX_SAMPLES_READ} samples, even though some are skipped\n' + }, + # OWNERSHIP 'Test_Ownership_0' : { 'apps' : ['-P -t Square -s -1', '-S -t Square -s -1'], From 53a9a550976a65bfff8b097e0eecb115204710a4 Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Wed, 16 Sep 2026 13:02:39 +0000 Subject: [PATCH 5/8] Add Test Coverage: Multi-Topic Participants --- test_suite.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test_suite.py b/test_suite.py index f2b83c8..1e8766f 100644 --- a/test_suite.py +++ b/test_suite.py @@ -508,6 +508,19 @@ 'The test passes if the publisher and subscriber do not discover each other\n' }, + # MULTIPLE TOPICS + 'Test_MultiTopic_0' : { + 'apps' : ['-P -t Square --num-topics 3', '-S -t Square --num-topics 3'], + 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'title' : 'Communication using several topics on the same participant', + 'description' : 'Verifies a publisher and a subscriber, each publishing/subscribing to several topics ' + 'from a single participant, discover and match on every topic\n\n' + ' * Configures the publisher and subscriber with 3 topics: "Square", "Square1", "Square2"\n' + ' * Each topic has its own DataWriter / DataReader on the same participant\n' + ' * Verifies the publisher and subscriber discover and match each other on every topic\n\n' + 'The test passes if the subscriber receives samples from the publisher\n' + }, + # Content Filtered Topic 'Test_Cft_0' : { 'apps' : ['-P -t Square -r -k 0 -c BLUE', '-P -t Square -r -k 0 -c RED', '-S -t Square -r -k 0 -c RED'], From dbaf3e6c8404631e325ec76c5d45ca57f20f5ace Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Tue, 29 Sep 2026 16:14:50 +0100 Subject: [PATCH 6/8] Update test_suite.py Co-authored-by: Angel Martinez --- test_suite.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_suite.py b/test_suite.py index 1e8766f..5f61a0d 100644 --- a/test_suite.py +++ b/test_suite.py @@ -560,7 +560,8 @@ # Combines a key and a non-key field in one compound expression, unlike # Test_Cft_0 (key only) and Test_Cft_1 (non-key only). 'Test_Cft_2': { - 'apps': ['-P -t Square -r -k 0 -c RED -z 15', '-P -t Square -r -k 0 -c RED -z 25', + 'apps': ['-P -t Square -r -k 0 -c RED -z 15', + '-P -t Square -r -k 0 -c RED -z 25', '-S -t Square -r -k 0 --cft "(color = \'RED\') AND (shapesize <= 20)"'], 'expected_codes': [ReturnCode.OK, ReturnCode.OK, ReturnCode.RECEIVING_FROM_ONE], 'check_function': tsf.test_size_receivers, From 94408b322d0464e41d0f62c1388eca1fe6a9ebd4 Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Tue, 29 Sep 2026 16:14:58 +0100 Subject: [PATCH 7/8] Update test_suite.py Co-authored-by: Angel Martinez --- test_suite.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test_suite.py b/test_suite.py index 5f61a0d..80be197 100644 --- a/test_suite.py +++ b/test_suite.py @@ -619,7 +619,9 @@ # Exercises the single-character "?" wildcard, a distinct partition # matching rule from the "*" (any number of characters) case above. 'Test_Partition_3' : { - 'apps' : ['-P -t Square -p "p1" -c BLUE', '-P -t Square -p "pxx" -c RED', '-S -t Square -p "p?"'], + 'apps' : ['-P -t Square -p "p1" -c BLUE', + '-P -t Square -p "pxx" -c RED', + '-S -t Square -p "p?"'], 'check_function' : tsf.test_color_receivers, 'expected_codes' : [ReturnCode.OK, ReturnCode.READER_NOT_MATCHED, ReturnCode.RECEIVING_FROM_ONE], 'title' : 'Usage of a partition "?" wildcard to match a single character', From b92f083b60e7078b56745684a19f6b85dfc7ab2b Mon Sep 17 00:00:00 2001 From: Timothy Simpson Date: Tue, 29 Sep 2026 16:41:20 +0100 Subject: [PATCH 8/8] resolving review comments --- test_suite.py | 6 ++--- test_suite_functions.py | 60 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/test_suite.py b/test_suite.py index 80be197..e146162 100644 --- a/test_suite.py +++ b/test_suite.py @@ -508,17 +508,17 @@ 'The test passes if the publisher and subscriber do not discover each other\n' }, - # MULTIPLE TOPICS - 'Test_MultiTopic_0' : { + 'Test_Topic_2' : { 'apps' : ['-P -t Square --num-topics 3', '-S -t Square --num-topics 3'], 'expected_codes' : [ReturnCode.OK, ReturnCode.OK], + 'check_function' : tsf.test_receiving_on_3_topics, 'title' : 'Communication using several topics on the same participant', 'description' : 'Verifies a publisher and a subscriber, each publishing/subscribing to several topics ' 'from a single participant, discover and match on every topic\n\n' ' * Configures the publisher and subscriber with 3 topics: "Square", "Square1", "Square2"\n' ' * Each topic has its own DataWriter / DataReader on the same participant\n' ' * Verifies the publisher and subscriber discover and match each other on every topic\n\n' - 'The test passes if the subscriber receives samples from the publisher\n' + 'The test passes if the subscriber receives samples on every topic\n' }, # Content Filtered Topic diff --git a/test_suite_functions.py b/test_suite_functions.py index 54a8cad..de16759 100644 --- a/test_suite_functions.py +++ b/test_suite_functions.py @@ -137,6 +137,66 @@ def test_color_receivers(child_sub, samples_sent, last_sample_saved, timeout): print(f'Samples read: {samples_read}') return ReturnCode.RECEIVING_FROM_ONE +def _test_receiving_on_all_topics(child_sub, samples_sent, last_sample_saved, + timeout, num_topics): + """ + This function is used by test cases in which the subscriber application + creates one DataReader per topic. This tests that samples are received on + every topic, and therefore by every DataReader. + + child_sub: child program generated with pexpect + samples_sent: not used + last_sample_saved: not used + timeout: time pexpect waits until it matches a pattern. + num_topics: number of topics that must receive samples. + """ + basic_check_retcode = basic_check(child_sub, samples_sent, last_sample_saved, timeout) + + if basic_check_retcode != ReturnCode.OK: + return basic_check_retcode + + sub_string = re.search(r'(\w+)\s+\w+\s+[0-9]+ [0-9]+ \[[0-9]+\]', + child_sub.before + child_sub.after) + + topics_received = set() + max_samples_received = MAX_SAMPLES_READ + samples_read = 0 + + while sub_string is not None and samples_read < max_samples_received: + topics_received.add(sub_string.group(1)) + if len(topics_received) == num_topics: + break + + index = child_sub.expect( + [ + r'\[[0-9]+\]', # index = 0 + pexpect.TIMEOUT, # index = 1 + pexpect.EOF # index = 2 + ], + timeout + ) + + if index == 1: + break + elif index == 2: + return ReturnCode.DATA_NOT_RECEIVED + + samples_read += 1 + + sub_string = re.search(r'(\w+)\s+\w+\s+[0-9]+ [0-9]+ \[[0-9]+\]', + child_sub.before + child_sub.after) + + print(f'Samples read: {samples_read}, ' + f'topics received: {", ".join(sorted(topics_received))}') + if len(topics_received) != num_topics: + return ReturnCode.DATA_NOT_RECEIVED + return ReturnCode.OK + +def test_receiving_on_3_topics(child_sub, samples_sent, last_sample_saved, timeout): + """_test_receiving_on_all_topics for a subscriber with 3 topics.""" + return _test_receiving_on_all_topics(child_sub, samples_sent, last_sample_saved, + timeout, num_topics=3) + def test_size_less_than_20(child_sub, samples_sent, last_sample_saved, timeout): """ Checks that all received samples have size between 1 and 20 (inclusive).