Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SL-UP] Add fabric subscription check to the interaction model engine #117

Merged
merged 3 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 11 additions & 1 deletion src/app/SubscriptionsInfoProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/app/icd/server/tests/TestICDManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
131 changes: 126 additions & 5 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 @@ -375,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));

Expand All @@ -392,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);

Expand Down Expand Up @@ -710,5 +712,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
Loading