Skip to content

Commit

Permalink
Added unit test for Early firing timers in the report scheduler (#29113)
Browse files Browse the repository at this point in the history
  • Loading branch information
lpbeliveau-silabs authored and pull[bot] committed Dec 1, 2023
1 parent 9ce8cf2 commit 4da868d
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/app/reporting/ReportScheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class ReportScheduler : public ReadHandler::Observer, public ICDStateObserver
ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
return node->GetMaxTimestamp();
}
ReadHandlerNode * GetReadHandlerNode(const ReadHandler * aReadHandler) { return FindReadHandlerNode(aReadHandler); }
#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST

protected:
Expand Down
143 changes: 143 additions & 0 deletions src/app/tests/TestReadInteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)

class TestReadInteraction
{
using Seconds16 = System::Clock::Seconds16;
using Milliseconds32 = System::Clock::Milliseconds32;

public:
static void TestReadClient(nlTestSuite * apSuite, void * apContext);
static void TestReadUnexpectedSubscriptionId(nlTestSuite * apSuite, void * apContext);
Expand Down Expand Up @@ -349,6 +352,7 @@ class TestReadInteraction
static void TestReadChunking(nlTestSuite * apSuite, void * apContext);
static void TestSetDirtyBetweenChunks(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeRoundtrip(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeEarlyReport(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeUrgentWildcardEvent(nlTestSuite * apSuite, void * apContext);
static void TestSubscribeWildcard(nlTestSuite * apSuite, void * apContext);
static void TestSubscribePartialOverlap(nlTestSuite * apSuite, void * apContext);
Expand Down Expand Up @@ -2099,6 +2103,144 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a
NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0);
}

void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * apContext)
{
TestContext & ctx = *static_cast<TestContext *>(apContext);
CHIP_ERROR err = CHIP_NO_ERROR;

Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr();
// Shouldn't have anything in the retransmit table when starting the test.
NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0);

MockInteractionModelApp delegate;
auto * engine = chip::app::InteractionModelEngine::GetInstance();
ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler();
err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse);

ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice());
chip::app::EventPathParams eventPathParams[1];
readPrepareParams.mpEventPathParamsList = eventPathParams;
readPrepareParams.mpEventPathParamsList[0].mEndpointId = kTestEventEndpointId;
readPrepareParams.mpEventPathParamsList[0].mClusterId = kTestEventClusterId;

readPrepareParams.mEventPathParamsListSize = 1;

readPrepareParams.mpAttributePathParamsList = nullptr;
readPrepareParams.mAttributePathParamsListSize = 0;

readPrepareParams.mMinIntervalFloorSeconds = 1;
readPrepareParams.mMaxIntervalCeilingSeconds = 5;

readPrepareParams.mKeepSubscriptions = true;

{
app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate,
chip::app::ReadClient::InteractionType::Subscribe);
readPrepareParams.mpEventPathParamsList[0].mIsUrgentEvent = true;
delegate.mGotEventResponse = false;
err = readClient.SendRequest(readPrepareParams);
NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR);

ctx.DrainAndServiceIO();
System::Clock::Timestamp startTime = gMockClock.GetMonotonicTimestamp();

NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers() == 1);
NL_TEST_ASSERT(apSuite, engine->ActiveHandlerAt(0) != nullptr);
delegate.mpReadHandler = engine->ActiveHandlerAt(0);

NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse);
NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 1);

NL_TEST_ASSERT(apSuite,
reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds));
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMaxIntervalCeilingSeconds));

// Confirm that the node is scheduled to run
NL_TEST_ASSERT(apSuite, reportScheduler->IsReportScheduled(delegate.mpReadHandler));
ReportScheduler::ReadHandlerNode * node = reportScheduler->GetReadHandlerNode(delegate.mpReadHandler);
NL_TEST_ASSERT(apSuite, node != nullptr);

GenerateEvents(apSuite, apContext);

