From d2ebe299de8519ee102faf2310b5f5d42d9ebb9b Mon Sep 17 00:00:00 2001 From: Mathieu Kardous Date: Wed, 20 Nov 2024 16:56:16 -0500 Subject: [PATCH 1/3] Add fabric subscription check --- src/app/InteractionModelEngine.cpp | 24 +++++++- src/app/InteractionModelEngine.h | 2 + src/app/SubscriptionsInfoProvider.h | 12 +++- src/app/tests/TestInteractionModelEngine.cpp | 59 ++++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index b122333ac2..c33ebeca4e 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -61,7 +61,8 @@ namespace app { class AutoReleaseSubscriptionInfoIterator { public: - AutoReleaseSubscriptionInfoIterator(SubscriptionResumptionStorage::SubscriptionInfoIterator * iterator) : mIterator(iterator){}; + AutoReleaseSubscriptionInfoIterator(SubscriptionResumptionStorage::SubscriptionInfoIterator * iterator) : + mIterator(iterator) {}; ~AutoReleaseSubscriptionInfoIterator() { mIterator->Release(); } SubscriptionResumptionStorage::SubscriptionInfoIterator * operator->() const { return mIterator; } @@ -398,6 +399,27 @@ 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) + { + hasActiveSubscription = handler->IsActiveSubscription(); + VerifyOrReturnValue(!hasActiveSubscription, 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 ba0a099bb7..072d323650 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -314,6 +314,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..6d04850ed3 100644 --- a/src/app/SubscriptionsInfoProvider.h +++ b/src/app/SubscriptionsInfoProvider.h @@ -35,7 +35,7 @@ namespace app { class SubscriptionsInfoProvider { public: - virtual ~SubscriptionsInfoProvider(){}; + virtual ~SubscriptionsInfoProvider() {}; /** * @brief Check if a given subject (CAT or operational NodeId) has at least 1 active subscription. @@ -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 + * @return 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/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index 0a4a8bd5af..e06cbca4a7 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -68,6 +68,7 @@ class TestInteractionModelEngine : public chip::Test::AppContext void TestSubjectHasActiveSubscriptionSubWithCAT(); void TestSubscriptionResumptionTimer(); void TestDecrementNumSubscriptionsToResume(); + void TestFabricHasAtLeastOneActiveSubscription(); static int GetAttributePathListLength(SingleLinkedListNode * apattributePathParamsList); }; @@ -710,5 +711,63 @@ 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)); +} + } // namespace app } // namespace chip From cebc8af24626e6e18f25766d248966201fe2ae04 Mon Sep 17 00:00:00 2001 From: Mathieu Kardous Date: Wed, 20 Nov 2024 17:05:38 -0500 Subject: [PATCH 2/3] Single fabric unit tests --- src/app/tests/TestInteractionModelEngine.cpp | 72 ++++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/src/app/tests/TestInteractionModelEngine.cpp b/src/app/tests/TestInteractionModelEngine.cpp index e06cbca4a7..0809c565b6 100644 --- a/src/app/tests/TestInteractionModelEngine.cpp +++ b/src/app/tests/TestInteractionModelEngine.cpp @@ -69,6 +69,7 @@ class TestInteractionModelEngine : public chip::Test::AppContext void TestSubscriptionResumptionTimer(); void TestDecrementNumSubscriptionsToResume(); void TestFabricHasAtLeastOneActiveSubscription(); + void TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates(); static int GetAttributePathListLength(SingleLinkedListNode * apattributePathParamsList); }; @@ -376,11 +377,6 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx1, ReadHandler::InteractionType::Subscribe, reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); - // Create and setup readHandler 2 - ReadHandler * readHandler2 = - engine->GetReadHandlerPool().CreateObject(nullCallback, exchangeCtx2, ReadHandler::InteractionType::Subscribe, - reporting::GetDefaultReportScheduler(), CodegenDataModelProviderInstance()); - // Verify that Bob still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(bobFabricIndex, bobNodeId)); @@ -393,6 +389,11 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestSubjectHasActiveSubscription // Verify that Alice still doesn't have an active subscription EXPECT_FALSE(engine->SubjectHasActiveSubscription(aliceFabricIndex, aliceNodeId)); + // 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); @@ -769,5 +770,66 @@ TEST_F_FROM_FIXTURE(TestInteractionModelEngine, TestFabricHasAtLeastOneActiveSub 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 From 914ee929ed2207487bee14fc4be18c91ed739385 Mon Sep 17 00:00:00 2001 From: Mathieu Kardous Date: Wed, 20 Nov 2024 17:19:17 -0500 Subject: [PATCH 3/3] Add function to stubbed sub info provider in ICDManager tests --- src/app/icd/server/tests/TestICDManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/icd/server/tests/TestICDManager.cpp b/src/app/icd/server/tests/TestICDManager.cpp index f6f999c1a3..bdd02d5ca8 100644 --- a/src/app/icd/server/tests/TestICDManager.cpp +++ b/src/app/icd/server/tests/TestICDManager.cpp @@ -115,13 +115,14 @@ class TestSubscriptionsInfoProvider : public SubscriptionsInfoProvider { public: TestSubscriptionsInfoProvider() = default; - ~TestSubscriptionsInfoProvider(){}; + ~TestSubscriptionsInfoProvider() {}; void SetHasActiveSubscription(bool value) { mHasActiveSubscription = value; }; void SetHasPersistedSubscription(bool value) { mHasPersistedSubscription = value; }; 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;