From cb1f4b0432baf96de7a4cdef4965cd1e08c720c4 Mon Sep 17 00:00:00 2001 From: Mathieu Kardous <84793247+mkardous-silabs@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:59:59 -0500 Subject: [PATCH] Adds SubscriptionInfoProvider API to check if a given fabric has at least 1 subscription (#36627) * [SL-UP] Add fabric subscription check to the interaction model engine (#117) * Remove unwanted change * Restyle * Improve readability * Restyled by clang-format * Update src/app/SubscriptionsInfoProvider.h Co-authored-by: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> --------- Co-authored-by: Restyled.io Co-authored-by: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> --- src/app/InteractionModelEngine.cpp | 22 ++++ src/app/InteractionModelEngine.h | 2 + src/app/SubscriptionsInfoProvider.h | 10 ++ src/app/icd/server/tests/TestICDManager.cpp | 1 + src/app/tests/TestInteractionModelEngine.cpp | 121 +++++++++++++++++++ 5 files changed, 156 insertions(+) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index 9b76dd31bf..56acd09661 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -462,6 +462,28 @@ bool InteractionModelEngine::SubjectHasPersistedSubscription(FabricIndex aFabric return persistedSubMatches; } +bool InteractionModelEngine::FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) +{ + bool hasActiveSubscription = false; + mReadHandlers.ForEachActiveObject([aFabricIndex, &hasActiveSubscription](ReadHandler * handler) { + VerifyOrReturnValue(handler->IsType(ReadHandler::InteractionType::Subscribe), Loop::Continue); + + Access::SubjectDescriptor subject = handler->GetSubjectDescriptor(); + VerifyOrReturnValue(subject.fabricIndex == aFabricIndex, Loop::Continue); + + if ((subject.authMode == Access::AuthMode::kCase) && handler->IsActiveSubscription()) + { + // On first subscription found for fabric, we can immediately stop checking. + hasActiveSubscription = true; + return Loop::Break; + } + + return Loop::Continue; + }); + + return hasActiveSubscription; +} + void InteractionModelEngine::OnDone(CommandResponseSender & apResponderObj) { mCommandResponderObjs.ReleaseObject(&apResponderObj); diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 3fa379129a..36aa2ccc6e 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -315,6 +315,8 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) override; + bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) override; + #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * @brief Function decrements the number of subscriptions to resume counter - mNumOfSubscriptionsToResume. diff --git a/src/app/SubscriptionsInfoProvider.h b/src/app/SubscriptionsInfoProvider.h index cb8d470cbb..6fe383e1ff 100644 --- a/src/app/SubscriptionsInfoProvider.h +++ b/src/app/SubscriptionsInfoProvider.h @@ -61,6 +61,16 @@ class SubscriptionsInfoProvider * false subject doesn't have any persisted subscription with the device */ virtual bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subjectID) = 0; + + /** + * @brief Check if a given fabric has at least 1 active subscription. + * + * @param aFabricIndex fabric index for which we want to validate is has at least one active subscription + * + * @return true fabric has at least one active subscription + * false fabric doesn't have any active subscription or failed to validate + */ + virtual bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) = 0; }; } // namespace app diff --git a/src/app/icd/server/tests/TestICDManager.cpp b/src/app/icd/server/tests/TestICDManager.cpp index f6f999c1a3..4d0791c714 100644 --- a/src/app/icd/server/tests/TestICDManager.cpp +++ b/src/app/icd/server/tests/TestICDManager.cpp @@ -122,6 +122,7 @@ class TestSubscriptionsInfoProvider : public SubscriptionsInfoProvider bool SubjectHasActiveSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasActiveSubscription; }; bool SubjectHasPersistedSubscription(FabricIndex aFabricIndex, NodeId subject) { return mHasPersistedSubscription; }; + bool FabricHasAtLeastOneActiveSubscription(FabricIndex aFabricIndex) { return false; }; private: bool mHasActiveSubscription = false; diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index acc19c12f4..88b3c07a6d 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -68,6 +68,8 @@ class TestInteractionModelEngine : public chip::Test::AppContext void TestSubjectHasActiveSubscriptionSubWithCAT(); void TestSubscriptionResumptionTimer(); void TestDecrementNumSubscriptionsToResume(); + void TestFabricHasAtLeastOneActiveSubscription(); + void TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates(); static int GetAttributePathListLength(SingleLinkedListNode * apattributePathParamsList); }; @@ -720,5 +722,124 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestDecrementNumSubscriptionsToR } #endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS +TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestFabricHasAtLeastOneActiveSubscription) +{ + NullReadHandlerCallback nullCallback; + InteractionModelEngine * engine = InteractionModelEngine::GetInstance(); + + FabricIndex fabricIndex1 = 1; + FabricIndex fabricIndex2 = 2; + + // Create ExchangeContexts + Messaging::ExchangeContext * exchangeCtx1 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx1); + + Messaging::ExchangeContext * exchangeCtx2 = NewExchangeToAlice(nullptr, false); + ASSERT_TRUE(exchangeCtx2); + + // InteractionModelEngine init + EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler())); + + // Verify that both fabrics have no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Create and setup readHandler 1 + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that fabric 1 still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + + // Set readHandler1 to active + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that fabric 1 has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + + // Verify that fabric 2 still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Create and setup readHandler 2 + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Set readHandler2 to active + readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that fabric 2 has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); + + // Clean up read handlers + engine->GetReadHandlerPool().ReleaseAll(); + + // Verify that both fabrics have no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex1)); + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex2)); +} + +TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates) +{ + NullReadHandlerCallback nullCallback; + InteractionModelEngine * engine = InteractionModelEngine::GetInstance(); + + FabricIndex fabricIndex = 1; + + // Create ExchangeContexts + Messaging::ExchangeContext * exchangeCtx1 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx1); + + Messaging::ExchangeContext * exchangeCtx2 = NewExchangeToBob(nullptr, false); + ASSERT_TRUE(exchangeCtx2); + + // InteractionModelEngine init + EXPECT_EQ(CHIP_NO_ERROR, engine->Init(&GetExchangeManager(), &GetFabricTable(), reporting::GetDefaultReportScheduler())); + + // Verify that the fabric has no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Create and setup readHandler 1 + ReadHandler * readHandler1 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that the fabric still doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler1 to active + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, true); + + // Verify that the fabric has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Create and setup readHandler 2 + ReadHandler * readHandler2 = + engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, + reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); + + // Verify that the fabric still has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler2 to inactive + readHandler2->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false); + + // Verify that the fabric still has an active subscription + EXPECT_TRUE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Set readHandler1 to inactive + readHandler1->SetStateFlag(ReadHandler::ReadHandlerFlags::ActiveSubscription, false); + + // Verify that the fabric doesn't have an active subscription + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); + + // Clean up read handlers + engine->GetReadHandlerPool().ReleaseAll(); + + // Verify that the fabric has no active subscriptions + EXPECT_FALSE(engine->FabricHasAtLeastOneActiveSubscription(fabricIndex)); +} + } // namespace app } // namespace chip