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

[Test] Add multi-fabric test for 15831 #17729

Merged
merged 2 commits into from
May 5, 2022
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
16 changes: 16 additions & 0 deletions src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ uint32_t InteractionModelEngine::GetNumActiveReadHandlers(ReadHandler::Interacti
return count;
}

uint32_t InteractionModelEngine::GetNumActiveReadHandlers(ReadHandler::InteractionType aType, FabricIndex aFabricIndex) const
{
uint32_t count = 0;

mReadHandlers.ForEachActiveObject([aType, aFabricIndex, &count](const ReadHandler * handler) {
if (handler->IsType(aType) && handler->GetAccessingFabricIndex() == aFabricIndex)
{
count++;
}

return Loop::Continue;
});

return count;
}

ReadHandler * InteractionModelEngine::ActiveHandlerAt(unsigned int aIndex)
{
if (aIndex >= mReadHandlers.Allocated())
Expand Down
5 changes: 5 additions & 0 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
uint32_t GetNumActiveReadHandlers() const;
uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type) const;

/**
* Returns the number of active readhandlers with a specific type on a specific fabric.
*/
uint32_t GetNumActiveReadHandlers(ReadHandler::InteractionType type, FabricIndex fabricIndex) const;
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved

uint32_t GetNumActiveWriteHandlers() const;

