Add a test for a subscription trying to send reports on a defunct session. (#24113)
This is testing the situation described in
https://github.com/project-chip/connectedhomeip/pull/24093
diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp
index af2e7a1..3333e97 100644
--- a/src/app/tests/TestReadInteraction.cpp
+++ b/src/app/tests/TestReadInteraction.cpp
@@ -339,6 +339,7 @@
static void TestReadHandlerInvalidSubscribeRequest(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeInvalidateFabric(nlTestSuite * apSuite, void * apContext);
static void TestShutdownSubscription(nlTestSuite * apSuite, void * apContext);
+ static void TestSubscriptionReportWithDefunctSession(nlTestSuite * apSuite, void * apContext);
static void TestReadHandlerMalformedSubscribeRequest(nlTestSuite * apSuite, void * apContext);
private:
@@ -4148,6 +4149,92 @@
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}
+/**
+ * Tests what happens when a subscription tries to deliver reports but the
+ * session it has is defunct. Makes sure we correctly tear down the ReadHandler
+ * and don't increment the "reports in flight" count.
+ */
+void TestReadInteraction::TestSubscriptionReportWithDefunctSession(nlTestSuite * apSuite, void * apContext)
+{
+ TestContext & ctx = *static_cast<TestContext *>(apContext);
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);
+
+ MockInteractionModelApp delegate;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable());
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ AttributePathParams subscribePath(Test::kMockEndpoint3, Test::MockClusterId(2), Test::MockAttributeId(4));
+
+ ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
+ readPrepareParams.mpAttributePathParamsList = &subscribePath;
+ readPrepareParams.mAttributePathParamsListSize = 1;
+
+ readPrepareParams.mMinIntervalFloorSeconds = 0;
+ readPrepareParams.mMaxIntervalCeilingSeconds = 0;
+
+ {
+ app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
+ chip::app::ReadClient::InteractionType::Subscribe);
+
+ delegate.mGotReport = false;
+
+ err = readClient.SendSubscribeRequest(std::move(readPrepareParams));
+ NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
+
+ ctx.DrainAndServiceIO();
+
+ NL_TEST_ASSERT(apSuite, delegate.mGotReport);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 1);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Read) == 0);
+ NL_TEST_ASSERT(apSuite, engine->GetReportingEngine().GetNumReportsInFlight() == 0);
+
+ NL_TEST_ASSERT(apSuite, engine->ActiveHandlerAt(0) != nullptr);
+ auto * readHandler = engine->ActiveHandlerAt(0);
+
+ // Verify that the session we will reset later is the one we will mess
+ // with now.
+ NL_TEST_ASSERT(apSuite, SessionHandle(*readHandler->GetSession()) == ctx.GetSessionAliceToBob());
+
+ // Test that we send reports as needed.
+ readHandler->mFlags.Set(ReadHandler::ReadHandlerFlags::HoldReport, false);
+ delegate.mGotReport = false;
+ engine->GetReportingEngine().SetDirty(subscribePath);
+
+ ctx.DrainAndServiceIO();
+
+ NL_TEST_ASSERT(apSuite, delegate.mGotReport);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 1);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Read) == 0);
+ NL_TEST_ASSERT(apSuite, engine->GetReportingEngine().GetNumReportsInFlight() == 0);
+
+ // Test that if the session is defunct we don't send reports and clean
+ // up properly.
+ readHandler->GetSession()->MarkAsDefunct();
+ readHandler->mFlags.Set(ReadHandler::ReadHandlerFlags::HoldReport, false);
+ delegate.mGotReport = false;
+ engine->GetReportingEngine().SetDirty(subscribePath);
+
+ ctx.DrainAndServiceIO();
+
+ NL_TEST_ASSERT(apSuite, !delegate.mGotReport);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 0);
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Read) == 0);
+ NL_TEST_ASSERT(apSuite, engine->GetReportingEngine().GetNumReportsInFlight() == 0);
+ }
+ engine->Shutdown();
+ NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0);
+ NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
+
+ // Get rid of our defunct session.
+ ctx.ExpireSessionAliceToBob();
+ ctx.CreateSessionAliceToBob();
+}
+
} // namespace app
} // namespace chip
@@ -4212,6 +4299,7 @@
NL_TEST_DEF("TestPostSubscribeRoundtripChunkStatusReportTimeout", chip::app::TestReadInteraction::TestPostSubscribeRoundtripChunkStatusReportTimeout),
NL_TEST_DEF("TestPostSubscribeRoundtripChunkReportTimeout", chip::app::TestReadInteraction::TestPostSubscribeRoundtripChunkReportTimeout),
NL_TEST_DEF("TestReadShutdown", chip::app::TestReadInteraction::TestReadShutdown),
+ NL_TEST_DEF("TestSubscriptionReportWithDefunctSession", chip::app::TestReadInteraction::TestSubscriptionReportWithDefunctSession),
NL_TEST_SENTINEL()
};
// clang-format on