| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements unit tests for the ReliableMessageProtocol |
| * implementation. |
| */ |
| #include <errno.h> |
| |
| #include <pw_unit_test/framework.h> |
| |
| #include <app/icd/server/ICDServerConfig.h> |
| #include <lib/core/CHIPCore.h> |
| #include <lib/core/StringBuilderAdapters.h> |
| #include <lib/support/CodeUtils.h> |
| #include <messaging/ReliableMessageContext.h> |
| #include <messaging/ReliableMessageMgr.h> |
| #include <messaging/ReliableMessageProtocolConfig.h> |
| #include <protocols/Protocols.h> |
| #include <protocols/echo/Echo.h> |
| #include <transport/SessionManager.h> |
| #include <transport/TransportMgr.h> |
| |
| #include <messaging/ExchangeContext.h> |
| #include <messaging/ExchangeMgr.h> |
| #include <messaging/Flags.h> |
| #include <messaging/tests/MessagingContext.h> |
| |
| #if CHIP_CONFIG_ENABLE_ICD_SERVER |
| #include <app/icd/server/ICDConfigurationData.h> // nogncheck |
| #endif |
| |
| #if CHIP_CRYPTO_PSA |
| #include "psa/crypto.h" |
| #endif |
| |
| namespace { |
| |
| using namespace chip; |
| using namespace chip::Inet; |
| using namespace chip::Transport; |
| using namespace chip::Messaging; |
| using namespace chip::Protocols; |
| using namespace chip::System::Clock::Literals; |
| |
| const char PAYLOAD[] = "Hello!"; |
| |
| class TestReliableMessageProtocol : public chip::Test::LoopbackMessagingContext |
| { |
| public: |
| // Performs setup for each individual test in the test suite |
| void SetUp() override |
| { |
| #if CHIP_CRYPTO_PSA |
| ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS); |
| #endif |
| chip::Test::LoopbackMessagingContext::SetUp(); |
| GetSessionAliceToBob()->AsSecureSession()->SetRemoteSessionParameters(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); |
| GetSessionBobToAlice()->AsSecureSession()->SetRemoteSessionParameters(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); |
| } |
| }; |
| |
| class MockAppDelegate : public UnsolicitedMessageHandler, public ExchangeDelegate |
| { |
| public: |
| MockAppDelegate(TestReliableMessageProtocol & ctx) : mTestReliableMessageProtocol(ctx) {} |
| |
| CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override |
| { |
| // Handle messages by myself |
| newDelegate = this; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader, |
| System::PacketBufferHandle && buffer) override |
| { |
| IsOnMessageReceivedCalled = true; |
| |
| if (ec->HasSessionHandle() && ec->GetSessionHolder()->IsSecureSession()) |
| { |
| mLastSubjectDescriptor = ec->GetSessionHolder()->AsSecureSession()->GetSubjectDescriptor(); |
| } |
| |
| if (payloadHeader.IsAckMsg()) |
| { |
| mReceivedPiggybackAck = true; |
| } |
| if (mDropAckResponse) |
| { |
| auto * rc = ec->GetReliableMessageContext(); |
| if (rc->HasPiggybackAckPending()) |
| { |
| // Make sure we don't accidentally retransmit and end up acking |
| // the retransmit. |
| rc->GetReliableMessageMgr()->StopTimer(); |
| (void) rc->TakePendingPeerAckMessageCounter(); |
| } |
| } |
| |
| if (mExchange != ec) |
| { |
| CloseExchangeIfNeeded(); |
| } |
| |
| if (!mRetainExchange) |
| { |
| ec = nullptr; |
| } |
| else |
| { |
| ec->WillSendMessage(); |
| } |
| mExchange = ec; |
| |
| EXPECT_EQ(buffer->TotalLength(), sizeof(PAYLOAD)); |
| EXPECT_EQ(memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()), 0); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void OnResponseTimeout(ExchangeContext * ec) override { mResponseTimedOut = true; } |
| |
| void CloseExchangeIfNeeded() |
| { |
| if (mExchange != nullptr) |
| { |
| mExchange->Close(); |
| mExchange = nullptr; |
| } |
| } |
| |
| void SetDropAckResponse(bool dropResponse) |
| { |
| mDropAckResponse = dropResponse; |
| if (!mDropAckResponse) |
| { |
| // Restart the MRP retransmit timer, now that we are not going to be |
| // dropping acks anymore, so we send out pending retransmits, if |
| // any, as needed. |
| mTestReliableMessageProtocol.GetExchangeManager().GetReliableMessageMgr()->StartTimer(); |
| } |
| } |
| |
| Access::SubjectDescriptor mLastSubjectDescriptor{}; |
| |
| bool IsOnMessageReceivedCalled = false; |
| bool mReceivedPiggybackAck = false; |
| bool mRetainExchange = false; |
| bool mResponseTimedOut = false; |
| ExchangeContext * mExchange = nullptr; |
| |
| private: |
| TestReliableMessageProtocol & mTestReliableMessageProtocol; |
| bool mDropAckResponse = false; |
| }; |
| |
| class MockSessionEstablishmentExchangeDispatch : public Messaging::ApplicationExchangeDispatch |
| { |
| public: |
| bool IsReliableTransmissionAllowed() const override { return mRetainMessageOnSend; } |
| |
| bool MessagePermitted(Protocols::Id protocol, uint8_t type) override { return true; } |
| |
| bool IsEncryptionRequired() const override { return mRequireEncryption; } |
| |
| bool mRetainMessageOnSend = true; |
| |
| bool mRequireEncryption = false; |
| }; |
| |
| class MockSessionEstablishmentDelegate : public UnsolicitedMessageHandler, public ExchangeDelegate |
| { |
| public: |
| CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override |
| { |
| // Handle messages by myself |
| newDelegate = this; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR OnMessageReceived(ExchangeContext * ec, const PayloadHeader & payloadHeader, |
| System::PacketBufferHandle && buffer) override |
| { |
| IsOnMessageReceivedCalled = true; |
| EXPECT_EQ(buffer->TotalLength(), sizeof(PAYLOAD)); |
| EXPECT_EQ(memcmp(buffer->Start(), PAYLOAD, buffer->TotalLength()), 0); |
| return CHIP_NO_ERROR; |
| } |
| |
| void OnResponseTimeout(ExchangeContext * ec) override {} |
| |
| virtual ExchangeMessageDispatch & GetMessageDispatch() override { return mMessageDispatch; } |
| |
| bool IsOnMessageReceivedCalled = false; |
| MockSessionEstablishmentExchangeDispatch mMessageDispatch; |
| }; |
| |
| struct BackoffComplianceTestVector |
| { |
| uint8_t sendCount; |
| System::Clock::Timeout backoffBase; |
| System::Clock::Timeout backoffMin; |
| System::Clock::Timeout backoffMax; |
| }; |
| |
| struct BackoffComplianceTestVector theBackoffComplianceTestVector[] = { { |
| .sendCount = 0, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(330), |
| .backoffMax = System::Clock::Timeout(413), |
| }, |
| { |
| .sendCount = 1, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(330), |
| .backoffMax = System::Clock::Timeout(413), |
| }, |
| { |
| .sendCount = 2, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(528), |
| .backoffMax = System::Clock::Timeout(661), |
| }, |
| { |
| .sendCount = 3, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(844), |
| .backoffMax = System::Clock::Timeout(1057), |
| }, |
| { |
| .sendCount = 4, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(1351), |
| .backoffMax = System::Clock::Timeout(1691), |
| }, |
| { |
| .sendCount = 5, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(2162), |
| .backoffMax = System::Clock::Timeout(2705), |
| }, |
| { |
| .sendCount = 6, |
| .backoffBase = System::Clock::Timeout(300), |
| .backoffMin = System::Clock::Timeout(2162), |
| .backoffMax = System::Clock::Timeout(2705), |
| }, |
| { |
| .sendCount = 0, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(4400), |
| .backoffMax = System::Clock::Timeout(5503), |
| }, |
| { |
| .sendCount = 1, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(4400), |
| .backoffMax = System::Clock::Timeout(5503), |
| }, |
| { |
| .sendCount = 2, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(7040), |
| .backoffMax = System::Clock::Timeout(8805), |
| }, |
| { |
| .sendCount = 3, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(11264), |
| .backoffMax = System::Clock::Timeout(14088), |
| }, |
| { |
| .sendCount = 4, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(18022), |
| .backoffMax = System::Clock::Timeout(22541), |
| }, |
| { |
| .sendCount = 5, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(28835), |
| .backoffMax = System::Clock::Timeout(36065), |
| }, |
| { |
| .sendCount = 6, |
| .backoffBase = System::Clock::Timeout(4000), |
| .backoffMin = System::Clock::Timeout(28835), |
| .backoffMax = System::Clock::Timeout(36065), |
| }, |
| { |
| // test theoretical worst-case 1-hour interval |
| .sendCount = 4, |
| .backoffBase = System::Clock::Timeout(3'600'000), |
| .backoffMin = System::Clock::Timeout(16'220'160), |
| .backoffMax = System::Clock::Timeout(20'286'001), |
| } }; |
| |
| void CheckGetBackoffImpl(System::Clock::Timeout additionalMRPBackoffTime) |
| { |
| ReliableMessageMgr::SetAdditionalMRPBackoffTime(MakeOptional(additionalMRPBackoffTime)); |
| |
| // Run 3x iterations to thoroughly test random jitter always results in backoff within bounds. |
| for (uint32_t j = 0; j < 3; j++) |
| { |
| for (const auto & test : theBackoffComplianceTestVector) |
| { |
| System::Clock::Timeout backoff = ReliableMessageMgr::GetBackoff(test.backoffBase, test.sendCount); |
| System::Clock::Timeout extraBackoff = additionalMRPBackoffTime; |
| |
| #if CHIP_CONFIG_ENABLE_ICD_SERVER |
| // If running as an ICD, increase maxBackoff to account for the polling interval |
| extraBackoff += ICDConfigurationData::GetInstance().GetFastPollingInterval(); |
| #endif |
| |
| ChipLogProgress(Test, "Backoff base %" PRIu32 " extra %" PRIu32 " # %d: %" PRIu32, test.backoffBase.count(), |
| extraBackoff.count(), test.sendCount, backoff.count()); |
| |
| EXPECT_GE(backoff, test.backoffMin + extraBackoff); |
| EXPECT_LE(backoff, test.backoffMax + extraBackoff); |
| } |
| } |
| |
| ReliableMessageMgr::SetAdditionalMRPBackoffTime(NullOptional); |
| } |
| |
| } // namespace |
| |
| TEST_F(TestReliableMessageProtocol, CheckAddClearRetrans) |
| { |
| MockAppDelegate mockAppDelegate(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockAppDelegate); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ReliableMessageContext * rc = exchange->GetReliableMessageContext(); |
| ASSERT_NE(rm, nullptr); |
| ASSERT_NE(rc, nullptr); |
| |
| ReliableMessageMgr::RetransTableEntry * entry; |
| |
| rm->AddToRetransTable(rc, &entry); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| rm->ClearRetransTable(*entry); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| exchange->Close(); |
| } |
| |
| /** |
| * Tests MRP retransmission logic with the following scenario: |
| * |
| * DUT = sender, PEER = remote device |
| * |
| * 1) DUT configured to use sleepy peer parameters of active = 64ms, idle = 64ms |
| * 2) DUT sends message attempt #1 to PEER |
| * - Force PEER to drop message |
| * - Observe DUT timeout with no ack |
| * - Confirm MRP backoff interval is correct |
| * 3) DUT resends message attempt #2 to PEER |
| * - Force PEER to drop message |
| * - Observe DUT timeout with no ack |
| * - Confirm MRP backoff interval is correct |
| * 4) DUT resends message attempt #3 to PEER |
| * - Force PEER to drop message |
| * - Observe DUT timeout with no ack |
| * - Confirm MRP backoff interval is correct |
| * 5) DUT resends message attempt #4 to PEER |
| * - Force PEER to drop message |
| * - Observe DUT timeout with no ack |
| * - Confirm MRP backoff interval is correct |
| * 6) DUT resends message attempt #5 to PEER |
| * - PEER to acknowledge message |
| * - Observe DUT signal successful reliable transmission |
| */ |
| TEST_F(TestReliableMessageProtocol, CheckResendApplicationMessage) |
| { |
| BackoffComplianceTestVector * expectedBackoff; |
| System::Clock::Timestamp now, startTime; |
| System::Clock::Timeout timeoutTime, margin; |
| margin = System::Clock::Timeout(15); |
| |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockSender(*this); |
| // TODO: temporarily create a SessionHandle from node id, will be fix in PR 3602 |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| System::Clock::Timestamp(300), // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| System::Clock::Timestamp(300), // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's drop the initial message |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 4; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // Ensure the exchange stays open after we send (unlike the CheckCloseExchangeAndResendApplicationMessage case), by claiming to |
| // expect a response. |
| startTime = System::SystemClock().GetMonotonicTimestamp(); |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the initial message was dropped and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 3u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the initial message to fail (should take 330-413ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| now = System::SystemClock().GetMonotonicTimestamp(); |
| timeoutTime = now - startTime; |
| ChipLogProgress(Test, "Attempt #1 Timeout : %" PRIu32 "ms", timeoutTime.count()); |
| expectedBackoff = &theBackoffComplianceTestVector[0]; |
| EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); |
| |
| startTime = System::SystemClock().GetMonotonicTimestamp(); |
| DrainAndServiceIO(); |
| |
| // Ensure the 1st retry was dropped, and is still there in the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 2u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the 1st retry to fail (should take 330-413ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); |
| now = System::SystemClock().GetMonotonicTimestamp(); |
| timeoutTime = now - startTime; |
| ChipLogProgress(Test, "Attempt #2 Timeout : %" PRIu32 "ms", timeoutTime.count()); |
| expectedBackoff = &theBackoffComplianceTestVector[1]; |
| EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); |
| |
| startTime = System::SystemClock().GetMonotonicTimestamp(); |
| DrainAndServiceIO(); |
| |
| // Ensure the 2nd retry was dropped, and is still there in the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 3u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 3u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the 2nd retry to fail (should take 528-660ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); |
| now = System::SystemClock().GetMonotonicTimestamp(); |
| timeoutTime = now - startTime; |
| ChipLogProgress(Test, "Attempt #3 Timeout : %" PRIu32 "ms", timeoutTime.count()); |
| expectedBackoff = &theBackoffComplianceTestVector[2]; |
| EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); |
| |
| startTime = System::SystemClock().GetMonotonicTimestamp(); |
| DrainAndServiceIO(); |
| |
| // Ensure the 3rd retry was dropped, and is still there in the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 4u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 4u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the 3rd retry to fail (should take 845-1056ms) |
| GetIOContext().DriveIOUntil(1500_ms32, [&] { return loopback.mSentMessageCount >= 5; }); |
| now = System::SystemClock().GetMonotonicTimestamp(); |
| timeoutTime = now - startTime; |
| ChipLogProgress(Test, "Attempt #4 Timeout : %" PRIu32 "ms", timeoutTime.count()); |
| expectedBackoff = &theBackoffComplianceTestVector[3]; |
| EXPECT_GE(timeoutTime, expectedBackoff->backoffMin - margin); |
| |
| // Trigger final transmission |
| DrainAndServiceIO(); |
| |
| // Ensure the last retransmission was NOT dropped, and the retransmit table is empty, as we should have gotten an ack |
| EXPECT_GE(loopback.mSentMessageCount, 5u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 4u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| exchange->Close(); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckCloseExchangeAndResendApplicationMessage) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockSender(*this); |
| // TODO: temporarily create a SessionHandle from node id, will be fixed in PR 3602 |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's drop the initial message |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 2; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped, and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the first re-transmit (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was dropped, and is still there in the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 2u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Wait for the second re-transmit (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was NOT dropped, and the retransmit table is empty, as we should have gotten an ack |
| EXPECT_GE(loopback.mSentMessageCount, 3u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 2u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckFailedMessageRetainOnSend) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockSessionEstablishmentDelegate mockSender; |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| mockSender.mMessageDispatch.mRetainMessageOnSend = false; |
| // Let's drop the initial message |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| |
| // Wait for the first re-transmit (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit table is empty, as we did not provide a message to retain |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckUnencryptedMessageReceiveFailure) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| MockSessionEstablishmentDelegate mockReceiver; |
| CHIP_ERROR err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| // Expect the received messages to be encrypted |
| mockReceiver.mMessageDispatch.mRequireEncryption = true; |
| |
| MockSessionEstablishmentDelegate mockSender; |
| ExchangeContext * exchange = NewUnauthenticatedExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| |
| // We are sending a malicious packet, doesn't expect an ack |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kNoAutoRequestAck)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Test that the message was actually sent (and not dropped) |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| // Test that the message was dropped by the receiver |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckResendApplicationMessageWithPeerExchange) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's drop the initial message |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped, and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // Wait for the first re-transmit (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was not dropped, and is no longer in the retransmit table |
| EXPECT_GE(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckDuplicateMessageClosedExchange) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Drop the ack, and also close the peer exchange |
| mockReceiver.SetDropAckResponse(true); |
| mockReceiver.mRetainExchange = false; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent |
| // The ack was dropped, and message was added to the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Let's not drop the duplicate message |
| mockReceiver.SetDropAckResponse(false); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| // Wait for the first re-transmit and ack (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was sent and the ack was sent |
| // and retransmit table was cleared |
| EXPECT_EQ(loopback.mSentMessageCount, 3u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckDuplicateOldMessageClosedExchange) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Drop the ack, and also close the peer exchange |
| mockReceiver.SetDropAckResponse(true); |
| mockReceiver.mRetainExchange = false; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent |
| // The ack was dropped, and message was added to the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Now send CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE + 2 messages to make |
| // sure our original message is out of the message counter window. These |
| // messages can be sent withour MRP, because we are not expecting acks for |
| // them anyway. |
| size_t extraMessages = CHIP_CONFIG_MESSAGE_COUNTER_WINDOW_SIZE + 2; |
| for (size_t i = 0; i < extraMessages; ++i) |
| { |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| ExchangeContext * newExchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(newExchange, nullptr); |
| |
| mockReceiver.mRetainExchange = false; |
| |
| // Ensure the retransmit table has our one message right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Send without MRP. |
| err = newExchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kNoAutoRequestAck); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent, but not added to the retransmit table. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u + (i + 1u)); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| } |
| |
| // Let's not drop the duplicate message's ack. |
| mockReceiver.SetDropAckResponse(false); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| // Wait for the first re-transmit and ack (should take 64ms) |
| rm->StartTimer(); |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3 + extraMessages; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was sent and the ack was sent |
| // and retransmit table was cleared |
| EXPECT_EQ(loopback.mSentMessageCount, 3u + extraMessages); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckResendSessionEstablishmentMessageWithPeerExchange) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| ASSERT_FALSE(buffer.IsNull()); |
| |
| MockSessionEstablishmentDelegate mockReceiver; |
| CHIP_ERROR err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockSessionEstablishmentDelegate mockSender; |
| ExchangeContext * exchange = NewUnauthenticatedExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's drop the initial message |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped, and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // Wait for the first re-transmit (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was not dropped, and is no longer in the retransmit table |
| EXPECT_GE(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckDuplicateMessage) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_INITIAL_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_RMP_DEFAULT_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Let's not drop the message. Expectation is that it is received by the peer, but the ack is dropped |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Drop the ack, and keep the exchange around to receive the duplicate message |
| mockReceiver.SetDropAckResponse(true); |
| mockReceiver.mRetainExchange = true; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent |
| // The ack was dropped, and message was added to the retransmit table |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| // Let's not drop the duplicate message |
| mockReceiver.SetDropAckResponse(false); |
| mockReceiver.mRetainExchange = false; |
| |
| // Wait for the first re-transmit and ack (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); |
| DrainAndServiceIO(); |
| |
| // Ensure the retransmit message was sent and the ack was sent |
| // and retransmit table was cleared |
| EXPECT_EQ(loopback.mSentMessageCount, 3u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| mockReceiver.CloseExchangeIfNeeded(); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckReceiveAfterStandaloneAck) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // We send a message, have it get received by the peer, then an ack is |
| // returned, then a reply is returned. We need to keep the receiver |
| // exchange alive until it does the message send (so we can send the |
| // response from the receiver and so the initial sender exchange can get |
| // it). |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| mockReceiver.mRetainExchange = true; |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we have not seen an ack yet. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); |
| EXPECT_TRUE(receiverRc->IsAckPending()); |
| |
| // Send the standalone ack. |
| receiverRc->SendStandaloneAckMessage(); |
| DrainAndServiceIO(); |
| |
| // Ensure the ack was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have not gotten any app-level responses so far. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // And that we have now gotten our ack. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // Now send a message from the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response and its ack was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 4u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have received that response. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckPiggybackAfterPiggyback) |
| { |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // We send a message, have it get received by the peer, have the peer return |
| // a piggybacked ack. Then we send a second message this time _not_ |
| // requesting an ack, get a response, and see whether an ack was |
| // piggybacked. We need to keep both exchanges alive for that (so we can |
| // send the response from the receiver and so the initial sender exchange |
| // can get it). |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| mockReceiver.mRetainExchange = true; |
| mockSender.mRetainExchange = true; |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we have not seen an ack yet. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); |
| EXPECT_TRUE(receiverRc->IsAckPending()); |
| |
| // Ensure that we have not gotten any app-level responses or acks so far. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.mReceivedPiggybackAck); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Now send a message from the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = |
| mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer), |
| SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have received that response and it had a piggyback ack. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_TRUE(mockSender.mReceivedPiggybackAck); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // Reset various state so we can measure things again. |
| mockReceiver.IsOnMessageReceivedCalled = false; |
| mockSender.IsOnMessageReceivedCalled = false; |
| mockSender.mReceivedPiggybackAck = false; |
| |
| // Now send a new message to the other side, but don't ask for an ack. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), |
| SendFlags(SendMessageFlags::kExpectResponse).Set(SendMessageFlags::kNoAutoRequestAck)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 3u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we are not expecting an ack. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // Send the final response. At this point we don't need to keep the |
| // exchanges alive anymore. |
| mockReceiver.mRetainExchange = false; |
| mockSender.mRetainExchange = false; |
| |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response and its ack was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 5u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have received that response and it had a piggyback ack. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_TRUE(mockSender.mReceivedPiggybackAck); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckSendUnsolicitedStandaloneAckMessage) |
| { |
| /** |
| * Tests sending a standalone ack message that is: |
| * 1) Unsolicited. |
| * 2) Requests an ack. |
| * |
| * This is not a thing that would normally happen, but a malicious entity |
| * could absolutely do this. |
| */ |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData("", 0); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // We send a message, have it get received by the peer, expect an ack from |
| // the peer. |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Purposefully sending a standalone ack that requests an ack! |
| err = exchange->SendMessage(SecureChannel::MsgType::StandaloneAck, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| // Needs a manual close, because SendMessage does not close for standalone acks. |
| exchange->Close(); |
| DrainAndServiceIO(); |
| |
| // Ensure the message and its ack were sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that nothing is waiting for acks. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckSendStandaloneAckMessage) |
| { |
| MockAppDelegate mockAppDelegate(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockAppDelegate); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ReliableMessageContext * rc = exchange->GetReliableMessageContext(); |
| ASSERT_NE(rm, nullptr); |
| ASSERT_NE(rc, nullptr); |
| |
| EXPECT_EQ(rc->SendStandaloneAckMessage(), CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Need manual close because standalone acks don't close exchanges. |
| exchange->Close(); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckMessageAfterClosed) |
| { |
| /** |
| * This test performs the following sequence of actions, where all messages |
| * are sent with MRP enabled: |
| * |
| * 1) Initiator sends message to responder. |
| * 2) Responder responds to the message (piggybacking an ack) and closes |
| * the exchange. |
| * 3) Initiator sends a response to the response on the same exchange, again |
| * piggybacking an ack. |
| * |
| * This is basically the "command, response, status response" flow, with the |
| * responder closing the exchange after it sends the response. |
| */ |
| |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| // We need to keep both exchanges alive for the thing we are testing here. |
| mockReceiver.mRetainExchange = true; |
| mockSender.mRetainExchange = true; |
| |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockReceiver.mReceivedPiggybackAck); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockReceiver.mReceivedPiggybackAck); |
| |
| // And that we have not seen an ack yet. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); |
| EXPECT_TRUE(receiverRc->IsAckPending()); |
| |
| // Ensure that we have not gotten any app-level responses or acks so far. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.mReceivedPiggybackAck); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Now send a message from the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have received that response and it had a piggyback ack. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_TRUE(mockSender.mReceivedPiggybackAck); |
| // And that we are now waiting for an ack for the response. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // Reset various state so we can measure things again. |
| mockReceiver.IsOnMessageReceivedCalled = false; |
| mockReceiver.mReceivedPiggybackAck = false; |
| mockSender.IsOnMessageReceivedCalled = false; |
| mockSender.mReceivedPiggybackAck = false; |
| |
| // Now send a second message to the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent (and the ack for it was also sent). |
| EXPECT_EQ(loopback.mSentMessageCount, 4u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was not received (because the exchange is closed on the |
| // receiver). |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we are not expecting an ack; acks should have been flushed |
| // immediately on the receiver, due to the exchange being closed. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckLostResponseWithPiggyback) |
| { |
| /** |
| * This tests the following scenario: |
| * 1) A reliable message is sent from initiator to responder. |
| * 2) The responder sends a response with a piggybacked ack, which is lost. |
| * 3) Initiator resends the message. |
| * 4) Responder responds to the resent message with a standalone ack. |
| * 5) The responder retransmits the application-level response. |
| * 4) The initiator should receive the application-level response. |
| */ |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // Make sure that we resend our message before the other side does. |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // We send a message, the other side sends an application-level response |
| // (which is lost), then we do a retransmit that is acked, then the other |
| // side does a retransmit. We need to keep the receiver exchange alive (so |
| // we can send the response from the receiver), but don't need anything |
| // special for the sender exchange, because it will be waiting for the |
| // application-level response. |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| mockReceiver.mRetainExchange = true; |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we have not gotten any app-level responses or acks so far. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); |
| // Should have pending ack here. |
| EXPECT_TRUE(receiverRc->IsAckPending()); |
| // Make sure receiver resends after sender does, and there's enough of a gap |
| // that we are very unlikely to actually trigger the resends on the receiver |
| // when we trigger the resends on the sender. |
| mockReceiver.mExchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 256_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 256_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| // Now send a message from the other side, but drop it. |
| loopback.mNumMessagesToDrop = 1; |
| |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| // Stop keeping receiver exchange alive. |
| mockReceiver.mRetainExchange = true; |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response was sent but dropped. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| |
| // Ensure that we have not received that response. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.mReceivedPiggybackAck); |
| // We now have our un-acked message still waiting to retransmit and the |
| // message that the other side sent is waiting for an ack. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 2); |
| |
| // Reset various state so we can measure things again. |
| mockReceiver.IsOnMessageReceivedCalled = false; |
| mockReceiver.mReceivedPiggybackAck = false; |
| mockSender.IsOnMessageReceivedCalled = false; |
| mockSender.mReceivedPiggybackAck = false; |
| |
| // Wait for re-transmit from sender and ack (should take 64ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); |
| DrainAndServiceIO(); |
| |
| // We resent our first message, which did not make it to the app-level |
| // listener on the receiver (because it's a duplicate) but did trigger a |
| // standalone ack. |
| // |
| // Now the annoying part is that depending on how long we _actually_ slept |
| // we might have also triggered the retransmit from the other side, even |
| // though we did not want to. Handle both cases here. |
| EXPECT_TRUE(loopback.mSentMessageCount == 4 || loopback.mSentMessageCount == 6); |
| if (loopback.mSentMessageCount == 4) |
| { |
| // Just triggered the retransmit from the sender. |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| } |
| else |
| { |
| // Also triggered the retransmit from the receiver. |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| // Wait for re-transmit from receiver (should take 256ms) |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 6; }); |
| DrainAndServiceIO(); |
| |
| // And now we've definitely resent our response message, which should show |
| // up as an app-level message and trigger a standalone ack. |
| EXPECT_EQ(loopback.mSentMessageCount, 6u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| |
| // Should be all done now. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckIsPeerActiveNotInitiator) |
| { |
| /** |
| * This tests the following scenario: |
| * 1) A reliable message expecting a response is sent from the initiator to responder which is lost |
| * 2) Initiator resends the message at the IdleRetrans interval |
| * 3) Responder receives the message and sends a standalone ack |
| * 4) Responder sends a response and fails |
| * 5) Responder retries at the ActiveRestrans interval |
| * 6) Initiator receives the response |
| */ |
| |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| mockReceiver.mRetainExchange = true; |
| mockSender.mRetainExchange = true; |
| |
| EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Verify that the first message is dropped |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| |
| // Make sure retransmit was not done before the idle restrans interval hits |
| GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 1; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); |
| |
| // // Make sure nothing happened |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // // Retrasnmit message |
| GetIOContext().DriveIOUntil(2000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); |
| |
| // // Make sure nothing happened |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // // Verify that the receiver considers the sender is active |
| EXPECT_FALSE(exchange->HasReceivedAtLeastOneMessage()); |
| EXPECT_TRUE(mockReceiver.mExchange->HasReceivedAtLeastOneMessage()); |
| |
| mockReceiver.mExchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 100_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| mockReceiver.mRetainExchange = false; |
| mockSender.mRetainExchange = false; |
| |
| // Now send a message from the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| // Make receiver message fail once |
| loopback.mNumMessagesToDrop = 1; |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Make sure nothing happened |
| EXPECT_EQ(loopback.mDroppedMessageCount, 2u); |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mSentMessageCount, 3u); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // // Retrasnmit message |
| GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 4; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_EQ(loopback.mSentMessageCount, 5u); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckLostStandaloneAck) |
| { |
| /** |
| * This tests the following scenario: |
| * 1) A reliable message is sent from initiator to responder. |
| * 2) The responder sends a standalone ack, which is lost. |
| * 3) The responder sends an application-level response. |
| * 4) The initiator sends a reliable response to the app-level response. |
| * |
| * This should succeed, with all application-level messages being delivered |
| * and no crashes. |
| */ |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| // We send a message, the other side sends a standalone ack first (which is |
| // lost), then an application response, then we respond to that response. |
| // We need to keep both exchanges alive for that (so we can send the |
| // response from the receiver and so the initial sender exchange can send a |
| // response to that). |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = 0; |
| loopback.mDroppedMessageCount = 0; |
| mockReceiver.mRetainExchange = true; |
| mockSender.mRetainExchange = true; |
| |
| // And ensure the ack heading back our way is dropped. |
| mockReceiver.SetDropAckResponse(true); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| |
| // And that we have not gotten any app-level responses or acks so far. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| ReliableMessageContext * receiverRc = mockReceiver.mExchange->GetReliableMessageContext(); |
| // Ack should have been dropped. |
| EXPECT_FALSE(receiverRc->IsAckPending()); |
| |
| // Don't drop any more acks. |
| mockReceiver.SetDropAckResponse(false); |
| |
| // Now send a message from the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer), |
| SendFlags(SendMessageFlags::kExpectResponse)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the response was sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 2u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // Ensure that we have received that response and had a piggyback ack. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); |
| EXPECT_TRUE(mockSender.mReceivedPiggybackAck); |
| // We now have just the received message waiting for an ack. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| |
| // And receiver still has no ack pending. |
| EXPECT_FALSE(receiverRc->IsAckPending()); |
| |
| // Reset various state so we can measure things again. |
| mockReceiver.IsOnMessageReceivedCalled = false; |
| mockReceiver.mReceivedPiggybackAck = false; |
| mockSender.IsOnMessageReceivedCalled = false; |
| mockSender.mReceivedPiggybackAck = false; |
| |
| // Stop retaining the recipient exchange. |
| mockReceiver.mRetainExchange = false; |
| |
| // Now send a new message to the other side. |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message and the standalone ack to it were sent. |
| EXPECT_EQ(loopback.mSentMessageCount, 4u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 0u); |
| |
| // And that it was received. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_TRUE(mockReceiver.mReceivedPiggybackAck); |
| |
| // At this point all our exchanges and reliable message contexts should be |
| // dead, so we can't test anything about their state. |
| |
| // And that there are no un-acked messages left. |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckGetBackoff) |
| { |
| CheckGetBackoffImpl(System::Clock::kZero); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckGetBackoffAdditionalTime) |
| { |
| CheckGetBackoffImpl(System::Clock::Seconds32(1)); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckApplicationResponseDelayed) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Make sure we are using CASE sessions, because there is no defunct-marking for PASE. |
| ExpireSessionBobToAlice(); |
| ExpireSessionAliceToBob(); |
| err = CreateCASESessionBobToAlice(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| err = CreateCASESessionAliceToBob(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| mockReceiver.mRetainExchange = true; |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| constexpr uint32_t kMaxMRPTransmits = 5; // Counting the initial message. |
| |
| // Let's drop all but the last MRP transmit. |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = kMaxMRPTransmits - 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| exchange->SetResponseTimeout(3000_ms32); |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped, and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, kMaxMRPTransmits - 2u); |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // Wait for all but the last retransmit to happen. |
| GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); |
| DrainAndServiceIO(); |
| |
| // Ensure that nothing has been sent yet. |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // Now allow through the next message (our last retransmit), but make sure |
| // there is no standalone ack for it. |
| mockReceiver.SetDropAckResponse(true); |
| GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); |
| DrainAndServiceIO(); |
| |
| // Verify that message was sent and received but nothing else has been sent. |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We have no ack yet. |
| ASSERT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| |
| // It was not a commissioning CASE session so that is lined-up properly. |
| EXPECT_FALSE(mockReceiver.mLastSubjectDescriptor.isCommissioning); |
| |
| // Ensure there will be no more weirdness with acks and that our MRP timer is restarted properly. |
| mockReceiver.SetDropAckResponse(false); |
| |
| // Now send a response, but drop all but the last MRP retransmit. |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = kMaxMRPTransmits - 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| mockReceiver.mExchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // At this point, we should have two MRP contexts pending. |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 2); // We have no ack yet. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| |
| // Now wait for all but the last retransmit to happen from the other side. |
| GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits - 1; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| // We might have timed our MRP resends out, or not, but the other side is waiting for an ack. |
| EXPECT_TRUE(rm->TestGetCountRetransTable() == 1 || rm->TestGetCountRetransTable() == 2); |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| |
| // Now wait for us to time out our MRP context for sure. |
| GetIOContext().DriveIOUntil(5000_ms32, [&] { return rm->TestGetCountRetransTable() == 1; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We timed out our MRP context. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. |
| |
| // Now wait for the last retransmit (and our ack) to to happen. |
| GetIOContext().DriveIOUntil(5000_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits + 1); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // Everything has been acked. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_TRUE(mockSender.IsOnMessageReceivedCalled); // We got the response. |
| EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. |
| |
| // Ensure that we did not mark any sessions defunct. |
| EXPECT_FALSE(GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); |
| EXPECT_FALSE(GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); |
| |
| // Reset our sessions, so other tests get the usual PASE session |
| ExpireSessionBobToAlice(); |
| ExpireSessionAliceToBob(); |
| err = CreateSessionBobToAlice(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| err = CreateSessionAliceToBob(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| } |
| |
| TEST_F(TestReliableMessageProtocol, CheckApplicationResponseNeverComes) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| // Make sure we are using CASE sessions, because there is no defunct-marking for PASE. |
| ExpireSessionBobToAlice(); |
| ExpireSessionAliceToBob(); |
| err = CreateCASESessionBobToAlice(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| err = CreateCASESessionAliceToBob(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); |
| EXPECT_FALSE(buffer.IsNull()); |
| |
| MockAppDelegate mockReceiver(*this); |
| err = GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| MockAppDelegate mockSender(*this); |
| ExchangeContext * exchange = NewExchangeToAlice(&mockSender); |
| ASSERT_NE(exchange, nullptr); |
| |
| ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); |
| ASSERT_NE(rm, nullptr); |
| |
| exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL |
| 30_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL |
| })); |
| |
| constexpr uint32_t kMaxMRPTransmits = 5; // Counting the initial message. |
| |
| // Let's drop all but the last MRP transmit. |
| auto & loopback = GetLoopback(); |
| loopback.mSentMessageCount = 0; |
| loopback.mNumMessagesToDrop = kMaxMRPTransmits - 1; |
| loopback.mDroppedMessageCount = 0; |
| |
| // Ensure the retransmit table is empty right now |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); |
| |
| exchange->SetResponseTimeout(2500_ms32); |
| err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendMessageFlags::kExpectResponse); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| DrainAndServiceIO(); |
| |
| // Ensure the message was dropped, and was added to retransmit table |
| EXPECT_EQ(loopback.mNumMessagesToDrop, kMaxMRPTransmits - 2u); |
| EXPECT_EQ(loopback.mSentMessageCount, 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // Wait for all but the last retransmit to happen. |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mDroppedMessageCount >= kMaxMRPTransmits - 1; }); |
| DrainAndServiceIO(); |
| |
| // Ensure that nothing has been sent yet. |
| EXPECT_EQ(loopback.mNumMessagesToDrop, 0u); |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits - 1u); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1u); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); |
| EXPECT_FALSE(mockReceiver.IsOnMessageReceivedCalled); |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); |
| |
| // Now allow through the next message (our last retransmit), but make sure |
| // there is no standalone ack for it. |
| mockReceiver.SetDropAckResponse(true); |
| GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= kMaxMRPTransmits; }); |
| DrainAndServiceIO(); |
| |
| // Verify that message was sent and received but nothing else has been sent. |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 1); // We have no ack yet. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got the message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| |
| // Ensure there will be no more weirdness with acks and that our MRP timer is restarted properly. |
| mockReceiver.SetDropAckResponse(false); |
| |
| // Now wait for us to time out our MRP context. |
| GetIOContext().DriveIOUntil(1000_ms32, [&] { return rm->TestGetCountRetransTable() == 0; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We timed out our MRP context. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We did not get a response. |
| EXPECT_FALSE(mockSender.mResponseTimedOut); // We did not time out yet. |
| |
| // Now wait for our exchange to time out. |
| GetIOContext().DriveIOUntil(3000_ms32, [&] { return mockSender.mResponseTimedOut; }); |
| DrainAndServiceIO(); |
| |
| EXPECT_EQ(loopback.mSentMessageCount, kMaxMRPTransmits); |
| EXPECT_EQ(loopback.mDroppedMessageCount, kMaxMRPTransmits - 1); |
| EXPECT_EQ(rm->TestGetCountRetransTable(), 0); // We timed this out long ago. |
| EXPECT_TRUE(mockReceiver.IsOnMessageReceivedCalled); // Other side got original message. |
| EXPECT_FALSE(mockSender.IsOnMessageReceivedCalled); // We never got a response. |
| EXPECT_TRUE(mockSender.mResponseTimedOut); // We tiemd out |
| |
| // We should have marked out session defunct. |
| EXPECT_TRUE(GetSessionBobToAlice()->AsSecureSession()->IsDefunct()); |
| // Other side had no reason to mark its session defunct. |
| EXPECT_FALSE(GetSessionAliceToBob()->AsSecureSession()->IsDefunct()); |
| |
| // Reset our sessions, so other tests get the usual PASE session |
| ExpireSessionBobToAlice(); |
| ExpireSessionAliceToBob(); |
| err = CreateSessionBobToAlice(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| err = CreateSessionAliceToBob(); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| |
| err = GetExchangeManager().UnregisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest); |
| EXPECT_EQ(err, CHIP_NO_ERROR); |
| } |
| |
| /** |
| * TODO: A test that we should have but can't write with the existing |
| * infrastructure we have: |
| * |
| * 1. A sends message 1 to B |
| * 2. B is slow to respond, A does a resend and the resend is delayed in the network. |
| * 3. B responds with message 2, which acks message 1. |
| * 4. A sends message 3 to B |
| * 5. B sends standalone ack to message 3, which is lost |
| * 6. The duplicate message from step 3 is delivered and triggers a standalone ack. |
| * 7. B responds with message 4, which should carry a piggyback ack for message 3 |
| * (this is the part that needs testing!) |
| * 8. A sends message 5 to B. |
| */ |