/**
Expand Down
4 changes: 4 additions & 0 deletions src/app/reporting/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class Engine

void ScheduleUrgentEventDeliverySync();

#if CONFIG_IM_BUILD_FOR_UNIT_TEST
size_t GetGlobalDirtySetSize() { return mGlobalDirtySet.Allocated(); }
#endif

private:
friend class TestReportingEngine;

Expand Down
120 changes: 90 additions & 30 deletions src/controller/tests/data_model/TestRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1517,11 +1517,9 @@ class TestReadCallback : public app::ReadClient::Callback
CHIP_ERROR mLastError = CHIP_NO_ERROR;
};

void EstablishSubscriptions(nlTestSuite * apSuite, void * apContext, int32_t numSubs, int32_t pathPerSub,
void EstablishSubscriptions(nlTestSuite * apSuite, SessionHandle sessionHandle, int32_t numSubs, int32_t pathPerSub,
app::ReadClient::Callback * callback, std::vector<std::unique_ptr<app::ReadClient>> & readClients)
{
TestContext & ctx = *static_cast<TestContext *>(apContext);
auto sessionHandle = ctx.GetSessionBobToAlice();
std::vector<app::AttributePathParams> attributePaths(
pathPerSub, app::AttributePathParams(kTestEndpointId, TestCluster::Id, TestCluster::Attributes::Int16u::Id));

Expand All @@ -1533,17 +1531,16 @@ void EstablishSubscriptions(nlTestSuite * apSuite, void * apContext, int32_t num

for (int32_t i = 0; i < numSubs; i++)
{
std::unique_ptr<app::ReadClient> readClient =
std::make_unique<app::ReadClient>(app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), *callback,
app::ReadClient::InteractionType::Subscribe);
std::unique_ptr<app::ReadClient> readClient = std::make_unique<app::ReadClient>(
app::InteractionModelEngine::GetInstance(), app::InteractionModelEngine::GetInstance()->GetExchangeManager(), *callback,
app::ReadClient::InteractionType::Subscribe);
NL_TEST_ASSERT(apSuite, readClient->SendRequest(readParams) == CHIP_NO_ERROR);
readClients.push_back(std::move(readClient));
}
}

} // namespace SubscriptionPathQuotaHelpers

// TODO: (#17381) Need to add the case with more than one fabrics.
void TestReadInteraction::TestReadHandler_KillOverQuotaSubscriptions(nlTestSuite * apSuite, void * apContext)
{
using namespace SubscriptionPathQuotaHelpers;
Expand All @@ -1557,24 +1554,26 @@ void TestReadInteraction::TestReadHandler_KillOverQuotaSubscriptions(nlTestSuite
app::InteractionModelEngine::GetInstance()->RegisterReadHandlerAppCallback(&gTestReadInteraction);

TestReadCallback readCallback;
TestReadCallback readCallbackFabric2;
std::vector<std::unique_ptr<app::ReadClient>> readClients;

// Intentially establish subscriptions using exceeded resources.
app::InteractionModelEngine::GetInstance()->SetForceHandlerQuota(false);
EstablishSubscriptions(apSuite, apContext, kExpectedParallelSubs,
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), 1,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1, &readCallback, readClients);
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), kExpectedParallelSubs,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallback, readClients);

// There are too many messages and the test (gcc_debug, which includes many sanity checks) will be quite slow. Note: report
// engine is using ScheduleWork which cannot be handled by DrainAndServiceIO correctly.
ctx.GetIOContext().DriveIOUntil(System::Clock::Seconds16(60), [&]() {
return readCallback.mOnSubscriptionEstablishedCount == static_cast<size_t>(CHIP_IM_MAX_NUM_READ_HANDLER);
});
ctx.GetIOContext().DriveIOUntil(System::Clock::Seconds16(60),
[&]() { return readCallback.mOnSubscriptionEstablishedCount == kExpectedParallelSubs + 1; });

NL_TEST_ASSERT(apSuite,
readCallback.mAttributeCount ==
kExpectedParallelSubs *
static_cast<int32_t>(app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1));
NL_TEST_ASSERT(apSuite, readCallback.mOnSubscriptionEstablishedCount == kExpectedParallelSubs);
static_cast<int32_t>(kExpectedParallelSubs * app::InteractionModelEngine::kMinSupportedPathsPerSubscription +
app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1));
NL_TEST_ASSERT(apSuite, readCallback.mOnSubscriptionEstablishedCount == kExpectedParallelSubs + 1);

// We have set up the environment for testing the evicting logic.
app::InteractionModelEngine::GetInstance()->SetForceHandlerQuota(true);
Expand All @@ -1585,8 +1584,8 @@ void TestReadInteraction::TestReadHandler_KillOverQuotaSubscriptions(nlTestSuite
{
TestReadCallback callback;
std::vector<std::unique_ptr<app::ReadClient>> outReadClient;
EstablishSubscriptions(apSuite, apContext, 1, app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1, &callback,
outReadClient);
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), 1,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1, &callback, outReadClient);

ctx.DrainAndServiceIO();

Expand All @@ -1597,16 +1596,15 @@ void TestReadInteraction::TestReadHandler_KillOverQuotaSubscriptions(nlTestSuite

// The following check will trigger the logic in im to kill the read handlers that uses more paths than the limit per fabric.
{
TestReadCallback callback;
std::vector<std::unique_ptr<app::ReadClient>> outReadClient;
EstablishSubscriptions(apSuite, apContext, 1, app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &callback,
outReadClient);
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), 1,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallback, readClients);

readCallback.ClearCounters();
ctx.DrainAndServiceIO();

// This read handler should evict some existing subscriptions for enough space
NL_TEST_ASSERT(apSuite, callback.mOnSubscriptionEstablishedCount == 1);
NL_TEST_ASSERT(apSuite, callback.mAttributeCount == app::InteractionModelEngine::kMinSupportedPathsPerSubscription);
NL_TEST_ASSERT(apSuite, readCallback.mOnSubscriptionEstablishedCount == 1);
NL_TEST_ASSERT(apSuite, readCallback.mAttributeCount == app::InteractionModelEngine::kMinSupportedPathsPerSubscription);
}

{
Expand All @@ -1617,12 +1615,74 @@ void TestReadInteraction::TestReadHandler_KillOverQuotaSubscriptions(nlTestSuite
app::InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(path);
}
readCallback.ClearCounters();

ctx.DrainAndServiceIO();
// Ensure the global dirty set is cleared.
NL_TEST_ASSERT(apSuite, app::InteractionModelEngine::GetInstance()->GetReportingEngine().GetGlobalDirtySetSize() == 0);

NL_TEST_ASSERT(apSuite, readCallback.mAttributeCount <= kExpectedParallelPaths);
// We should evict the subscriptions with excess resources, so we should use exactly all resources.
NL_TEST_ASSERT(apSuite, readCallback.mAttributeCount == kExpectedParallelPaths);
NL_TEST_ASSERT(apSuite,
app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() <=
static_cast<size_t>(kExpectedParallelSubs));
app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() ==
static_cast<uint32_t>(kExpectedParallelSubs));

NL_TEST_ASSERT(apSuite,
app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers(
app::ReadHandler::InteractionType::Subscribe, ctx.GetAliceFabricIndex()) >
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);

// The following check will trigger the logic in im to kill the read handlers that use more paths than the limit per fabric.
{
EstablishSubscriptions(apSuite, ctx.GetSessionAliceToBob(),
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallbackFabric2, readClients);

ctx.GetIOContext().DriveIOUntil(System::Clock::Seconds16(60),
[&]() { return readCallbackFabric2.mOnSubscriptionEstablishedCount == 1; });

// This read handler should evict some existing subscriptions for enough space
NL_TEST_ASSERT(apSuite,
readCallbackFabric2.mOnSubscriptionEstablishedCount ==
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);
NL_TEST_ASSERT(apSuite,
readCallbackFabric2.mAttributeCount ==
app::InteractionModelEngine::kMinSupportedPathsPerSubscription *
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);
}

{
app::AttributePathParams path;
path.mEndpointId = kTestEndpointId;
path.mClusterId = TestCluster::Id;
path.mAttributeId = TestCluster::Attributes::Int16u::Id;
app::InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(path);
}
readCallback.ClearCounters();
readCallbackFabric2.ClearCounters();

// Run until the global dirtyset is cleared.
ctx.GetIOContext().DriveIOUntil(System::Clock::Seconds16(60), []() {
return app::InteractionModelEngine::GetInstance()->GetReportingEngine().GetGlobalDirtySetSize() == 0 &&
app::InteractionModelEngine::GetInstance()->GetReportingEngine().GetNumReportsInFlight() == 0;
});

// Some subscriptions on fabric 1 should be evicted since fabric 1 is using more resources than the limits.
NL_TEST_ASSERT(apSuite,
readCallback.mAttributeCount ==
app::InteractionModelEngine::kMinSupportedPathsPerSubscription *
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);
NL_TEST_ASSERT(apSuite,
readCallbackFabric2.mAttributeCount ==
app::InteractionModelEngine::kMinSupportedPathsPerSubscription *
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);
NL_TEST_ASSERT(apSuite,
app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers(
app::ReadHandler::InteractionType::Subscribe, ctx.GetAliceFabricIndex()) ==
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);
NL_TEST_ASSERT(apSuite,
app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers(
app::ReadHandler::InteractionType::Subscribe, ctx.GetBobFabricIndex()) ==
app::InteractionModelEngine::kMinSupportedSubscriptionsPerFabric);

app::InteractionModelEngine::GetInstance()->ShutdownActiveReads();
ctx.DrainAndServiceIO();
Expand Down Expand Up @@ -1656,7 +1716,7 @@ void TestReadInteraction::TestReadHandler_KillOldestSubscriptions(nlTestSuite *
app::InteractionModelEngine::GetInstance()->SetPathPoolCapacityForSubscriptions(kExpectedParallelPaths);

// This should just use all availbale resources.
EstablishSubscriptions(apSuite, apContext, kExpectedParallelSubs,
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), kExpectedParallelSubs,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallback, readClients);

ctx.DrainAndServiceIO();
Expand All @@ -1674,8 +1734,8 @@ void TestReadInteraction::TestReadHandler_KillOldestSubscriptions(nlTestSuite *
{
TestReadCallback callback;
std::vector<std::unique_ptr<app::ReadClient>> outReadClient;
EstablishSubscriptions(apSuite, apContext, 1, app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1, &callback,
outReadClient);
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), 1,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription + 1, &callback, outReadClient);

ctx.DrainAndServiceIO();

Expand All @@ -1686,8 +1746,8 @@ void TestReadInteraction::TestReadHandler_KillOldestSubscriptions(nlTestSuite *

// The following check will trigger the logic in im to kill the read handlers that uses more paths than the limit per fabric.
{
EstablishSubscriptions(apSuite, apContext, 1, app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallback,
readClients);
EstablishSubscriptions(apSuite, ctx.GetSessionBobToAlice(), 1,
app::InteractionModelEngine::kMinSupportedPathsPerSubscription, &readCallback, readClients);
readCallback.ClearCounters();

ctx.DrainAndServiceIO();
Expand Down