From cf995e52e34e580bea4b525eda252746e16b685f Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Thu, 13 Jun 2024 08:49:18 +1000 Subject: [PATCH] More gateway discovery unittests for client library. --- client/lib/doxygen/main.dox | 6 +- client/lib/src/op/SearchOp.cpp | 2 +- client/lib/templ/client.cpp.templ | 6 + client/lib/templ/client.h.templ | 6 + client/lib/test/UnitTestCommonBase.cpp | 62 ++++++++- client/lib/test/UnitTestCommonBase.h | 13 +- client/lib/test/UnitTestDefaultBase.cpp | 1 + client/lib/test/UnitTestGwDiscover.th | 176 +++++++++++++++++++++++- 8 files changed, 266 insertions(+), 6 deletions(-) diff --git a/client/lib/doxygen/main.dox b/client/lib/doxygen/main.dox index ecadbac..5e0a54f 100644 --- a/client/lib/doxygen/main.dox +++ b/client/lib/doxygen/main.dox @@ -491,14 +491,18 @@ /// from withing the operation completion callback to update the stored gateway address. /// @code /// CC_MqttsnGatewayInfo info; +/// cc_mqttsn_client_init_gateway_info(&info); /// info.m_gwId = ...; /// info.m_addr = ...; /// info.m_addrLen = ...; -/// CC_MqttsnErrorCode ec = cc_mqttsn_client_set_available_gateway_info(client, info); +/// CC_MqttsnErrorCode ec = cc_mqttsn_client_set_available_gateway_info(client, &info); /// if (ec != CC_MqttsnErrorCode_Success) { /// printf("Something is wrong"); /// } /// @endcode +/// It is recommended to initialize @ref CC_MqttsnGatewayInfo structure using the +/// @b cc_mqttsn_client_init_gateway_info() function before update, even though all +/// the member fields are assigned new values. /// /// The application can force the library to discard the available gateway information /// by issuing the @b cc_mqttsn_client_discard_available_gateway_info() function. diff --git a/client/lib/src/op/SearchOp.cpp b/client/lib/src/op/SearchOp.cpp index 005e2c7..977b504 100644 --- a/client/lib/src/op/SearchOp.cpp +++ b/client/lib/src/op/SearchOp.cpp @@ -164,7 +164,7 @@ void SearchOp::timeoutInternal() void SearchOp::opTimeoutCb(void* data) { - asSearchOp(data)->sendInternal(); + asSearchOp(data)->timeoutInternal(); } } // namespace op diff --git a/client/lib/templ/client.cpp.templ b/client/lib/templ/client.cpp.templ index da005c4..7e7e501 100644 --- a/client/lib/templ/client.cpp.templ +++ b/client/lib/templ/client.cpp.templ @@ -146,6 +146,12 @@ unsigned cc_mqttsn_##NAME##client_get_available_gateways_count(CC_MqttsnClientHa return static_cast(clientFromHandle(client)->clientState().m_gwInfos.size()); } +void cc_mqttsn_##NAME##client_init_gateway_info(CC_MqttsnGatewayInfo* info) +{ + COMMS_ASSERT(info != nullptr); + *info = CC_MqttsnGatewayInfo(); +} + CC_MqttsnErrorCode cc_mqttsn_##NAME##client_get_available_gateway_info(CC_MqttsnClientHandle client, unsigned idx, CC_MqttsnGatewayInfo* info) { COMMS_ASSERT(client != nullptr); diff --git a/client/lib/templ/client.h.templ b/client/lib/templ/client.h.templ index eabb038..c7e7f52 100644 --- a/client/lib/templ/client.h.templ +++ b/client/lib/templ/client.h.templ @@ -127,11 +127,16 @@ unsigned cc_mqttsn_##NAME##client_get_default_broadcast_radius(CC_MqttsnClientHa /// @ingroup client unsigned cc_mqttsn_##NAME##client_get_available_gateways_count(CC_MqttsnClientHandle client); +/// @brief Initialize the @ref CC_MqttsnGatewayInfo structure. +/// @details Zeroes all the member fields. +void cc_mqttsn_##NAME##client_init_gateway_info(CC_MqttsnGatewayInfo* info); + /// @brief Retrieve stored available gateway information /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_alloc() function. /// @param[in] idx Index of the available gateway information. /// @param[out] info Stored gateway information. /// @return Result code of the call. +/// @ingroup client CC_MqttsnErrorCode cc_mqttsn_##NAME##client_get_available_gateway_info(CC_MqttsnClientHandle client, unsigned idx, CC_MqttsnGatewayInfo* info); /// @brief Update stored available gateway information @@ -141,6 +146,7 @@ CC_MqttsnErrorCode cc_mqttsn_##NAME##client_get_available_gateway_info(CC_Mqttsn /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_alloc() function. /// @param[in] info Updated gateway information. /// @return Result code of the call. +/// @ingroup client CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_available_gateway_info(CC_MqttsnClientHandle client, const CC_MqttsnGatewayInfo* info); /// @brief Discard stored available gateway information diff --git a/client/lib/test/UnitTestCommonBase.cpp b/client/lib/test/UnitTestCommonBase.cpp index 1594092..9720194 100644 --- a/client/lib/test/UnitTestCommonBase.cpp +++ b/client/lib/test/UnitTestCommonBase.cpp @@ -35,6 +35,7 @@ UnitTestCommonBase::UnitTestCommonBase(const LibFuncs& funcs) : test_assert(m_funcs.m_set_default_retry_count != nullptr); test_assert(m_funcs.m_get_default_retry_count != nullptr); test_assert(m_funcs.m_set_default_broadcast_radius != nullptr); + test_assert(m_funcs.m_init_gateway_info != nullptr); test_assert(m_funcs.m_get_available_gateway_info != nullptr); test_assert(m_funcs.m_set_available_gateway_info != nullptr); test_assert(m_funcs.m_discard_available_gateway_info != nullptr); @@ -97,6 +98,13 @@ UnitTestCommonBase::UnitTestSearchCompleteReport::UnitTestSearchCompleteReport(C } } +void UnitTestCommonBase::UnitTestSearchCompleteReport::assignInfo(CC_MqttsnGatewayInfo& info) const +{ + info.m_gwId = m_info.m_gwId; + info.m_addr = m_info.m_addr.data(); + info.m_addrLen = static_cast(m_info.m_addr.size()); +} + void UnitTestCommonBase::unitTestSetUp() { } @@ -217,6 +225,7 @@ std::vector UnitTestCommonBase::unitTestPopAllOuputMessages(bool // readPtr is advanced in read operation above } + m_data.m_outData.pop_front(); } while (false); test_assert((!mustExist) || (!result.empty())) @@ -270,7 +279,7 @@ const UnitTestCommonBase::UnitTestSearchCompleteReport* UnitTestCommonBase::unit return &m_data.m_searchCompleteReports.front(); } -void UnitTestCommonBase::unitTestPopSearchCompletereport() +void UnitTestCommonBase::unitTestPopSearchCompleteReport() { test_assert(unitTestHasSearchCompleteReport()); m_data.m_searchCompleteReports.pop_front(); @@ -294,16 +303,67 @@ void UnitTestCommonBase::unitTestSearch(CC_MqttsnClient* client, UnitTestSearchC m_funcs.m_search(client, &UnitTestCommonBase::unitTestSearchCompleteCb, this); } +void UnitTestCommonBase::unitTestSearchUpdateAddr(CC_MqttsnClient* client, const UnitTestData& addr) +{ + unitTestSearch( + client, + [this, client, addr](const UnitTestSearchCompleteReport& report) + { + if (report.m_status != CC_MqttsnAsyncOpStatus_Complete) { + return false; + } + + auto prevCount = m_funcs.m_get_available_gateways_count(client); + + CC_MqttsnGatewayInfo updInfo; + m_funcs.m_init_gateway_info(&updInfo); + updInfo.m_gwId = report.m_info.m_gwId; + updInfo.m_addr = addr.data(); + updInfo.m_addrLen = static_cast(addr.size()); + auto ec = m_funcs.m_set_available_gateway_info(client, &updInfo); + test_assert(ec == CC_MqttsnErrorCode_Success); + + auto afterUpdateCount = m_funcs.m_get_available_gateways_count(client); + test_assert(prevCount == afterUpdateCount); // Mustn't change + return false; + }); +} + void UnitTestCommonBase::apiProcessData(CC_MqttsnClient* client, const unsigned char* buf, unsigned bufLen) { m_funcs.m_process_data(client, buf, bufLen); } +CC_MqttsnErrorCode UnitTestCommonBase::apiSetDefaultRetryPeriod(CC_MqttsnClient* client, unsigned value) +{ + return m_funcs.m_set_default_retry_period(client, value); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSetDefaultRetryCount(CC_MqttsnClient* client, unsigned value) +{ + return m_funcs.m_set_default_retry_count(client, value); +} + CC_MqttsnSearchHandle UnitTestCommonBase::apiSearchPrepare(CC_MqttsnClient* client, CC_MqttsnErrorCode* ec) { return m_funcs.m_search_prepare(client, ec); } +CC_MqttsnErrorCode UnitTestCommonBase::apiSearchSetRetryPeriod(CC_MqttsnSearchHandle search, unsigned value) +{ + return m_funcs.m_search_set_retry_period(search, value); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSearchSetRetryCount(CC_MqttsnSearchHandle search, unsigned value) +{ + return m_funcs.m_search_set_retry_count(search, value); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSearchSetBroadcastRadius(CC_MqttsnSearchHandle search, unsigned value) +{ + return m_funcs.m_search_set_broadcast_radius(search, value); +} + void UnitTestCommonBase::unitTestTickProgramCb(void* data, unsigned duration) { auto* thisPtr = asThis(data); diff --git a/client/lib/test/UnitTestCommonBase.h b/client/lib/test/UnitTestCommonBase.h index b4be3d4..5bbbae7 100644 --- a/client/lib/test/UnitTestCommonBase.h +++ b/client/lib/test/UnitTestCommonBase.h @@ -25,6 +25,7 @@ class UnitTestCommonBase CC_MqttsnErrorCode (*m_set_default_broadcast_radius)(CC_MqttsnClientHandle, unsigned) = nullptr; unsigned (*m_get_default_broadcast_radius)(CC_MqttsnClientHandle) = nullptr; unsigned (*m_get_available_gateways_count)(CC_MqttsnClientHandle) = nullptr; + void (*m_init_gateway_info)(CC_MqttsnGatewayInfo* info) = nullptr; CC_MqttsnErrorCode (*m_get_available_gateway_info)(CC_MqttsnClientHandle, unsigned, CC_MqttsnGatewayInfo*) = nullptr; CC_MqttsnErrorCode (*m_set_available_gateway_info)(CC_MqttsnClientHandle, const CC_MqttsnGatewayInfo*) = nullptr; CC_MqttsnErrorCode (*m_discard_available_gateway_info)(CC_MqttsnClientHandle, unsigned char) = nullptr; @@ -96,7 +97,7 @@ class UnitTestCommonBase struct UnitTestGwInfo { - unsigned m_gwId = 0U; + std::uint8_t m_gwId = 0U; UnitTestData m_addr; UnitTestGwInfo() = default; @@ -121,6 +122,7 @@ class UnitTestCommonBase UnitTestGwInfo m_info; UnitTestSearchCompleteReport(CC_MqttsnAsyncOpStatus status, const CC_MqttsnGatewayInfo* info); + void assignInfo(CC_MqttsnGatewayInfo& info) const; }; using UnitTestSearchCompleteReportsList = std::list; @@ -152,13 +154,20 @@ class UnitTestCommonBase bool unitTestHasSearchCompleteReport() const; const UnitTestSearchCompleteReport* unitTestSearchCompleteReport(bool mustExist = true) const; - void unitTestPopSearchCompletereport(); + void unitTestPopSearchCompleteReport(); void unitTestSearchSend(CC_MqttsnSearchHandle search, UnitTestSearchCompleteCb&& cb = UnitTestSearchCompleteCb()); void unitTestSearch(CC_MqttsnClient* client, UnitTestSearchCompleteCb&& cb = UnitTestSearchCompleteCb()); + void unitTestSearchUpdateAddr(CC_MqttsnClient* client, const UnitTestData& addr); void apiProcessData(CC_MqttsnClient* client, const unsigned char* buf, unsigned bufLen); + CC_MqttsnErrorCode apiSetDefaultRetryPeriod(CC_MqttsnClient* client, unsigned value); + CC_MqttsnErrorCode apiSetDefaultRetryCount(CC_MqttsnClient* client, unsigned value); CC_MqttsnSearchHandle apiSearchPrepare(CC_MqttsnClient* client, CC_MqttsnErrorCode* ec = nullptr); + CC_MqttsnErrorCode apiSearchSetRetryPeriod(CC_MqttsnSearchHandle search, unsigned value); + CC_MqttsnErrorCode apiSearchSetRetryCount(CC_MqttsnSearchHandle search, unsigned value); + CC_MqttsnErrorCode apiSearchSetBroadcastRadius(CC_MqttsnSearchHandle search, unsigned value); + protected: explicit UnitTestCommonBase(const LibFuncs& funcs); diff --git a/client/lib/test/UnitTestDefaultBase.cpp b/client/lib/test/UnitTestDefaultBase.cpp index 21ef424..672e186 100644 --- a/client/lib/test/UnitTestDefaultBase.cpp +++ b/client/lib/test/UnitTestDefaultBase.cpp @@ -16,6 +16,7 @@ const UnitTestDefaultBase::LibFuncs& UnitTestDefaultBase::getFuncs() funcs.m_set_default_broadcast_radius = &cc_mqttsn_client_set_default_broadcast_radius; funcs.m_get_default_broadcast_radius = &cc_mqttsn_client_get_default_broadcast_radius; funcs.m_get_available_gateways_count = &cc_mqttsn_client_get_available_gateways_count; + funcs.m_init_gateway_info = &cc_mqttsn_client_init_gateway_info; funcs.m_get_available_gateway_info = &cc_mqttsn_client_get_available_gateway_info; funcs.m_set_available_gateway_info = &cc_mqttsn_client_set_available_gateway_info; funcs.m_discard_available_gateway_info = &cc_mqttsn_client_discard_available_gateway_info; diff --git a/client/lib/test/UnitTestGwDiscover.th b/client/lib/test/UnitTestGwDiscover.th index 77c6900..1d9e501 100644 --- a/client/lib/test/UnitTestGwDiscover.th +++ b/client/lib/test/UnitTestGwDiscover.th @@ -11,6 +11,9 @@ public: void test1(); void test2(); void test3(); + void test4(); + void test5(); + void test6(); private: virtual void setUp() override @@ -114,7 +117,6 @@ void UnitTestGwDiscover::test2() void UnitTestGwDiscover::test3() { // Testing search, response from another client - auto clientPtr = unitTestAllocClient(); auto* client = clientPtr.get(); @@ -150,4 +152,176 @@ void UnitTestGwDiscover::test3() TS_ASSERT_EQUALS(gwInfoReport->m_info.m_addr, Addr); TS_ASSERT(unitTestHasTickReq()); +} + +void UnitTestGwDiscover::test4() +{ + // Testing search completed by ADVERTISE + auto clientPtr = unitTestAllocClient(); + auto* client = clientPtr.get(); + + const UnitTestData Addr = {0, 1, 2, 3}; + + unitTestSearchUpdateAddr(client, Addr); + TS_ASSERT(unitTestHasOutputData()); + auto broadcastRadius = unitTestOutputDataInfo()->m_broadcastRadius; + TS_ASSERT_LESS_THAN(0U, broadcastRadius); + + auto sentMsg = unitTestPopOutputMessage(); + auto* searchMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(searchMsg, nullptr); + TS_ASSERT_EQUALS(searchMsg->field_radius().value(), broadcastRadius); + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client, 100); + + const std::uint8_t GwId = 1U; + const unsigned AdvDurationMin = 10U; + + UnitTestAdvertiseMsg advertiseMsg; + advertiseMsg.field_gwId().setValue(GwId); + comms::units::setMinutes(advertiseMsg.field_duration(), AdvDurationMin); + unitTestClientInputMessage(client, advertiseMsg); + + TS_ASSERT(unitTestHasSearchCompleteReport()); + auto* searchCompleteReport = unitTestSearchCompleteReport(); + TS_ASSERT_EQUALS(searchCompleteReport->m_status, CC_MqttsnAsyncOpStatus_Complete); + TS_ASSERT_EQUALS(searchCompleteReport->m_info.m_gwId, GwId); + TS_ASSERT(searchCompleteReport->m_info.m_addr.empty()); + + auto* gwInfoReport = unitTestGetGwInfoReport(); + TS_ASSERT_EQUALS(gwInfoReport->m_status, CC_MqttsnGwStatus_AddedByGateway); + TS_ASSERT_EQUALS(gwInfoReport->m_info.m_gwId, GwId); + TS_ASSERT_EQUALS(gwInfoReport->m_info.m_addr, Addr); + + TS_ASSERT(unitTestHasTickReq()); +} + +void UnitTestGwDiscover::test5() +{ + // Testing search without response + + auto clientPtr = unitTestAllocClient(); + auto* client = clientPtr.get(); + + const unsigned RetryPeriod = 2000; + const unsigned BroadcastRadius = 5; + + auto search = apiSearchPrepare(client); + TS_ASSERT_DIFFERS(search, nullptr); + + auto ec = apiSearchSetRetryPeriod(search, RetryPeriod); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + ec = apiSearchSetRetryCount(search, 2U); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + ec = apiSearchSetBroadcastRadius(search, BroadcastRadius); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + unitTestSearchSend(search); + + TS_ASSERT_EQUALS(unitTestOutputDataInfo()->m_broadcastRadius, BroadcastRadius); + + { + auto sentMsg = unitTestPopOutputMessage(); + auto* searchMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(searchMsg, nullptr); + TS_ASSERT_EQUALS(searchMsg->field_radius().value(), BroadcastRadius); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + TS_ASSERT_EQUALS(unitTestTickInfo()->m_req, RetryPeriod); + unitTestTick(client); + + TS_ASSERT(!unitTestHasSearchCompleteReport()); + TS_ASSERT(unitTestHasOutputData()); + + { + auto sentMsg = unitTestPopOutputMessage(); + auto* searchMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(searchMsg, nullptr); + TS_ASSERT_EQUALS(searchMsg->field_radius().value(), BroadcastRadius); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + TS_ASSERT_EQUALS(unitTestTickInfo()->m_req, RetryPeriod); + unitTestTick(client); + + TS_ASSERT(unitTestHasSearchCompleteReport()); + auto* report = unitTestSearchCompleteReport(); + TS_ASSERT_EQUALS(report->m_status, CC_MqttsnAsyncOpStatus_Timeout); + unitTestPopSearchCompleteReport(); + + TS_ASSERT(!unitTestHasTickReq()); +} + + +void UnitTestGwDiscover::test6() +{ + // Testing search with packet loss + + auto clientPtr = unitTestAllocClient(); + auto* client = clientPtr.get(); + + const unsigned RetryPeriod = 2000; + const unsigned BroadcastRadius = 5; + + auto search = apiSearchPrepare(client); + TS_ASSERT_DIFFERS(search, nullptr); + + auto ec = apiSearchSetRetryPeriod(search, RetryPeriod); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + ec = apiSearchSetRetryCount(search, 2U); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + ec = apiSearchSetBroadcastRadius(search, BroadcastRadius); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + unitTestSearchSend(search); + + TS_ASSERT_EQUALS(unitTestOutputDataInfo()->m_broadcastRadius, BroadcastRadius); + + { + auto sentMsg = unitTestPopOutputMessage(); + auto* searchMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(searchMsg, nullptr); + TS_ASSERT_EQUALS(searchMsg->field_radius().value(), BroadcastRadius); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + TS_ASSERT_EQUALS(unitTestTickInfo()->m_req, RetryPeriod); + unitTestTick(client); + + TS_ASSERT(!unitTestHasSearchCompleteReport()); + TS_ASSERT(unitTestHasOutputData()); + + { + auto sentMsg = unitTestPopOutputMessage(); + auto* searchMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(searchMsg, nullptr); + TS_ASSERT_EQUALS(searchMsg->field_radius().value(), BroadcastRadius); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + TS_ASSERT_EQUALS(unitTestTickInfo()->m_req, RetryPeriod); + unitTestTick(client, 100); + + + const std::uint8_t GwId = 1U; + UnitTestGwinfoMsg gwinfoMsg; + gwinfoMsg.field_gwId().setValue(GwId); + unitTestClientInputMessage(client, gwinfoMsg); + + TS_ASSERT(unitTestHasSearchCompleteReport()); + auto* searchCompleteReport = unitTestSearchCompleteReport(); + TS_ASSERT_EQUALS(searchCompleteReport->m_status, CC_MqttsnAsyncOpStatus_Complete); + TS_ASSERT_EQUALS(searchCompleteReport->m_info.m_gwId, GwId); + TS_ASSERT(searchCompleteReport->m_info.m_addr.empty()); + + auto* gwInfoReport = unitTestGetGwInfoReport(); + TS_ASSERT_EQUALS(gwInfoReport->m_status, CC_MqttsnGwStatus_AddedByGateway); + TS_ASSERT_EQUALS(gwInfoReport->m_info.m_gwId, GwId); + TS_ASSERT(gwInfoReport->m_info.m_addr.empty()); + + TS_ASSERT(unitTestHasTickReq()); // For the ADVERTISE } \ No newline at end of file