From 05bdd5b61798126b60c3adb266bbe69f0141a98f Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Mon, 4 Dec 2023 13:56:40 +0100 Subject: [PATCH] Full test coverage for loans/types/PSMX This runs over: - memcpy-safe types and non-memcpy-safe types - use of writer loans - use of reader loans - use of PSMX - writing and disposing (sample vs key) That should provide basic test coverage for all combinations that the code distinguishes between. The type definitions are chosen so that raw samples and CDR have different memory layouts and one cannot be confused for the other. Signed-off-by: Erik Boasson --- src/core/ddsc/tests/PsmxDataModels.idl | 17 +++ src/core/ddsc/tests/psmx.c | 175 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/src/core/ddsc/tests/PsmxDataModels.idl b/src/core/ddsc/tests/PsmxDataModels.idl index 5f30b988b3..9bc79b78fa 100644 --- a/src/core/ddsc/tests/PsmxDataModels.idl +++ b/src/core/ddsc/tests/PsmxDataModels.idl @@ -39,3 +39,20 @@ struct PsmxType1 { struct PsmxKeySupportCheck { @key long k; }; + +struct PsmxLoanTest0 { + PsmxType1xy xy; + @key octet z; +}; + +struct PsmxLoanTest1 { + PsmxType1xy xy; + @key octet z; + string str; +}; + +struct PsmxLoanTest2 { + PsmxType1xy xy; + @key octet z; + @key string str; +}; diff --git a/src/core/ddsc/tests/psmx.c b/src/core/ddsc/tests/psmx.c index 10c66e5b37..e54cd30db5 100644 --- a/src/core/ddsc/tests/psmx.c +++ b/src/core/ddsc/tests/psmx.c @@ -1396,3 +1396,178 @@ CU_Test (ddsc_psmx, zero_copy) } dds_delete (dds_get_parent (pp)); } + +static void deepcopy_sample_contents (const dds_topic_descriptor_t *tpdesc, void *output, const void *input) +{ + struct dds_cdrstream_desc desc; + dds_cdrstream_desc_from_topic_desc (&desc, tpdesc); + struct dds_ostream os; + dds_ostream_init (&os, &dds_cdrstream_default_allocator, 0, DDSI_RTPS_CDR_ENC_VERSION_2); + dds_stream_write_sample (&os, &dds_cdrstream_default_allocator, input, &desc); + struct dds_istream is; + dds_istream_init (&is, os.m_index, os.m_buffer, os.m_xcdr_version); + memset (output, 0, desc.size); + dds_stream_read_sample (&is, output, &dds_cdrstream_default_allocator, &desc); + dds_istream_fini (&is); + dds_ostream_fini (&os, &dds_cdrstream_default_allocator); + dds_cdrstream_desc_fini (&desc, &dds_cdrstream_default_allocator); +} + +static const void *get_write_datapointer (dds_entity_t wr, const dds_topic_descriptor_t *tpdesc, const void *template, bool wrloan) +{ + if (!wrloan) + return template; + else + { + void *tmp; + dds_return_t rc = dds_request_loan (wr, &tmp); + CU_ASSERT_FATAL (rc == 0); + deepcopy_sample_contents (tpdesc, tmp, template); + return tmp; + } +} + +static bool data_equal (const dds_topic_descriptor_t *tpdesc, const void *a, const void *b, bool justkey) +{ + struct dds_cdrstream_desc desc; + dds_cdrstream_desc_from_topic_desc (&desc, tpdesc); + struct dds_ostream osa, osb; + dds_ostream_init (&osa, &dds_cdrstream_default_allocator, 0, DDSI_RTPS_CDR_ENC_VERSION_2); + dds_ostream_init (&osb, &dds_cdrstream_default_allocator, 0, DDSI_RTPS_CDR_ENC_VERSION_2); + if (justkey) + { + dds_stream_write_key (&osa, DDS_CDR_KEY_SERIALIZATION_SAMPLE, &dds_cdrstream_default_allocator, a, &desc); + dds_stream_write_key (&osb, DDS_CDR_KEY_SERIALIZATION_SAMPLE, &dds_cdrstream_default_allocator, b, &desc); + } + else + { + dds_stream_write_sample (&osa, &dds_cdrstream_default_allocator, a, &desc); + dds_stream_write_sample (&osb, &dds_cdrstream_default_allocator, b, &desc); + } + const bool eq = (osa.m_index == osb.m_index) && memcmp (osa.m_buffer, osb.m_buffer, osa.m_index) == 0; + dds_ostream_fini (&osa, &dds_cdrstream_default_allocator); + dds_ostream_fini (&osb, &dds_cdrstream_default_allocator); + dds_cdrstream_desc_fini (&desc, &dds_cdrstream_default_allocator); + return eq; +} + +CU_Test (ddsc_psmx, writer_loan) +{ + const struct { + const dds_topic_descriptor_t *desc; + const struct { + dds_return_t (*op) (dds_entity_t wr, const void *data); + const void *data; + } step[2]; + } cases[] = { + { &PsmxLoanTest0_desc, { + { dds_write, &(PsmxLoanTest0){ { 0x12345678, 0x55 }, 0x22 } }, + { dds_dispose, &(PsmxLoanTest0){ { 0, 0 }, 0x22 } } } }, + { &PsmxLoanTest1_desc, { + { dds_write, &(PsmxLoanTest1){ { 0x12345678, 0x55 }, 0x22, "aap" } }, + { dds_dispose, &(PsmxLoanTest1){ { 0, 0, }, 0x22, "" } } } }, + { &PsmxLoanTest2_desc, { + { dds_write, &(PsmxLoanTest2){ { 0x12345678, 0x55 }, 0x22, "noot" } }, + { dds_dispose, &(PsmxLoanTest2){ { 0, 0, }, 0x22, "noot" } } } }, + }; + dds_return_t rc; + + 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++) + { + char topicname[100]; + create_unique_topic_name ("writer_loan", topicname, sizeof (topicname)); + const dds_entity_t tp = dds_create_topic (pp, cases[k].desc, topicname, NULL, NULL); + CU_ASSERT_FATAL (tp > 0); + for (int wr_psmx_enabled_i = 0; wr_psmx_enabled_i <= 1; wr_psmx_enabled_i++) + { + const bool wr_psmx_enabled = wr_psmx_enabled_i; + for (int rd_psmx_enabled_i = 0; rd_psmx_enabled_i <= 1; rd_psmx_enabled_i++) + { + const bool rd_psmx_enabled = rd_psmx_enabled_i; + for (int wrloan_i = 0; wrloan_i <= 1; wrloan_i++) + { + const bool wrloan = wrloan_i; + for (int rdloan_i = 0; rdloan_i <= 1; rdloan_i++) + { + const bool rdloan = rdloan_i; + dds_qos_t *qos; + printf ("ddsc_psmx writer_loan: %s psmx %d %d wrloan %d rdloan %d", cases[k].desc->m_typename, wr_psmx_enabled, rd_psmx_enabled, wrloan, rdloan); + fflush (stdout); + + qos = dds_create_qos (); + if (!wr_psmx_enabled) + dds_qset_psmx_instances (qos, 0, NULL); + const dds_entity_t wr = dds_create_writer (pp, tp, qos, NULL); + CU_ASSERT_FATAL (wr > 0); + CU_ASSERT_FATAL (endpoint_has_psmx_enabled (wr) == wr_psmx_enabled); + dds_delete_qos (qos); + + qos = dds_create_qos (); + if (!rd_psmx_enabled) + dds_qset_psmx_instances (qos, 0, NULL); + dds_entity_t rd; + const dds_entity_t ws = dds_create_waitset (pp); + rd = dds_create_reader (pp, tp, qos, NULL); + CU_ASSERT_FATAL (rd > 0); + CU_ASSERT_FATAL (endpoint_has_psmx_enabled (rd) == rd_psmx_enabled); + dds_delete_qos (qos); + + rc = dds_set_status_mask (rd, DDS_DATA_AVAILABLE_STATUS); + CU_ASSERT_FATAL (rc == 0); + rc = dds_waitset_attach (ws, rd, 0); + CU_ASSERT_FATAL (rc == 0); + + for (int step_i = 0; step_i < (int) (sizeof (cases[k].step) / sizeof (cases[k].step[0])); step_i++) + { + const void *wrdata = cases[k].step[step_i].data; + dds_return_t (* const op) (dds_entity_t wr, const void *data) = cases[k].step[step_i].op; + + // dispose + loan is not supported (triggers use-after-free; this is documented, but bad) + if (op == dds_dispose && wrloan) + continue; + + const bool justkey = (op != dds_write); + printf (" | %s %p", (op == dds_write) ? "write" : "dispose", wrdata); fflush (stdout); + rc = op (wr, get_write_datapointer (wr, cases[k].desc, wrdata, wrloan)); + CU_ASSERT_FATAL (rc == 0); + (void) dds_waitset_wait (ws, NULL, 0, DDS_INFINITY); + dds_sample_info_t si; + void *rddata; + if (rdloan) + rddata = NULL; + else + { + rddata = ddsrt_malloc (cases[k].desc->m_size); + memset (rddata, 0, cases[k].desc->m_size); + } + rc = dds_take (rd, &rddata, &si, 1, 1); + CU_ASSERT_FATAL (rc == 1); + printf (" | rddata %p", rddata); fflush (stdout); + CU_ASSERT_FATAL (data_equal (cases[k].desc, wrdata, rddata, justkey)); + if (!rdloan) + dds_sample_free (rddata, cases[k].desc, DDS_FREE_ALL); + else + { + rc = dds_return_loan (rd, &rddata, 1); + CU_ASSERT_FATAL (rc == DDS_RETCODE_OK); + } + } + printf ("\n"); fflush (stdout); + + rc = dds_delete (wr); + CU_ASSERT_FATAL (rc == 0); + rc = dds_delete (rd); + CU_ASSERT_FATAL (rc == 0); + rc = dds_delete (ws); + CU_ASSERT_FATAL (rc == 0); + } + } + } + } + rc = dds_delete (tp); + CU_ASSERT_FATAL (rc == 0); + } + dds_delete (dds_get_parent (pp)); +}