Skip to content

Commit

Permalink
Adds SubscriptionInfoProvider API to check if a given fabric has at l…
Browse files Browse the repository at this point in the history
…east 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 <[email protected]>

---------

Co-authored-by: Restyled.io <[email protected]>
Co-authored-by: lpbeliveau-silabs <[email protected]>
  • Loading branch information
3 people authored Nov 27, 2024
1 parent e98f2b2 commit cb1f4b0
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions src/app/SubscriptionsInfoProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/app/icd/server/tests/TestICDManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
121 changes: 121 additions & 0 deletions src/app/tests/TestInteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class TestInteractionModelEngine : public chip::Test::AppContext
void TestSubjectHasActiveSubscriptionSubWithCAT();
void TestSubscriptionResumptionTimer();
void TestDecrementNumSubscriptionsToResume();
void TestFabricHasAtLeastOneActiveSubscription();
void TestFabricHasAtLeastOneActiveSubscriptionWithMixedStates();
static int GetAttributePathListLength(SingleLinkedListNode<AttributePathParams> * apattributePathParamsList);
};

Expand Down Expand Up @@ -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

0 comments on commit cb1f4b0

Please sign in to comment.