diff --git a/client/lib/src/ClientImpl.cpp b/client/lib/src/ClientImpl.cpp index 1284054..dba86e3 100644 --- a/client/lib/src/ClientImpl.cpp +++ b/client/lib/src/ClientImpl.cpp @@ -251,7 +251,7 @@ op::ConnectOp* ClientImpl::connectPrepare(CC_MqttsnErrorCode* ec) if (!m_disconnectOps.empty()) { // Already allocated - errorLog("Another disconnect operation is in progress."); + errorLog("Another disconnect or sleep operation is in progress."); updateEc(ec, CC_MqttsnErrorCode_Busy); break; } @@ -308,7 +308,7 @@ op::DisconnectOp* ClientImpl::disconnectPrepare(CC_MqttsnErrorCode* ec) } if (!m_disconnectOps.empty()) { - errorLog("Another disconnect operation is in progress."); + errorLog("Another disconnect or sleep operation is in progress."); updateEc(ec, CC_MqttsnErrorCode_Busy); break; } diff --git a/client/lib/src/SessionState.h b/client/lib/src/SessionState.h index 06f2595..a76f871 100644 --- a/client/lib/src/SessionState.h +++ b/client/lib/src/SessionState.h @@ -7,13 +7,17 @@ #pragma once +#include "ProtocolDefs.h" + namespace cc_mqttsn_client { struct SessionState { + using ClientIdStr = ConnectMsg::Field_clientId::ValueType; static constexpr unsigned DefaultKeepAlive = 60; + ClientIdStr m_clientId; unsigned m_keepAliveMs = 0U; CC_MqttsnConnectionStatus m_connectionStatus = CC_MqttsnConnectionStatus_Disconnected; bool m_disconnecting = false; diff --git a/client/lib/src/op/ConnectOp.cpp b/client/lib/src/op/ConnectOp.cpp index d1e23af..85b5043 100644 --- a/client/lib/src/op/ConnectOp.cpp +++ b/client/lib/src/op/ConnectOp.cpp @@ -151,6 +151,7 @@ CC_MqttsnErrorCode ConnectOp::send(CC_MqttsnConnectCompleteCb cb, void* cbData) return ec; } + client().sessionState().m_clientId.clear(); if (m_connectMsg.field_flags().field_mid().getBitValue_CleanSession()) { // Don't wait for acknowledgement, assume state cleared upon send client().reuseState() = ReuseState(); @@ -250,6 +251,7 @@ void ConnectOp::handle(ConnackMsg& msg) #endif // #if CC_MQTTSN_CLIENT_HAS_WILL if (info.m_returnCode == CC_MqttsnReturnCode_Accepted) { + client().sessionState().m_clientId = m_connectMsg.field_clientId().value(); client().sessionState().m_keepAliveMs = comms::units::getMilliseconds(m_connectMsg.field_duration()); client().gatewayConnected(); } diff --git a/client/lib/src/op/DisconnectOp.cpp b/client/lib/src/op/DisconnectOp.cpp index df47319..b5b623f 100644 --- a/client/lib/src/op/DisconnectOp.cpp +++ b/client/lib/src/op/DisconnectOp.cpp @@ -169,7 +169,7 @@ CC_MqttsnErrorCode DisconnectOp::sendInternal() void DisconnectOp::timeoutInternal() { if (getRetryCount() == 0U) { - errorLog("All retries of the disconnect operation have been exhausted."); + errorLog("All retries of the disconnect or sleep operation have been exhausted."); completeOpInternal(CC_MqttsnAsyncOpStatus_Timeout); return; } diff --git a/client/lib/src/op/KeepAliveOp.cpp b/client/lib/src/op/KeepAliveOp.cpp index 04c0b1c..717ddfc 100644 --- a/client/lib/src/op/KeepAliveOp.cpp +++ b/client/lib/src/op/KeepAliveOp.cpp @@ -115,8 +115,15 @@ void KeepAliveOp::sendPing() return; // Ping has already been sent, waiting for response } + auto& cl = client(); + auto& st = cl.sessionState(); + PingreqMsg msg; - client().sendMessage(msg); + if (st.m_connectionStatus == CC_MqttsnConnectionStatus_Asleep) { + msg.field_clientId().setValue(st.m_clientId); + } + + cl.sendMessage(msg); restartRespTimer(); } diff --git a/client/lib/test/CMakeLists.txt b/client/lib/test/CMakeLists.txt index fc267a6..92ad711 100644 --- a/client/lib/test/CMakeLists.txt +++ b/client/lib/test/CMakeLists.txt @@ -47,6 +47,7 @@ if (TARGET cc::cc_mqttsn_client) cc_mqttsn_client_add_unit_test(UnitTestSubscribe ${DEFAULT_BASE_LIB_NAME}) cc_mqttsn_client_add_unit_test(UnitTestUnsubscribe ${DEFAULT_BASE_LIB_NAME}) cc_mqttsn_client_add_unit_test(UnitTestWill ${DEFAULT_BASE_LIB_NAME}) + cc_mqttsn_client_add_unit_test(UnitTestSleep ${DEFAULT_BASE_LIB_NAME}) endif () # TODO diff --git a/client/lib/test/UnitTestCommonBase.cpp b/client/lib/test/UnitTestCommonBase.cpp index 7dfe67e..bcbdf56 100644 --- a/client/lib/test/UnitTestCommonBase.cpp +++ b/client/lib/test/UnitTestCommonBase.cpp @@ -54,6 +54,7 @@ UnitTestCommonBase::UnitTestCommonBase(const LibFuncs& funcs) : test_assert(m_funcs.m_get_outgoing_topic_id_storage_limit != nullptr); test_assert(m_funcs.m_set_incoming_topic_id_storage_limit != nullptr); test_assert(m_funcs.m_get_incoming_topic_id_storage_limit != nullptr); + test_assert(m_funcs.m_asleep_check_messages != nullptr); test_assert(m_funcs.m_search_prepare != nullptr); test_assert(m_funcs.m_search_set_retry_period != nullptr); test_assert(m_funcs.m_search_get_retry_period != nullptr); @@ -124,8 +125,17 @@ UnitTestCommonBase::UnitTestCommonBase(const LibFuncs& funcs) : test_assert(m_funcs.m_will_config != nullptr); test_assert(m_funcs.m_will_send != nullptr); test_assert(m_funcs.m_will_cancel != nullptr); - test_assert(m_funcs.m_will != nullptr); - + test_assert(m_funcs.m_will != nullptr); + test_assert(m_funcs.m_sleep_prepare != nullptr); + test_assert(m_funcs.m_sleep_set_retry_period != nullptr); + test_assert(m_funcs.m_sleep_get_retry_period != nullptr); + test_assert(m_funcs.m_sleep_set_retry_count != nullptr); + test_assert(m_funcs.m_sleep_get_retry_count != nullptr); + test_assert(m_funcs.m_sleep_init_config != nullptr); + test_assert(m_funcs.m_sleep_config != nullptr); + test_assert(m_funcs.m_sleep_send != nullptr); + test_assert(m_funcs.m_sleep_cancel != nullptr); + test_assert(m_funcs.m_sleep != nullptr); test_assert(m_funcs.m_set_next_tick_program_callback != nullptr); test_assert(m_funcs.m_set_cancel_next_tick_wait_callback != nullptr); test_assert(m_funcs.m_set_send_output_data_callback != nullptr); @@ -800,6 +810,28 @@ CC_MqttsnErrorCode UnitTestCommonBase::unitTestWillSend(CC_MqttsnWillHandle will return m_funcs.m_will_send(will, &UnitTestCommonBase::unitTestWillCompleteCb, this); } +bool UnitTestCommonBase::unitTestHasSleepCompleteReport() const +{ + return !m_data.m_sleepCompleteReports.empty(); +} + +UnitTestCommonBase::UnitTestSleepCompleteReportPtr UnitTestCommonBase::unitTestSleepCompleteReport(bool mustExist) +{ + if (!unitTestHasSleepCompleteReport()) { + test_assert(!mustExist); + return UnitTestSleepCompleteReportPtr(); + } + + auto ptr = std::move(m_data.m_sleepCompleteReports.front()); + m_data.m_sleepCompleteReports.pop_front(); + return ptr; +} + +CC_MqttsnErrorCode UnitTestCommonBase::unitTestSleepSend(CC_MqttsnSleepHandle sleep) +{ + return m_funcs.m_sleep_send(sleep, &UnitTestCommonBase::unitTestSleepCompleteCb, this); +} + bool UnitTestCommonBase::unitTestHasReceivedMessage() const { return !m_data.m_recvMsgs.empty(); @@ -827,11 +859,21 @@ CC_MqttsnErrorCode UnitTestCommonBase::apiSetDefaultRetryPeriod(CC_MqttsnClient* return m_funcs.m_set_default_retry_period(client, value); } +unsigned UnitTestCommonBase::apiGetDefaultRetryPeriod(CC_MqttsnClientHandle client) +{ + return m_funcs.m_get_default_retry_period(client); +} + CC_MqttsnErrorCode UnitTestCommonBase::apiSetDefaultRetryCount(CC_MqttsnClient* client, unsigned value) { return m_funcs.m_set_default_retry_count(client, value); } +unsigned UnitTestCommonBase::apiGetDefaultRetryCount(CC_MqttsnClientHandle client) +{ + return m_funcs.m_get_default_retry_count(client); +} + CC_MqttsnErrorCode UnitTestCommonBase::apiSetVerifyIncomingMsgSubscribed(CC_MqttsnClient* client, bool enabled) { return m_funcs.m_set_verify_incoming_msg_subscribed(client, enabled); @@ -1060,6 +1102,31 @@ CC_MqttsnErrorCode UnitTestCommonBase::apiWillCancel(CC_MqttsnWillHandle will) return m_funcs.m_will_cancel(will); } +CC_MqttsnSleepHandle UnitTestCommonBase::apiSleepPrepare(CC_MqttsnClient* client, CC_MqttsnErrorCode* ec) +{ + return m_funcs.m_sleep_prepare(client, ec); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSleepSetRetryCount(CC_MqttsnSleepHandle sleep, unsigned count) +{ + return m_funcs.m_sleep_set_retry_count(sleep, count); +} + +void UnitTestCommonBase::apiSleepInitConfig(CC_MqttsnSleepConfig* config) +{ + m_funcs.m_sleep_init_config(config); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSleepConfig(CC_MqttsnSleepHandle sleep, const CC_MqttsnSleepConfig* config) +{ + return m_funcs.m_sleep_config(sleep, config); +} + +CC_MqttsnErrorCode UnitTestCommonBase::apiSleepCancel(CC_MqttsnSleepHandle sleep) +{ + return m_funcs.m_sleep_cancel(sleep); +} + void UnitTestCommonBase::unitTestMessageReportCb(void* data, const CC_MqttsnMessageInfo* msgInfo) { test_assert(msgInfo != nullptr); @@ -1143,4 +1210,10 @@ void UnitTestCommonBase::unitTestWillCompleteCb(void* data, CC_MqttsnAsyncOpStat { auto* thisPtr = asThis(data); thisPtr->m_data.m_willCompleteReports.push_back(std::make_unique(status, info)); +} + +void UnitTestCommonBase::unitTestSleepCompleteCb(void* data, CC_MqttsnAsyncOpStatus status) +{ + auto* thisPtr = asThis(data); + thisPtr->m_data.m_sleepCompleteReports.push_back(std::make_unique(status)); } \ No newline at end of file diff --git a/client/lib/test/UnitTestCommonBase.h b/client/lib/test/UnitTestCommonBase.h index 4a5b999..9d39797 100644 --- a/client/lib/test/UnitTestCommonBase.h +++ b/client/lib/test/UnitTestCommonBase.h @@ -44,6 +44,7 @@ class UnitTestCommonBase unsigned long long (*m_get_outgoing_topic_id_storage_limit)(CC_MqttsnClientHandle) = nullptr; CC_MqttsnErrorCode (*m_set_incoming_topic_id_storage_limit)(CC_MqttsnClientHandle, unsigned long long) = nullptr; unsigned long long (*m_get_incoming_topic_id_storage_limit)(CC_MqttsnClientHandle) = nullptr; + CC_MqttsnErrorCode (*m_asleep_check_messages)(CC_MqttsnClientHandle client) = nullptr; CC_MqttsnSearchHandle (*m_search_prepare)(CC_MqttsnClientHandle, CC_MqttsnErrorCode*) = nullptr; CC_MqttsnErrorCode (*m_search_set_retry_period)(CC_MqttsnSearchHandle, unsigned) = nullptr; unsigned (*m_search_get_retry_period)(CC_MqttsnSearchHandle) = nullptr; @@ -115,7 +116,16 @@ class UnitTestCommonBase CC_MqttsnErrorCode (*m_will_send)(CC_MqttsnWillHandle, CC_MqttsnWillCompleteCb, void*) = nullptr; CC_MqttsnErrorCode (*m_will_cancel)(CC_MqttsnWillHandle) = nullptr; CC_MqttsnErrorCode (*m_will)(CC_MqttsnClientHandle, const CC_MqttsnWillConfig*, CC_MqttsnWillCompleteCb, void* cbData) = nullptr; - + CC_MqttsnSleepHandle (*m_sleep_prepare)(CC_MqttsnClientHandle, CC_MqttsnErrorCode*) = nullptr; + CC_MqttsnErrorCode (*m_sleep_set_retry_period)(CC_MqttsnSleepHandle, unsigned) = nullptr; + unsigned (*m_sleep_get_retry_period)(CC_MqttsnSleepHandle) = nullptr; + CC_MqttsnErrorCode (*m_sleep_set_retry_count)(CC_MqttsnSleepHandle, unsigned) = nullptr; + unsigned (*m_sleep_get_retry_count)(CC_MqttsnSleepHandle) = nullptr; + void (*m_sleep_init_config)(CC_MqttsnSleepConfig*) = nullptr; + CC_MqttsnErrorCode (*m_sleep_config)(CC_MqttsnSleepHandle, const CC_MqttsnSleepConfig*) = nullptr; + CC_MqttsnErrorCode (*m_sleep_send)(CC_MqttsnSleepHandle, CC_MqttsnSleepCompleteCb, void*) = nullptr; + CC_MqttsnErrorCode (*m_sleep_cancel)(CC_MqttsnSleepHandle) = nullptr; + CC_MqttsnErrorCode (*m_sleep)(CC_MqttsnClientHandle, const CC_MqttsnSleepConfig*, CC_MqttsnSleepCompleteCb, void* cbData) = nullptr; void (*m_set_next_tick_program_callback)(CC_MqttsnClientHandle, CC_MqttsnNextTickProgramCb, void*) = nullptr; void (*m_set_cancel_next_tick_wait_callback)(CC_MqttsnClientHandle, CC_MqttsnCancelNextTickWaitCb, void*) = nullptr; void (*m_set_send_output_data_callback)(CC_MqttsnClientHandle, CC_MqttsnSendOutputDataCb, void*) = nullptr; @@ -272,7 +282,6 @@ class UnitTestCommonBase CC_MqttsnTopicId m_topicId = 0U; }; - struct UnitTestUnsubscribeCompleteReport { CC_MqttsnUnsubscribeHandle m_handle = nullptr; @@ -335,6 +344,15 @@ class UnitTestCommonBase using UnitTestWillCompleteReportPtr = std::unique_ptr; using UnitTestWillCompleteReportList = std::list; + struct UnitTestSleepCompleteReport + { + CC_MqttsnAsyncOpStatus m_status = CC_MqttsnAsyncOpStatus_ValuesLimit; + + UnitTestSleepCompleteReport(CC_MqttsnAsyncOpStatus status) : m_status(status) {}; + }; + + using UnitTestSleepCompleteReportPtr = std::unique_ptr; + using UnitTestSleepCompleteReportList = std::list; struct UnitTestMessageInfo { @@ -422,12 +440,19 @@ class UnitTestCommonBase CC_MqttsnErrorCode unitTestWillSend(CC_MqttsnWillHandle will); + bool unitTestHasSleepCompleteReport() const; + UnitTestSleepCompleteReportPtr unitTestSleepCompleteReport(bool mustExist = true); + + CC_MqttsnErrorCode unitTestSleepSend(CC_MqttsnSleepHandle sleep); + bool unitTestHasReceivedMessage() const; UnitTestMessageInfoPtr unitTestReceivedMessage(bool mustExist = true); void apiProcessData(CC_MqttsnClient* client, const unsigned char* buf, unsigned bufLen); CC_MqttsnErrorCode apiSetDefaultRetryPeriod(CC_MqttsnClient* client, unsigned value); + unsigned apiGetDefaultRetryPeriod(CC_MqttsnClientHandle client); CC_MqttsnErrorCode apiSetDefaultRetryCount(CC_MqttsnClient* client, unsigned value); + unsigned apiGetDefaultRetryCount(CC_MqttsnClientHandle client); CC_MqttsnErrorCode apiSetVerifyIncomingMsgSubscribed(CC_MqttsnClient* client, bool enabled); void apiInitGatewayInfo(CC_MqttsnGatewayInfo* info); CC_MqttsnErrorCode apiSetAvailableGatewayInfo(CC_MqttsnClient* client, const CC_MqttsnGatewayInfo* info); @@ -472,7 +497,13 @@ class UnitTestCommonBase CC_MqttsnErrorCode apiWillSetRetryCount(CC_MqttsnWillHandle will, unsigned count); void apiWillInitConfig(CC_MqttsnWillConfig* config); CC_MqttsnErrorCode apiWillConfig(CC_MqttsnWillHandle will, const CC_MqttsnWillConfig* config); - CC_MqttsnErrorCode apiWillCancel(CC_MqttsnWillHandle will); + CC_MqttsnErrorCode apiWillCancel(CC_MqttsnWillHandle will); + + CC_MqttsnSleepHandle apiSleepPrepare(CC_MqttsnClient* client, CC_MqttsnErrorCode* ec = nullptr); + CC_MqttsnErrorCode apiSleepSetRetryCount(CC_MqttsnSleepHandle sleep, unsigned count); + void apiSleepInitConfig(CC_MqttsnSleepConfig* config); + CC_MqttsnErrorCode apiSleepConfig(CC_MqttsnSleepHandle sleep, const CC_MqttsnSleepConfig* config); + CC_MqttsnErrorCode apiSleepCancel(CC_MqttsnSleepHandle sleep); protected: @@ -494,6 +525,7 @@ class UnitTestCommonBase UnitTestUnsubscribeCompleteReportList m_unsubscribeCompleteReports; UnitTestPublishCompleteReportList m_publishCompleteReports; UnitTestWillCompleteReportList m_willCompleteReports; + UnitTestSleepCompleteReportList m_sleepCompleteReports; UnitTestMessageInfosList m_recvMsgs; }; @@ -512,6 +544,7 @@ class UnitTestCommonBase static void unitTestUnsubscribeCompleteCb(void* data, CC_MqttsnUnsubscribeHandle handle, CC_MqttsnAsyncOpStatus status); static void unitTestPublishCompleteCb(void* data, CC_MqttsnPublishHandle handle, CC_MqttsnAsyncOpStatus status, const CC_MqttsnPublishInfo* info); static void unitTestWillCompleteCb(void* data, CC_MqttsnAsyncOpStatus status, const CC_MqttsnWillInfo* info); + static void unitTestSleepCompleteCb(void* data, CC_MqttsnAsyncOpStatus status); LibFuncs m_funcs; ClientData m_data; diff --git a/client/lib/test/UnitTestDefaultBase.cpp b/client/lib/test/UnitTestDefaultBase.cpp index 536dfd5..f04f4a4 100644 --- a/client/lib/test/UnitTestDefaultBase.cpp +++ b/client/lib/test/UnitTestDefaultBase.cpp @@ -35,6 +35,7 @@ const UnitTestDefaultBase::LibFuncs& UnitTestDefaultBase::getFuncs() funcs.m_get_outgoing_topic_id_storage_limit = &cc_mqttsn_client_get_outgoing_topic_id_storage_limit; funcs.m_set_incoming_topic_id_storage_limit = &cc_mqttsn_client_set_incoming_topic_id_storage_limit; funcs.m_get_incoming_topic_id_storage_limit = &cc_mqttsn_client_get_incoming_topic_id_storage_limit; + funcs.m_asleep_check_messages = &cc_mqttsn_client_asleep_check_messages; funcs.m_search_prepare = &cc_mqttsn_client_search_prepare; funcs.m_search_set_retry_period = &cc_mqttsn_client_search_set_retry_period; funcs.m_search_get_retry_period = &cc_mqttsn_client_search_get_retry_period; @@ -105,8 +106,17 @@ const UnitTestDefaultBase::LibFuncs& UnitTestDefaultBase::getFuncs() funcs.m_will_config = &cc_mqttsn_client_will_config; funcs.m_will_send = &cc_mqttsn_client_will_send; funcs.m_will_cancel = &cc_mqttsn_client_will_cancel; - funcs.m_will = &cc_mqttsn_client_will; - + funcs.m_will = &cc_mqttsn_client_will; + funcs.m_sleep_prepare = &cc_mqttsn_client_sleep_prepare; + funcs.m_sleep_set_retry_period = &cc_mqttsn_client_sleep_set_retry_period; + funcs.m_sleep_get_retry_period = &cc_mqttsn_client_sleep_get_retry_period; + funcs.m_sleep_set_retry_count = &cc_mqttsn_client_sleep_set_retry_count; + funcs.m_sleep_get_retry_count = &cc_mqttsn_client_sleep_get_retry_count; + funcs.m_sleep_init_config = &cc_mqttsn_client_sleep_init_config; + funcs.m_sleep_config = &cc_mqttsn_client_sleep_config; + funcs.m_sleep_send = &cc_mqttsn_client_sleep_send; + funcs.m_sleep_cancel = &cc_mqttsn_client_sleep_cancel; + funcs.m_sleep = &cc_mqttsn_client_sleep; funcs.m_set_next_tick_program_callback = &cc_mqttsn_client_set_next_tick_program_callback; funcs.m_set_cancel_next_tick_wait_callback = &cc_mqttsn_client_set_cancel_next_tick_wait_callback; funcs.m_set_send_output_data_callback = &cc_mqttsn_client_set_send_output_data_callback; diff --git a/client/lib/test/UnitTestSleep.th b/client/lib/test/UnitTestSleep.th new file mode 100644 index 0000000..960964b --- /dev/null +++ b/client/lib/test/UnitTestSleep.th @@ -0,0 +1,206 @@ +#include "UnitTestDefaultBase.h" +#include "UnitTestProtocolDefs.h" + +#include "comms/units.h" + +#include + +class UnitTestSleep : public CxxTest::TestSuite, public UnitTestDefaultBase +{ +public: + void test1(); + void test2(); + +private: + virtual void setUp() override + { + unitTestSetUp(); + } + + virtual void tearDown() override + { + unitTestTearDown(); + } + + using TopicIdType = UnitTestPublishMsg::Field_flags::Field_topicIdType::ValueType; +}; + +void UnitTestSleep::test1() +{ + // Testing basic sleep and reconnect + + auto clientPtr = unitTestAllocClient(true); + auto* client = clientPtr.get(); + + const std::string ClientId("bla"); + unitTestDoConnectBasic(client, ClientId); + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client, 1000); + + const CC_MqttsnTopicId TopicId = 123; + unitTestDoSubscribeTopicId(client, TopicId); + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client, 1000); + + const unsigned SleepDurationSec = 10 * 60; + + CC_MqttsnSleepConfig config; + apiSleepInitConfig(&config); + + config.m_duration = SleepDurationSec; + + auto sleep = apiSleepPrepare(client); + TS_ASSERT_DIFFERS(sleep, nullptr); + + auto ec = apiSleepConfig(sleep, &config); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + + unitTestSleepSend(sleep); + + { + TS_ASSERT(unitTestHasOutputData()); + auto sentMsg = unitTestPopOutputMessage(); + auto* disconnectMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(disconnectMsg, nullptr); + TS_ASSERT(disconnectMsg->field_duration().doesExist()); + TS_ASSERT_EQUALS(disconnectMsg->field_duration().field().value(), SleepDurationSec); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client, 100); + + { + UnitTestDisconnectMsg disconnectMsg; + unitTestClientInputMessage(client, disconnectMsg); + } + + TS_ASSERT(unitTestHasSleepCompleteReport()); + auto sleepReport = unitTestSleepCompleteReport(); + TS_ASSERT_EQUALS(sleepReport->m_status, CC_MqttsnAsyncOpStatus_Complete) + TS_ASSERT_EQUALS(apiGetConnectionState(client), CC_MqttsnConnectionStatus_Asleep); + + + auto period = apiGetDefaultRetryPeriod(client); + auto count = apiGetDefaultRetryCount(client) + 1U; + auto diff = (period * count); + auto expTick = (SleepDurationSec * 1000U) - diff; + + TS_ASSERT(unitTestHasTickReq()); // Timer for ping + { + auto* tickInfo = unitTestTickInfo(); + TS_ASSERT_EQUALS(tickInfo->m_req, expTick); + } + unitTestTick(client); + + { + TS_ASSERT(unitTestHasOutputData()); + auto sentMsg = unitTestPopOutputMessage(); + auto* pingreqMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(pingreqMsg, nullptr); + TS_ASSERT_EQUALS(pingreqMsg->field_clientId().value(), ClientId); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + //unitTestTick(client, 100); + + { + const UnitTestData Data = {1, 2, 3, 4}; + UnitTestPublishMsg publishMsg; + publishMsg.field_flags().field_topicIdType().value() = TopicIdType::PredefinedTopicId; + publishMsg.field_topicId().setValue(TopicId); + publishMsg.field_data().value() = Data; + unitTestClientInputMessage(client, publishMsg); + + TS_ASSERT(unitTestHasReceivedMessage()) + auto msgInfo = unitTestReceivedMessage(); + TS_ASSERT_EQUALS(msgInfo->m_topicId, TopicId); + TS_ASSERT_EQUALS(msgInfo->m_data, Data); + TS_ASSERT(!unitTestHasReceivedMessage()) + } + + { + UnitTestPingrespMsg pingrespMsg; + unitTestClientInputMessage(client, pingrespMsg); + } + + TS_ASSERT(!unitTestHasOutputData()); + TS_ASSERT(unitTestHasTickReq()); // Timer for next ping + { + auto* tickInfo = unitTestTickInfo(); + TS_ASSERT_EQUALS(tickInfo->m_req, expTick); + } + + unitTestTick(client, 1000); + + unitTestDoConnectBasic(client, ClientId, false); + TS_ASSERT_EQUALS(apiGetConnectionState(client), CC_MqttsnConnectionStatus_Connected); +} + +void UnitTestSleep::test2() +{ + // Testing attempts to enter sleep mode + + auto clientPtr = unitTestAllocClient(); + auto* client = clientPtr.get(); + + const std::string ClientId("bla"); + unitTestDoConnectBasic(client, ClientId); + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client, 1000); + + const unsigned SleepDurationSec = 10 * 60; + + CC_MqttsnSleepConfig config; + apiSleepInitConfig(&config); + + config.m_duration = SleepDurationSec; + + auto sleep = apiSleepPrepare(client); + TS_ASSERT_DIFFERS(sleep, nullptr); + + auto ec = apiSleepSetRetryCount(sleep, 1U); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + + ec = apiSleepConfig(sleep, &config); + TS_ASSERT_EQUALS(ec, CC_MqttsnErrorCode_Success); + + unitTestSleepSend(sleep); + + { + TS_ASSERT(unitTestHasOutputData()); + auto sentMsg = unitTestPopOutputMessage(); + auto* disconnectMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(disconnectMsg, nullptr); + TS_ASSERT(disconnectMsg->field_duration().doesExist()); + TS_ASSERT_EQUALS(disconnectMsg->field_duration().field().value(), SleepDurationSec); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client); // Timeout + + { + TS_ASSERT(unitTestHasOutputData()); + auto sentMsg = unitTestPopOutputMessage(); + auto* disconnectMsg = dynamic_cast(sentMsg.get()); + TS_ASSERT_DIFFERS(disconnectMsg, nullptr); + TS_ASSERT(disconnectMsg->field_duration().doesExist()); + TS_ASSERT_EQUALS(disconnectMsg->field_duration().field().value(), SleepDurationSec); + TS_ASSERT(!unitTestHasOutputData()); + } + + TS_ASSERT(unitTestHasTickReq()); + unitTestTick(client); // Timeout + + TS_ASSERT(unitTestHasSleepCompleteReport()); + auto sleepReport = unitTestSleepCompleteReport(); + TS_ASSERT_EQUALS(sleepReport->m_status, CC_MqttsnAsyncOpStatus_Timeout) + TS_ASSERT_EQUALS(apiGetConnectionState(client), CC_MqttsnConnectionStatus_Connected); +} + +// TODO: test gateway disconnection report when there is no response to PINGREQ