Skip to content

Commit 6b75d87

Browse files
authored
Added support for cft expression as parameters and a new test
* Added cft test with a provided expression. Also added --size-modulo and the test Test_Cft_1
1 parent 2e1f98d commit 6b75d87

File tree

3 files changed

+148
-36
lines changed

3 files changed

+148
-36
lines changed

srcCxx/shape_main.cxx

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ class ShapeOptions {
276276

277277
useconds_t periodic_announcement_period_us;
278278

279+
char* cft_expression;
280+
281+
int size_modulo;
282+
279283
public:
280284
//-------------------------------------------------------------
281285
ShapeOptions()
@@ -330,6 +334,10 @@ class ShapeOptions {
330334
take_read_next_instance = true;
331335

332336
periodic_announcement_period_us = 0;
337+
338+
cft_expression = NULL;
339+
340+
size_modulo = 0; // 0 means disabled
333341
}
334342

335343
//-------------------------------------------------------------
@@ -338,6 +346,7 @@ class ShapeOptions {
338346
STRING_FREE(topic_name);
339347
STRING_FREE(color);
340348
STRING_FREE(partition);
349+
STRING_FREE(cft_expression);
341350
}
342351

343352
//-------------------------------------------------------------
@@ -403,6 +412,13 @@ class ShapeOptions {
403412
printf(" read_next_instance()\n");
404413
printf(" --periodic-announcement <ms> : indicates the periodic participant\n");
405414
printf(" announcement period in ms. Default 0 (off)\n");
415+
printf(" --cft <expression> : ContentFilteredTopic filter expression (quotes\n");
416+
printf(" required around the expression). Cannot be used with\n");
417+
printf(" -c on subscriber applications\n");
418+
printf(" --size-modulo <int> : If set, the modulo operation is applied to the\n");
419+
printf(" shapesize. This will make that shapesize is in the\n");
420+
printf(" range [1,N]. This only applies if shapesize is\n");
421+
printf(" increased (-z 0)\n");
406422
}
407423

408424
//-------------------------------------------------------------
@@ -415,7 +431,7 @@ class ShapeOptions {
415431
logger.log_message("please specify publish [-P] or subscribe [-S]", Verbosity::ERROR);
416432
return false;
417433
}
418-
if ( publish && subscribe ) {
434+
if (publish && subscribe) {
419435
logger.log_message("please specify only one of: publish [-P] or subscribe [-S]", Verbosity::ERROR);
420436
return false;
421437
}
@@ -432,6 +448,9 @@ class ShapeOptions {
432448
if (publish && take_read_next_instance == false ) {
433449
logger.log_message("warning: --take-read ignored on publisher applications", Verbosity::ERROR);
434450
}
451+
if (publish && cft_expression != NULL) {
452+
logger.log_message("warning: --cft ignored on publisher applications", Verbosity::ERROR);
453+
}
435454
if (subscribe && shapesize != 20) {
436455
logger.log_message("warning: shapesize [-z] ignored on subscriber applications", Verbosity::ERROR);
437456
}
@@ -456,6 +475,13 @@ class ShapeOptions {
456475
if (!coherent_set_enabled && !ordered_access_enabled && coherent_set_access_scope_set) {
457476
logger.log_message("warning: --access-scope ignored because not coherent, or ordered access enabled", Verbosity::ERROR);
458477
}
478+
if (size_modulo > 0 && shapesize != 0) {
479+
logger.log_message("warning: --size-modulo has no effect unless shapesize (-z) is set to 0", Verbosity::ERROR);
480+
}
481+
if (subscribe && color != NULL && cft_expression != NULL) {
482+
logger.log_message("error: cannot specify both --cft and -c/--color for subscriber applications", Verbosity::ERROR);
483+
return false;
484+
}
459485

460486
return true;
461487
}
@@ -483,6 +509,8 @@ class ShapeOptions {
483509
{"take-read", no_argument, NULL, 'K'},
484510
{"time-filter", required_argument, NULL, 'i'},
485511
{"periodic-announcement", required_argument, NULL, 'N'},
512+
{"cft", required_argument, NULL, 'F'},
513+
{"size-modulo", required_argument, NULL, 'Q'},
486514
{NULL, 0, NULL, 0 }
487515
};
488516

@@ -867,6 +895,19 @@ class ShapeOptions {
867895
periodic_announcement_period_us = (useconds_t) converted_param * 1000;
868896
break;
869897
}
898+
case 'F':
899+
cft_expression = strdup(optarg);
900+
break;
901+
case 'Q': {
902+
int converted_param = 0;
903+
if (sscanf(optarg, "%d", &converted_param) == 0 || converted_param < 1) {
904+
logger.log_message("incorrect value for size-modulo, must be >=1", Verbosity::ERROR);
905+
parse_ok = false;
906+
} else {
907+
size_modulo = converted_param;
908+
}
909+
break;
910+
}
870911
case '?':
871912
parse_ok = false;
872913
break;
@@ -1500,50 +1541,58 @@ class ShapeApplication {
15001541
logger.log_message(" HistoryDepth = " + std::to_string(dr_qos.history FIELD_ACCESSOR.depth), Verbosity::DEBUG);
15011542
}
15021543

1503-
if ( options->color != NULL ) {
1504-
/* filter on specified color */
1544+
if ( options->cft_expression != NULL || options->color != NULL) {
15051545
ContentFilteredTopic *cft = NULL;
1506-
StringSeq cf_params;
1546+
StringSeq cf_params;
15071547

1508-
for (unsigned int i = 0; i < options->num_topics; ++i) {
1509-
const std::string filtered_topic_name_str =
1510-
std::string(options->topic_name) +
1511-
(i > 0 ? std::to_string(i) : "") +
1512-
"_filtered";
1513-
const char* filtered_topic_name = filtered_topic_name_str.c_str();
1548+
for (unsigned int i = 0; i < options->num_topics; ++i) {
1549+
const std::string filtered_topic_name_str =
1550+
std::string(options->topic_name) +
1551+
(i > 0 ? std::to_string(i) : "") +
1552+
"_filtered";
1553+
const char* filtered_topic_name = filtered_topic_name_str.c_str();
1554+
const char* filter_expr = nullptr;
1555+
1556+
if (options->cft_expression != NULL) {
1557+
filter_expr = options->cft_expression;
1558+
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], filter_expr, cf_params);
1559+
logger.log_message(" ContentFilterTopic = \"" + std::string(filter_expr) + "\"", Verbosity::DEBUG);
1560+
} else if (options->color != NULL) {
15141561
#if defined(RTI_CONNEXT_DDS)
1515-
char parameter[64];
1516-
snprintf(parameter, 64, "'%s'", options->color);
1517-
StringSeq_push(cf_params, parameter);
1562+
char parameter[64];
1563+
snprintf(parameter, 64, "'%s'", options->color);
1564+
StringSeq_push(cf_params, parameter);
15181565

1519-
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color MATCH %0", cf_params);
1520-
logger.log_message(" ContentFilterTopic = \"color MATCH "
1521-
+ std::string(parameter) + std::string("\""), Verbosity::DEBUG);
1566+
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color MATCH %0", cf_params);
1567+
logger.log_message(" ContentFilterTopic = \"color MATCH "
1568+
+ std::string(parameter) + std::string("\""), Verbosity::DEBUG);
15221569
#elif defined(INTERCOM_DDS)
1523-
char parameter[64];
1524-
snprintf(parameter, 64, "'%s'", options->color);
1525-
StringSeq_push(cf_params, parameter);
1570+
char parameter[64];
1571+
snprintf(parameter, 64, "'%s'", options->color);
1572+
StringSeq_push(cf_params, parameter);
15261573

1527-
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1528-
logger.log_message(" ContentFilterTopic = \"color = "
1529-
+ std::string(parameter) + std::string("\""), Verbosity::DEBUG);
1574+
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1575+
logger.log_message(" ContentFilterTopic = \"color = "
1576+
+ std::string(parameter) + std::string("\""), Verbosity::DEBUG);
15301577
#elif defined(TWINOAKS_COREDX) || defined(OPENDDS)
1531-
StringSeq_push(cf_params, options->color);
1532-
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1533-
logger.log_message(" ContentFilterTopic = \"color = "
1534-
+ std::string(options->color) + std::string("\""), Verbosity::DEBUG);
1578+
StringSeq_push(cf_params, options->color);
1579+
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1580+
logger.log_message(" ContentFilterTopic = \"color = "
1581+
+ std::string(options->color) + std::string("\""), Verbosity::DEBUG);
15351582
#elif defined(EPROSIMA_FAST_DDS)
1536-
cf_params.push_back(std::string("'") + options->color + std::string("'"));
1537-
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1538-
logger.log_message(" ContentFilterTopic = \"color = "
1539-
+ cf_params[0] + std::string("\""), Verbosity::DEBUG);
1583+
cf_params.push_back(std::string("'") + options->color + std::string("'"));
1584+
cft = dp->create_contentfilteredtopic(filtered_topic_name, topics[i], "color = %0", cf_params);
1585+
logger.log_message(" ContentFilterTopic = \"color = "
1586+
+ cf_params[0] + std::string("\""), Verbosity::DEBUG);
15401587
#endif
1588+
}
1589+
15411590
if (cft == NULL) {
15421591
logger.log_message("failed to create content filtered topic", Verbosity::ERROR);
15431592
return false;
15441593
}
15451594

1546-
printf("Create reader for topic: %s color: %s\n", cft->get_name() NAME_ACCESSOR, options->color );
1595+
printf("Create reader for topic: %s\n", cft->get_name() NAME_ACCESSOR);
15471596
drs[i] = dynamic_cast<ShapeTypeDataReader *>(sub->create_datareader(cft, dr_qos, NULL, LISTENER_STATUS_MASK_NONE));
15481597
if (drs[i] == NULL) {
15491598
logger.log_message("failed to create datareader[" + std::to_string(i) + "] topic: " + topics[i]->get_name(), Verbosity::ERROR);
@@ -1728,6 +1777,7 @@ class ShapeApplication {
17281777
} else {
17291778
ShapeType shape_key;
17301779
shape_initialize_w_color(shape_key, NULL);
1780+
17311781
#if defined(EPROSIMA_FAST_DDS)
17321782
shape_key.color FIELD_ACCESSOR = instance_handle_color[sample_info->instance_handle] NAME_ACCESSOR;
17331783
#else
@@ -1831,7 +1881,13 @@ class ShapeApplication {
18311881
moveShape(&shape);
18321882

18331883
if (options->shapesize == 0) {
1834-
shape.shapesize FIELD_ACCESSOR += 1;
1884+
if (options->size_modulo > 0) {
1885+
// Size cannot be 0, so increase it after modulo operation
1886+
shape.shapesize FIELD_ACCESSOR =
1887+
(shape.shapesize FIELD_ACCESSOR % options->size_modulo) + 1;
1888+
} else {
1889+
shape.shapesize FIELD_ACCESSOR += 1;
1890+
}
18351891
}
18361892

18371893
if (options->coherent_set_enabled || options->ordered_access_enabled) {

test_suite.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,22 +425,39 @@
425425

426426
# Content Filtered Topic
427427
'Test_Cft_0' : {
428-
'apps' : ['-P -t Square -r -c BLUE', '-P -t Square -r -c RED', '-S -t Square -r -c RED'],
428+
'apps' : ['-P -t Square -r -k 0 -c BLUE', '-P -t Square -r -k 0 -c RED', '-S -t Square -r -k 0 --cft "color = \'RED\'"'],
429429
'expected_codes' : [ReturnCode.OK, ReturnCode.OK, ReturnCode.RECEIVING_FROM_ONE],
430430
'check_function' : tsf.test_color_receivers,
431-
'title' : 'Use of Content filter to avoid receiving undesired data',
432-
'description' : 'Verifies a subscription using a ContentFilteredTopic does not receive data that does not pass the filter\n\n'
431+
'title' : 'Use of Content filter to avoid receiving undesired data (key)',
432+
'description' : 'Verifies a subscription using a ContentFilteredTopic does not receive data that does not '
433+
'pass the filter. The filter is applied to the key "color"\n\n'
433434
' * Configures a subscriber with a ContentFilteredTopic that selects only the shapes that '
434435
'have "color" equal to "RED"\n'
435436
' * Configures a first publisher to publish samples with "color" equal to "BLUE"\n'
436437
' * Configures a second publisher to publish samples with "color" equal to "RED"\n'
437438
' * Use RELIABLE Qos in all publishers and subscriber to ensure any samples that are not '
438439
'received are due to filtering\n'
440+
' * Configures the publishers / subscriber with history KEEP_ALL\n'
439441
' * Verifies that both publishers discover and match the subscriber and vice-versa\n'
440442
' * Note that this test does not check whether the filtering happens in the publisher side or '
441443
'the subscriber side. It only checks the middleware filters the samples somewhere.\n\n'
442444
f'The test passes if the subscriber receives {tsf.MAX_SAMPLES_READ} samples of one color\n'
443-
},
445+
},
446+
447+
'Test_Cft_1': {
448+
'apps': ['-P -t Square -r -k 0 -z 0 --size-modulo 50', '-S -t Square -r -k 0 --cft "shapesize <= 20"'],
449+
'expected_codes': [ReturnCode.OK, ReturnCode.OK],
450+
'check_function': tsf.test_size_less_than_20,
451+
'title' : 'Use of Content filter to avoid receiving undesired data (non-key)',
452+
'description': 'Verifies a subscription using a ContentFilteredTopic does not receive data that does not '
453+
'pass the filter. The filter is applied to the non-key member "shapesize".\n\n'
454+
' * Use RELIABLE Qos in all publishers and subscriber to avoid samples losses\n'
455+
' * Configures the publisher / subscriber with history KEEP_ALL\n'
456+
' * The publisher application sends samples with increasing value of the "size" member\n'
457+
' * Publisher sends samples with size cycling from 1 to 50 (using --size-modulo 50 and -z 0)\n'
458+
' * Subscriber uses --cft "shapesize <= 20"\n'
459+
' * The test passes if all received samples have size < 20\n'
460+
},
444461

445462
# PARTITION
446463
'Test_Partition_0' : {

test_suite_functions.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,45 @@ def test_color_receivers(child_sub, samples_sent, last_sample_saved, timeout):
305305
print(f'Samples read: {samples_read}')
306306
return ReturnCode.RECEIVING_FROM_ONE
307307

308+
def test_size_less_than_20(child_sub, samples_sent, last_sample_saved, timeout):
309+
"""
310+
Checks that all received samples have size between 1 and 20 (inclusive).
311+
Returns ReturnCode.OK if all samples are in range, otherwise ReturnCode.DATA_NOT_CORRECT.
312+
"""
313+
import re
314+
from rtps_test_utilities import ReturnCode
315+
316+
max_samples_received = MAX_SAMPLES_READ / 2
317+
samples_read = 0
318+
return_code = ReturnCode.OK
319+
320+
sub_string = re.search('[0-9]+ [0-9]+ \[([0-9]+)\]', child_sub.before + child_sub.after)
321+
322+
while sub_string is not None and samples_read < max_samples_received:
323+
size = int(sub_string.group(1))
324+
if size < 1 or size > 20:
325+
return_code = ReturnCode.DATA_NOT_CORRECT
326+
break
327+
328+
index = child_sub.expect(
329+
[
330+
'\[[0-9]+\]', # index = 0
331+
pexpect.TIMEOUT, # index = 1
332+
pexpect.EOF # index = 2
333+
],
334+
timeout
335+
)
336+
if index == 1 or index == 2:
337+
return_code = ReturnCode.DATA_NOT_RECEIVED
338+
break
339+
340+
samples_read += 1
341+
sub_string = re.search('[0-9]+ [0-9]+ \[([0-9]+)\]', child_sub.before + child_sub.after)
342+
343+
print(f'Samples read: {samples_read}')
344+
return return_code
345+
346+
308347
def test_reliability_order(child_sub, samples_sent, last_sample_saved, timeout):
309348
"""
310349
This function tests reliability, it checks whether the subscriber receives

0 commit comments

Comments
 (0)