From 1942b8c442a1a039924f030cd8c72ed4b383b438 Mon Sep 17 00:00:00 2001 From: Terence Hampson Date: Thu, 6 Feb 2025 17:40:30 +0000 Subject: [PATCH 1/2] Adding MRP analytics delegate This is a starting point for MRP events to be sent to some sort of delegate interested. The design is intentionally done in this way to reduce code size within the SDK and is meant for applications such as a controller to registers a delegate for MRP events allowing for it to construct analytics. --- src/messaging/BUILD.gn | 1 + .../ReliableMessageAnalyticsDelegate.h | 56 +++ src/messaging/ReliableMessageMgr.cpp | 51 +++ src/messaging/ReliableMessageMgr.h | 9 + .../tests/TestReliableMessageProtocol.cpp | 324 ++++++++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 src/messaging/ReliableMessageAnalyticsDelegate.h diff --git a/src/messaging/BUILD.gn b/src/messaging/BUILD.gn index a170d625111a06..8e94a2b0d0b5e7 100644 --- a/src/messaging/BUILD.gn +++ b/src/messaging/BUILD.gn @@ -63,6 +63,7 @@ static_library("messaging") { "ExchangeMgr.cpp", "ExchangeMgr.h", "Flags.h", + "ReliableMessageAnalyticsDelegate.h", "ReliableMessageContext.cpp", "ReliableMessageContext.h", "ReliableMessageMgr.cpp", diff --git a/src/messaging/ReliableMessageAnalyticsDelegate.h b/src/messaging/ReliableMessageAnalyticsDelegate.h new file mode 100644 index 00000000000000..49fad476164991 --- /dev/null +++ b/src/messaging/ReliableMessageAnalyticsDelegate.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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 defines interface for objects interested in MRP events for analytics + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Messaging { + +class ReliableMessageAnalyticsDelegate +{ +public: + virtual ~ReliableMessageAnalyticsDelegate() = default; + enum class EventType + { + kInitialSend, + kRetransmission, + kAcknowledged, + kFailed, + kUndefined, /* Should be last element in enum */ + }; + + struct TransmitEvent + { + NodeId nodeId = kUndefinedNodeId; + FabricIndex fabricIndex = kUndefinedFabricIndex; + EventType eventType = EventType::kUndefined; + uint32_t messageCounter = 0; + }; + + virtual void OnTransmitEvent(const TransmitEvent & event) = 0; +}; + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/ReliableMessageMgr.cpp b/src/messaging/ReliableMessageMgr.cpp index 119adeef876972..6c366106089fe5 100644 --- a/src/messaging/ReliableMessageMgr.cpp +++ b/src/messaging/ReliableMessageMgr.cpp @@ -155,6 +155,17 @@ void ReliableMessageMgr::ExecuteActions() Transport::GetSessionTypeString(session), fabricIndex, ChipLogValueX64(destination), CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS); + if (mAnalyticsDelegate) + { + ReliableMessageAnalyticsDelegate::TransmitEvent event = { .nodeId = destination, + .fabricIndex = fabricIndex, + .eventType = + ReliableMessageAnalyticsDelegate::EventType::kFailed, + .messageCounter = messageCounter }; + + mAnalyticsDelegate->OnTransmitEvent(event); + } + // If the exchange is expecting a response, it will handle sending // this notification once it detects that it has not gotten a // response. Otherwise, we need to do it. @@ -295,6 +306,25 @@ bool ReliableMessageMgr::CheckAndRemRetransTable(ReliableMessageContext * rc, ui mRetransTable.ForEachActiveObject([&](auto * entry) { if (entry->ec->GetReliableMessageContext() == rc && entry->retainedBuf.GetMessageCounter() == ackMessageCounter) { + if (mAnalyticsDelegate) + { + auto session = entry->ec->GetSessionHandle(); + auto fabricIndex = session->GetFabricIndex(); + auto destination = kUndefinedNodeId; + if (session->IsSecureSession()) + { + destination = session->AsSecureSession()->GetPeerNodeId(); + } + ReliableMessageAnalyticsDelegate::TransmitEvent event = { + .nodeId = destination, + .fabricIndex = fabricIndex, + .eventType = ReliableMessageAnalyticsDelegate::EventType::kAcknowledged, + .messageCounter = ackMessageCounter + }; + + mAnalyticsDelegate->OnTransmitEvent(event); + } + // Clear the entry from the retransmision table. ClearRetransTable(*entry); @@ -440,6 +470,11 @@ void ReliableMessageMgr::RegisterSessionUpdateDelegate(SessionUpdateDelegate * s mSessionUpdateDelegate = sessionUpdateDelegate; } +void ReliableMessageMgr::RegisterAnalyticsDelegate(ReliableMessageAnalyticsDelegate * analyticsDelegate) +{ + mAnalyticsDelegate = analyticsDelegate; +} + CHIP_ERROR ReliableMessageMgr::MapSendError(CHIP_ERROR error, uint16_t exchangeId, bool isInitiator) { if ( @@ -511,6 +546,22 @@ void ReliableMessageMgr::CalculateNextRetransTime(RetransTableEntry & entry) peerIsActive = sessionHandle->AsUnauthenticatedSession()->IsPeerActive(); } + // For initial send the packet has already been submitted to transport layer successfully. + // On re-transmits we do not know if transport layer is unable to retransmit for some + // reason, so saying we have sent re-transmit here is a little presumptuous. + if (mAnalyticsDelegate) + { + ReliableMessageAnalyticsDelegate::TransmitEvent event = { + .nodeId = destination, + .fabricIndex = fabricIndex, + .eventType = entry.sendCount == 0 ? ReliableMessageAnalyticsDelegate::EventType::kInitialSend + : ReliableMessageAnalyticsDelegate::EventType::kRetransmission, + .messageCounter = messageCounter + }; + + mAnalyticsDelegate->OnTransmitEvent(event); + } + ChipLogProgress(ExchangeManager, "??%d [E:" ChipLogFormatExchange " S:%u M:" ChipLogFormatMessageCounter "] (%s) Msg Retransmission to %u:" ChipLogFormatX64 " in %" PRIu32 "ms [State:%s II:%" PRIu32 " AI:%" PRIu32 diff --git a/src/messaging/ReliableMessageMgr.h b/src/messaging/ReliableMessageMgr.h index 5036b832108443..2875518f0acd19 100644 --- a/src/messaging/ReliableMessageMgr.h +++ b/src/messaging/ReliableMessageMgr.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,13 @@ class ReliableMessageMgr */ void RegisterSessionUpdateDelegate(SessionUpdateDelegate * sessionUpdateDelegate); + /** + * Registers a delegate interested in analytic information + * + * @param[in] analyticsDelegate - Pointer to delegate for reporting analytic + */ + void RegisterAnalyticsDelegate(ReliableMessageAnalyticsDelegate * analyticsDelegate); + /** * Map a send error code to the error code we should actually use for * success checks. This maps some error codes to CHIP_NO_ERROR as @@ -249,6 +257,7 @@ class ReliableMessageMgr ObjectPool mRetransTable; SessionUpdateDelegate * mSessionUpdateDelegate = nullptr; + ReliableMessageAnalyticsDelegate * mAnalyticsDelegate = nullptr; static System::Clock::Timeout sAdditionalMRPBackoffTime; }; diff --git a/src/messaging/tests/TestReliableMessageProtocol.cpp b/src/messaging/tests/TestReliableMessageProtocol.cpp index 6390e4eca1920e..7f1a7be7f15fad 100644 --- a/src/messaging/tests/TestReliableMessageProtocol.cpp +++ b/src/messaging/tests/TestReliableMessageProtocol.cpp @@ -21,6 +21,8 @@ * This file implements unit tests for the ReliableMessageProtocol * implementation. */ +#include + #include #include @@ -61,6 +63,13 @@ using namespace chip::System::Clock::Literals; const char PAYLOAD[] = "Hello!"; +class TestReliablityAnalyticDelegate : public ReliableMessageAnalyticsDelegate +{ +public: + virtual void OnTransmitEvent(const TransmitEvent & event) override { mTransmitEvents.push(event); } + std::queue mTransmitEvents; +}; + class TestReliableMessageProtocol : public chip::Test::LoopbackMessagingContext { public: @@ -2043,6 +2052,321 @@ TEST_F(TestReliableMessageProtocol, CheckApplicationResponseNeverComes) EXPECT_EQ(err, CHIP_NO_ERROR); } +TEST_F(TestReliableMessageProtocol, CheckReliableMessageAnalyticsForTransmitEventualSuccess) +{ + chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); + 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); + TestReliablityAnalyticDelegate testAnalyticsDelegate; + rm->RegisterAnalyticsDelegate(&testAnalyticsDelegate); + + exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ + 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL + 30_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + })); + + const auto expectedFabricIndex = exchange->GetSessionHandle()->GetFabricIndex(); + const auto expectedNodeId = exchange->GetSessionHandle()->AsSecureSession()->GetPeerNodeId(); + + // 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); + + err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer)); + 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; }); + 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); + + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + 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); + + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + 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); + + // Trigger final transmission + GetIOContext().DriveIOUntil(1500_ms32, [&] { return loopback.mSentMessageCount >= 5; }); + 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); + + ASSERT_EQ(testAnalyticsDelegate.mTransmitEvents.size(), 6u); + auto firstTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(firstTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(firstTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(firstTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kInitialSend); + // We have no way of validating the first messageCounter since this is a randomly generated value, but it should + // remain constant for all subsequent transmit events in this test. + const uint32_t messageCounter = firstTransmitEvent.messageCounter; + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto secondTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(secondTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(secondTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(secondTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, secondTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto thirdTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(thirdTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(thirdTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(thirdTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, thirdTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto forthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(forthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(forthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(forthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, forthTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto fifthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(fifthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(fifthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(fifthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, fifthTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto sixthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(sixthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(sixthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(sixthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kAcknowledged); + EXPECT_EQ(messageCounter, sixthTransmitEvent.messageCounter); +} + +TEST_F(TestReliableMessageProtocol, CheckReliableMessageAnalyticsForTransmitFailure) +{ + chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); + 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); + TestReliablityAnalyticDelegate testAnalyticsDelegate; + rm->RegisterAnalyticsDelegate(&testAnalyticsDelegate); + + exchange->GetSessionHandle()->AsSecureSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ + 30_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL + 30_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + })); + + const auto expectedFabricIndex = exchange->GetSessionHandle()->GetFabricIndex(); + const auto expectedNodeId = exchange->GetSessionHandle()->AsSecureSession()->GetPeerNodeId(); + + // Let's drop the initial message + auto & loopback = GetLoopback(); + loopback.mSentMessageCount = 0; + loopback.mNumMessagesToDrop = 5; + 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 initial message was dropped and was added to retransmit table + EXPECT_EQ(loopback.mNumMessagesToDrop, 4u); + 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; }); + DrainAndServiceIO(); + + // Ensure the 1st retry was dropped, and is still there in the retransmit table + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 3u); + EXPECT_EQ(loopback.mDroppedMessageCount, 2u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 3; }); + DrainAndServiceIO(); + + // Ensure the 2nd retry was dropped, and is still there in the retransmit table + EXPECT_EQ(loopback.mSentMessageCount, 3u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 3u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + + GetIOContext().DriveIOUntil(1000_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + DrainAndServiceIO(); + + // Ensure the 3rd retry was dropped, and is still there in the retransmit table + EXPECT_EQ(loopback.mSentMessageCount, 4u); + EXPECT_EQ(loopback.mNumMessagesToDrop, 1u); + EXPECT_EQ(loopback.mDroppedMessageCount, 4u); + EXPECT_EQ(rm->TestGetCountRetransTable(), 1); + + // Trigger final transmission + GetIOContext().DriveIOUntil(1500_ms32, [&] { return loopback.mSentMessageCount >= 5; }); + 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(rm->TestGetCountRetransTable(), 1); + EXPECT_EQ(loopback.mDroppedMessageCount, 5u); + + // Now wait for our exchange to time out. + GetIOContext().DriveIOUntil(3000_ms32, [&] { return rm->TestGetCountRetransTable() == 0; }); + DrainAndServiceIO(); + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); + + ASSERT_EQ(testAnalyticsDelegate.mTransmitEvents.size(), 6u); + auto firstTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(firstTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(firstTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(firstTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kInitialSend); + // We have no way of validating the first messageCounter since this is a randomly generated value, but it should + // remain constant for all subsequent transmit events in this test. + const uint32_t messageCounter = firstTransmitEvent.messageCounter; + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto secondTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(secondTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(secondTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(secondTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, secondTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto thirdTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(thirdTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(thirdTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(thirdTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, thirdTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto forthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(forthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(forthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(forthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, forthTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto fifthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(fifthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(fifthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(fifthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kRetransmission); + EXPECT_EQ(messageCounter, fifthTransmitEvent.messageCounter); + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto sixthTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(sixthTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(sixthTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(sixthTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kFailed); + EXPECT_EQ(messageCounter, sixthTransmitEvent.messageCounter); +} + +TEST_F(TestReliableMessageProtocol, CheckReliableMessageAnalyticsForTransmitUnauthenticatedExchange) +{ + 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); + + MockSessionEstablishmentDelegate mockSender; + ExchangeContext * exchange = NewUnauthenticatedExchangeToAlice(&mockSender); + ASSERT_NE(exchange, nullptr); + + ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr(); + ASSERT_NE(rm, nullptr); + + TestReliablityAnalyticDelegate testAnalyticsDelegate; + rm->RegisterAnalyticsDelegate(&testAnalyticsDelegate); + + exchange->GetSessionHandle()->AsUnauthenticatedSession()->SetRemoteSessionParameters(ReliableMessageProtocolConfig({ + 64_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL + 64_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + })); + + const auto expectedFabricIndex = exchange->GetSessionHandle()->GetFabricIndex(); + const auto expectedNodeId = exchange->GetSessionHandle()->AsUnauthenticatedSession()->GetPeerNodeId(); + + auto & loopback = GetLoopback(); + loopback.mSentMessageCount = 0; + loopback.mNumMessagesToDrop = 0; + 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(); + + // Test that the message was actually sent (and not dropped) + EXPECT_EQ(loopback.mSentMessageCount, 2u); + EXPECT_EQ(loopback.mDroppedMessageCount, 0u); + + EXPECT_EQ(rm->TestGetCountRetransTable(), 0); + + ASSERT_EQ(testAnalyticsDelegate.mTransmitEvents.size(), 2u); + auto firstTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(firstTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(firstTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(firstTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kInitialSend); + // We have no way of validating the first messageCounter since this is a randomly generated value, but it should + // remain constant for all subsequent transmit events in this test. + const uint32_t messageCounter = firstTransmitEvent.messageCounter; + + testAnalyticsDelegate.mTransmitEvents.pop(); + auto secondTransmitEvent = testAnalyticsDelegate.mTransmitEvents.front(); + EXPECT_EQ(secondTransmitEvent.nodeId, expectedNodeId); + EXPECT_EQ(secondTransmitEvent.fabricIndex, expectedFabricIndex); + EXPECT_EQ(secondTransmitEvent.eventType, ReliableMessageAnalyticsDelegate::EventType::kAcknowledged); + EXPECT_EQ(messageCounter, secondTransmitEvent.messageCounter); +} + /** * TODO: A test that we should have but can't write with the existing * infrastructure we have: From 2c393c939d63cb1716229b42ce22237bce777f90 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 6 Feb 2025 20:52:35 +0000 Subject: [PATCH 2/2] Restyled by clang-format --- src/messaging/ReliableMessageMgr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messaging/ReliableMessageMgr.h b/src/messaging/ReliableMessageMgr.h index 2875518f0acd19..0227377eec7f5d 100644 --- a/src/messaging/ReliableMessageMgr.h +++ b/src/messaging/ReliableMessageMgr.h @@ -256,7 +256,7 @@ class ReliableMessageMgr // ReliableMessageProtocol Global tables for timer context ObjectPool mRetransTable; - SessionUpdateDelegate * mSessionUpdateDelegate = nullptr; + SessionUpdateDelegate * mSessionUpdateDelegate = nullptr; ReliableMessageAnalyticsDelegate * mAnalyticsDelegate = nullptr; static System::Clock::Timeout sAdditionalMRPBackoffTime;