From 608328d94b53436d8afaad02829641604274214b Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Thu, 29 Feb 2024 16:59:37 +0100 Subject: [PATCH 1/6] add psmx test to check whether shared memory can be enabled and the locator can be set Signed-off-by: Michel van den Hoek --- src/core/ddsc/tests/psmx.c | 434 +++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index 050e537115..3eecc53169 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -15,6 +15,7 @@ #include "dds/ddsrt/md5.h" #include "dds/ddsrt/io.h" #include "dds/ddsrt/heap.h" +#include "dds/ddsrt/string.h" #include "dds/ddsrt/bswap.h" #include "dds/ddsrt/environ.h" #include "dds/ddsrt/static_assert.h" @@ -99,6 +100,67 @@ static dds_entity_t create_participant (dds_domainid_t int_dom) return pp; } +static void config_psmx_free_content(struct ddsi_config_psmx* cfg) +{ + assert(cfg); + ddsrt_free(cfg->name); + ddsrt_free(cfg->library); + ddsrt_free(cfg->config); +} + +static void config_psmx_elm_free(struct ddsi_config_psmx_listelem* elm) +{ + assert(elm); + config_psmx_free_content(&elm->cfg); + ddsrt_free(elm); +} + +static bool domain_pin(dds_entity_t domain, dds_domain** dom_out) +{ + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(domain, &x); + if ( rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_DOMAIN ) { + *dom_out = (dds_domain*)x; + return true; + } + return false; +} + +static void domain_unpin(dds_domain* dom) +{ + assert(dom); + dds_entity_unpin((dds_entity*)dom); +} + +static bool domain_get_psmx_locator(dds_entity_t domain, ddsi_locator_t* l_out) +{ + dds_domain* dom = NULL; + bool ret = domain_pin(domain, &dom); + if ( ret ) { + memcpy(l_out, dom->psmx_instances.instances[0]->locator, sizeof(ddsi_locator_t)); + domain_unpin(dom); + } + return ret; +} + +static bool domain_has_psmx_instance_name(dds_entity_t domain, const char* inst_name) +{ + dds_domain* dom = NULL; + bool ret = domain_pin(domain, &dom); + bool match = false; + if ( ret ) { + uint32_t len = dom->psmx_instances.length; + for (uint32_t idx = 0; idx < len; ++idx) { + if ( strcmp(dom->psmx_instances.instances[idx]->instance_name, inst_name) == 0 ) { + match = true; + break; + } + } + domain_unpin(dom); + } + return ret && match; +} + struct tracebuf { char buf[512]; size_t pos; @@ -1265,6 +1327,378 @@ CU_Test (ddsc_psmx, basic) dds_delete (dds_get_parent (participant)); } +/// @brief Check that shared memory can be enabled and the used locator can be set. +/// @methodology +/// Case with Iceoryx: +/// - Check that the data types I'm planning to use are actually suitable for use with shared memory. +/// - Expectation: They are memcopy-safe. +/// - Create a configuration with a psmx interface capable of shared memory and specify the locator. +/// - Create a domain using this configuration. +/// - Create some entities +/// - Check the locator used for shared memory. +/// - Expectation: The locator is the same as specified in the config for the domain. +/// - Check if shared memory is enabled. +/// - Expectation: Shared memory is enabled. +/// - Write and read some topics. +/// - Expectation: Sent data is received by readers. +/// +/// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. +/// - Create a domain using this configuration. +/// - Create some entities +/// - Check the locator used for shared memory. +/// - Expectation: The second half of the locator is all zeros. +/// - Check if shared memory is enabled. +/// - Expectation: Shared memory is enabled. +/// +/// Case with cdds: +/// - Create a configuration with a psmx interface incapable of shared memory. +/// - Create a domain using this configuration. +/// - Create some entities +/// - Check if shared memory is enabled. +/// - Expectation: Shared memory is not enabled. +/// +CU_Test(ddsc_psmx, shared_memory) +{ + #define sample_cnt 8 + { + // Check that the data types I'm planning to use are actually suitable for use with shared memory. + dds_data_type_properties_t props; + props = dds_stream_data_types(SC_Model_desc.m_ops); + CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); + props = dds_stream_data_types(PsmxType1_desc.m_ops); + CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); + } + + const dds_domainid_t domainId = 42; + char* CDDS_PSMX_NAME = ddsrt_expand_envvars("${CDDS_PSMX_NAME:-cdds}", domainId); + bool using_psmx_iox = (strcmp(CDDS_PSMX_NAME, "iox") == 0); + ddsrt_free(CDDS_PSMX_NAME); + + if ( using_psmx_iox ) { + { + // Test case using a config with specified locator. + uint8_t locator_in[16]; + memset(locator_in, 0x0, sizeof(locator_in)); // avoid warning 'uninitialized value' + ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; + ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; + char config_str[100]; + { + // Create the config string including a locator. + char locator_str[50]; + uint8_t* l = locator_in; + snprintf( + locator_str, + sizeof(locator_str), + "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] + ); + snprintf( + config_str, + sizeof(config_str), + "SERVICE_NAME=psmx1;%s", + locator_str + ); + } + dds_entity_t domain = -1; + { + // Create domain + struct ddsi_config cfg; + ddsi_config_init_default(&cfg); + cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); + cfg.psmx_instances->cfg.name = ddsrt_strdup("iox"); + cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_iox"); + cfg.psmx_instances->cfg.config = ddsrt_strdup(config_str); // config including a locator + cfg.psmx_instances->cfg.priority.isdefault = 1; + cfg.psmx_instances->cfg.priority.value = 1000000; + cfg.psmx_instances->next = NULL; + domain = dds_create_domain_with_rawconfig(domainId, &cfg); + config_psmx_elm_free(cfg.psmx_instances); + } + CU_ASSERT_FATAL(domain > 0); + CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "CycloneDDS-IOX-PSMX")); + dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + dds_entity_t writer1, reader1, writer2, reader2; + { + char topicname[100]; + dds_qos_t* qos = dds_create_qos(); + dds_qset_history(qos, DDS_HISTORY_KEEP_LAST, sample_cnt); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, qos, NULL); + CU_ASSERT_FATAL(topic1 > 0); + + writer1 = dds_create_writer(participant, topic1, qos, NULL); + CU_ASSERT_FATAL(writer1 > 0); + + reader1 = dds_create_reader(participant, topic1, qos, NULL); + CU_ASSERT_FATAL(reader1 > 0); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, qos, NULL); + CU_ASSERT_FATAL(topic2 > 0); + + writer2 = dds_create_writer(participant, topic2, qos, NULL); + CU_ASSERT_FATAL(writer2 > 0); + + reader2 = dds_create_reader(participant, topic2, qos, NULL); + CU_ASSERT_FATAL(reader2 > 0); + dds_delete_qos(qos); + } + { + // Check that I get the same locator that I provided with the config. + ddsi_locator_t locator_out; + CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); + CU_ASSERT_FATAL(memcmp(locator_in, locator_out.address, sizeof(locator_in)) == 0); + } + { + // Check that shared memory is enabled. + bool psmx_enabled; + psmx_enabled = endpoint_has_psmx_enabled(writer1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1)); + + psmx_enabled = endpoint_has_psmx_enabled(reader1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1)); + + psmx_enabled = endpoint_has_psmx_enabled(writer2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2)); + + psmx_enabled = endpoint_has_psmx_enabled(reader2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2)); + } + { + // Write and read samples + dds_return_t result; + { + SC_Model* sample; + for (int i = 0; i < sample_cnt; ++i) { + result = dds_request_loan(writer1, (void**)&sample); + CU_ASSERT_FATAL(result == DDS_RETCODE_OK); + sample->a = (uint8_t)i; + sample->b = (uint8_t)i + 1; + sample->c = (uint8_t)i + 2; + result = dds_write(writer1, sample); + CU_ASSERT_FATAL(result == DDS_RETCODE_OK); + } + } + { + PsmxType1* sample; + for (int i = 0; i < sample_cnt; ++i) { + result = dds_request_loan(writer2, (void**)&sample); + CU_ASSERT_FATAL(result == DDS_RETCODE_OK); + sample->xy.x = i; + sample->xy.y = (uint8_t)i + 1; + sample->z = (uint8_t)i + 2; + result = dds_write(writer2, sample); + CU_ASSERT_FATAL(result == DDS_RETCODE_OK); + } + } + + { + SC_Model* samples1[sample_cnt]; + dds_sample_info_t sinfo[sample_cnt]; + const dds_entity_t ws1 = dds_create_waitset(participant); + dds_entity_t rcond1 = dds_create_readcondition(reader1, DDS_ANY_STATE); + memset(samples1, 0x0, sizeof(samples1)); + dds_waitset_attach(ws1, rcond1, rcond1); + + int j; + int cnt = 0; + bool match = true; + while ( match && cnt < sample_cnt ) { + result = dds_waitset_wait(ws1, NULL, 0, DDS_SECS(10)); + CU_ASSERT_FATAL(result > 0); + result = dds_take(reader1, (void**)samples1, sinfo, sample_cnt, sample_cnt); + CU_ASSERT_FATAL(result > 0); + for (int i = 0; i < result; ++i) { + j = cnt + i; + if ( + samples1[i]->a != j || + samples1[i]->b != j + 1 || + samples1[i]->c != j + 2 + ) { + match = false; + } + } + cnt += result; + } + CU_ASSERT_FATAL(match); + CU_ASSERT_FATAL(cnt == sample_cnt); + } + { + PsmxType1* samples2[sample_cnt]; + dds_sample_info_t sinfo[sample_cnt]; + const dds_entity_t ws2 = dds_create_waitset(participant); + dds_entity_t rcond2 = dds_create_readcondition(reader2, DDS_ANY_STATE); + memset(samples2, 0x0, sizeof(samples2)); + dds_waitset_attach(ws2, rcond2, rcond2); + + int j; + int cnt = 0; + bool match = true; + while ( match && cnt < sample_cnt ) { + result = dds_waitset_wait(ws2, NULL, 0, DDS_SECS(10)); + CU_ASSERT_FATAL(result > 0); + result = dds_take(reader2, (void**)samples2, sinfo, sample_cnt, sample_cnt); + CU_ASSERT_FATAL(result > 0); + for (int i = 0; i < result; ++i) { + j = cnt + i; + if ( + samples2[i]->xy.x != j || + samples2[i]->xy.y != (uint8_t)j + 1 || + samples2[i]->z != (uint8_t)j + 2 + ) { + match = false; + } + } + cnt += result; + } + CU_ASSERT_FATAL(match); + CU_ASSERT_FATAL(cnt == sample_cnt); + } + } + dds_delete(domain); + } + { + // Test case using a config without specifying a locator. + dds_entity_t domain = -1; + { + // Create domain + struct ddsi_config cfg; + ddsi_config_init_default(&cfg); + cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); + cfg.psmx_instances->cfg.name = ddsrt_strdup("iox"); + cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_iox"); + cfg.psmx_instances->cfg.config = ddsrt_strdup("SERVICE_NAME=psmx1;"); // config witout a locator + cfg.psmx_instances->cfg.priority.isdefault = 1; + cfg.psmx_instances->cfg.priority.value = 1000000; + cfg.psmx_instances->next = NULL; + domain = dds_create_domain_with_rawconfig(domainId, &cfg); + config_psmx_elm_free(cfg.psmx_instances); + } + CU_ASSERT_FATAL(domain > 0); + CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "CycloneDDS-IOX-PSMX")); + dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + { + // Check that the second half of the locator is all zeros. + uint8_t locator_zero[16]; + size_t locator_size_half = sizeof(locator_zero) / 2; + memset(locator_zero, 0, sizeof(locator_zero)); + ddsi_locator_t locator_out; + CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); + CU_ASSERT_FATAL(memcmp(locator_zero + locator_size_half, locator_out.address + locator_size_half, locator_size_half) == 0); + } + { + // Check that shared memory is enabled. + bool psmx_enabled; + char topicname[100]; + { + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic > 0); + + dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(writer > 0); + psmx_enabled = endpoint_has_psmx_enabled(writer); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer)); + + dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(reader > 0); + psmx_enabled = endpoint_has_psmx_enabled(reader); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader)); + } + { + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic > 0); + + dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(writer > 0); + psmx_enabled = endpoint_has_psmx_enabled(writer); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer)); + + dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(reader > 0); + psmx_enabled = endpoint_has_psmx_enabled(reader); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader)); + } + } + dds_delete(domain); + } + } else { + // No shared memory. + dds_entity_t domain = -1; + { + // Create domain + struct ddsi_config cfg; + ddsi_config_init_default(&cfg); + cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); + cfg.psmx_instances->cfg.name = ddsrt_strdup("cdds"); + cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_cdds"); + cfg.psmx_instances->cfg.config = ddsrt_strdup("SERVICE_NAME=cdds1;"); + cfg.psmx_instances->cfg.priority.isdefault = 1; + cfg.psmx_instances->cfg.priority.value = 1000000; + cfg.psmx_instances->next = NULL; + domain = dds_create_domain_with_rawconfig(domainId, &cfg); + config_psmx_elm_free(cfg.psmx_instances); + } + CU_ASSERT_FATAL(domain > 0); + CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "cdds-psmx")); + dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + { + // Check that shared memory is not enabled. + bool psmx_enabled; + char topicname[100]; + { + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic > 0); + + dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(writer > 0); + psmx_enabled = endpoint_has_psmx_enabled(writer); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(!dds_is_shared_memory_available(writer)); + + dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(reader > 0); + psmx_enabled = endpoint_has_psmx_enabled(reader); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(!dds_is_shared_memory_available(reader)); + } + { + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic > 0); + + dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(writer > 0); + psmx_enabled = endpoint_has_psmx_enabled(writer); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(!dds_is_shared_memory_available(writer)); + + dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(reader > 0); + psmx_enabled = endpoint_has_psmx_enabled(reader); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(!dds_is_shared_memory_available(reader)); + } + } + dds_delete(domain); + } + #undef sample_cnt +} + CU_Test (ddsc_psmx, zero_copy) { const struct { From 6ee007116da79341ff89add24804c21e39112cd7 Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Tue, 19 Mar 2024 15:38:45 +0100 Subject: [PATCH 2/6] process review of 'add psmx test...' Signed-off-by: Michel van den Hoek --- src/core/ddsc/tests/psmx.c | 500 +++++++++---------------------------- 1 file changed, 120 insertions(+), 380 deletions(-) diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index 3eecc53169..b1310cd96a 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -64,17 +64,35 @@ static void fail_too_much_data (void) { fail (); } #define TRACE_CATEGORY "discovery" #endif -static dds_entity_t create_participant (dds_domainid_t int_dom) +/** + * @brief Create a participant object + * + * @param[in] int_dom the domain id + * @param[in] locator an array of 16 bytes, NULL if not used + * @return dds_entity_t the participant + */ +static dds_entity_t create_participant (dds_domainid_t int_dom, const uint8_t* locator) { assert (int_dom < MAX_DOMAINS); - const unsigned char *l = psmx_locators[int_dom].a; char *configstr; + char locator_str[74]; + if ( locator == NULL ) { + locator_str[0] = 0; + } else { + const uint8_t *l = locator; + snprintf( + locator_str, + sizeof(locator_str), + "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x;", + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] + ); + } ddsrt_asprintf (&configstr, "\ ${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ \ spdp\ \ - \ + \ \ \ \ @@ -86,8 +104,8 @@ static dds_entity_t create_participant (dds_domainid_t int_dom) cdds.log.%d\ \ ", - l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15], - (int) l[0], // This prevents Iceoryx and Cyclone-based plugins from forwarding across the "network" + locator_str, + locator ? (int) locator[0] : -1, // This prevents Iceoryx and Cyclone-based plugins from forwarding across the "network" (int) int_dom // log file name ); char *xconfigstr = ddsrt_expand_envvars (configstr, int_dom); @@ -100,28 +118,18 @@ static dds_entity_t create_participant (dds_domainid_t int_dom) return pp; } -static void config_psmx_free_content(struct ddsi_config_psmx* cfg) -{ - assert(cfg); - ddsrt_free(cfg->name); - ddsrt_free(cfg->library); - ddsrt_free(cfg->config); -} - -static void config_psmx_elm_free(struct ddsi_config_psmx_listelem* elm) -{ - assert(elm); - config_psmx_free_content(&elm->cfg); - ddsrt_free(elm); -} - static bool domain_pin(dds_entity_t domain, dds_domain** dom_out) { dds_entity* x = NULL; dds_return_t rc = dds_entity_pin(domain, &x); - if ( rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_DOMAIN ) { - *dom_out = (dds_domain*)x; - return true; + if ( rc == DDS_RETCODE_OK ) { + if( dds_entity_kind(x) == DDS_KIND_DOMAIN ) { + *dom_out = (dds_domain*)x; + return true; + } else { + dds_entity_unpin(x); + return false; + } } return false; } @@ -143,24 +151,6 @@ static bool domain_get_psmx_locator(dds_entity_t domain, ddsi_locator_t* l_out) return ret; } -static bool domain_has_psmx_instance_name(dds_entity_t domain, const char* inst_name) -{ - dds_domain* dom = NULL; - bool ret = domain_pin(domain, &dom); - bool match = false; - if ( ret ) { - uint32_t len = dom->psmx_instances.length; - for (uint32_t idx = 0; idx < len; ++idx) { - if ( strcmp(dom->psmx_instances.instances[idx]->instance_name, inst_name) == 0 ) { - match = true; - break; - } - } - domain_unpin(dom); - } - return ret && match; -} - struct tracebuf { char buf[512]; size_t pos; @@ -697,7 +687,7 @@ static void dotest (const dds_topic_descriptor_t *tpdesc, const void *sample, bo create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); for (int i = 0; i < MAX_DOMAINS; i++) { - pp[i] = create_participant ((dds_domainid_t) i); // FIXME: vary shm_enable for i > 0 + pp[i] = create_participant ((dds_domainid_t) i, psmx_locators[(dds_domainid_t) i].a); // FIXME: vary shm_enable for i > 0 tp[i] = dds_create_topic (pp[i], tpdesc, topicname, NULL, NULL); CU_ASSERT_FATAL (tp[i] > 0); gvs[i] = get_domaingv (pp[i]); @@ -1150,7 +1140,7 @@ CU_Test(ddsc_psmx, one_writer_forwardcdr_dynsize_strkey, .timeout = 240) CU_Test(ddsc_psmx, return_loan) { dds_return_t rc; - const dds_entity_t pp = create_participant (0); + const dds_entity_t pp = create_participant (0, psmx_locators[0].a); char topicname[100]; create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); const dds_entity_t tp = dds_create_topic (pp, &Array100_desc, topicname, NULL, NULL); @@ -1175,7 +1165,7 @@ CU_Test(ddsc_psmx, return_loan) CU_Test(ddsc_psmx, partition_xtalk) { dds_return_t rc; - const dds_entity_t pp = create_participant (0); + const dds_entity_t pp = create_participant (0, psmx_locators[0].a); char topicname[100]; create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); const dds_entity_t tp = dds_create_topic (pp, &PsmxType1_desc, topicname, NULL, NULL); @@ -1280,7 +1270,7 @@ CU_Test (ddsc_psmx, basic) dds_entity_t participant, topic, writer, reader; bool psmx_enabled; - participant = create_participant (0); + participant = create_participant (0, psmx_locators[0].a); CU_ASSERT_FATAL (participant > 0); char topicname[100]; @@ -1327,39 +1317,36 @@ CU_Test (ddsc_psmx, basic) dds_delete (dds_get_parent (participant)); } -/// @brief Check that shared memory can be enabled and the used locator can be set. +/// @brief Check that shared memory can be enabled (when supported) and the used locator can be set. /// @methodology -/// Case with Iceoryx: /// - Check that the data types I'm planning to use are actually suitable for use with shared memory. /// - Expectation: They are memcopy-safe. -/// - Create a configuration with a psmx interface capable of shared memory and specify the locator. +/// +/// - Create a configuration with a psmx interface and specify the locator. /// - Create a domain using this configuration. -/// - Create some entities -/// - Check the locator used for shared memory. +/// - Check the locator used by the psmx instance. /// - Expectation: The locator is the same as specified in the config for the domain. +/// - Query whether shared memory is supported. +/// - Assert that there is exactly one psmx instance. +/// - Assert that the psmx instance has a nonempty instance_name. +/// - Create some entities /// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled. -/// - Write and read some topics. -/// - Expectation: Sent data is received by readers. +/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). /// /// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. /// - Create a domain using this configuration. +/// - Query whether shared memory is supported. +/// - Assert that there is exactly one psmx instance. +/// - Assert that the psmx instance has a nonempty instance_name. /// - Create some entities -/// - Check the locator used for shared memory. -/// - Expectation: The second half of the locator is all zeros. /// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled. +/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// +/// - The above two cases (with and without locator) are repeated for a +/// psmx interface that supports shared memory, and a psmx interface that doesn't. /// -/// Case with cdds: -/// - Create a configuration with a psmx interface incapable of shared memory. -/// - Create a domain using this configuration. -/// - Create some entities -/// - Check if shared memory is enabled. -/// - Expectation: Shared memory is not enabled. -/// CU_Test(ddsc_psmx, shared_memory) { - #define sample_cnt 8 { // Check that the data types I'm planning to use are actually suitable for use with shared memory. dds_data_type_properties_t props; @@ -1369,334 +1356,87 @@ CU_Test(ddsc_psmx, shared_memory) CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); } - const dds_domainid_t domainId = 42; - char* CDDS_PSMX_NAME = ddsrt_expand_envvars("${CDDS_PSMX_NAME:-cdds}", domainId); - bool using_psmx_iox = (strcmp(CDDS_PSMX_NAME, "iox") == 0); - ddsrt_free(CDDS_PSMX_NAME); + const dds_domainid_t domainId = 3; + bool supports_shared_memory = false; // Overwritten by `plugin's supported_features()`. + bool specify_locator[] = {true, false}; + uint8_t locator_in[16]; + memset(locator_in, 0x0, sizeof(locator_in)); // Avoid warning 'uninitialized value'. + ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; + ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; + + int N = sizeof(specify_locator) / sizeof(specify_locator[0]); + for (int i = 0; i < N; ++i) + { + const uint8_t* locator = specify_locator[i] ? locator_in : NULL; + dds_entity_t participant = create_participant(domainId, locator); + CU_ASSERT_FATAL(participant > 0); + dds_entity_t domain = dds_get_parent(participant); + CU_ASSERT_FATAL(domain > 0); + if ( specify_locator[i] ) { + // Check that I get the same locator that I provided with the config. + ddsi_locator_t locator_out; + CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); + CU_ASSERT_FATAL(memcmp(locator_in, locator_out.address, sizeof(locator_out.address)) == 0); + } - if ( using_psmx_iox ) { { - // Test case using a config with specified locator. - uint8_t locator_in[16]; - memset(locator_in, 0x0, sizeof(locator_in)); // avoid warning 'uninitialized value' - ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; - ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; - char config_str[100]; - { - // Create the config string including a locator. - char locator_str[50]; - uint8_t* l = locator_in; - snprintf( - locator_str, - sizeof(locator_str), - "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] - ); - snprintf( - config_str, - sizeof(config_str), - "SERVICE_NAME=psmx1;%s", - locator_str - ); - } - dds_entity_t domain = -1; - { - // Create domain - struct ddsi_config cfg; - ddsi_config_init_default(&cfg); - cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); - cfg.psmx_instances->cfg.name = ddsrt_strdup("iox"); - cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_iox"); - cfg.psmx_instances->cfg.config = ddsrt_strdup(config_str); // config including a locator - cfg.psmx_instances->cfg.priority.isdefault = 1; - cfg.psmx_instances->cfg.priority.value = 1000000; - cfg.psmx_instances->next = NULL; - domain = dds_create_domain_with_rawconfig(domainId, &cfg); - config_psmx_elm_free(cfg.psmx_instances); - } - CU_ASSERT_FATAL(domain > 0); - CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "CycloneDDS-IOX-PSMX")); - dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); - CU_ASSERT_FATAL(participant > 0); - dds_entity_t writer1, reader1, writer2, reader2; - { - char topicname[100]; - dds_qos_t* qos = dds_create_qos(); - dds_qset_history(qos, DDS_HISTORY_KEEP_LAST, sample_cnt); + // Query whether shared memory is supported. + dds_domain* dom = NULL; + CU_ASSERT_FATAL(domain_pin(domain, &dom)); + CU_ASSERT_FATAL(dom->psmx_instances.length == 1); // There shall be exactly one psmx instance. + dds_psmx_t* psmx_ptr = dom->psmx_instances.instances[0]; + // The psmx must have an instance_name that is not an empty string. + CU_ASSERT_FATAL(psmx_ptr->instance_name != NULL && strcmp(psmx_ptr->instance_name, "") != 0); + supports_shared_memory = ((psmx_ptr->ops.supported_features(psmx_ptr) & DDS_PSMX_FEATURE_SHARED_MEMORY) == DDS_PSMX_FEATURE_SHARED_MEMORY); + domain_unpin(dom); + } + dds_entity_t writer1, reader1, writer2, reader2; + { + char topicname[100]; + dds_qos_t* qos = dds_create_qos(); - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, qos, NULL); - CU_ASSERT_FATAL(topic1 > 0); + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, qos, NULL); + CU_ASSERT_FATAL(topic1 > 0); - writer1 = dds_create_writer(participant, topic1, qos, NULL); - CU_ASSERT_FATAL(writer1 > 0); + writer1 = dds_create_writer(participant, topic1, qos, NULL); + CU_ASSERT_FATAL(writer1 > 0); - reader1 = dds_create_reader(participant, topic1, qos, NULL); - CU_ASSERT_FATAL(reader1 > 0); + reader1 = dds_create_reader(participant, topic1, qos, NULL); + CU_ASSERT_FATAL(reader1 > 0); - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, qos, NULL); - CU_ASSERT_FATAL(topic2 > 0); + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, qos, NULL); + CU_ASSERT_FATAL(topic2 > 0); - writer2 = dds_create_writer(participant, topic2, qos, NULL); - CU_ASSERT_FATAL(writer2 > 0); + writer2 = dds_create_writer(participant, topic2, qos, NULL); + CU_ASSERT_FATAL(writer2 > 0); - reader2 = dds_create_reader(participant, topic2, qos, NULL); - CU_ASSERT_FATAL(reader2 > 0); - dds_delete_qos(qos); - } - { - // Check that I get the same locator that I provided with the config. - ddsi_locator_t locator_out; - CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); - CU_ASSERT_FATAL(memcmp(locator_in, locator_out.address, sizeof(locator_in)) == 0); - } - { - // Check that shared memory is enabled. - bool psmx_enabled; - psmx_enabled = endpoint_has_psmx_enabled(writer1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1)); - - psmx_enabled = endpoint_has_psmx_enabled(reader1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1)); - - psmx_enabled = endpoint_has_psmx_enabled(writer2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2)); - - psmx_enabled = endpoint_has_psmx_enabled(reader2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2)); - } - { - // Write and read samples - dds_return_t result; - { - SC_Model* sample; - for (int i = 0; i < sample_cnt; ++i) { - result = dds_request_loan(writer1, (void**)&sample); - CU_ASSERT_FATAL(result == DDS_RETCODE_OK); - sample->a = (uint8_t)i; - sample->b = (uint8_t)i + 1; - sample->c = (uint8_t)i + 2; - result = dds_write(writer1, sample); - CU_ASSERT_FATAL(result == DDS_RETCODE_OK); - } - } - { - PsmxType1* sample; - for (int i = 0; i < sample_cnt; ++i) { - result = dds_request_loan(writer2, (void**)&sample); - CU_ASSERT_FATAL(result == DDS_RETCODE_OK); - sample->xy.x = i; - sample->xy.y = (uint8_t)i + 1; - sample->z = (uint8_t)i + 2; - result = dds_write(writer2, sample); - CU_ASSERT_FATAL(result == DDS_RETCODE_OK); - } - } - - { - SC_Model* samples1[sample_cnt]; - dds_sample_info_t sinfo[sample_cnt]; - const dds_entity_t ws1 = dds_create_waitset(participant); - dds_entity_t rcond1 = dds_create_readcondition(reader1, DDS_ANY_STATE); - memset(samples1, 0x0, sizeof(samples1)); - dds_waitset_attach(ws1, rcond1, rcond1); - - int j; - int cnt = 0; - bool match = true; - while ( match && cnt < sample_cnt ) { - result = dds_waitset_wait(ws1, NULL, 0, DDS_SECS(10)); - CU_ASSERT_FATAL(result > 0); - result = dds_take(reader1, (void**)samples1, sinfo, sample_cnt, sample_cnt); - CU_ASSERT_FATAL(result > 0); - for (int i = 0; i < result; ++i) { - j = cnt + i; - if ( - samples1[i]->a != j || - samples1[i]->b != j + 1 || - samples1[i]->c != j + 2 - ) { - match = false; - } - } - cnt += result; - } - CU_ASSERT_FATAL(match); - CU_ASSERT_FATAL(cnt == sample_cnt); - } - { - PsmxType1* samples2[sample_cnt]; - dds_sample_info_t sinfo[sample_cnt]; - const dds_entity_t ws2 = dds_create_waitset(participant); - dds_entity_t rcond2 = dds_create_readcondition(reader2, DDS_ANY_STATE); - memset(samples2, 0x0, sizeof(samples2)); - dds_waitset_attach(ws2, rcond2, rcond2); - - int j; - int cnt = 0; - bool match = true; - while ( match && cnt < sample_cnt ) { - result = dds_waitset_wait(ws2, NULL, 0, DDS_SECS(10)); - CU_ASSERT_FATAL(result > 0); - result = dds_take(reader2, (void**)samples2, sinfo, sample_cnt, sample_cnt); - CU_ASSERT_FATAL(result > 0); - for (int i = 0; i < result; ++i) { - j = cnt + i; - if ( - samples2[i]->xy.x != j || - samples2[i]->xy.y != (uint8_t)j + 1 || - samples2[i]->z != (uint8_t)j + 2 - ) { - match = false; - } - } - cnt += result; - } - CU_ASSERT_FATAL(match); - CU_ASSERT_FATAL(cnt == sample_cnt); - } - } - dds_delete(domain); - } - { - // Test case using a config without specifying a locator. - dds_entity_t domain = -1; - { - // Create domain - struct ddsi_config cfg; - ddsi_config_init_default(&cfg); - cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); - cfg.psmx_instances->cfg.name = ddsrt_strdup("iox"); - cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_iox"); - cfg.psmx_instances->cfg.config = ddsrt_strdup("SERVICE_NAME=psmx1;"); // config witout a locator - cfg.psmx_instances->cfg.priority.isdefault = 1; - cfg.psmx_instances->cfg.priority.value = 1000000; - cfg.psmx_instances->next = NULL; - domain = dds_create_domain_with_rawconfig(domainId, &cfg); - config_psmx_elm_free(cfg.psmx_instances); - } - CU_ASSERT_FATAL(domain > 0); - CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "CycloneDDS-IOX-PSMX")); - dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); - CU_ASSERT_FATAL(participant > 0); - { - // Check that the second half of the locator is all zeros. - uint8_t locator_zero[16]; - size_t locator_size_half = sizeof(locator_zero) / 2; - memset(locator_zero, 0, sizeof(locator_zero)); - ddsi_locator_t locator_out; - CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); - CU_ASSERT_FATAL(memcmp(locator_zero + locator_size_half, locator_out.address + locator_size_half, locator_size_half) == 0); - } - { - // Check that shared memory is enabled. - bool psmx_enabled; - char topicname[100]; - { - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic > 0); - - dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(writer > 0); - psmx_enabled = endpoint_has_psmx_enabled(writer); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer)); - - dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(reader > 0); - psmx_enabled = endpoint_has_psmx_enabled(reader); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader)); - } - { - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic > 0); - - dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(writer > 0); - psmx_enabled = endpoint_has_psmx_enabled(writer); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer)); - - dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(reader > 0); - psmx_enabled = endpoint_has_psmx_enabled(reader); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader)); - } - } - dds_delete(domain); + reader2 = dds_create_reader(participant, topic2, qos, NULL); + CU_ASSERT_FATAL(reader2 > 0); + dds_delete_qos(qos); } - } else { - // No shared memory. - dds_entity_t domain = -1; { - // Create domain - struct ddsi_config cfg; - ddsi_config_init_default(&cfg); - cfg.psmx_instances = ddsrt_malloc(sizeof(*cfg.psmx_instances)); - cfg.psmx_instances->cfg.name = ddsrt_strdup("cdds"); - cfg.psmx_instances->cfg.library = ddsrt_strdup("psmx_cdds"); - cfg.psmx_instances->cfg.config = ddsrt_strdup("SERVICE_NAME=cdds1;"); - cfg.psmx_instances->cfg.priority.isdefault = 1; - cfg.psmx_instances->cfg.priority.value = 1000000; - cfg.psmx_instances->next = NULL; - domain = dds_create_domain_with_rawconfig(domainId, &cfg); - config_psmx_elm_free(cfg.psmx_instances); - } - CU_ASSERT_FATAL(domain > 0); - CU_ASSERT_FATAL(domain_has_psmx_instance_name(domain, "cdds-psmx")); - dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); - CU_ASSERT_FATAL(participant > 0); - { - // Check that shared memory is not enabled. + // Check that shared memory is enabled when it should, and not enabled when it shouldn't. bool psmx_enabled; - char topicname[100]; - { - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic > 0); - - dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(writer > 0); - psmx_enabled = endpoint_has_psmx_enabled(writer); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(!dds_is_shared_memory_available(writer)); - - dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(reader > 0); - psmx_enabled = endpoint_has_psmx_enabled(reader); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(!dds_is_shared_memory_available(reader)); - } - { - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic > 0); - - dds_entity_t writer = dds_create_writer(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(writer > 0); - psmx_enabled = endpoint_has_psmx_enabled(writer); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(!dds_is_shared_memory_available(writer)); - - dds_entity_t reader = dds_create_reader(participant, topic, NULL, NULL); - CU_ASSERT_FATAL(reader > 0); - psmx_enabled = endpoint_has_psmx_enabled(reader); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(!dds_is_shared_memory_available(reader)); - } + psmx_enabled = endpoint_has_psmx_enabled(writer1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1) == supports_shared_memory); + + psmx_enabled = endpoint_has_psmx_enabled(reader1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1) == supports_shared_memory); + + psmx_enabled = endpoint_has_psmx_enabled(writer2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2) == supports_shared_memory); + + psmx_enabled = endpoint_has_psmx_enabled(reader2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory); } dds_delete(domain); } - #undef sample_cnt } CU_Test (ddsc_psmx, zero_copy) @@ -1711,7 +1451,7 @@ CU_Test (ddsc_psmx, zero_copy) }; dds_return_t rc; - const dds_entity_t pp = create_participant (0); + const dds_entity_t pp = create_participant (0, psmx_locators[0].a); CU_ASSERT_FATAL (pp > 0); for (size_t k = 0; k < sizeof (cases) / sizeof (cases[0]); k++) { @@ -1907,7 +1647,7 @@ CU_Test (ddsc_psmx, writer_loan) }; dds_return_t rc; - const dds_entity_t pp = create_participant (0); + const dds_entity_t pp = create_participant (0, psmx_locators[0].a); CU_ASSERT_FATAL (pp > 0); for (size_t k = 0; k < sizeof (cases) / sizeof (cases[0]); k++) { From 4901b3da32669fdd3d87e0b73d4070d768ff5679 Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Wed, 3 Apr 2024 13:39:36 +0200 Subject: [PATCH 3/6] add dummy psmx interface Signed-off-by: Michel van den Hoek --- src/core/ddsc/tests/CMakeLists.txt | 34 +++ src/core/ddsc/tests/psmx.c | 125 ++++++++++- src/core/ddsc/tests/psmx_dummy_impl.c | 274 ++++++++++++++++++++++++ src/core/ddsc/tests/psmx_dummy_impl.h | 26 +++ src/core/ddsc/tests/psmx_dummy_public.h | 48 +++++ 5 files changed, 500 insertions(+), 7 deletions(-) create mode 100644 src/core/ddsc/tests/psmx_dummy_impl.c create mode 100644 src/core/ddsc/tests/psmx_dummy_impl.h create mode 100644 src/core/ddsc/tests/psmx_dummy_public.h diff --git a/src/core/ddsc/tests/CMakeLists.txt b/src/core/ddsc/tests/CMakeLists.txt index 27007b0357..d975cc3c38 100644 --- a/src/core/ddsc/tests/CMakeLists.txt +++ b/src/core/ddsc/tests/CMakeLists.txt @@ -243,6 +243,40 @@ target_include_directories( target_link_libraries(oneliner PRIVATE RoundTrip Space ddsc) +# PSMX dummy implementation for interface testing +set( + psmx_dummy_sources + "psmx_dummy_impl.c" + "psmx_dummy_impl.h" +) +if(BUILD_SHARED_LIBS) + add_library(psmx_dummy SHARED ${psmx_dummy_sources}) +else() + add_library(psmx_dummy OBJECT ${psmx_dummy_sources}) + set_property(GLOBAL APPEND PROPERTY cdds_plugin_list psmx_dummy) + set_property(GLOBAL PROPERTY psmx_dummy_symbols dummy_create_psmx) +endif() +generate_export_header(psmx_dummy BASE_NAME PSMX_DUMMY EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/dds/psmx_dummy/export.h") +target_include_directories( + psmx_dummy PRIVATE + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) +if(BUILD_SHARED_LIBS) + target_link_libraries(psmx_dummy PRIVATE ddsc) +endif() +install( + TARGETS psmx_dummy + EXPORT "${PROJECT_NAME}" + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) # PSMX implementation with Cyclone as transport, for testing if (BUILD_SHARED_LIBS) diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index b1310cd96a..1e1feed491 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -31,6 +31,7 @@ #include "config_env.h" #include "test_common.h" +#include "psmx_dummy_public.h" #include "Array100.h" #include "DynamicData.h" #include "PsmxDataModels.h" @@ -1032,6 +1033,80 @@ static bool eq_DynamicData_KMsg (const void *vsent, const void *vrecvd, bool val } } +/** @brief Compare two sets of stats. + * + * @param[in] mockstats1 first stats to compare + * @param[in] mockstats2 second stats to compare + * + * @return A bitmask to indicate which elements are different, zero if all are equal. +*/ +static uint32_t dummy_mockstats_cmp(const dummy_mockstats_t* mockstats1, const dummy_mockstats_t* mockstats2) +{ + uint32_t cmp = 0; + uint32_t shift = 0; + + cmp |= ((uint32_t)(mockstats1->cnt_type_qos_supported != mockstats2->cnt_type_qos_supported) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_create_topic != mockstats2->cnt_create_topic) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_delete_topic != mockstats2->cnt_delete_topic) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_deinit != mockstats2->cnt_deinit) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_get_node_id != mockstats2->cnt_get_node_id) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_supported_features != mockstats2->cnt_supported_features) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_create_endpoint != mockstats2->cnt_create_endpoint) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_delete_endpoint != mockstats2->cnt_delete_endpoint) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_request_loan != mockstats2->cnt_request_loan) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_write != mockstats2->cnt_write) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_take != mockstats2->cnt_take) << shift++); + cmp |= ((uint32_t)(mockstats1->cnt_on_data_available != mockstats2->cnt_on_data_available) << shift++); + return cmp; +} + +/** @brief Convert a set of stats to a string. + * + * Truncates the output string if the string buffer capacity is too small. + * + * @param[in] dmock stats to convert + * @param[out] str_out string buffer to write the string into + * @param[in] str_capacity number of bytes the string buffer can hold + * + * @return Upon successful return, it returns the number of characters printed + * (excluding the null byte used to end output to strings). + * If an output error is encountered, a negative value is returned. +*/ +static int dummy_mockstats_tostring(const dummy_mockstats_t* dmock, char* str_out, size_t str_capacity) +{ + return snprintf( + str_out, + str_capacity, + "\ + type_qos_supported: %i\n\ + create_topic: %i\n\ + delete_topic: %i\n\ + deinit: %i\n\ + get_node_id: %i\n\ + supported_features: %i\n\ + \n\ + create_endpoint: %i\n\ + delete_endpoint: %i\n\ + \n\ + request_loan: %i\n\ + write: %i\n\ + take: %i\n\ + on_data_available: %i\n", + dmock->cnt_type_qos_supported, + dmock->cnt_create_topic, + dmock->cnt_delete_topic, + dmock->cnt_deinit, + dmock->cnt_get_node_id, + dmock->cnt_supported_features, + dmock->cnt_create_endpoint, + dmock->cnt_delete_endpoint, + dmock->cnt_request_loan, + dmock->cnt_write, + dmock->cnt_take, + dmock->cnt_on_data_available + ); +} + CU_Test(ddsc_psmx, one_writer, .timeout = 240) { failed = false; @@ -1332,6 +1407,9 @@ CU_Test (ddsc_psmx, basic) /// - Create some entities /// - Check if shared memory is enabled. /// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// - Delete the domain +/// - Check the function call counts of the dummy psmx. +/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. /// /// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. /// - Create a domain using this configuration. @@ -1341,12 +1419,19 @@ CU_Test (ddsc_psmx, basic) /// - Create some entities /// - Check if shared memory is enabled. /// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// - Delete the domain +/// - Check the function call counts of the dummy psmx. +/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. /// -/// - The above two cases (with and without locator) are repeated for a -/// psmx interface that supports shared memory, and a psmx interface that doesn't. -/// -CU_Test(ddsc_psmx, shared_memory) +CU_Test(ddsc_psmxif, shared_memory) { + const char* CDDS_PSMX_NAME = NULL; + ddsrt_getenv("CDDS_PSMX_NAME", &CDDS_PSMX_NAME); + ddsrt_setenv("CDDS_PSMX_NAME", "dummy"); // Make `create_participant()` use the dummy psmx. + dummy_mockstats_t dmock, dmock_expected; + char strbuf1[512]; + char strbuf2[512]; + size_t strbuf_size = sizeof(strbuf1); { // Check that the data types I'm planning to use are actually suitable for use with shared memory. dds_data_type_properties_t props; @@ -1384,10 +1469,12 @@ CU_Test(ddsc_psmx, shared_memory) dds_domain* dom = NULL; CU_ASSERT_FATAL(domain_pin(domain, &dom)); CU_ASSERT_FATAL(dom->psmx_instances.length == 1); // There shall be exactly one psmx instance. - dds_psmx_t* psmx_ptr = dom->psmx_instances.instances[0]; + struct dummy_psmx* dpsmx = (struct dummy_psmx*)dom->psmx_instances.instances[0]; + dpsmx->mockstats_get_ownership(&dmock); + // The psmx must have an instance_name that is not an empty string. - CU_ASSERT_FATAL(psmx_ptr->instance_name != NULL && strcmp(psmx_ptr->instance_name, "") != 0); - supports_shared_memory = ((psmx_ptr->ops.supported_features(psmx_ptr) & DDS_PSMX_FEATURE_SHARED_MEMORY) == DDS_PSMX_FEATURE_SHARED_MEMORY); + CU_ASSERT_FATAL(dpsmx->c.instance_name != NULL && strcmp(dpsmx->c.instance_name, "") != 0); + supports_shared_memory = ((dpsmx->c.ops.supported_features(&dpsmx->c) & DDS_PSMX_FEATURE_SHARED_MEMORY) == DDS_PSMX_FEATURE_SHARED_MEMORY); domain_unpin(dom); } dds_entity_t writer1, reader1, writer2, reader2; @@ -1416,6 +1503,7 @@ CU_Test(ddsc_psmx, shared_memory) CU_ASSERT_FATAL(reader2 > 0); dds_delete_qos(qos); } + { // Check that shared memory is enabled when it should, and not enabled when it shouldn't. bool psmx_enabled; @@ -1436,6 +1524,29 @@ CU_Test(ddsc_psmx, shared_memory) CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory); } dds_delete(domain); + + // Check number of calls against expected counts. + memset(&dmock_expected, 0, sizeof(dummy_mockstats_t)); + dmock_expected.cnt_type_qos_supported = 10; + dmock_expected.cnt_create_topic = 2; + dmock_expected.cnt_delete_topic = 2; + dmock_expected.cnt_deinit = 1; + dmock_expected.cnt_get_node_id = 1; + dmock_expected.cnt_supported_features = 5; + dmock_expected.cnt_create_endpoint = 4; + dmock_expected.cnt_delete_endpoint = 4; + dmock_expected.cnt_on_data_available = 2; // This is perhaps unexpected, see below. + /* + NOTE: The `on_data_available` is triggered in + `dds_create_reader_int()`, even if no samples have been received at all. + */ + uint32_t cmp = dummy_mockstats_cmp(&dmock, &dmock_expected); + if( cmp != 0 ){ + dummy_mockstats_tostring(&dmock, strbuf1, strbuf_size); + dummy_mockstats_tostring(&dmock_expected, strbuf2, strbuf_size); + printf("actual calls:\n%s\nexpected calls:\n%s\n", strbuf1, strbuf2); + CU_ASSERT_FATAL(cmp == 0); + } } } diff --git a/src/core/ddsc/tests/psmx_dummy_impl.c b/src/core/ddsc/tests/psmx_dummy_impl.c new file mode 100644 index 0000000000..a0c840cdd0 --- /dev/null +++ b/src/core/ddsc/tests/psmx_dummy_impl.c @@ -0,0 +1,274 @@ + +#include +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/string.h" +#include "dds/ddsrt/strtol.h" +#include "dds/ddsi/ddsi_locator.h" +#include "dds/ddsc/dds_psmx.h" +#include "psmx_dummy_public.h" +#include "psmx_dummy_impl.h" + +//### Helper functions ### + +static char* get_config_option_value (const char* conf, const char* option_name) +{ + char* copy = ddsrt_strdup(conf), *cursor = copy, *tok; + while ((tok = ddsrt_strsep(&cursor, ",/|;")) != NULL) + { + if (strlen(tok) == 0) + continue; + char* name = ddsrt_strsep(&tok, "="); + if (name == NULL || tok == NULL) + { + ddsrt_free(copy); + return NULL; + } + if (strcmp(name, option_name) == 0) + { + char* ret = ddsrt_strdup(tok); + ddsrt_free(copy); + return ret; + } + } + ddsrt_free(copy); + return NULL; +} + +//### Dynamic library functions ### + +static dummy_mockstats_t* g_mockstats; +static bool g_mockstats_owned; + +static bool dummy_psmx_type_qos_supported( + struct dds_psmx* psmx, + dds_psmx_endpoint_type_t forwhat, + dds_data_type_properties_t data_type_props, + const struct dds_qos* qos +); +static struct dds_psmx_topic* dummy_psmx_create_topic( + struct dds_psmx* psmx, + const char* topic_name, + const char* type_name, + dds_data_type_properties_t data_type_props +); +static dds_return_t dummy_psmx_delete_topic(struct dds_psmx_topic* psmx_topic); +static dds_return_t dummy_psmx_deinit(struct dds_psmx* psmx); +static dds_psmx_node_identifier_t dummy_psmx_get_node_id(const struct dds_psmx* psmx); +static dds_psmx_features_t dummy_supported_features(const struct dds_psmx* psmx); + +static struct dds_psmx_endpoint* dummy_psmx_create_endpoint( + struct dds_psmx_topic* psmx_topic, + const struct dds_qos* qos, + dds_psmx_endpoint_type_t endpoint_type +); +static dds_return_t dummy_psmx_delete_endpoint(struct dds_psmx_endpoint* psmx_endpoint); + +static dds_loaned_sample_t* dummy_psmx_ep_request_loan(struct dds_psmx_endpoint* psmx_endpoint, uint32_t size_requested); +static dds_return_t dummy_psmx_ep_write(struct dds_psmx_endpoint* psmx_endpoint, dds_loaned_sample_t* data); +static dds_loaned_sample_t* dummy_psmx_ep_take(struct dds_psmx_endpoint* psmx_endpoint); +static dds_return_t dummy_psmx_ep_on_data_available(struct dds_psmx_endpoint* psmx_endpoint, dds_entity_t reader); + +static const dds_psmx_ops_t psmx_instance_ops = { + .type_qos_supported = dummy_psmx_type_qos_supported, + .create_topic = dummy_psmx_create_topic, + .delete_topic = dummy_psmx_delete_topic, + .deinit = dummy_psmx_deinit, + .get_node_id = dummy_psmx_get_node_id, + .supported_features = dummy_supported_features +}; + +static void dummy_mockstats_get_ownership(dummy_mockstats_t* mockstats) +{ + // Transfer ownership of mockstats to user. + memcpy(mockstats, g_mockstats, sizeof(dummy_mockstats_t)); + if ( g_mockstats_owned ) { + ddsrt_free(g_mockstats); + g_mockstats_owned = false; + } + g_mockstats = mockstats; +} + +static bool dummy_psmx_type_qos_supported( + struct dds_psmx* psmx, + dds_psmx_endpoint_type_t forwhat, + dds_data_type_properties_t data_type_props, + const struct dds_qos* qos +) { + (void)psmx; + (void)forwhat; + (void)data_type_props; + (void)qos; + ++g_mockstats->cnt_type_qos_supported; + return true; +} + +static struct dds_psmx_topic* dummy_psmx_create_topic( + struct dds_psmx* psmx, + const char* topic_name, + const char* type_name, + dds_data_type_properties_t data_type_props +) { + (void)data_type_props; + struct dds_psmx_topic* topic = dds_alloc(sizeof(struct dds_psmx_topic)); + memset(topic, 0, sizeof(struct dds_psmx_topic)); + topic->ops.create_endpoint = dummy_psmx_create_endpoint; + topic->ops.delete_endpoint = dummy_psmx_delete_endpoint; + topic->psmx_instance = psmx; + topic->topic_name = ddsrt_strdup(topic_name); + topic->type_name = ddsrt_strdup(type_name); + dds_add_psmx_topic_to_list(topic, &psmx->psmx_topics); + ++g_mockstats->cnt_create_topic; + return topic; +} + +static dds_return_t dummy_psmx_delete_topic(struct dds_psmx_topic* psmx_topic) +{ + dds_psmx_topic_cleanup_generic(psmx_topic); + dds_free(psmx_topic); + ++g_mockstats->cnt_delete_topic; + return DDS_RETCODE_OK; +} + +static dds_return_t dummy_psmx_deinit(struct dds_psmx* psmx) +{ + dds_psmx_cleanup_generic(psmx); + dds_free(psmx); + ++g_mockstats->cnt_deinit; + if ( g_mockstats_owned ) { + ddsrt_free(g_mockstats); + g_mockstats = NULL; + g_mockstats_owned = false; + } + return DDS_RETCODE_OK; +} + +static dds_psmx_node_identifier_t dummy_psmx_get_node_id(const struct dds_psmx* psmx) +{ + (void)psmx; + dds_psmx_node_identifier_t node_id; + memset(&node_id, 0, sizeof(dds_psmx_node_identifier_t)); + ++g_mockstats->cnt_get_node_id; + return node_id; +} + +static dds_psmx_features_t dummy_supported_features(const struct dds_psmx* psmx) +{ + (void)psmx; + ++g_mockstats->cnt_supported_features; + return DDS_PSMX_FEATURE_SHARED_MEMORY | DDS_PSMX_FEATURE_ZERO_COPY; +} + +dds_return_t dummy_create_psmx(dds_psmx_t** psmx_out, dds_psmx_instance_id_t instance_id, const char* config) +{ + assert(psmx_out); + + g_mockstats = ddsrt_malloc(sizeof(dummy_mockstats_t)); + memset(g_mockstats, 0, sizeof(dummy_mockstats_t)); + g_mockstats_owned = true; // I own the mockstats until transferred to the user. + + struct dummy_psmx* psmx = dds_alloc(sizeof(struct dummy_psmx)); + memset(psmx, 0, sizeof(struct dummy_psmx)); + psmx->c.instance_name = dds_string_dup("dummy_psmx"); + psmx->c.instance_id = instance_id; + psmx->c.ops = psmx_instance_ops; + dds_psmx_init_generic(&psmx->c); + + if (config != NULL && strlen (config) > 0) + { + char* lstr = get_config_option_value (config, "LOCATOR"); + if (lstr != NULL) + { + if (strlen (lstr) != 32) + { + dds_free (lstr); + goto err_locator; + } + uint8_t* const dst = (uint8_t*) psmx->c.locator->address; + for (uint32_t n = 0; n < 32 && lstr[n]; n++) + { + int32_t num; + if ((num = ddsrt_todigit (lstr[n])) < 0 || num >= 16) + { + dds_free (lstr); + goto err_locator; + } + if ((n % 2) == 0) + dst[n / 2] = (uint8_t) (num << 4); + else + dst[n / 2] |= (uint8_t) num; + } + dds_free (lstr); + } + } + + psmx->mockstats_get_ownership = dummy_mockstats_get_ownership; + *psmx_out = (dds_psmx_t*)psmx; + return DDS_RETCODE_OK; + +err_locator: + dds_psmx_cleanup_generic (&psmx->c); + dds_free (psmx); + return DDS_RETCODE_BAD_PARAMETER; +} + +static struct dds_psmx_endpoint* dummy_psmx_create_endpoint( + struct dds_psmx_topic* psmx_topic, + const struct dds_qos* qos, + dds_psmx_endpoint_type_t endpoint_type +) { + (void)qos; + struct dds_psmx_endpoint* endp = dds_alloc(sizeof(struct dds_psmx_endpoint)); + memset(endp, 0, sizeof(struct dds_psmx_endpoint)); + endp->ops.request_loan = dummy_psmx_ep_request_loan; + endp->ops.write = dummy_psmx_ep_write; + endp->ops.take = dummy_psmx_ep_take; + endp->ops.on_data_available = dummy_psmx_ep_on_data_available; + + endp->psmx_topic = psmx_topic; + endp->endpoint_type = endpoint_type; + dds_add_psmx_endpoint_to_list(endp, &psmx_topic->psmx_endpoints); + ++g_mockstats->cnt_create_endpoint; + return endp; +} + +static dds_return_t dummy_psmx_delete_endpoint(struct dds_psmx_endpoint* psmx_endpoint) +{ + dds_free(psmx_endpoint); + ++g_mockstats->cnt_delete_endpoint; + return DDS_RETCODE_OK; +} + +static dds_loaned_sample_t* dummy_psmx_ep_request_loan(struct dds_psmx_endpoint* psmx_endpoint, uint32_t size_requested) +{ + (void)psmx_endpoint; + (void)size_requested; + // Details yet to be implemented + ++g_mockstats->cnt_request_loan; + return NULL; +} + +static dds_return_t dummy_psmx_ep_write(struct dds_psmx_endpoint* psmx_endpoint, dds_loaned_sample_t* data) +{ + (void)psmx_endpoint; + (void)data; + // Details yet to be implemented + ++g_mockstats->cnt_write; + return DDS_RETCODE_OK; +} + +static dds_loaned_sample_t* dummy_psmx_ep_take(struct dds_psmx_endpoint* psmx_endpoint) +{ + (void)psmx_endpoint; + // Details yet to be implemented + ++g_mockstats->cnt_take; + return NULL; +} + +static dds_return_t dummy_psmx_ep_on_data_available(struct dds_psmx_endpoint* psmx_endpoint, dds_entity_t reader) +{ + (void)psmx_endpoint; + (void)reader; + // Details yet to be implemented + ++g_mockstats->cnt_on_data_available; + return DDS_RETCODE_OK; +} diff --git a/src/core/ddsc/tests/psmx_dummy_impl.h b/src/core/ddsc/tests/psmx_dummy_impl.h new file mode 100644 index 0000000000..1e03474f4c --- /dev/null +++ b/src/core/ddsc/tests/psmx_dummy_impl.h @@ -0,0 +1,26 @@ +// Copyright(c) 2023 ZettaScale Technology and others +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License +// v. 1.0 which is available at +// http://www.eclipse.org/org/documents/edl-v10.php. +// +// SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +#ifndef PSMX_DUMMY_IMPL_H +#define PSMX_DUMMY_IMPL_H + +#include "dds/psmx_dummy/export.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +PSMX_DUMMY_EXPORT dds_return_t dummy_create_psmx(dds_psmx_t **psmx, dds_psmx_instance_id_t instance_id, const char *config); + +#if defined (__cplusplus) +} +#endif + +#endif /* PSMX_DUMMY_IMPL_H */ diff --git a/src/core/ddsc/tests/psmx_dummy_public.h b/src/core/ddsc/tests/psmx_dummy_public.h new file mode 100644 index 0000000000..336a5032ea --- /dev/null +++ b/src/core/ddsc/tests/psmx_dummy_public.h @@ -0,0 +1,48 @@ + +// Copyright(c) 2023 ZettaScale Technology and others +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License +// v. 1.0 which is available at +// http://www.eclipse.org/org/documents/edl-v10.php. +// +// SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +#ifndef PSMX_DUMMY_PUBLIC_H +#define PSMX_DUMMY_PUBLIC_H + +#if defined (__cplusplus) +extern "C" { +#endif + +typedef struct dummy_mockstats_s{ + // dds_psmx_ops + int cnt_type_qos_supported; + int cnt_create_topic; + int cnt_delete_topic; + int cnt_deinit; + int cnt_get_node_id; + int cnt_supported_features; + + // dds_psmx_topic_ops + int cnt_create_endpoint; + int cnt_delete_endpoint; + + // dds_psmx_endpoint_ops + int cnt_request_loan; + int cnt_write; + int cnt_take; + int cnt_on_data_available; +}dummy_mockstats_t; + +struct dummy_psmx { + struct dds_psmx c; + void (*mockstats_get_ownership)(dummy_mockstats_t*); +}; + +#if defined (__cplusplus) +} +#endif + +#endif /* PSMX_DUMMY_PUBLIC_H */ From d1dc466e30c23e3457e72ccd205947babb046d19 Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Tue, 16 Apr 2024 18:02:28 +0200 Subject: [PATCH 4/6] process review of 'add dummy psmx interface' Signed-off-by: Michel van den Hoek --- src/core/ddsc/tests/CMakeLists.txt | 78 ++--- src/core/ddsc/tests/psmx.c | 307 +---------------- src/core/ddsc/tests/psmx_dummy_impl.c | 289 ++++++---------- src/core/ddsc/tests/psmx_dummy_public.h | 24 +- src/core/ddsc/tests/psmxif.c | 441 ++++++++++++++++++++++++ 5 files changed, 612 insertions(+), 527 deletions(-) create mode 100644 src/core/ddsc/tests/psmxif.c diff --git a/src/core/ddsc/tests/CMakeLists.txt b/src/core/ddsc/tests/CMakeLists.txt index d975cc3c38..c10d9e9a30 100644 --- a/src/core/ddsc/tests/CMakeLists.txt +++ b/src/core/ddsc/tests/CMakeLists.txt @@ -63,6 +63,7 @@ set(ddsc_test_sources "nwpart.c" "participant.c" "pp_lease_dur.c" + "psmxif.c" "publisher.c" "qos.c" "qosmatch.c" @@ -157,6 +158,43 @@ if(ENABLE_QOS_PROVIDER) "qos_provider.c") endif() +# PSMX dummy implementation for interface testing +set( + psmx_dummy_sources + "psmx_dummy_impl.c" + "psmx_dummy_impl.h" +) +if(BUILD_SHARED_LIBS) + add_library(psmx_dummy SHARED ${psmx_dummy_sources}) +else() + add_library(psmx_dummy OBJECT ${psmx_dummy_sources}) + set_property(GLOBAL APPEND PROPERTY cdds_plugin_list psmx_dummy) + set_property(GLOBAL PROPERTY psmx_dummy_symbols dummy_create_psmx) +endif() +generate_export_header(psmx_dummy BASE_NAME PSMX_DUMMY EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/dds/psmx_dummy/export.h") +target_include_directories( + psmx_dummy PRIVATE + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) +if(BUILD_SHARED_LIBS) + target_link_libraries(psmx_dummy PRIVATE ddsc) +else() + install( + TARGETS psmx_dummy + EXPORT "${PROJECT_NAME}" + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endif() + + add_cunit_executable(cunit_ddsc ${ddsc_test_sources}) target_include_directories( cunit_ddsc PRIVATE @@ -181,6 +219,7 @@ target_link_libraries(cunit_ddsc PRIVATE CdrStreamSkipDefault CdrStreamDataTypeInfo PsmxDataModels + psmx_dummy DynamicData Array100 CdrStreamKeySize @@ -243,41 +282,6 @@ target_include_directories( target_link_libraries(oneliner PRIVATE RoundTrip Space ddsc) -# PSMX dummy implementation for interface testing -set( - psmx_dummy_sources - "psmx_dummy_impl.c" - "psmx_dummy_impl.h" -) -if(BUILD_SHARED_LIBS) - add_library(psmx_dummy SHARED ${psmx_dummy_sources}) -else() - add_library(psmx_dummy OBJECT ${psmx_dummy_sources}) - set_property(GLOBAL APPEND PROPERTY cdds_plugin_list psmx_dummy) - set_property(GLOBAL PROPERTY psmx_dummy_symbols dummy_create_psmx) -endif() -generate_export_header(psmx_dummy BASE_NAME PSMX_DUMMY EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/dds/psmx_dummy/export.h") -target_include_directories( - psmx_dummy PRIVATE - "$" - "$" - "$" - "$" - "$" - "$" - "$" - "$" -) -if(BUILD_SHARED_LIBS) - target_link_libraries(psmx_dummy PRIVATE ddsc) -endif() -install( - TARGETS psmx_dummy - EXPORT "${PROJECT_NAME}" - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - # PSMX implementation with Cyclone as transport, for testing if (BUILD_SHARED_LIBS) idlc_generate(TARGET psmx_cdds_data FILES psmx_cdds_data.idl) @@ -292,10 +296,6 @@ if (BUILD_SHARED_LIBS) target_link_libraries(psmx_cdds PRIVATE ddsc psmx_cdds_data) generate_export_header(psmx_cdds BASE_NAME PSMX_CDDS EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/dds/psmx_cdds/export.h") - - install(TARGETS psmx_cdds - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) endif() # If Iceoryx is available, then also run all PSMX tests using Iceoryx. Colcon complicates diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index 1e1feed491..050e537115 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -15,7 +15,6 @@ #include "dds/ddsrt/md5.h" #include "dds/ddsrt/io.h" #include "dds/ddsrt/heap.h" -#include "dds/ddsrt/string.h" #include "dds/ddsrt/bswap.h" #include "dds/ddsrt/environ.h" #include "dds/ddsrt/static_assert.h" @@ -31,7 +30,6 @@ #include "config_env.h" #include "test_common.h" -#include "psmx_dummy_public.h" #include "Array100.h" #include "DynamicData.h" #include "PsmxDataModels.h" @@ -65,35 +63,17 @@ static void fail_too_much_data (void) { fail (); } #define TRACE_CATEGORY "discovery" #endif -/** - * @brief Create a participant object - * - * @param[in] int_dom the domain id - * @param[in] locator an array of 16 bytes, NULL if not used - * @return dds_entity_t the participant - */ -static dds_entity_t create_participant (dds_domainid_t int_dom, const uint8_t* locator) +static dds_entity_t create_participant (dds_domainid_t int_dom) { assert (int_dom < MAX_DOMAINS); + const unsigned char *l = psmx_locators[int_dom].a; char *configstr; - char locator_str[74]; - if ( locator == NULL ) { - locator_str[0] = 0; - } else { - const uint8_t *l = locator; - snprintf( - locator_str, - sizeof(locator_str), - "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x;", - l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] - ); - } ddsrt_asprintf (&configstr, "\ ${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ \ spdp\ \ - \ + \ \ \ \ @@ -105,8 +85,8 @@ static dds_entity_t create_participant (dds_domainid_t int_dom, const uint8_t* l cdds.log.%d\ \ ", - locator_str, - locator ? (int) locator[0] : -1, // This prevents Iceoryx and Cyclone-based plugins from forwarding across the "network" + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15], + (int) l[0], // This prevents Iceoryx and Cyclone-based plugins from forwarding across the "network" (int) int_dom // log file name ); char *xconfigstr = ddsrt_expand_envvars (configstr, int_dom); @@ -119,39 +99,6 @@ static dds_entity_t create_participant (dds_domainid_t int_dom, const uint8_t* l return pp; } -static bool domain_pin(dds_entity_t domain, dds_domain** dom_out) -{ - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(domain, &x); - if ( rc == DDS_RETCODE_OK ) { - if( dds_entity_kind(x) == DDS_KIND_DOMAIN ) { - *dom_out = (dds_domain*)x; - return true; - } else { - dds_entity_unpin(x); - return false; - } - } - return false; -} - -static void domain_unpin(dds_domain* dom) -{ - assert(dom); - dds_entity_unpin((dds_entity*)dom); -} - -static bool domain_get_psmx_locator(dds_entity_t domain, ddsi_locator_t* l_out) -{ - dds_domain* dom = NULL; - bool ret = domain_pin(domain, &dom); - if ( ret ) { - memcpy(l_out, dom->psmx_instances.instances[0]->locator, sizeof(ddsi_locator_t)); - domain_unpin(dom); - } - return ret; -} - struct tracebuf { char buf[512]; size_t pos; @@ -688,7 +635,7 @@ static void dotest (const dds_topic_descriptor_t *tpdesc, const void *sample, bo create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); for (int i = 0; i < MAX_DOMAINS; i++) { - pp[i] = create_participant ((dds_domainid_t) i, psmx_locators[(dds_domainid_t) i].a); // FIXME: vary shm_enable for i > 0 + pp[i] = create_participant ((dds_domainid_t) i); // FIXME: vary shm_enable for i > 0 tp[i] = dds_create_topic (pp[i], tpdesc, topicname, NULL, NULL); CU_ASSERT_FATAL (tp[i] > 0); gvs[i] = get_domaingv (pp[i]); @@ -1033,80 +980,6 @@ static bool eq_DynamicData_KMsg (const void *vsent, const void *vrecvd, bool val } } -/** @brief Compare two sets of stats. - * - * @param[in] mockstats1 first stats to compare - * @param[in] mockstats2 second stats to compare - * - * @return A bitmask to indicate which elements are different, zero if all are equal. -*/ -static uint32_t dummy_mockstats_cmp(const dummy_mockstats_t* mockstats1, const dummy_mockstats_t* mockstats2) -{ - uint32_t cmp = 0; - uint32_t shift = 0; - - cmp |= ((uint32_t)(mockstats1->cnt_type_qos_supported != mockstats2->cnt_type_qos_supported) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_create_topic != mockstats2->cnt_create_topic) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_delete_topic != mockstats2->cnt_delete_topic) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_deinit != mockstats2->cnt_deinit) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_get_node_id != mockstats2->cnt_get_node_id) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_supported_features != mockstats2->cnt_supported_features) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_create_endpoint != mockstats2->cnt_create_endpoint) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_delete_endpoint != mockstats2->cnt_delete_endpoint) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_request_loan != mockstats2->cnt_request_loan) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_write != mockstats2->cnt_write) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_take != mockstats2->cnt_take) << shift++); - cmp |= ((uint32_t)(mockstats1->cnt_on_data_available != mockstats2->cnt_on_data_available) << shift++); - return cmp; -} - -/** @brief Convert a set of stats to a string. - * - * Truncates the output string if the string buffer capacity is too small. - * - * @param[in] dmock stats to convert - * @param[out] str_out string buffer to write the string into - * @param[in] str_capacity number of bytes the string buffer can hold - * - * @return Upon successful return, it returns the number of characters printed - * (excluding the null byte used to end output to strings). - * If an output error is encountered, a negative value is returned. -*/ -static int dummy_mockstats_tostring(const dummy_mockstats_t* dmock, char* str_out, size_t str_capacity) -{ - return snprintf( - str_out, - str_capacity, - "\ - type_qos_supported: %i\n\ - create_topic: %i\n\ - delete_topic: %i\n\ - deinit: %i\n\ - get_node_id: %i\n\ - supported_features: %i\n\ - \n\ - create_endpoint: %i\n\ - delete_endpoint: %i\n\ - \n\ - request_loan: %i\n\ - write: %i\n\ - take: %i\n\ - on_data_available: %i\n", - dmock->cnt_type_qos_supported, - dmock->cnt_create_topic, - dmock->cnt_delete_topic, - dmock->cnt_deinit, - dmock->cnt_get_node_id, - dmock->cnt_supported_features, - dmock->cnt_create_endpoint, - dmock->cnt_delete_endpoint, - dmock->cnt_request_loan, - dmock->cnt_write, - dmock->cnt_take, - dmock->cnt_on_data_available - ); -} - CU_Test(ddsc_psmx, one_writer, .timeout = 240) { failed = false; @@ -1215,7 +1088,7 @@ CU_Test(ddsc_psmx, one_writer_forwardcdr_dynsize_strkey, .timeout = 240) CU_Test(ddsc_psmx, return_loan) { dds_return_t rc; - const dds_entity_t pp = create_participant (0, psmx_locators[0].a); + const dds_entity_t pp = create_participant (0); char topicname[100]; create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); const dds_entity_t tp = dds_create_topic (pp, &Array100_desc, topicname, NULL, NULL); @@ -1240,7 +1113,7 @@ CU_Test(ddsc_psmx, return_loan) CU_Test(ddsc_psmx, partition_xtalk) { dds_return_t rc; - const dds_entity_t pp = create_participant (0, psmx_locators[0].a); + const dds_entity_t pp = create_participant (0); char topicname[100]; create_unique_topic_name ("test_psmx", topicname, sizeof (topicname)); const dds_entity_t tp = dds_create_topic (pp, &PsmxType1_desc, topicname, NULL, NULL); @@ -1345,7 +1218,7 @@ CU_Test (ddsc_psmx, basic) dds_entity_t participant, topic, writer, reader; bool psmx_enabled; - participant = create_participant (0, psmx_locators[0].a); + participant = create_participant (0); CU_ASSERT_FATAL (participant > 0); char topicname[100]; @@ -1392,164 +1265,6 @@ CU_Test (ddsc_psmx, basic) dds_delete (dds_get_parent (participant)); } -/// @brief Check that shared memory can be enabled (when supported) and the used locator can be set. -/// @methodology -/// - Check that the data types I'm planning to use are actually suitable for use with shared memory. -/// - Expectation: They are memcopy-safe. -/// -/// - Create a configuration with a psmx interface and specify the locator. -/// - Create a domain using this configuration. -/// - Check the locator used by the psmx instance. -/// - Expectation: The locator is the same as specified in the config for the domain. -/// - Query whether shared memory is supported. -/// - Assert that there is exactly one psmx instance. -/// - Assert that the psmx instance has a nonempty instance_name. -/// - Create some entities -/// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). -/// - Delete the domain -/// - Check the function call counts of the dummy psmx. -/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. -/// -/// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. -/// - Create a domain using this configuration. -/// - Query whether shared memory is supported. -/// - Assert that there is exactly one psmx instance. -/// - Assert that the psmx instance has a nonempty instance_name. -/// - Create some entities -/// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). -/// - Delete the domain -/// - Check the function call counts of the dummy psmx. -/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. -/// -CU_Test(ddsc_psmxif, shared_memory) -{ - const char* CDDS_PSMX_NAME = NULL; - ddsrt_getenv("CDDS_PSMX_NAME", &CDDS_PSMX_NAME); - ddsrt_setenv("CDDS_PSMX_NAME", "dummy"); // Make `create_participant()` use the dummy psmx. - dummy_mockstats_t dmock, dmock_expected; - char strbuf1[512]; - char strbuf2[512]; - size_t strbuf_size = sizeof(strbuf1); - { - // Check that the data types I'm planning to use are actually suitable for use with shared memory. - dds_data_type_properties_t props; - props = dds_stream_data_types(SC_Model_desc.m_ops); - CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); - props = dds_stream_data_types(PsmxType1_desc.m_ops); - CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); - } - - const dds_domainid_t domainId = 3; - bool supports_shared_memory = false; // Overwritten by `plugin's supported_features()`. - bool specify_locator[] = {true, false}; - uint8_t locator_in[16]; - memset(locator_in, 0x0, sizeof(locator_in)); // Avoid warning 'uninitialized value'. - ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; - ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; - - int N = sizeof(specify_locator) / sizeof(specify_locator[0]); - for (int i = 0; i < N; ++i) - { - const uint8_t* locator = specify_locator[i] ? locator_in : NULL; - dds_entity_t participant = create_participant(domainId, locator); - CU_ASSERT_FATAL(participant > 0); - dds_entity_t domain = dds_get_parent(participant); - CU_ASSERT_FATAL(domain > 0); - if ( specify_locator[i] ) { - // Check that I get the same locator that I provided with the config. - ddsi_locator_t locator_out; - CU_ASSERT_FATAL(domain_get_psmx_locator(domain, &locator_out)); - CU_ASSERT_FATAL(memcmp(locator_in, locator_out.address, sizeof(locator_out.address)) == 0); - } - - { - // Query whether shared memory is supported. - dds_domain* dom = NULL; - CU_ASSERT_FATAL(domain_pin(domain, &dom)); - CU_ASSERT_FATAL(dom->psmx_instances.length == 1); // There shall be exactly one psmx instance. - struct dummy_psmx* dpsmx = (struct dummy_psmx*)dom->psmx_instances.instances[0]; - dpsmx->mockstats_get_ownership(&dmock); - - // The psmx must have an instance_name that is not an empty string. - CU_ASSERT_FATAL(dpsmx->c.instance_name != NULL && strcmp(dpsmx->c.instance_name, "") != 0); - supports_shared_memory = ((dpsmx->c.ops.supported_features(&dpsmx->c) & DDS_PSMX_FEATURE_SHARED_MEMORY) == DDS_PSMX_FEATURE_SHARED_MEMORY); - domain_unpin(dom); - } - dds_entity_t writer1, reader1, writer2, reader2; - { - char topicname[100]; - dds_qos_t* qos = dds_create_qos(); - - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, qos, NULL); - CU_ASSERT_FATAL(topic1 > 0); - - writer1 = dds_create_writer(participant, topic1, qos, NULL); - CU_ASSERT_FATAL(writer1 > 0); - - reader1 = dds_create_reader(participant, topic1, qos, NULL); - CU_ASSERT_FATAL(reader1 > 0); - - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, qos, NULL); - CU_ASSERT_FATAL(topic2 > 0); - - writer2 = dds_create_writer(participant, topic2, qos, NULL); - CU_ASSERT_FATAL(writer2 > 0); - - reader2 = dds_create_reader(participant, topic2, qos, NULL); - CU_ASSERT_FATAL(reader2 > 0); - dds_delete_qos(qos); - } - - { - // Check that shared memory is enabled when it should, and not enabled when it shouldn't. - bool psmx_enabled; - psmx_enabled = endpoint_has_psmx_enabled(writer1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1) == supports_shared_memory); - - psmx_enabled = endpoint_has_psmx_enabled(reader1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1) == supports_shared_memory); - - psmx_enabled = endpoint_has_psmx_enabled(writer2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2) == supports_shared_memory); - - psmx_enabled = endpoint_has_psmx_enabled(reader2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory); - } - dds_delete(domain); - - // Check number of calls against expected counts. - memset(&dmock_expected, 0, sizeof(dummy_mockstats_t)); - dmock_expected.cnt_type_qos_supported = 10; - dmock_expected.cnt_create_topic = 2; - dmock_expected.cnt_delete_topic = 2; - dmock_expected.cnt_deinit = 1; - dmock_expected.cnt_get_node_id = 1; - dmock_expected.cnt_supported_features = 5; - dmock_expected.cnt_create_endpoint = 4; - dmock_expected.cnt_delete_endpoint = 4; - dmock_expected.cnt_on_data_available = 2; // This is perhaps unexpected, see below. - /* - NOTE: The `on_data_available` is triggered in - `dds_create_reader_int()`, even if no samples have been received at all. - */ - uint32_t cmp = dummy_mockstats_cmp(&dmock, &dmock_expected); - if( cmp != 0 ){ - dummy_mockstats_tostring(&dmock, strbuf1, strbuf_size); - dummy_mockstats_tostring(&dmock_expected, strbuf2, strbuf_size); - printf("actual calls:\n%s\nexpected calls:\n%s\n", strbuf1, strbuf2); - CU_ASSERT_FATAL(cmp == 0); - } - } -} - CU_Test (ddsc_psmx, zero_copy) { const struct { @@ -1562,7 +1277,7 @@ CU_Test (ddsc_psmx, zero_copy) }; dds_return_t rc; - const dds_entity_t pp = create_participant (0, psmx_locators[0].a); + const dds_entity_t pp = create_participant (0); CU_ASSERT_FATAL (pp > 0); for (size_t k = 0; k < sizeof (cases) / sizeof (cases[0]); k++) { @@ -1758,7 +1473,7 @@ CU_Test (ddsc_psmx, writer_loan) }; dds_return_t rc; - const dds_entity_t pp = create_participant (0, psmx_locators[0].a); + const dds_entity_t pp = create_participant (0); CU_ASSERT_FATAL (pp > 0); for (size_t k = 0; k < sizeof (cases) / sizeof (cases[0]); k++) { diff --git a/src/core/ddsc/tests/psmx_dummy_impl.c b/src/core/ddsc/tests/psmx_dummy_impl.c index a0c840cdd0..ec58928d71 100644 --- a/src/core/ddsc/tests/psmx_dummy_impl.c +++ b/src/core/ddsc/tests/psmx_dummy_impl.c @@ -8,88 +8,91 @@ #include "psmx_dummy_public.h" #include "psmx_dummy_impl.h" -//### Helper functions ### +void dummy_topics_alloc(dummy_mockstats_t* mockstats, uint32_t topics_capacity) +{ + mockstats->topics._maximum = topics_capacity; + mockstats->topics._length = 0; + mockstats->topics._buffer = ddsrt_malloc(topics_capacity * sizeof(dds_psmx_topic_t)); +} -static char* get_config_option_value (const char* conf, const char* option_name) +void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, uint32_t endpoints_capacity) { - char* copy = ddsrt_strdup(conf), *cursor = copy, *tok; - while ((tok = ddsrt_strsep(&cursor, ",/|;")) != NULL) - { - if (strlen(tok) == 0) - continue; - char* name = ddsrt_strsep(&tok, "="); - if (name == NULL || tok == NULL) - { - ddsrt_free(copy); - return NULL; - } - if (strcmp(name, option_name) == 0) - { - char* ret = ddsrt_strdup(tok); - ddsrt_free(copy); - return ret; - } - } - ddsrt_free(copy); + mockstats->endpoints._maximum = endpoints_capacity; + mockstats->endpoints._length = 0; + mockstats->endpoints._buffer = ddsrt_malloc(endpoints_capacity * sizeof(dds_psmx_endpoint_t)); +} + +static dummy_mockstats_t g_mockstats; + +static dummy_mockstats_t* dummy_mockstats_get_ptr() +{ + return &g_mockstats; +} + +static dds_loaned_sample_t* dummy_psmx_ep_request_loan(dds_psmx_endpoint_t* psmx_endpoint, uint32_t size_requested) +{ + (void)psmx_endpoint; + (void)size_requested; + // Details yet to be implemented + ++g_mockstats.cnt_request_loan; return NULL; } -//### Dynamic library functions ### +static dds_return_t dummy_psmx_ep_write(dds_psmx_endpoint_t* psmx_endpoint, dds_loaned_sample_t* data) +{ + (void)psmx_endpoint; + (void)data; + // Details yet to be implemented + ++g_mockstats.cnt_write; + return DDS_RETCODE_OK; +} -static dummy_mockstats_t* g_mockstats; -static bool g_mockstats_owned; +static dds_loaned_sample_t* dummy_psmx_ep_take(dds_psmx_endpoint_t* psmx_endpoint) +{ + (void)psmx_endpoint; + // Details yet to be implemented + ++g_mockstats.cnt_take; + return NULL; +} -static bool dummy_psmx_type_qos_supported( - struct dds_psmx* psmx, - dds_psmx_endpoint_type_t forwhat, - dds_data_type_properties_t data_type_props, - const struct dds_qos* qos -); -static struct dds_psmx_topic* dummy_psmx_create_topic( - struct dds_psmx* psmx, - const char* topic_name, - const char* type_name, - dds_data_type_properties_t data_type_props -); -static dds_return_t dummy_psmx_delete_topic(struct dds_psmx_topic* psmx_topic); -static dds_return_t dummy_psmx_deinit(struct dds_psmx* psmx); -static dds_psmx_node_identifier_t dummy_psmx_get_node_id(const struct dds_psmx* psmx); -static dds_psmx_features_t dummy_supported_features(const struct dds_psmx* psmx); +static dds_return_t dummy_psmx_ep_on_data_available(dds_psmx_endpoint_t* psmx_endpoint, dds_entity_t reader) +{ + (void)psmx_endpoint; + (void)reader; + // Details yet to be implemented + ++g_mockstats.cnt_on_data_available; + return DDS_RETCODE_OK; +} -static struct dds_psmx_endpoint* dummy_psmx_create_endpoint( - struct dds_psmx_topic* psmx_topic, +static dds_psmx_endpoint_t* dummy_psmx_create_endpoint( + dds_psmx_topic_t* psmx_topic, const struct dds_qos* qos, dds_psmx_endpoint_type_t endpoint_type -); -static dds_return_t dummy_psmx_delete_endpoint(struct dds_psmx_endpoint* psmx_endpoint); - -static dds_loaned_sample_t* dummy_psmx_ep_request_loan(struct dds_psmx_endpoint* psmx_endpoint, uint32_t size_requested); -static dds_return_t dummy_psmx_ep_write(struct dds_psmx_endpoint* psmx_endpoint, dds_loaned_sample_t* data); -static dds_loaned_sample_t* dummy_psmx_ep_take(struct dds_psmx_endpoint* psmx_endpoint); -static dds_return_t dummy_psmx_ep_on_data_available(struct dds_psmx_endpoint* psmx_endpoint, dds_entity_t reader); +) { + (void)qos; + dds_psmx_endpoint_t* endp = (dds_psmx_endpoint_t*)g_mockstats.endpoints._buffer + g_mockstats.endpoints._length++; + memset(endp, 0, sizeof(dds_psmx_endpoint_t)); + endp->ops.request_loan = dummy_psmx_ep_request_loan; + endp->ops.write = dummy_psmx_ep_write; + endp->ops.take = dummy_psmx_ep_take; + endp->ops.on_data_available = dummy_psmx_ep_on_data_available; -static const dds_psmx_ops_t psmx_instance_ops = { - .type_qos_supported = dummy_psmx_type_qos_supported, - .create_topic = dummy_psmx_create_topic, - .delete_topic = dummy_psmx_delete_topic, - .deinit = dummy_psmx_deinit, - .get_node_id = dummy_psmx_get_node_id, - .supported_features = dummy_supported_features -}; + endp->psmx_topic = psmx_topic; + endp->endpoint_type = endpoint_type; + dds_add_psmx_endpoint_to_list(endp, &psmx_topic->psmx_endpoints); + ++g_mockstats.cnt_create_endpoint; + return endp; +} -static void dummy_mockstats_get_ownership(dummy_mockstats_t* mockstats) +static dds_return_t dummy_psmx_delete_endpoint(dds_psmx_endpoint_t* psmx_endpoint) { - // Transfer ownership of mockstats to user. - memcpy(mockstats, g_mockstats, sizeof(dummy_mockstats_t)); - if ( g_mockstats_owned ) { - ddsrt_free(g_mockstats); - g_mockstats_owned = false; - } - g_mockstats = mockstats; + memcpy(psmx_endpoint, (dds_psmx_endpoint_t*)g_mockstats.endpoints._buffer + (--g_mockstats.endpoints._length), sizeof(dds_psmx_endpoint_t)); + ++g_mockstats.cnt_delete_endpoint; + return DDS_RETCODE_OK; } static bool dummy_psmx_type_qos_supported( - struct dds_psmx* psmx, + dds_psmx_t* psmx, dds_psmx_endpoint_type_t forwhat, dds_data_type_properties_t data_type_props, const struct dds_qos* qos @@ -98,177 +101,87 @@ static bool dummy_psmx_type_qos_supported( (void)forwhat; (void)data_type_props; (void)qos; - ++g_mockstats->cnt_type_qos_supported; + ++g_mockstats.cnt_type_qos_supported; return true; } -static struct dds_psmx_topic* dummy_psmx_create_topic( - struct dds_psmx* psmx, +static dds_psmx_topic_t* dummy_psmx_create_topic( + dds_psmx_t* psmx, const char* topic_name, const char* type_name, dds_data_type_properties_t data_type_props ) { (void)data_type_props; - struct dds_psmx_topic* topic = dds_alloc(sizeof(struct dds_psmx_topic)); - memset(topic, 0, sizeof(struct dds_psmx_topic)); + assert(g_mockstats.topics._length < g_mockstats.topics._maximum); + dds_psmx_topic_t* topic = (dds_psmx_topic_t*)g_mockstats.topics._buffer + g_mockstats.topics._length++; + memset(topic, 0, sizeof(dds_psmx_topic_t)); topic->ops.create_endpoint = dummy_psmx_create_endpoint; topic->ops.delete_endpoint = dummy_psmx_delete_endpoint; topic->psmx_instance = psmx; topic->topic_name = ddsrt_strdup(topic_name); topic->type_name = ddsrt_strdup(type_name); dds_add_psmx_topic_to_list(topic, &psmx->psmx_topics); - ++g_mockstats->cnt_create_topic; + ++g_mockstats.cnt_create_topic; return topic; } -static dds_return_t dummy_psmx_delete_topic(struct dds_psmx_topic* psmx_topic) +static dds_return_t dummy_psmx_delete_topic(dds_psmx_topic_t* psmx_topic) { dds_psmx_topic_cleanup_generic(psmx_topic); - dds_free(psmx_topic); - ++g_mockstats->cnt_delete_topic; + memcpy(psmx_topic, (dds_psmx_topic_t*)g_mockstats.topics._buffer + (--g_mockstats.topics._length), sizeof(dds_psmx_topic_t)); + ++g_mockstats.cnt_delete_topic; return DDS_RETCODE_OK; } -static dds_return_t dummy_psmx_deinit(struct dds_psmx* psmx) +static dds_return_t dummy_psmx_deinit(dds_psmx_t* psmx) { dds_psmx_cleanup_generic(psmx); dds_free(psmx); - ++g_mockstats->cnt_deinit; - if ( g_mockstats_owned ) { - ddsrt_free(g_mockstats); - g_mockstats = NULL; - g_mockstats_owned = false; - } + ++g_mockstats.cnt_deinit; + ddsrt_free(g_mockstats.config); + ddsrt_free(g_mockstats.topics._buffer); + ddsrt_free(g_mockstats.endpoints._buffer); return DDS_RETCODE_OK; } -static dds_psmx_node_identifier_t dummy_psmx_get_node_id(const struct dds_psmx* psmx) +static dds_psmx_node_identifier_t dummy_psmx_get_node_id(const dds_psmx_t* psmx) { (void)psmx; dds_psmx_node_identifier_t node_id; memset(&node_id, 0, sizeof(dds_psmx_node_identifier_t)); - ++g_mockstats->cnt_get_node_id; + ++g_mockstats.cnt_get_node_id; return node_id; } -static dds_psmx_features_t dummy_supported_features(const struct dds_psmx* psmx) +static dds_psmx_features_t dummy_supported_features(const dds_psmx_t* psmx) { (void)psmx; - ++g_mockstats->cnt_supported_features; + ++g_mockstats.cnt_supported_features; return DDS_PSMX_FEATURE_SHARED_MEMORY | DDS_PSMX_FEATURE_ZERO_COPY; } dds_return_t dummy_create_psmx(dds_psmx_t** psmx_out, dds_psmx_instance_id_t instance_id, const char* config) { assert(psmx_out); + memset(&g_mockstats, 0, sizeof(dummy_mockstats_t)); + g_mockstats.cnt_create_psmx = 1; - g_mockstats = ddsrt_malloc(sizeof(dummy_mockstats_t)); - memset(g_mockstats, 0, sizeof(dummy_mockstats_t)); - g_mockstats_owned = true; // I own the mockstats until transferred to the user. - - struct dummy_psmx* psmx = dds_alloc(sizeof(struct dummy_psmx)); - memset(psmx, 0, sizeof(struct dummy_psmx)); + dummy_psmx_t* psmx = dds_alloc(sizeof(dummy_psmx_t)); + memset(psmx, 0, sizeof(dummy_psmx_t)); psmx->c.instance_name = dds_string_dup("dummy_psmx"); psmx->c.instance_id = instance_id; - psmx->c.ops = psmx_instance_ops; + + psmx->c.ops.type_qos_supported = dummy_psmx_type_qos_supported; + psmx->c.ops.create_topic = dummy_psmx_create_topic; + psmx->c.ops.delete_topic = dummy_psmx_delete_topic; + psmx->c.ops.deinit = dummy_psmx_deinit; + psmx->c.ops.get_node_id = dummy_psmx_get_node_id; + psmx->c.ops.supported_features = dummy_supported_features; dds_psmx_init_generic(&psmx->c); - if (config != NULL && strlen (config) > 0) - { - char* lstr = get_config_option_value (config, "LOCATOR"); - if (lstr != NULL) - { - if (strlen (lstr) != 32) - { - dds_free (lstr); - goto err_locator; - } - uint8_t* const dst = (uint8_t*) psmx->c.locator->address; - for (uint32_t n = 0; n < 32 && lstr[n]; n++) - { - int32_t num; - if ((num = ddsrt_todigit (lstr[n])) < 0 || num >= 16) - { - dds_free (lstr); - goto err_locator; - } - if ((n % 2) == 0) - dst[n / 2] = (uint8_t) (num << 4); - else - dst[n / 2] |= (uint8_t) num; - } - dds_free (lstr); - } - } + g_mockstats.config = ddsrt_strdup(config); - psmx->mockstats_get_ownership = dummy_mockstats_get_ownership; + psmx->mockstats_get_ptr = dummy_mockstats_get_ptr; *psmx_out = (dds_psmx_t*)psmx; return DDS_RETCODE_OK; - -err_locator: - dds_psmx_cleanup_generic (&psmx->c); - dds_free (psmx); - return DDS_RETCODE_BAD_PARAMETER; -} - -static struct dds_psmx_endpoint* dummy_psmx_create_endpoint( - struct dds_psmx_topic* psmx_topic, - const struct dds_qos* qos, - dds_psmx_endpoint_type_t endpoint_type -) { - (void)qos; - struct dds_psmx_endpoint* endp = dds_alloc(sizeof(struct dds_psmx_endpoint)); - memset(endp, 0, sizeof(struct dds_psmx_endpoint)); - endp->ops.request_loan = dummy_psmx_ep_request_loan; - endp->ops.write = dummy_psmx_ep_write; - endp->ops.take = dummy_psmx_ep_take; - endp->ops.on_data_available = dummy_psmx_ep_on_data_available; - - endp->psmx_topic = psmx_topic; - endp->endpoint_type = endpoint_type; - dds_add_psmx_endpoint_to_list(endp, &psmx_topic->psmx_endpoints); - ++g_mockstats->cnt_create_endpoint; - return endp; -} - -static dds_return_t dummy_psmx_delete_endpoint(struct dds_psmx_endpoint* psmx_endpoint) -{ - dds_free(psmx_endpoint); - ++g_mockstats->cnt_delete_endpoint; - return DDS_RETCODE_OK; -} - -static dds_loaned_sample_t* dummy_psmx_ep_request_loan(struct dds_psmx_endpoint* psmx_endpoint, uint32_t size_requested) -{ - (void)psmx_endpoint; - (void)size_requested; - // Details yet to be implemented - ++g_mockstats->cnt_request_loan; - return NULL; -} - -static dds_return_t dummy_psmx_ep_write(struct dds_psmx_endpoint* psmx_endpoint, dds_loaned_sample_t* data) -{ - (void)psmx_endpoint; - (void)data; - // Details yet to be implemented - ++g_mockstats->cnt_write; - return DDS_RETCODE_OK; -} - -static dds_loaned_sample_t* dummy_psmx_ep_take(struct dds_psmx_endpoint* psmx_endpoint) -{ - (void)psmx_endpoint; - // Details yet to be implemented - ++g_mockstats->cnt_take; - return NULL; -} - -static dds_return_t dummy_psmx_ep_on_data_available(struct dds_psmx_endpoint* psmx_endpoint, dds_entity_t reader) -{ - (void)psmx_endpoint; - (void)reader; - // Details yet to be implemented - ++g_mockstats->cnt_on_data_available; - return DDS_RETCODE_OK; } diff --git a/src/core/ddsc/tests/psmx_dummy_public.h b/src/core/ddsc/tests/psmx_dummy_public.h index 336a5032ea..d19cff7cd6 100644 --- a/src/core/ddsc/tests/psmx_dummy_public.h +++ b/src/core/ddsc/tests/psmx_dummy_public.h @@ -16,7 +16,15 @@ extern "C" { #endif +typedef struct dynamic_array_s{ + uint32_t _maximum; + uint32_t _length; + void* _buffer; +}dynamic_array_t; + typedef struct dummy_mockstats_s{ + int cnt_create_psmx; + // dds_psmx_ops int cnt_type_qos_supported; int cnt_create_topic; @@ -34,12 +42,20 @@ typedef struct dummy_mockstats_s{ int cnt_write; int cnt_take; int cnt_on_data_available; + + // Exposed internals + char* config; + dynamic_array_t topics; + dynamic_array_t endpoints; }dummy_mockstats_t; -struct dummy_psmx { - struct dds_psmx c; - void (*mockstats_get_ownership)(dummy_mockstats_t*); -}; +typedef struct dummy_psmx { + dds_psmx_t c; + dummy_mockstats_t* (*mockstats_get_ptr)(); +}dummy_psmx_t; + +DDS_EXPORT void dummy_topics_alloc(dummy_mockstats_t* mockstats, uint32_t topics_capacity); +DDS_EXPORT void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, uint32_t endpoints_capacity); #if defined (__cplusplus) } diff --git a/src/core/ddsc/tests/psmxif.c b/src/core/ddsc/tests/psmxif.c new file mode 100644 index 0000000000..dc0280d8d7 --- /dev/null +++ b/src/core/ddsc/tests/psmxif.c @@ -0,0 +1,441 @@ +// Copyright(c) 2020 to 2023 ZettaScale Technology and others +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License +// v. 1.0 which is available at +// http://www.eclipse.org/org/documents/edl-v10.php. +// +// SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +#include +#include + +#include "dds/ddsrt/mh3.h" +#include "dds/ddsrt/md5.h" +#include "dds/ddsrt/io.h" +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/string.h" +#include "dds/ddsrt/bswap.h" +#include "dds/ddsrt/environ.h" +#include "dds/ddsrt/static_assert.h" + +#include "dds/dds.h" +#include "dds/ddsi/ddsi_entity_index.h" +#include "ddsi__addrset.h" +#include "ddsi__entity.h" +#include "ddsi__xevent.h" +#include "dds__entity.h" +#include "dds__serdata_default.h" + +#include "config_env.h" +#include "test_common.h" +#include "psmx_dummy_public.h" +#include "Array100.h" +#include "DynamicData.h" +#include "PsmxDataModels.h" + +static void free_strings(uint32_t len, char** strings) +{ + if (len != 0 && strings != NULL) { + for (uint32_t i = 0; i < len; i++) { + dds_free(strings[i]); + } + } + dds_free(strings); +} + +/** + * @brief Create a participant object, providing a configuration to choose the psmx interface. + * + * @param[in] int_dom the domain id + * @param[in] cdds_psmx_name the name of the psmx interface to use + * @param[in] locator an array of 16 bytes, NULL if not used + * @return dds_entity_t the participant + */ +static char* create_config(dds_domainid_t int_dom, const char* cdds_psmx_name, const uint8_t* locator) +{ + char *configstr; + char locator_str[74]; + if ( locator == NULL ) { + locator_str[0] = 0; + } else { + const uint8_t *l = locator; + snprintf( + locator_str, + sizeof(locator_str), + "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x;", + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] + ); + } + ddsrt_asprintf (&configstr, "\ +${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ +\ + spdp\ + \ + \ + \ +\ +\ + ${CYCLONEDDS_PID}\ + 0\ +\ +\ + cdds.log.%d\ +\ +", + cdds_psmx_name, + cdds_psmx_name, + locator_str, + locator ? (int) locator[0] : -1, // This prevents plugins from forwarding across the "network". + (int) int_dom // log file name + ); + char *xconfigstr = ddsrt_expand_envvars (configstr, int_dom); + ddsrt_free (configstr); + return xconfigstr; +} + +static bool endpoint_has_psmx_enabled (dds_entity_t rd_or_wr) +{ + dds_return_t rc; + struct dds_entity *x; + bool psmx_enabled = false; + rc = dds_entity_pin (rd_or_wr, &x); + CU_ASSERT_FATAL (rc == DDS_RETCODE_OK); + switch (dds_entity_kind (x)) + { + case DDS_KIND_READER: { + struct dds_reader const * const rd = (struct dds_reader *) x; + psmx_enabled = (rd->m_endpoint.psmx_endpoints.length > 0); + break; + } + case DDS_KIND_WRITER: { + struct dds_writer const * const wr = (struct dds_writer *) x; + psmx_enabled = (wr->m_endpoint.psmx_endpoints.length > 0); + break; + } + default: { + CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER || dds_entity_kind (x) == DDS_KIND_WRITER); + break; + } + } + dds_entity_unpin (x); + return psmx_enabled; +} + +/** @brief Convert a set of stats to a string. + * + * Truncates the output string if the string buffer capacity is too small. + * + * @param[in] dmock stats to convert + * @param[out] str_out string buffer to write the string into + * @param[in] str_capacity number of bytes the string buffer can hold + * + * @return Upon successful return, it returns the number of characters printed + * (excluding the null byte used to end output to strings). + * If an output error is encountered, a negative value is returned. +*/ +static int dummy_mockstats_tostring(const dummy_mockstats_t* dmock, char* str_out, size_t str_capacity) +{ + return snprintf( + str_out, + str_capacity, + "\ + create_psmx: %i\n\ + \n\ + type_qos_supported: %i\n\ + create_topic: %i\n\ + delete_topic: %i\n\ + deinit: %i\n\ + get_node_id: %i\n\ + supported_features: %i\n\ + \n\ + create_endpoint: %i\n\ + delete_endpoint: %i\n\ + \n\ + request_loan: %i\n\ + write: %i\n\ + take: %i\n\ + on_data_available: %i\n", + dmock->cnt_create_psmx, + dmock->cnt_type_qos_supported, + dmock->cnt_create_topic, + dmock->cnt_delete_topic, + dmock->cnt_deinit, + dmock->cnt_get_node_id, + dmock->cnt_supported_features, + dmock->cnt_create_endpoint, + dmock->cnt_delete_endpoint, + dmock->cnt_request_loan, + dmock->cnt_write, + dmock->cnt_take, + dmock->cnt_on_data_available + ); +} + +/// @brief Check that creating a domain with more than one psmx interface fails. +/// @methodology +/// - Create a config string with two psmx interfaces. +/// - Try to create a domain using this config string. +/// - Expectation: Failed to create the domain. +/// +CU_Test(ddsc_psmxif, config_multiple_psmx) +{ + dds_domainid_t domainId = 0; + const char* cdds_psmx_name1 = "dummy"; + const char* cdds_psmx_name2 = "iox"; + char* configstr_in = NULL; + { + char *configstr; + ddsrt_asprintf (&configstr, "\ +${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ +\ + spdp\ + \ + \ + \ + \ +\ +\ + ${CYCLONEDDS_PID}\ + 0\ +\ +\ + cdds.log.%d\ +\ + ", + cdds_psmx_name1, + cdds_psmx_name1, + -1, // This prevents plugins from forwarding across the "network". + cdds_psmx_name2, + cdds_psmx_name2, + -1, // This prevents plugins from forwarding across the "network". + (int) domainId // log file name + ); + char *xconfigstr = ddsrt_expand_envvars(configstr, domainId); + ddsrt_free(configstr); + configstr_in = xconfigstr; + } + const dds_entity_t domain = dds_create_domain (domainId, configstr_in); + CU_ASSERT_FATAL(domain <= 0); +} + +/// @brief Check that shared memory can be enabled (when supported) and the used locator can be set. +/// @methodology +/// - Check that the data types I'm planning to use are actually suitable for use with shared memory. +/// - Expectation: They are memcopy-safe. +/// +/// - Create a configuration with a psmx interface and specify the locator. +/// - Create a domain using this configuration. +/// - Check the locator used by the psmx instance. +/// - Expectation: The locator is the same as specified in the config for the domain. +/// - Query whether shared memory is supported. +/// - Assert that there is exactly one psmx instance. +/// - Assert that the psmx instance has a nonempty instance_name. +/// - Create some entities +/// - Check if shared memory is enabled. +/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// - Delete the domain +/// - Check the function call counts of the dummy psmx. +/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. +/// +/// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. +/// - Create a domain using this configuration. +/// - Query whether shared memory is supported. +/// - Assert that there is exactly one psmx instance. +/// - Assert that the psmx instance has a nonempty instance_name. +/// - Create some entities +/// - Check if shared memory is enabled. +/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// - Delete the domain +/// - Check the function call counts of the dummy psmx. +/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. +/// +CU_Test(ddsc_psmxif, shared_memory) +{ + dummy_mockstats_t* dmock = NULL; + char strbuf[512]; + const size_t strbuf_size = sizeof(strbuf); + { + // Check that the data types I'm planning to use are actually suitable for use with shared memory. + dds_data_type_properties_t props; + props = dds_stream_data_types(SC_Model_desc.m_ops); + CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); + props = dds_stream_data_types(PsmxType1_desc.m_ops); + CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); + } + + const dds_domainid_t domainId = 0; + bool supports_shared_memory_expected = true; + dds_psmx_endpoint_t* psmx_endpt_expected = NULL; + dds_psmx_topic_t* psmx_topic_expected = NULL; + bool specify_locator[] = {true, false}; + uint8_t locator_in[16]; + memset(locator_in, 0x0, sizeof(locator_in)); // Avoid warning 'uninitialized value'. + ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; + ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; + + int N = sizeof(specify_locator) / sizeof(specify_locator[0]); + for (int i = 0; i < N; ++i) + { + const uint8_t* locator = specify_locator[i] ? locator_in : NULL; + char* configstr_in = create_config(domainId, "dummy", locator); + const dds_entity_t domain = dds_create_domain(domainId, configstr_in); + CU_ASSERT_FATAL(domain > 0); + const dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + { + // Query whether shared memory is supported. + dds_domain* dom = NULL; + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(domain, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_DOMAIN); + dom = (dds_domain*)x; + } + assert(dom->psmx_instances.length >= 1); + struct dummy_psmx* dpsmx = (struct dummy_psmx*)dom->psmx_instances.instances[0]; + dmock = dpsmx->mockstats_get_ptr(); + CU_ASSERT_FATAL(dmock->cnt_create_psmx == 1); // Confirm the dummy psmx has been loaded. + dds_entity_unpin(&dom->m_entity); + } + // Check that the config string passed to `dds_create_domain()` has been correctly forwarded to the dummy psmx. + CU_ASSERT_FATAL(strstr(configstr_in, dmock->config) != NULL); // dmock->config is a substring of the original xml. + ddsrt_free(configstr_in); + + dds_entity_t writer1 = 0, reader1 = 0, writer2 = 0, reader2 = 0; + { + char topicname[100]; + dummy_topics_alloc(dmock, 2); + dummy_endpoints_alloc(dmock, 4); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; + dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic1 > 0); + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(topic1, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_TOPIC); + struct dds_psmx_topics_set* topics_set = &((dds_topic*)x)->m_ktopic->psmx_topics; + CU_ASSERT_FATAL(topics_set->length == 1 && topics_set->topics[0] == psmx_topic_expected); + dds_entity_unpin(x); + } + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + writer1 = dds_create_writer(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(writer1 > 0); + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(writer1, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_WRITER); + struct dds_psmx_endpoints_set* endpt_set = &((dds_writer*)x)->m_endpoint.psmx_endpoints; + CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); + dds_entity_unpin(x); + } + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + reader1 = dds_create_reader(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(reader1 > 0); + { + // Check the dummy psmx instance_name. + dds_qos_t* qos = dds_create_qos(); + dds_get_qos(reader1, qos); + uint32_t strs_len = 0; + char** strs = NULL; + CU_ASSERT_FATAL(dds_qget_psmx_instances(qos, &strs_len, &strs)); + CU_ASSERT_FATAL(strs_len == 1 && strcmp(strs[0], "dummy_psmx") == 0); + free_strings(strs_len, strs); + dds_delete_qos(qos); + } + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(reader1, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_READER); + struct dds_psmx_endpoints_set* endpt_set = &((dds_reader*)x)->m_endpoint.psmx_endpoints; + CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); + dds_entity_unpin(x); + } + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; + dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic2 > 0); + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(topic2, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_TOPIC); + struct dds_psmx_topics_set* topics_set = &((dds_topic*)x)->m_ktopic->psmx_topics; + CU_ASSERT_FATAL(topics_set->length == 1 && topics_set->topics[0] == psmx_topic_expected); + dds_entity_unpin(x); + } + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + writer2 = dds_create_writer(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(writer2 > 0); + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(writer2, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_WRITER); + struct dds_psmx_endpoints_set* endpt_set = &((dds_writer*)x)->m_endpoint.psmx_endpoints; + CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); + dds_entity_unpin(x); + } + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + reader2 = dds_create_reader(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(reader2 > 0); + { + dds_entity* x = NULL; + dds_return_t rc = dds_entity_pin(reader2, &x); + CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_READER); + struct dds_psmx_endpoints_set* endpt_set = &((dds_reader*)x)->m_endpoint.psmx_endpoints; + CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); + dds_entity_unpin(x); + } + } + + { + // Check that shared memory is enabled when it should, and not enabled when it shouldn't. + bool psmx_enabled; + psmx_enabled = endpoint_has_psmx_enabled(writer1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1) == supports_shared_memory_expected); + + psmx_enabled = endpoint_has_psmx_enabled(reader1); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1) == supports_shared_memory_expected); + + psmx_enabled = endpoint_has_psmx_enabled(writer2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2) == supports_shared_memory_expected); + + psmx_enabled = endpoint_has_psmx_enabled(reader2); + CU_ASSERT_FATAL(psmx_enabled); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory_expected); + } + dds_delete(domain); + + // Check number of calls against expected counts. + dummy_mockstats_tostring(dmock, strbuf, strbuf_size); + printf("ddsc_psmxif_shared_memory calls counts:\n%s\n", strbuf); + + CU_ASSERT_FATAL(dmock->cnt_create_psmx == 1); + + CU_ASSERT_FATAL(dmock->cnt_type_qos_supported == 10); + CU_ASSERT_FATAL(dmock->cnt_create_topic == 2); + CU_ASSERT_FATAL(dmock->cnt_delete_topic == 2); + CU_ASSERT_FATAL(dmock->cnt_deinit == 1); + CU_ASSERT_FATAL(dmock->cnt_get_node_id == 1); + CU_ASSERT_FATAL(dmock->cnt_supported_features == 4); + + CU_ASSERT_FATAL(dmock->cnt_create_endpoint == 4); + CU_ASSERT_FATAL(dmock->cnt_delete_endpoint == 4); + + CU_ASSERT_FATAL(dmock->cnt_request_loan == 0); + CU_ASSERT_FATAL(dmock->cnt_write == 0); + CU_ASSERT_FATAL(dmock->cnt_take == 0); + CU_ASSERT_FATAL(dmock->cnt_on_data_available == 2); + dmock = NULL; + } +} \ No newline at end of file From 22f2a7e30b4dc0e687db5e12b453b4c4359154c1 Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Fri, 3 May 2024 17:15:29 +0200 Subject: [PATCH 5/6] process review2 of 'add dummy psmx interface' Signed-off-by: Michel van den Hoek --- src/core/ddsc/tests/psmx.c | 49 ++- src/core/ddsc/tests/psmx_dummy_impl.c | 66 ++-- src/core/ddsc/tests/psmx_dummy_public.h | 23 +- src/core/ddsc/tests/psmxif.c | 457 ++++++++++-------------- 4 files changed, 276 insertions(+), 319 deletions(-) diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index 050e537115..bb55fb6580 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -113,22 +113,13 @@ static bool endpoint_has_psmx_enabled (dds_entity_t rd_or_wr) bool psmx_enabled = false; rc = dds_entity_pin (rd_or_wr, &x); CU_ASSERT_FATAL (rc == DDS_RETCODE_OK); - switch (dds_entity_kind (x)) - { - case DDS_KIND_READER: { - struct dds_reader const * const rd = (struct dds_reader *) x; - psmx_enabled = (rd->m_endpoint.psmx_endpoints.length > 0); - break; - } - case DDS_KIND_WRITER: { - struct dds_writer const * const wr = (struct dds_writer *) x; - psmx_enabled = (wr->m_endpoint.psmx_endpoints.length > 0); - break; - } - default: { - CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER || dds_entity_kind (x) == DDS_KIND_WRITER); - break; - } + if ( dds_entity_kind (x) == DDS_KIND_READER ) { + struct dds_reader const * const rd = (struct dds_reader *) x; + psmx_enabled = (rd->m_endpoint.psmx_endpoints.length > 0); + } else { + CU_ASSERT_FATAL(dds_entity_kind (x) == DDS_KIND_WRITER); + struct dds_writer const * const wr = (struct dds_writer *) x; + psmx_enabled = (wr->m_endpoint.psmx_endpoints.length > 0); } dds_entity_unpin (x); return psmx_enabled; @@ -1208,6 +1199,32 @@ CU_Test(ddsc_psmx, partition_xtalk) CU_ASSERT_FATAL (rc == 0); } +/// @brief Check that shared memory is not supported by endpoints created from a default Cyclone domain. +/// @methodology +/// - Create endpoints with a from a default domain. +/// - Assert that shared memory is not available. +CU_Test(ddsc_psmx, no_shared_memory) +{ + dds_entity_t participant, topic, writer, reader; + + participant = dds_create_participant(0, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + + char topicname[100]; + create_unique_topic_name("test_psmx_no_shared_memory", topicname, sizeof(topicname)); + topic = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic > 0); + + writer = dds_create_writer(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(writer > 0); + + reader = dds_create_reader(participant, topic, NULL, NULL); + CU_ASSERT_FATAL(reader > 0); + + CU_ASSERT_FATAL(!dds_is_shared_memory_available(writer)); + CU_ASSERT_FATAL(!dds_is_shared_memory_available(reader)); + dds_delete(dds_get_parent(participant)); +} #define MAX_SAMPLES 8 CU_Test (ddsc_psmx, basic) diff --git a/src/core/ddsc/tests/psmx_dummy_impl.c b/src/core/ddsc/tests/psmx_dummy_impl.c index ec58928d71..f11e7a1795 100644 --- a/src/core/ddsc/tests/psmx_dummy_impl.c +++ b/src/core/ddsc/tests/psmx_dummy_impl.c @@ -8,34 +8,46 @@ #include "psmx_dummy_public.h" #include "psmx_dummy_impl.h" -void dummy_topics_alloc(dummy_mockstats_t* mockstats, uint32_t topics_capacity) +static dummy_mockstats_t g_mockstats; + +dummy_mockstats_t* dummy_mockstats_get_ptr(void) +{ + return &g_mockstats; +} + +void dummy_topics_alloc(dummy_mockstats_t* mockstats, size_t topics_capacity) { mockstats->topics._maximum = topics_capacity; mockstats->topics._length = 0; mockstats->topics._buffer = ddsrt_malloc(topics_capacity * sizeof(dds_psmx_topic_t)); } -void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, uint32_t endpoints_capacity) +void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, size_t endpoints_capacity) { mockstats->endpoints._maximum = endpoints_capacity; mockstats->endpoints._length = 0; mockstats->endpoints._buffer = ddsrt_malloc(endpoints_capacity * sizeof(dds_psmx_endpoint_t)); } -static dummy_mockstats_t g_mockstats; - -static dummy_mockstats_t* dummy_mockstats_get_ptr() +static void dummy_psmx_loan_free(dds_loaned_sample_t* loan) { - return &g_mockstats; + (void)loan; } static dds_loaned_sample_t* dummy_psmx_ep_request_loan(dds_psmx_endpoint_t* psmx_endpoint, uint32_t size_requested) { - (void)psmx_endpoint; (void)size_requested; - // Details yet to be implemented ++g_mockstats.cnt_request_loan; - return NULL; + g_mockstats.request_loan_rcv_endpt = psmx_endpoint; + memset(&g_mockstats.loan, 0x0, sizeof(dds_loaned_sample_t)); + memset(&g_mockstats.loan_metadata, 0x0, sizeof(dds_psmx_metadata_t)); + g_mockstats.loan.ops.free = dummy_psmx_loan_free; + g_mockstats.loan.loan_origin.origin_kind = DDS_LOAN_ORIGIN_KIND_PSMX; + g_mockstats.loan.loan_origin.psmx_endpoint = psmx_endpoint; + g_mockstats.loan.metadata = &g_mockstats.loan_metadata; + g_mockstats.loan.sample_ptr = (void*)0x1; + ddsrt_atomic_st32(&g_mockstats.loan.refc, 1); + return &g_mockstats.loan; } static dds_return_t dummy_psmx_ep_write(dds_psmx_endpoint_t* psmx_endpoint, dds_loaned_sample_t* data) @@ -44,6 +56,8 @@ static dds_return_t dummy_psmx_ep_write(dds_psmx_endpoint_t* psmx_endpoint, dds_ (void)data; // Details yet to be implemented ++g_mockstats.cnt_write; + g_mockstats.write_rcv_endpt = psmx_endpoint; + g_mockstats.write_rcv_loan = data; return DDS_RETCODE_OK; } @@ -80,14 +94,17 @@ static dds_psmx_endpoint_t* dummy_psmx_create_endpoint( endp->psmx_topic = psmx_topic; endp->endpoint_type = endpoint_type; dds_add_psmx_endpoint_to_list(endp, &psmx_topic->psmx_endpoints); + ++g_mockstats.cnt_create_endpoint; + g_mockstats.create_endpoint_rcv_topic = psmx_topic; return endp; } static dds_return_t dummy_psmx_delete_endpoint(dds_psmx_endpoint_t* psmx_endpoint) { - memcpy(psmx_endpoint, (dds_psmx_endpoint_t*)g_mockstats.endpoints._buffer + (--g_mockstats.endpoints._length), sizeof(dds_psmx_endpoint_t)); + memset(psmx_endpoint, 0x0, sizeof(dds_psmx_endpoint_t)); ++g_mockstats.cnt_delete_endpoint; + g_mockstats.delete_endpoint_rcv_endpt = psmx_endpoint; return DDS_RETCODE_OK; } @@ -128,7 +145,7 @@ static dds_psmx_topic_t* dummy_psmx_create_topic( static dds_return_t dummy_psmx_delete_topic(dds_psmx_topic_t* psmx_topic) { dds_psmx_topic_cleanup_generic(psmx_topic); - memcpy(psmx_topic, (dds_psmx_topic_t*)g_mockstats.topics._buffer + (--g_mockstats.topics._length), sizeof(dds_psmx_topic_t)); + memset(psmx_topic, 0x0, sizeof(dds_psmx_topic_t)); ++g_mockstats.cnt_delete_topic; return DDS_RETCODE_OK; } @@ -157,7 +174,7 @@ static dds_psmx_features_t dummy_supported_features(const dds_psmx_t* psmx) { (void)psmx; ++g_mockstats.cnt_supported_features; - return DDS_PSMX_FEATURE_SHARED_MEMORY | DDS_PSMX_FEATURE_ZERO_COPY; + return g_mockstats.supports_shared_memory ? (DDS_PSMX_FEATURE_SHARED_MEMORY | DDS_PSMX_FEATURE_ZERO_COPY) : 0; } dds_return_t dummy_create_psmx(dds_psmx_t** psmx_out, dds_psmx_instance_id_t instance_id, const char* config) @@ -166,22 +183,21 @@ dds_return_t dummy_create_psmx(dds_psmx_t** psmx_out, dds_psmx_instance_id_t ins memset(&g_mockstats, 0, sizeof(dummy_mockstats_t)); g_mockstats.cnt_create_psmx = 1; - dummy_psmx_t* psmx = dds_alloc(sizeof(dummy_psmx_t)); - memset(psmx, 0, sizeof(dummy_psmx_t)); - psmx->c.instance_name = dds_string_dup("dummy_psmx"); - psmx->c.instance_id = instance_id; + dds_psmx_t* psmx = dds_alloc(sizeof(dds_psmx_t)); + memset(psmx, 0, sizeof(dds_psmx_t)); + psmx->instance_name = dds_string_dup("dummy_psmx"); + psmx->instance_id = instance_id; - psmx->c.ops.type_qos_supported = dummy_psmx_type_qos_supported; - psmx->c.ops.create_topic = dummy_psmx_create_topic; - psmx->c.ops.delete_topic = dummy_psmx_delete_topic; - psmx->c.ops.deinit = dummy_psmx_deinit; - psmx->c.ops.get_node_id = dummy_psmx_get_node_id; - psmx->c.ops.supported_features = dummy_supported_features; - dds_psmx_init_generic(&psmx->c); + psmx->ops.type_qos_supported = dummy_psmx_type_qos_supported; + psmx->ops.create_topic = dummy_psmx_create_topic; + psmx->ops.delete_topic = dummy_psmx_delete_topic; + psmx->ops.deinit = dummy_psmx_deinit; + psmx->ops.get_node_id = dummy_psmx_get_node_id; + psmx->ops.supported_features = dummy_supported_features; + dds_psmx_init_generic(psmx); g_mockstats.config = ddsrt_strdup(config); - psmx->mockstats_get_ptr = dummy_mockstats_get_ptr; - *psmx_out = (dds_psmx_t*)psmx; + *psmx_out = psmx; return DDS_RETCODE_OK; } diff --git a/src/core/ddsc/tests/psmx_dummy_public.h b/src/core/ddsc/tests/psmx_dummy_public.h index d19cff7cd6..3037900726 100644 --- a/src/core/ddsc/tests/psmx_dummy_public.h +++ b/src/core/ddsc/tests/psmx_dummy_public.h @@ -17,8 +17,8 @@ extern "C" { #endif typedef struct dynamic_array_s{ - uint32_t _maximum; - uint32_t _length; + size_t _maximum; + size_t _length; void* _buffer; }dynamic_array_t; @@ -44,18 +44,23 @@ typedef struct dummy_mockstats_s{ int cnt_on_data_available; // Exposed internals + bool supports_shared_memory; char* config; dynamic_array_t topics; dynamic_array_t endpoints; -}dummy_mockstats_t; + dds_loaned_sample_t loan; + dds_psmx_metadata_t loan_metadata; -typedef struct dummy_psmx { - dds_psmx_t c; - dummy_mockstats_t* (*mockstats_get_ptr)(); -}dummy_psmx_t; + dds_psmx_topic_t* create_endpoint_rcv_topic; + dds_psmx_endpoint_t* delete_endpoint_rcv_endpt; + dds_psmx_endpoint_t* request_loan_rcv_endpt; + dds_psmx_endpoint_t* write_rcv_endpt; + dds_loaned_sample_t* write_rcv_loan; +}dummy_mockstats_t; -DDS_EXPORT void dummy_topics_alloc(dummy_mockstats_t* mockstats, uint32_t topics_capacity); -DDS_EXPORT void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, uint32_t endpoints_capacity); +DDS_EXPORT dummy_mockstats_t* dummy_mockstats_get_ptr(void); +DDS_EXPORT void dummy_topics_alloc(dummy_mockstats_t* mockstats, size_t topics_capacity); +DDS_EXPORT void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, size_t endpoints_capacity); #if defined (__cplusplus) } diff --git a/src/core/ddsc/tests/psmxif.c b/src/core/ddsc/tests/psmxif.c index dc0280d8d7..f5c98ff9e0 100644 --- a/src/core/ddsc/tests/psmxif.c +++ b/src/core/ddsc/tests/psmxif.c @@ -37,90 +37,12 @@ static void free_strings(uint32_t len, char** strings) { - if (len != 0 && strings != NULL) { - for (uint32_t i = 0; i < len; i++) { - dds_free(strings[i]); - } - } - dds_free(strings); -} - -/** - * @brief Create a participant object, providing a configuration to choose the psmx interface. - * - * @param[in] int_dom the domain id - * @param[in] cdds_psmx_name the name of the psmx interface to use - * @param[in] locator an array of 16 bytes, NULL if not used - * @return dds_entity_t the participant - */ -static char* create_config(dds_domainid_t int_dom, const char* cdds_psmx_name, const uint8_t* locator) -{ - char *configstr; - char locator_str[74]; - if ( locator == NULL ) { - locator_str[0] = 0; - } else { - const uint8_t *l = locator; - snprintf( - locator_str, - sizeof(locator_str), - "LOCATOR=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x;", - l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] - ); - } - ddsrt_asprintf (&configstr, "\ -${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ -\ - spdp\ - \ - \ - \ -\ -\ - ${CYCLONEDDS_PID}\ - 0\ -\ -\ - cdds.log.%d\ -\ -", - cdds_psmx_name, - cdds_psmx_name, - locator_str, - locator ? (int) locator[0] : -1, // This prevents plugins from forwarding across the "network". - (int) int_dom // log file name - ); - char *xconfigstr = ddsrt_expand_envvars (configstr, int_dom); - ddsrt_free (configstr); - return xconfigstr; -} - -static bool endpoint_has_psmx_enabled (dds_entity_t rd_or_wr) -{ - dds_return_t rc; - struct dds_entity *x; - bool psmx_enabled = false; - rc = dds_entity_pin (rd_or_wr, &x); - CU_ASSERT_FATAL (rc == DDS_RETCODE_OK); - switch (dds_entity_kind (x)) - { - case DDS_KIND_READER: { - struct dds_reader const * const rd = (struct dds_reader *) x; - psmx_enabled = (rd->m_endpoint.psmx_endpoints.length > 0); - break; - } - case DDS_KIND_WRITER: { - struct dds_writer const * const wr = (struct dds_writer *) x; - psmx_enabled = (wr->m_endpoint.psmx_endpoints.length > 0); - break; - } - default: { - CU_ASSERT_FATAL (dds_entity_kind (x) == DDS_KIND_READER || dds_entity_kind (x) == DDS_KIND_WRITER); - break; + if (len != 0 && strings != NULL) { + for (uint32_t i = 0; i < len; i++) { + dds_free(strings[i]); } } - dds_entity_unpin (x); - return psmx_enabled; + dds_free(strings); } /** @brief Convert a set of stats to a string. @@ -173,6 +95,29 @@ static int dummy_mockstats_tostring(const dummy_mockstats_t* dmock, char* str_ou ); } +static dds_entity_t create_participant(dds_domainid_t domainId) +{ + char configstr[] = "\ +${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ +\ +spdp\ +\ + \ +\ +\ +\ +cdds.log.0\ +\ +"; + char* configstr_in = ddsrt_expand_envvars (configstr, domainId); + const dds_entity_t domain = dds_create_domain(domainId, configstr_in); + ddsrt_free(configstr_in); + CU_ASSERT_FATAL(domain > 0); + const dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); + CU_ASSERT_FATAL(participant > 0); + return participant; +} + /// @brief Check that creating a domain with more than one psmx interface fails. /// @methodology /// - Create a config string with two psmx interfaces. @@ -182,18 +127,15 @@ static int dummy_mockstats_tostring(const dummy_mockstats_t* dmock, char* str_ou CU_Test(ddsc_psmxif, config_multiple_psmx) { dds_domainid_t domainId = 0; - const char* cdds_psmx_name1 = "dummy"; - const char* cdds_psmx_name2 = "iox"; char* configstr_in = NULL; { - char *configstr; - ddsrt_asprintf (&configstr, "\ + char configstr[] = "\ ${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}\ \ spdp\ \ - \ - \ + \ + \ \ \ \ @@ -201,59 +143,89 @@ CU_Test(ddsc_psmxif, config_multiple_psmx) 0\ \ \ - cdds.log.%d\ + cdds.log.0\ \ - ", - cdds_psmx_name1, - cdds_psmx_name1, - -1, // This prevents plugins from forwarding across the "network". - cdds_psmx_name2, - cdds_psmx_name2, - -1, // This prevents plugins from forwarding across the "network". - (int) domainId // log file name - ); - char *xconfigstr = ddsrt_expand_envvars(configstr, domainId); - ddsrt_free(configstr); - configstr_in = xconfigstr; + "; + configstr_in = ddsrt_expand_envvars(configstr, domainId); } const dds_entity_t domain = dds_create_domain (domainId, configstr_in); CU_ASSERT_FATAL(domain <= 0); } -/// @brief Check that shared memory can be enabled (when supported) and the used locator can be set. +static void assert_psmx_instance_name(dds_entity_t endpt, const char* name_expected) +{ + dds_qos_t* qos = dds_create_qos(); + dds_get_qos(endpt, qos); + uint32_t strs_len = 0; + char** strs = NULL; + CU_ASSERT_FATAL(dds_qget_psmx_instances(qos, &strs_len, &strs)); + CU_ASSERT_FATAL(strs_len == 1 && strcmp(strs[0], name_expected) == 0); + free_strings(strs_len, strs); + dds_delete_qos(qos); +} + +/// @brief Check that the instance_name is as provided from dummy_create_psmx(). +/// @methodology +/// - Create domain with a config containing the name for the psmx instance. +/// - Create readers and writers. +/// - Using the QoS interface, for each endpoint check that the instance_name is correct. +/// +CU_Test(ddsc_psmxif, instance_name) +{ + const dds_domainid_t domainId = 0; + dds_entity_t participant = create_participant(domainId); + dds_entity_t domain = dds_get_parent(participant); + dummy_mockstats_t* dmock = dummy_mockstats_get_ptr(); + + dds_entity_t writer1 = 0, reader1 = 0, writer2 = 0, reader2 = 0; + + char topicname[100]; + dummy_topics_alloc(dmock, 2); + dummy_endpoints_alloc(dmock, 4); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic1 > 0); + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic2 > 0); + + writer1 = dds_create_writer(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(writer1 > 0); + assert_psmx_instance_name(writer1, "dummy_psmx"); + reader1 = dds_create_reader(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(reader1 > 0); + assert_psmx_instance_name(reader1, "dummy_psmx"); + writer2 = dds_create_writer(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(writer2 > 0); + assert_psmx_instance_name(writer2, "dummy_psmx"); + reader2 = dds_create_reader(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(reader2 > 0); + assert_psmx_instance_name(reader2, "dummy_psmx"); + dds_delete(domain); +} + +/// @brief Check that shared memory availability and entity and loan pointers are correctly propagated through the psmx interface. /// @methodology /// - Check that the data types I'm planning to use are actually suitable for use with shared memory. /// - Expectation: They are memcopy-safe. /// -/// - Create a configuration with a psmx interface and specify the locator. +/// - Create a configuration with a psmx interface. /// - Create a domain using this configuration. -/// - Check the locator used by the psmx instance. -/// - Expectation: The locator is the same as specified in the config for the domain. -/// - Query whether shared memory is supported. /// - Assert that there is exactly one psmx instance. -/// - Assert that the psmx instance has a nonempty instance_name. +/// - Decide whether shared memory is supported (communicated to the dummy psmx). /// - Create some entities -/// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). -/// - Delete the domain -/// - Check the function call counts of the dummy psmx. -/// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. -/// -/// - Create a configuration with a psmx interface capable of shared memory and don't specify a locator. -/// - Create a domain using this configuration. -/// - Query whether shared memory is supported. -/// - Assert that there is exactly one psmx instance. -/// - Assert that the psmx instance has a nonempty instance_name. -/// - Create some entities -/// - Check if shared memory is enabled. -/// - Expectation: Shared memory is enabled iff the psmx interface supports it (use queried value). +/// - Assert that the psmx_topic created during dummy_psmx_create_topic, is propagated to dummy_psmx_create_endpoint(). +/// - Assert that the psmx_endpoint created during dummy_psmx_create_endpoint(), is propagated to dummy_psmx_request_loan(), dummy_psmx_write(), dummy_psmx_delete_endpoint(). +/// - Assert that the loan created during dummy_psmx_request_loan(), is propagated to dummy_psmx_write(). +/// - Check if shared memory is available. +/// - Expectation: Shared memory is available iff the psmx interface supports it (check for the false and the true case). /// - Delete the domain /// - Check the function call counts of the dummy psmx. /// - Expectation: The counts match expectations. In particular, create counts must match their delete counterpart. /// CU_Test(ddsc_psmxif, shared_memory) { - dummy_mockstats_t* dmock = NULL; char strbuf[512]; const size_t strbuf_size = sizeof(strbuf); { @@ -265,155 +237,103 @@ CU_Test(ddsc_psmxif, shared_memory) CU_ASSERT_FATAL((props & DDS_DATA_TYPE_IS_MEMCPY_SAFE) == DDS_DATA_TYPE_IS_MEMCPY_SAFE); } - const dds_domainid_t domainId = 0; - bool supports_shared_memory_expected = true; - dds_psmx_endpoint_t* psmx_endpt_expected = NULL; - dds_psmx_topic_t* psmx_topic_expected = NULL; - bool specify_locator[] = {true, false}; - uint8_t locator_in[16]; - memset(locator_in, 0x0, sizeof(locator_in)); // Avoid warning 'uninitialized value'. - ((uint64_t*)locator_in)[0] = (uint64_t)0x4a4d203df6996395; - ((uint64_t*)locator_in)[1] = (uint64_t)0xe1412fbecc2de4b6; - - int N = sizeof(specify_locator) / sizeof(specify_locator[0]); - for (int i = 0; i < N; ++i) - { - const uint8_t* locator = specify_locator[i] ? locator_in : NULL; - char* configstr_in = create_config(domainId, "dummy", locator); - const dds_entity_t domain = dds_create_domain(domainId, configstr_in); - CU_ASSERT_FATAL(domain > 0); - const dds_entity_t participant = dds_create_participant(domainId, NULL, NULL); - CU_ASSERT_FATAL(participant > 0); + for (size_t i = 0; i < 2; ++i) { + const dds_domainid_t domainId = 0; + dds_entity_t participant = create_participant(domainId); + dds_entity_t domain = dds_get_parent(participant); + dummy_mockstats_t* dmock = dummy_mockstats_get_ptr(); + CU_ASSERT_FATAL(dmock->cnt_create_psmx == 1); // Confirm the dummy psmx has been loaded. { - // Query whether shared memory is supported. - dds_domain* dom = NULL; - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(domain, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_DOMAIN); - dom = (dds_domain*)x; - } - assert(dom->psmx_instances.length >= 1); - struct dummy_psmx* dpsmx = (struct dummy_psmx*)dom->psmx_instances.instances[0]; - dmock = dpsmx->mockstats_get_ptr(); - CU_ASSERT_FATAL(dmock->cnt_create_psmx == 1); // Confirm the dummy psmx has been loaded. - dds_entity_unpin(&dom->m_entity); + // Assert that there is exactly one psmx instance. + dds_entity* x = NULL; + CU_ASSERT_FATAL(dds_entity_pin(domain, &x) == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_DOMAIN); + CU_ASSERT_FATAL(((dds_domain*)x)->psmx_instances.length == 1); + dds_entity_unpin(x); } + + bool supports_shared_memory_expected = (bool)i; + dmock->supports_shared_memory = supports_shared_memory_expected; + + dds_psmx_topic_t* psmx_topic_expected = NULL; + dds_psmx_endpoint_t* psmx_endpt_expected = NULL; + dds_psmx_endpoint_t* delete_endpoint_expected[4]; + memset(delete_endpoint_expected, 0x0, sizeof(delete_endpoint_expected)); + size_t delete_endpoint_idx = 0; + const size_t endpt_cnt = sizeof(delete_endpoint_expected) / sizeof(delete_endpoint_expected[0]); + // Check that the config string passed to `dds_create_domain()` has been correctly forwarded to the dummy psmx. - CU_ASSERT_FATAL(strstr(configstr_in, dmock->config) != NULL); // dmock->config is a substring of the original xml. - ddsrt_free(configstr_in); + char dmock_config_expected[] = "LOCATOR=4a4d203df6996395e1412fbecc2de4b6;SERVICE_NAME=service_psmx_dummy;KEYED_TOPICS=true;"; + CU_ASSERT_FATAL(strcmp(dmock->config, dmock_config_expected) == 0); + void* sample = NULL; dds_entity_t writer1 = 0, reader1 = 0, writer2 = 0, reader2 = 0; - { - char topicname[100]; - dummy_topics_alloc(dmock, 2); - dummy_endpoints_alloc(dmock, 4); - - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; - dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic1 > 0); - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(topic1, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_TOPIC); - struct dds_psmx_topics_set* topics_set = &((dds_topic*)x)->m_ktopic->psmx_topics; - CU_ASSERT_FATAL(topics_set->length == 1 && topics_set->topics[0] == psmx_topic_expected); - dds_entity_unpin(x); - } - - psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; - writer1 = dds_create_writer(participant, topic1, NULL, NULL); - CU_ASSERT_FATAL(writer1 > 0); - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(writer1, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_WRITER); - struct dds_psmx_endpoints_set* endpt_set = &((dds_writer*)x)->m_endpoint.psmx_endpoints; - CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); - dds_entity_unpin(x); - } - - psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; - reader1 = dds_create_reader(participant, topic1, NULL, NULL); - CU_ASSERT_FATAL(reader1 > 0); - { - // Check the dummy psmx instance_name. - dds_qos_t* qos = dds_create_qos(); - dds_get_qos(reader1, qos); - uint32_t strs_len = 0; - char** strs = NULL; - CU_ASSERT_FATAL(dds_qget_psmx_instances(qos, &strs_len, &strs)); - CU_ASSERT_FATAL(strs_len == 1 && strcmp(strs[0], "dummy_psmx") == 0); - free_strings(strs_len, strs); - dds_delete_qos(qos); - } - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(reader1, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_READER); - struct dds_psmx_endpoints_set* endpt_set = &((dds_reader*)x)->m_endpoint.psmx_endpoints; - CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); - dds_entity_unpin(x); - } - - create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); - psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; - dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); - CU_ASSERT_FATAL(topic2 > 0); - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(topic2, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_TOPIC); - struct dds_psmx_topics_set* topics_set = &((dds_topic*)x)->m_ktopic->psmx_topics; - CU_ASSERT_FATAL(topics_set->length == 1 && topics_set->topics[0] == psmx_topic_expected); - dds_entity_unpin(x); - } - - psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; - writer2 = dds_create_writer(participant, topic2, NULL, NULL); - CU_ASSERT_FATAL(writer2 > 0); - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(writer2, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_WRITER); - struct dds_psmx_endpoints_set* endpt_set = &((dds_writer*)x)->m_endpoint.psmx_endpoints; - CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); - dds_entity_unpin(x); - } - - psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; - reader2 = dds_create_reader(participant, topic2, NULL, NULL); - CU_ASSERT_FATAL(reader2 > 0); - { - dds_entity* x = NULL; - dds_return_t rc = dds_entity_pin(reader2, &x); - CU_ASSERT_FATAL(rc == DDS_RETCODE_OK && dds_entity_kind(x) == DDS_KIND_READER); - struct dds_psmx_endpoints_set* endpt_set = &((dds_reader*)x)->m_endpoint.psmx_endpoints; - CU_ASSERT_FATAL(endpt_set->length == 1 && endpt_set->endpoints[0] == psmx_endpt_expected); - dds_entity_unpin(x); - } - } - { - // Check that shared memory is enabled when it should, and not enabled when it shouldn't. - bool psmx_enabled; - psmx_enabled = endpoint_has_psmx_enabled(writer1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1) == supports_shared_memory_expected); - - psmx_enabled = endpoint_has_psmx_enabled(reader1); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1) == supports_shared_memory_expected); - - psmx_enabled = endpoint_has_psmx_enabled(writer2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2) == supports_shared_memory_expected); - - psmx_enabled = endpoint_has_psmx_enabled(reader2); - CU_ASSERT_FATAL(psmx_enabled); - CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory_expected); - } + char topicname[100]; + dummy_topics_alloc(dmock, 2); + dummy_endpoints_alloc(dmock, endpt_cnt); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; + dds_entity_t topic1 = dds_create_topic(participant, &SC_Model_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic1 > 0); + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + delete_endpoint_expected[delete_endpoint_idx++] = psmx_endpt_expected; + writer1 = dds_create_writer(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(writer1 > 0); + CU_ASSERT_FATAL(dmock->create_endpoint_rcv_topic == psmx_topic_expected); + CU_ASSERT_FATAL(dds_request_loan(writer1, &sample) == DDS_RETCODE_OK); + CU_ASSERT_FATAL(dmock->request_loan_rcv_endpt == psmx_endpt_expected); + dmock->write_rcv_loan = NULL; + CU_ASSERT_FATAL(dds_write(writer1, sample) == DDS_RETCODE_OK); + CU_ASSERT_FATAL(dmock->write_rcv_endpt == psmx_endpt_expected); + CU_ASSERT_FATAL(dmock->write_rcv_loan == &dmock->loan); + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + delete_endpoint_expected[delete_endpoint_idx++] = psmx_endpt_expected; + reader1 = dds_create_reader(participant, topic1, NULL, NULL); + CU_ASSERT_FATAL(reader1 > 0); + CU_ASSERT_FATAL(dmock->create_endpoint_rcv_topic == psmx_topic_expected); + + create_unique_topic_name("shared_memory", topicname, sizeof(topicname)); + psmx_topic_expected = (dds_psmx_topic_t*)dmock->topics._buffer + dmock->topics._length; + dds_entity_t topic2 = dds_create_topic(participant, &PsmxType1_desc, topicname, NULL, NULL); + CU_ASSERT_FATAL(topic2 > 0); + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + delete_endpoint_expected[delete_endpoint_idx++] = psmx_endpt_expected; + writer2 = dds_create_writer(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(writer2 > 0); + CU_ASSERT_FATAL(dmock->create_endpoint_rcv_topic == psmx_topic_expected); + CU_ASSERT_FATAL(dds_request_loan(writer2, &sample) == DDS_RETCODE_OK); + CU_ASSERT_FATAL(dmock->request_loan_rcv_endpt == psmx_endpt_expected); + dmock->write_rcv_loan = NULL; + CU_ASSERT_FATAL(dds_write(writer2, sample) == DDS_RETCODE_OK); + CU_ASSERT_FATAL(dmock->write_rcv_endpt == psmx_endpt_expected); + CU_ASSERT_FATAL(dmock->write_rcv_loan == &dmock->loan); + + psmx_endpt_expected = (dds_psmx_endpoint_t*)dmock->endpoints._buffer + dmock->endpoints._length; + delete_endpoint_expected[delete_endpoint_idx++] = psmx_endpt_expected; + reader2 = dds_create_reader(participant, topic2, NULL, NULL); + CU_ASSERT_FATAL(reader2 > 0); + CU_ASSERT_FATAL(dmock->create_endpoint_rcv_topic == psmx_topic_expected); + + // Check that shared memory is available when it should, and not available when it shouldn't. + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer1) == supports_shared_memory_expected); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader1) == supports_shared_memory_expected); + CU_ASSERT_FATAL(dds_is_shared_memory_available(writer2) == supports_shared_memory_expected); + CU_ASSERT_FATAL(dds_is_shared_memory_available(reader2) == supports_shared_memory_expected); + + // Check that psmx_endpoint pointers originally from `dummy_psmx_create_endpoint()`, end up in `dummy_psmx_delete_endpoint()`. + CU_ASSERT_FATAL(delete_endpoint_idx == endpt_cnt); + dds_delete(reader2); + CU_ASSERT_FATAL(dmock->delete_endpoint_rcv_endpt == delete_endpoint_expected[--delete_endpoint_idx]); + dds_delete(writer2); + CU_ASSERT_FATAL(dmock->delete_endpoint_rcv_endpt == delete_endpoint_expected[--delete_endpoint_idx]); + dds_delete(reader1); + CU_ASSERT_FATAL(dmock->delete_endpoint_rcv_endpt == delete_endpoint_expected[--delete_endpoint_idx]); + dds_delete(writer1); + CU_ASSERT_FATAL(dmock->delete_endpoint_rcv_endpt == delete_endpoint_expected[--delete_endpoint_idx]); dds_delete(domain); // Check number of calls against expected counts. @@ -432,10 +352,9 @@ CU_Test(ddsc_psmxif, shared_memory) CU_ASSERT_FATAL(dmock->cnt_create_endpoint == 4); CU_ASSERT_FATAL(dmock->cnt_delete_endpoint == 4); - CU_ASSERT_FATAL(dmock->cnt_request_loan == 0); - CU_ASSERT_FATAL(dmock->cnt_write == 0); + CU_ASSERT_FATAL(dmock->cnt_request_loan == 2); + CU_ASSERT_FATAL(dmock->cnt_write == 2); CU_ASSERT_FATAL(dmock->cnt_take == 0); CU_ASSERT_FATAL(dmock->cnt_on_data_available == 2); - dmock = NULL; } -} \ No newline at end of file +} From 5f904c1a895b648aa8ce07f1b21048bab05bee7d Mon Sep 17 00:00:00 2001 From: Michel van den Hoek Date: Mon, 8 Jul 2024 16:54:02 +0200 Subject: [PATCH 6/6] psmx_dummy: fix Windows linkage issue, make Cyclone reject configuration with multiple psmx interfaces Signed-off-by: Michel van den Hoek --- src/core/ddsc/src/dds_psmx.c | 44 ++++++++++--------------- src/core/ddsc/tests/CMakeLists.txt | 10 ++---- src/core/ddsc/tests/psmx_dummy_public.h | 8 +++-- src/core/ddsc/tests/psmxif.c | 1 + 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/core/ddsc/src/dds_psmx.c b/src/core/ddsc/src/dds_psmx.c index 7f613984d9..20f52beed7 100644 --- a/src/core/ddsc/src/dds_psmx.c +++ b/src/core/ddsc/src/dds_psmx.c @@ -327,40 +327,32 @@ static dds_return_t psmx_instance_load (const struct ddsi_domaingv *gv, const st return ret; } -static int compare_psmx_prio (const void *va, const void *vb) -{ - const struct dds_psmx *psmx1 = va; - const struct dds_psmx *psmx2 = vb; - return (psmx1->priority == psmx2->priority) ? 0 : ((psmx1->priority < psmx2->priority) ? 1 : -1); -} - dds_return_t dds_pubsub_message_exchange_init (const struct ddsi_domaingv *gv, struct dds_domain *domain) { dds_return_t ret = DDS_RETCODE_OK; if (gv->config.psmx_instances != NULL) { struct ddsi_config_psmx_listelem *iface = gv->config.psmx_instances; - while (iface && domain->psmx_instances.length < DDS_MAX_PSMX_INSTANCES) - { - GVLOG(DDS_LC_INFO, "Loading PSMX instances %s\n", iface->cfg.name); - struct dds_psmx *psmx = NULL; - ddsrt_dynlib_t lib_handle; - if (psmx_instance_load (gv, &iface->cfg, &psmx, &lib_handle) == DDS_RETCODE_OK) - { - domain->psmx_instances.instances[domain->psmx_instances.length] = psmx; - domain->psmx_instances.lib_handles[domain->psmx_instances.length] = lib_handle; - domain->psmx_instances.length++; - } - else - { - GVERROR ("error loading PSMX instance \"%s\"\n", iface->cfg.name); - ret = DDS_RETCODE_ERROR; - break; + if ( iface != NULL ) { + if ( iface->next != NULL ) { + ret = DDS_RETCODE_UNSUPPORTED; // Only one psmx interface is supported. + }else{ + GVLOG(DDS_LC_INFO, "Loading PSMX instances %s\n", iface->cfg.name); + struct dds_psmx *psmx = NULL; + ddsrt_dynlib_t lib_handle; + if (psmx_instance_load (gv, &iface->cfg, &psmx, &lib_handle) == DDS_RETCODE_OK) + { + domain->psmx_instances.instances[domain->psmx_instances.length] = psmx; + domain->psmx_instances.lib_handles[domain->psmx_instances.length] = lib_handle; + domain->psmx_instances.length++; + } + else + { + GVERROR ("error loading PSMX instance \"%s\"\n", iface->cfg.name); + ret = DDS_RETCODE_ERROR; + } } - iface = iface->next; } - - qsort (domain->psmx_instances.instances, domain->psmx_instances.length, sizeof (*domain->psmx_instances.instances), compare_psmx_prio); } return ret; } diff --git a/src/core/ddsc/tests/CMakeLists.txt b/src/core/ddsc/tests/CMakeLists.txt index c10d9e9a30..b54e40ad0f 100644 --- a/src/core/ddsc/tests/CMakeLists.txt +++ b/src/core/ddsc/tests/CMakeLists.txt @@ -159,15 +159,10 @@ if(ENABLE_QOS_PROVIDER) endif() # PSMX dummy implementation for interface testing -set( - psmx_dummy_sources - "psmx_dummy_impl.c" - "psmx_dummy_impl.h" -) if(BUILD_SHARED_LIBS) - add_library(psmx_dummy SHARED ${psmx_dummy_sources}) + add_library(psmx_dummy SHARED "psmx_dummy_impl.c") else() - add_library(psmx_dummy OBJECT ${psmx_dummy_sources}) + add_library(psmx_dummy OBJECT "psmx_dummy_impl.c") set_property(GLOBAL APPEND PROPERTY cdds_plugin_list psmx_dummy) set_property(GLOBAL PROPERTY psmx_dummy_symbols dummy_create_psmx) endif() @@ -199,6 +194,7 @@ add_cunit_executable(cunit_ddsc ${ddsc_test_sources}) target_include_directories( cunit_ddsc PRIVATE "$" + "$" "$" "$" "$" diff --git a/src/core/ddsc/tests/psmx_dummy_public.h b/src/core/ddsc/tests/psmx_dummy_public.h index 3037900726..176ec63f4a 100644 --- a/src/core/ddsc/tests/psmx_dummy_public.h +++ b/src/core/ddsc/tests/psmx_dummy_public.h @@ -12,6 +12,8 @@ #ifndef PSMX_DUMMY_PUBLIC_H #define PSMX_DUMMY_PUBLIC_H +#include "dds/psmx_dummy/export.h" + #if defined (__cplusplus) extern "C" { #endif @@ -58,9 +60,9 @@ typedef struct dummy_mockstats_s{ dds_loaned_sample_t* write_rcv_loan; }dummy_mockstats_t; -DDS_EXPORT dummy_mockstats_t* dummy_mockstats_get_ptr(void); -DDS_EXPORT void dummy_topics_alloc(dummy_mockstats_t* mockstats, size_t topics_capacity); -DDS_EXPORT void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, size_t endpoints_capacity); +PSMX_DUMMY_EXPORT dummy_mockstats_t* dummy_mockstats_get_ptr(void); +PSMX_DUMMY_EXPORT void dummy_topics_alloc(dummy_mockstats_t* mockstats, size_t topics_capacity); +PSMX_DUMMY_EXPORT void dummy_endpoints_alloc(dummy_mockstats_t* mockstats, size_t endpoints_capacity); #if defined (__cplusplus) } diff --git a/src/core/ddsc/tests/psmxif.c b/src/core/ddsc/tests/psmxif.c index f5c98ff9e0..cdccc554a0 100644 --- a/src/core/ddsc/tests/psmxif.c +++ b/src/core/ddsc/tests/psmxif.c @@ -149,6 +149,7 @@ CU_Test(ddsc_psmxif, config_multiple_psmx) configstr_in = ddsrt_expand_envvars(configstr, domainId); } const dds_entity_t domain = dds_create_domain (domainId, configstr_in); + ddsrt_free(configstr_in); CU_ASSERT_FATAL(domain <= 0); }