// modify the node's min timestamp to be 50ms later than the timer expiration time
node->SetIntervalTimeStamps(delegate.mpReadHandler, startTime + Milliseconds32(50));
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds) +
Milliseconds32(50));

NL_TEST_ASSERT(apSuite, reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > startTime);
NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty());

// Advance monotonic timestamp for min interval to elapse
gMockClock.AdvanceMonotonic(Seconds16(readPrepareParams.mMinIntervalFloorSeconds));
NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled());
// Service Timer expired event
ctx.GetIOContext().DriveIO();

// Verify the ReadHandler is considered as reportable even if its node's min timestamp has not expired
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp());
NL_TEST_ASSERT(apSuite, reportScheduler->IsReportableNow(delegate.mpReadHandler));
NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled());

// Service Engine Run
ctx.GetIOContext().DriveIO();
// Service EventManagement event
ctx.GetIOContext().DriveIO();
ctx.GetIOContext().DriveIO();
NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse);

// Check the logic works for timer expiring at maximum as well
NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty());
delegate.mGotEventResponse = false;
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds));
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMaxIntervalCeilingSeconds));

// Confirm that the node is scheduled to run
NL_TEST_ASSERT(apSuite, reportScheduler->IsReportScheduled(delegate.mpReadHandler));
NL_TEST_ASSERT(apSuite, node != nullptr);

// modify the node's max timestamp to be 50ms later than the timer expiration time
node->SetIntervalTimeStamps(delegate.mpReadHandler, gMockClock.GetMonotonicTimestamp() + Milliseconds32(50));
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) ==
gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMaxIntervalCeilingSeconds) +
Milliseconds32(50));

// Advance monotonic timestamp for min interval to elapse
gMockClock.AdvanceMonotonic(Seconds16(readPrepareParams.mMaxIntervalCeilingSeconds));

NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled());
// Service Timer expired event
ctx.GetIOContext().DriveIO();

// Verify the ReadHandler is considered as reportable even if its node's min timestamp has not expired
NL_TEST_ASSERT(apSuite,
reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp());
NL_TEST_ASSERT(apSuite, reportScheduler->IsReportableNow(delegate.mpReadHandler));
NL_TEST_ASSERT(apSuite, !reportScheduler->IsReportScheduled(delegate.mpReadHandler));
NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty());
NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled());
// Service Engine Run
ctx.GetIOContext().DriveIO();
// Service EventManagement event
ctx.GetIOContext().DriveIO();
ctx.GetIOContext().DriveIO();
NL_TEST_ASSERT(apSuite, reportScheduler->IsReportScheduled(delegate.mpReadHandler));
NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled());
}
}

void TestReadInteraction::TestSubscribeUrgentWildcardEvent(nlTestSuite * apSuite, void * apContext)
{
TestContext & ctx = *static_cast<TestContext *>(apContext);
Expand Down Expand Up @@ -4796,6 +4938,7 @@ const nlTest sTests[] =
NL_TEST_DEF("TestICDProcessSubscribeRequestInvalidIdleModeInterval", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestInvalidIdleModeInterval),
#endif // #if CHIP_CONFIG_ENABLE_ICD_SERVER
NL_TEST_DEF("TestSubscribeRoundtrip", chip::app::TestReadInteraction::TestSubscribeRoundtrip),
NL_TEST_DEF("TestSubscribeEarlyReport", chip::app::TestReadInteraction::TestSubscribeEarlyReport),
NL_TEST_DEF("TestPostSubscribeRoundtripChunkReport", chip::app::TestReadInteraction::TestPostSubscribeRoundtripChunkReport),
NL_TEST_DEF("TestReadClientReceiveInvalidMessage", chip::app::TestReadInteraction::TestReadClientReceiveInvalidMessage),
NL_TEST_DEF("TestSubscribeClientReceiveInvalidStatusResponse", chip::app::TestReadInteraction::TestSubscribeClientReceiveInvalidStatusResponse),
Expand Down

0 comments on commit 4da868d

Please sign in to comment.