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 81f93906e0..e54cd30db5 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" @@ -30,7 +29,6 @@ #include "config_env.h" #include "test_common.h" -#include "psmx_dummy_public.h" #include "Array100.h" #include "DynamicData.h" #include "PsmxDataModels.h" @@ -64,35 +62,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\ \ - \ + \ \ \ \ @@ -104,8 +84,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); @@ -118,39 +98,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; @@ -687,7 +634,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]); @@ -1032,80 +979,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; @@ -1214,7 +1087,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); @@ -1239,7 +1112,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); @@ -1344,7 +1217,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]; @@ -1391,164 +1264,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 { @@ -1561,7 +1276,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++) { @@ -1757,7 +1472,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..31efd17730 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; + +void dummy_topics_alloc(dummy_mockstats_t* mockstats, uint32_t topics_capacity); +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..f79dff3d67 --- /dev/null +++ b/src/core/ddsc/tests/psmxif.c @@ -0,0 +1,498 @@ +// 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); +} + +#if 0 +/** + * @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 dds_entity_t create_participant(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 Iceoryx and Cyclone-based plugins from forwarding across the "network" + (int) int_dom // log file name + ); + char *xconfigstr = ddsrt_expand_envvars (configstr, int_dom); + const dds_entity_t dom = dds_create_domain (int_dom, xconfigstr); + ddsrt_free (xconfigstr); + ddsrt_free (configstr); + CU_ASSERT_FATAL (dom > 0); + const dds_entity_t pp = dds_create_participant (int_dom, NULL, NULL); + CU_ASSERT_FATAL (pp > 0); + return pp; +} +#endif + +/** + * @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); + assert(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); + assert(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); + assert(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; + assert(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); + assert(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); + assert(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); + assert(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); + assert(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