From 2298968c6e510f9eb441474118336b9593918be2 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:02:44 -0500 Subject: [PATCH] [ReportScheduler] Fixed chunked reporting for Synchronized Report Scheduler (#30608) * Fixed bug where chunked reports would fail on Synchronized report scheduler and added tests for Syncrhonized report scheduler interaction Also fixed behavior of Synchronized Report scheduler on Early Reports cases * Update src/app/reporting/SynchronizedReportSchedulerImpl.cpp Co-authored-by: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> * Clarified comment * Removed check on unused parameter in CalculateNextReportTiemout for Sync Scheduler --------- Co-authored-by: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> --- src/app/reporting/ReportScheduler.h | 6 +- src/app/reporting/ReportSchedulerImpl.h | 2 +- .../SynchronizedReportSchedulerImpl.cpp | 27 +- .../SynchronizedReportSchedulerImpl.h | 2 +- .../reporting/tests/MockReportScheduler.cpp | 4 +- src/app/tests/TestReadInteraction.cpp | 536 +++++++++++------- src/app/tests/TestReportScheduler.cpp | 2 +- 7 files changed, 351 insertions(+), 228 deletions(-) diff --git a/src/app/reporting/ReportScheduler.h b/src/app/reporting/ReportScheduler.h index 43441be73bddf5..b98832b9009fd2 100644 --- a/src/app/reporting/ReportScheduler.h +++ b/src/app/reporting/ReportScheduler.h @@ -96,6 +96,7 @@ class ReportScheduler : public ReadHandler::Observer, public ICDStateObserver IsEngineRunScheduled())); } + bool IsChunkedReport() const { return mReadHandler->IsChunkedReport(); } bool IsEngineRunScheduled() const { return mFlags.Has(ReadHandlerNodeFlags::EngineRunScheduled); } void SetEngineRunScheduled(bool aEngineRunScheduled) { @@ -153,7 +154,10 @@ class ReportScheduler : public ReadHandler::Observer, public ICDStateObserver } /// @brief Check if a ReadHandler is reportable without considering the timing - bool IsReadHandlerReportable(ReadHandler * aReadHandler) const { return aReadHandler->ShouldStartReporting(); } + bool IsReadHandlerReportable(ReadHandler * aReadHandler) const + { + return (nullptr != aReadHandler) ? aReadHandler->ShouldStartReporting() : false; + } /// @brief Sets the ForceDirty flag of a ReadHandler void HandlerForceDirtyState(ReadHandler * aReadHandler) { aReadHandler->ForceDirtyState(); } diff --git a/src/app/reporting/ReportSchedulerImpl.h b/src/app/reporting/ReportSchedulerImpl.h index a8304a824232e7..0f0399a9a00dd5 100644 --- a/src/app/reporting/ReportSchedulerImpl.h +++ b/src/app/reporting/ReportSchedulerImpl.h @@ -44,7 +44,7 @@ class ReportSchedulerImpl : public ReportScheduler void OnSubscriptionReportSent(ReadHandler * aReadHandler) final; void OnReadHandlerDestroyed(ReadHandler * aReadHandler) override; - bool IsReportScheduled(ReadHandler * aReadHandler); + virtual bool IsReportScheduled(ReadHandler * aReadHandler); void ReportTimerCallback() override; diff --git a/src/app/reporting/SynchronizedReportSchedulerImpl.cpp b/src/app/reporting/SynchronizedReportSchedulerImpl.cpp index d10a7ea4d3fea3..6627cf7116bec3 100644 --- a/src/app/reporting/SynchronizedReportSchedulerImpl.cpp +++ b/src/app/reporting/SynchronizedReportSchedulerImpl.cpp @@ -79,7 +79,7 @@ void SynchronizedReportSchedulerImpl::CancelReport() } /// @brief Checks if the timer is active for the ReportScheduler -bool SynchronizedReportSchedulerImpl::IsReportScheduled() +bool SynchronizedReportSchedulerImpl::IsReportScheduled(ReadHandler * ReadHandler) { return mTimerDelegate->IsTimerActive(this); } @@ -133,14 +133,13 @@ CHIP_ERROR SynchronizedReportSchedulerImpl::FindNextMinInterval(const Timestamp CHIP_ERROR SynchronizedReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now) { - VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT); ReturnErrorOnFailure(FindNextMaxInterval(now)); ReturnErrorOnFailure(FindNextMinInterval(now)); bool reportableNow = false; bool reportableAtMin = false; mNodesPool.ForEachActiveObject([&reportableNow, &reportableAtMin, this, now](ReadHandlerNode * node) { - if (!node->IsEngineRunScheduled()) + if (!node->IsEngineRunScheduled() || node->IsChunkedReport()) { if (node->IsReportableNow(now)) { @@ -180,11 +179,10 @@ CHIP_ERROR SynchronizedReportSchedulerImpl::CalculateNextReportTimeout(Timeout & /// the engine already verifies that read handlers are reportable before sending a report void SynchronizedReportSchedulerImpl::TimerFired() { - Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); + Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); + bool firedEarly = true; - InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun(); - - mNodesPool.ForEachActiveObject([now](ReadHandlerNode * node) { + mNodesPool.ForEachActiveObject([now, &firedEarly](ReadHandlerNode * node) { if (node->GetMinTimestamp() <= now) { node->SetCanBeSynced(true); @@ -192,6 +190,9 @@ void SynchronizedReportSchedulerImpl::TimerFired() if (node->IsReportableNow(now)) { + // We set firedEarly false here because we assume we fired the timer early if no handler is reportable at the moment, + // which becomes false if we find a handler that is reportable + firedEarly = false; node->SetEngineRunScheduled(true); ChipLogProgress(DataManagement, "Handler: %p with min: 0x" ChipLogFormatX64 " and max: 0x" ChipLogFormatX64 "", (node), ChipLogValueX64(node->GetMinTimestamp().count()), ChipLogValueX64(node->GetMaxTimestamp().count())); @@ -199,6 +200,18 @@ void SynchronizedReportSchedulerImpl::TimerFired() return Loop::Continue; }); + + // If there are no handlers registers, no need to schedule the next report + if (mNodesPool.Allocated() && firedEarly) + { + Timeout timeout = Milliseconds32(0); + ReturnOnFailure(CalculateNextReportTimeout(timeout, nullptr, now)); + ScheduleReport(timeout, nullptr, now); + } + else + { + InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun(); + } } } // namespace reporting diff --git a/src/app/reporting/SynchronizedReportSchedulerImpl.h b/src/app/reporting/SynchronizedReportSchedulerImpl.h index 69f9649b3a575f..1240395bb0fcea 100644 --- a/src/app/reporting/SynchronizedReportSchedulerImpl.h +++ b/src/app/reporting/SynchronizedReportSchedulerImpl.h @@ -40,7 +40,7 @@ class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public Timer void OnTransitionToIdle() override; - bool IsReportScheduled(); + bool IsReportScheduled(ReadHandler * ReadHandler) override; void TimerFired() override; diff --git a/src/app/reporting/tests/MockReportScheduler.cpp b/src/app/reporting/tests/MockReportScheduler.cpp index e42cf22e973e00..8e59203a4548d2 100644 --- a/src/app/reporting/tests/MockReportScheduler.cpp +++ b/src/app/reporting/tests/MockReportScheduler.cpp @@ -29,7 +29,7 @@ namespace reporting { static chip::app::DefaultTimerDelegate sTimerDelegate; static ReportSchedulerImpl sTestDefaultReportScheduler(&sTimerDelegate); -static SynchronizedReportSchedulerImpl sTestReportScheduler(&sTimerDelegate); +static SynchronizedReportSchedulerImpl sTestSyncReportScheduler(&sTimerDelegate); ReportSchedulerImpl * GetDefaultReportScheduler() { @@ -38,7 +38,7 @@ ReportSchedulerImpl * GetDefaultReportScheduler() SynchronizedReportSchedulerImpl * GetSynchronizedReportScheduler() { - return &sTestReportScheduler; + return &sTestSyncReportScheduler; } } // namespace reporting diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index 2ddc7d78fc64c6..4c7f7884a4b179 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -72,6 +71,8 @@ constexpr int kMockAttribute4ListLength = 6; static chip::System::Clock::Internal::MockClock gMockClock; static chip::System::Clock::ClockBase * gRealClock; +static chip::app::reporting::ReportSchedulerImpl * gReportScheduler; +static bool sUsingSubSync = false; class TestContext : public chip::Test::AppContext { @@ -81,6 +82,8 @@ class TestContext : public chip::Test::AppContext { gRealClock = &chip::System::SystemClock(); chip::System::Clock::Internal::SetSystemClockForTesting(&gMockClock); + gReportScheduler = chip::app::reporting::GetDefaultReportScheduler(); + if (AppContext::Initialize(context) != SUCCESS) return FAILURE; return SUCCESS; @@ -123,6 +126,18 @@ class TestContext : public chip::Test::AppContext return SUCCESS; } + static int SetUpSyncTestSuite(void * context) + { + gRealClock = &chip::System::SystemClock(); + chip::System::Clock::Internal::SetSystemClockForTesting(&gMockClock); + gReportScheduler = chip::app::reporting::GetSynchronizedReportScheduler(); + sUsingSubSync = true; + + if (AppContext::Initialize(context) != SUCCESS) + return FAILURE; + return SUCCESS; + } + private: chip::MonotonicallyIncreasingCounter mEventCounter; }; @@ -559,13 +574,12 @@ void TestReadInteraction::TestReadHandler(nlTestSuite * apSuite, void * apContex NullReadHandlerCallback nullCallback; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); { Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); - ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); GenerateReportData(apSuite, apContext, reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/); err = readHandler.SendReportData(std::move(reportDatabuf), false); @@ -736,13 +750,12 @@ void TestReadInteraction::TestReadHandlerInvalidAttributePath(nlTestSuite * apSu NullReadHandlerCallback nullCallback; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); { Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); - ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(nullCallback, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); GenerateReportData(apSuite, apContext, reportDatabuf, ReportType::kValid, false /* aSuppressResponse*/); err = readHandler.SendReportData(std::move(reportDatabuf), false); @@ -899,7 +912,7 @@ void TestReadInteraction::TestReadRoundtrip(nlTestSuite * apSuite, void * apCont MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -981,7 +994,7 @@ void TestReadInteraction::TestReadRoundtripWithDataVersionFilter(nlTestSuite * a MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1037,7 +1050,7 @@ void TestReadInteraction::TestReadRoundtripWithNoMatchPathDataVersionFilter(nlTe MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); chip::app::AttributePathParams attributePathParams[2]; @@ -1097,7 +1110,7 @@ void TestReadInteraction::TestReadRoundtripWithMultiSamePathDifferentDataVersion MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1158,7 +1171,7 @@ void TestReadInteraction::TestReadRoundtripWithSameDifferentPathsDataVersionFilt MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1219,7 +1232,7 @@ void TestReadInteraction::TestReadWildcard(nlTestSuite * apSuite, void * apConte MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1269,7 +1282,7 @@ void TestReadInteraction::TestReadChunking(nlTestSuite * apSuite, void * apConte MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1324,7 +1337,7 @@ void TestReadInteraction::TestSetDirtyBetweenChunks(nlTestSuite * apSuite, void GenerateEvents(apSuite, apContext); auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); chip::app::AttributePathParams attributePathParams[2]; @@ -1478,7 +1491,7 @@ void TestReadInteraction::TestReadInvalidAttributePathRoundtrip(nlTestSuite * ap MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -1518,14 +1531,13 @@ void TestReadInteraction::TestProcessSubscribeRequest(nlTestSuite * apSuite, voi System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1583,7 +1595,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestSupMaxIntervalCeiling(nl System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); uint16_t kMinInterval = 0; @@ -1592,8 +1604,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestSupMaxIntervalCeiling(nl Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1658,7 +1669,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestInfMaxIntervalCeiling(nl System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); uint16_t kMinInterval = 0; @@ -1667,8 +1678,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestInfMaxIntervalCeiling(nl Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1733,7 +1743,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestSupMinInterval(nlTestSui System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); uint16_t kMinInterval = 305; // Default IdleModeDuration is 300 @@ -1742,8 +1752,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestSupMinInterval(nlTestSui Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1808,7 +1817,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestMaxMinInterval(nlTestSui System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); uint16_t kMinInterval = System::Clock::Seconds16::max().count(); @@ -1817,8 +1826,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestMaxMinInterval(nlTestSui Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1881,7 +1889,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestInvalidIdleModeDuration( System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); SubscribeRequestMessage::Builder subscribeRequestBuilder; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); uint16_t kMinInterval = 400; @@ -1890,8 +1898,7 @@ void TestReadInteraction::TestICDProcessSubscribeRequestInvalidIdleModeDuration( Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); { - ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, - app::reporting::GetDefaultReportScheduler()); + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, gReportScheduler); writer.Init(std::move(subscribeRequestbuf)); err = subscribeRequestBuilder.Init(&writer); @@ -1954,9 +1961,8 @@ void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * a 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); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2163,9 +2169,8 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * 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); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2207,15 +2212,15 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers(ReadHandler::InteractionType::Subscribe) == 1); NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) == + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds)); NL_TEST_ASSERT(apSuite, - reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) == + gReportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(maxInterval)); // 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, gReportScheduler->IsReportScheduled(delegate.mpReadHandler)); + ReportScheduler::ReadHandlerNode * node = gReportScheduler->GetReadHandlerNode(delegate.mpReadHandler); NL_TEST_ASSERT(apSuite, node != nullptr); GenerateEvents(apSuite, apContext); @@ -2223,12 +2228,13 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * // 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) == + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds) + Milliseconds32(50)); - NL_TEST_ASSERT(apSuite, reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > startTime); + NL_TEST_ASSERT(apSuite, gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > startTime); NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty()); + delegate.mGotEventResponse = false; // Advance monotonic timestamp for min interval to elapse gMockClock.AdvanceMonotonic(Seconds16(readPrepareParams.mMinIntervalFloorSeconds)); @@ -2236,37 +2242,66 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * // 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()); + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp()); - // Service Engine Run - ctx.GetIOContext().DriveIO(); - // Service EventManagement event - ctx.GetIOContext().DriveIO(); - ctx.GetIOContext().DriveIO(); - NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); + // The behavior on min interval elapse is different between synced subscription and non-synced subscription + if (!sUsingSubSync) + { + // Verify the ReadHandler is considered as reportable even if its node's min timestamp has not expired + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReportableNow(delegate.mpReadHandler)); + NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); - // Check the logic works for timer expiring at maximum as well + // Service Engine Run + ctx.GetIOContext().DriveIO(); + // Service EventManagement event + ctx.GetIOContext().DriveIO(); + ctx.GetIOContext().DriveIO(); + NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); + } + else + { + // Verify logic on Min for synced subscription + // Verify the ReadHandler is not considered as reportable yet + NL_TEST_ASSERT(apSuite, !gReportScheduler->IsReportableNow(delegate.mpReadHandler)); + + // confirm that the timer was kicked off for the next min of the node + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReportScheduled(delegate.mpReadHandler)); + + // Expired new timer + gMockClock.AdvanceMonotonic(Milliseconds32(50)); + + // Service Timer expired event + ctx.GetIOContext().DriveIO(); + 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); + } + + // The behavior is identical on max since the sync subscription will interpret an early max firing as a earlier node got + // reportable and allow nodes that have passed their min to sync on it. NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); delegate.mGotEventResponse = false; NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) == + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(readPrepareParams.mMinIntervalFloorSeconds)); NL_TEST_ASSERT(apSuite, - reportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) == + gReportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(maxInterval)); // Confirm that the node is scheduled to run - NL_TEST_ASSERT(apSuite, reportScheduler->IsReportScheduled(delegate.mpReadHandler)); + NL_TEST_ASSERT(apSuite, gReportScheduler->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) == + gReportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) == gMockClock.GetMonotonicTimestamp() + Seconds16(maxInterval) + Milliseconds32(50)); // Advance monotonic timestamp for min interval to elapse @@ -2278,9 +2313,9 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * // 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)); + gReportScheduler->GetMaxTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReportableNow(delegate.mpReadHandler)); + NL_TEST_ASSERT(apSuite, !gReportScheduler->IsReportScheduled(delegate.mpReadHandler)); NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); NL_TEST_ASSERT(apSuite, InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); // Service Engine Run @@ -2288,9 +2323,10 @@ void TestReadInteraction::TestSubscribeEarlyReport(nlTestSuite * apSuite, void * // Service EventManagement event ctx.GetIOContext().DriveIO(); ctx.GetIOContext().DriveIO(); - NL_TEST_ASSERT(apSuite, reportScheduler->IsReportScheduled(delegate.mpReadHandler)); + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReportScheduled(delegate.mpReadHandler)); NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); } + ctx.DrainAndServiceIO(); engine->Shutdown(); NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); @@ -2307,9 +2343,8 @@ void TestReadInteraction::TestSubscribeUrgentWildcardEvent(nlTestSuite * apSuite MockInteractionModelApp delegate; MockInteractionModelApp nonUrgentDelegate; - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); @@ -2363,12 +2398,12 @@ void TestReadInteraction::TestSubscribeUrgentWildcardEvent(nlTestSuite * apSuite GenerateEvents(apSuite, apContext); - NL_TEST_ASSERT(apSuite, reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > startTime); + NL_TEST_ASSERT(apSuite, gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > startTime); NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty()); delegate.mGotEventResponse = false; delegate.mGotReport = false; - NL_TEST_ASSERT(apSuite, reportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) > startTime); + NL_TEST_ASSERT(apSuite, gReportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) > startTime); NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); nonUrgentDelegate.mGotEventResponse = false; nonUrgentDelegate.mGotReport = false; @@ -2399,77 +2434,147 @@ void TestReadInteraction::TestSubscribeUrgentWildcardEvent(nlTestSuite * apSuite ctx.GetIOContext().DriveIO(); NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); - - // Since we just sent a report for our urgent subscription, the min interval of the urgent subscription should have been - // updated - NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp()); - NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); - delegate.mGotEventResponse = false; - - // For our non-urgent subscription, we did not send anything, so the min interval should of the non urgent subscription - // should be in the past - NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) < - gMockClock.GetMonotonicTimestamp()); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); - - // Advance monotonic timestamp for min interval to elapse - gMockClock.AdvanceMonotonic(System::Clock::Milliseconds32(2100)); - ctx.GetIOContext().DriveIO(); - // No reporting should have happened. - NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); - - // The min-interval should have elapsed for the urgent subscription, and our handler should still - // not be dirty or reportable. - NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) < - System::SystemClock().GetMonotonicTimestamp()); - NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); - NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->ShouldStartReporting()); - - // And the non-urgent one should not have changed state either, since - // it's waiting for the max-interval. - NL_TEST_ASSERT(apSuite, - reportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) < - System::SystemClock().GetMonotonicTimestamp()); - NL_TEST_ASSERT(apSuite, - reportScheduler->GetMaxTimestampForHandler(nonUrgentDelegate.mpReadHandler) > - System::SystemClock().GetMonotonicTimestamp()); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); - - // There should be no reporting run scheduled. This is very important; - // otherwise we can get a false-positive pass below because the run was - // already scheduled by here. - NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); + // The logic differs here depending on what Scheduler is implemented + if (!sUsingSubSync) + { + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + + // Since we just sent a report for our urgent subscription, the min interval of the urgent subcription should have been + // updated + NL_TEST_ASSERT( + apSuite, gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); + delegate.mGotEventResponse = false; + + // For our non-urgent subscription, we did not send anything, so the min interval should of the non urgent subcription + // should be in the past + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) < + gMockClock.GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + + // Advance monotonic timestamp for min interval to elapse + gMockClock.AdvanceMonotonic(System::Clock::Milliseconds32(2100)); + ctx.GetIOContext().DriveIO(); - // Generate some events, which should get reported. - GenerateEvents(apSuite, apContext); + // No reporting should have happened. + NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + + // The min-interval should have elapsed for the urgent subscription, and our handler should still + // not be dirty or reportable. + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) < + System::SystemClock().GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->ShouldStartReporting()); + + // And the non-urgent one should not have changed state either, since + // it's waiting for the max-interval. + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) < + System::SystemClock().GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMaxTimestampForHandler(nonUrgentDelegate.mpReadHandler) > + System::SystemClock().GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); + + // There should be no reporting run scheduled. This is very important; + // otherwise we can get a false-positive pass below because the run was + // already scheduled by here. + NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); + + // Generate some events, which should get reported. + GenerateEvents(apSuite, apContext); + + // Urgent read handler should now be dirty, and reportable. + NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->ShouldStartReporting()); + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReadHandlerReportable(delegate.mpReadHandler)); + + // Non-urgent read handler should not be reportable. + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); + + // Still no reporting should have happened. + NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); - // Urgent read handler should now be dirty, and reportable. - NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty()); - NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->ShouldStartReporting()); - NL_TEST_ASSERT(apSuite, reportScheduler->IsReadHandlerReportable(delegate.mpReadHandler)); + ctx.DrainAndServiceIO(); - // Non-urgent read handler should not be reportable. - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); + // Should get those urgent events reported. + NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); - // Still no reporting should have happened. - NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + // Should get nothing reported on the non-urgent handler. + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + } + else + { + // If we're using the sub-sync scheduler, we should have gotten the non-urgent event as well. + NL_TEST_ASSERT(apSuite, nonUrgentDelegate.mGotEventResponse); + + // Since we just sent a report for both our subscriptions, the min interval of the urgent subcription should have been + NL_TEST_ASSERT( + apSuite, gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) > gMockClock.GetMonotonicTimestamp()); + + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) > + gMockClock.GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + delegate.mGotEventResponse = false; + nonUrgentDelegate.mGotEventResponse = false; + + // Advance monotonic timestamp for min interval to elapse + gMockClock.AdvanceMonotonic(System::Clock::Milliseconds32(2100)); + ctx.GetIOContext().DriveIO(); - ctx.DrainAndServiceIO(); + // No reporting should have happened. + NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + + // The min-interval should have elapsed for both subscriptions, and our handlers should still + // not be dirty or reportable. + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(delegate.mpReadHandler) < + System::SystemClock().GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, + gReportScheduler->GetMinTimestampForHandler(nonUrgentDelegate.mpReadHandler) < + System::SystemClock().GetMonotonicTimestamp()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !delegate.mpReadHandler->ShouldStartReporting()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); + + // There should be no reporting run scheduled. This is very important; + // otherwise we can get a false-positive pass below because the run was + // already scheduled by here. + NL_TEST_ASSERT(apSuite, !InteractionModelEngine::GetInstance()->GetReportingEngine().IsRunScheduled()); + + // Generate some events, which should get reported. + GenerateEvents(apSuite, apContext); + + // Urgent read handler should now be dirty, and reportable. + NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, delegate.mpReadHandler->ShouldStartReporting()); + NL_TEST_ASSERT(apSuite, gReportScheduler->IsReadHandlerReportable(delegate.mpReadHandler)); + + // Non-urgent read handler should not be reportable. + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->IsDirty()); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mpReadHandler->ShouldStartReporting()); + + // Still no reporting should have happened. + NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); + NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); - // Should get those urgent events reported. - NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); + ctx.DrainAndServiceIO(); - // Should get nothing reported on the non-urgent handler. - NL_TEST_ASSERT(apSuite, !nonUrgentDelegate.mGotEventResponse); + // Should get those urgent events reported and the non urgent reported for synchronisation + NL_TEST_ASSERT(apSuite, delegate.mGotEventResponse); + NL_TEST_ASSERT(apSuite, nonUrgentDelegate.mGotEventResponse); + } } // By now we should have closed all exchanges and sent all pending acks, so @@ -2493,9 +2598,8 @@ void TestReadInteraction::TestSubscribeWildcard(nlTestSuite * apSuite, void * ap GenerateEvents(apSuite, apContext); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2644,9 +2748,8 @@ void TestReadInteraction::TestSubscribePartialOverlap(nlTestSuite * apSuite, voi NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2720,9 +2823,8 @@ void TestReadInteraction::TestSubscribeSetDirtyFullyOverlap(nlTestSuite * apSuit NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2794,9 +2896,7 @@ void TestReadInteraction::TestSubscribeEarlyShutdown(nlTestSuite * apSuite, void // Initialize Interaction Model Engine NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); - NL_TEST_ASSERT(apSuite, - engine.Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()) == - CHIP_NO_ERROR); + NL_TEST_ASSERT(apSuite, engine.Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler) == CHIP_NO_ERROR); // Subscribe to the attribute AttributePathParams attributePathParams; @@ -2849,9 +2949,8 @@ void TestReadInteraction::TestSubscribeInvalidAttributePathRoundtrip(nlTestSuite GenerateEvents(apSuite, apContext); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -2956,7 +3055,7 @@ void TestReadInteraction::TestSubscribeInvalidInterval(nlTestSuite * apSuite, vo MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3000,9 +3099,8 @@ void TestReadInteraction::TestPostSubscribeRoundtripStatusReportTimeout(nlTestSu NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3124,7 +3222,7 @@ void TestReadInteraction::TestSubscribeRoundtripStatusReportTimeout(nlTestSuite MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3203,7 +3301,7 @@ void TestReadInteraction::TestReadChunkingStatusReportTimeout(nlTestSuite * apSu MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3257,7 +3355,7 @@ void TestReadInteraction::TestReadReportFailure(nlTestSuite * apSuite, void * ap MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3305,7 +3403,7 @@ void TestReadInteraction::TestSubscribeRoundtripChunkStatusReportTimeout(nlTestS MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3376,9 +3474,8 @@ void TestReadInteraction::TestPostSubscribeRoundtripChunkStatusReportTimeout(nlT NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3480,9 +3577,8 @@ void TestReadInteraction::TestPostSubscribeRoundtripChunkReportTimeout(nlTestSui NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3584,7 +3680,7 @@ void TestReadInteraction::TestPostSubscribeRoundtripChunkReport(nlTestSuite * ap MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -3645,16 +3741,16 @@ void TestReadInteraction::TestPostSubscribeRoundtripChunkReport(nlTestSuite * ap delegate.mNumAttributeResponse = 0; delegate.mNumArrayItems = 0; - // wait for min interval 1 seconds(in test, we use 0.9second considering the time variation), expect no event is received, - // then wait for 0.5 seconds, then all chunked dirty reports are sent out, which would not honor minInterval + // wait for min interval 1 seconds(in test, we use 0.9second considering the time variation), expect no event is + // received, then wait for 0.5 seconds, then all chunked dirty reports are sent out, which would not honor minInterval gMockClock.AdvanceMonotonic(System::Clock::Milliseconds32(900)); ctx.GetIOContext().DriveIO(); NL_TEST_ASSERT(apSuite, delegate.mNumAttributeResponse == 0); System::Clock::Timestamp startTime = gMockClock.GetMonotonicTimestamp(); - // Increment in time is done by steps here to allow for multiple IO processing at the right time and allow the timer to be - // rescheduled accordingly + // Increment in time is done by steps here to allow for multiple IO processing at the right time and allow the timer to + // be rescheduled accordingly while (true) { ctx.GetIOContext().DriveIO(); @@ -3723,8 +3819,8 @@ void CheckForInvalidAction(nlTestSuite * apSuite, Test::MessageCapturer & messag }); \ } -// Read Client sends the read request, Read Handler drops the response, then test injects unknown status reponse message for Read -// Client. +// Read Client sends the read request, Read Handler drops the response, then test injects unknown status reponse message for +// Read Client. void TestReadInteraction::TestReadClientReceiveInvalidMessage(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -3736,7 +3832,7 @@ void TestReadInteraction::TestReadClientReceiveInvalidMessage(nlTestSuite * apSu MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -3809,8 +3905,8 @@ void TestReadInteraction::TestReadClientReceiveInvalidMessage(nlTestSuite * apSu ctx.CreateSessionBobToAlice(); } -// Read Client sends the subscribe request, Read Handler drops the response, then test injects unknown status response message for -// Read Client. +// Read Client sends the subscribe request, Read Handler drops the response, then test injects unknown status response message +// for Read Client. void TestReadInteraction::TestSubscribeClientReceiveInvalidStatusResponse(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -3822,7 +3918,7 @@ void TestReadInteraction::TestSubscribeClientReceiveInvalidStatusResponse(nlTest MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -3902,8 +3998,8 @@ void TestReadInteraction::TestSubscribeClientReceiveInvalidStatusResponse(nlTest ctx.CreateSessionBobToAlice(); } -// Read Client sends the subscribe request, Read Handler drops the response, then test injects well-formed status response message -// with Success for Read Client, we expect the error with CHIP_ERROR_INVALID_MESSAGE_TYPE +// Read Client sends the subscribe request, Read Handler drops the response, then test injects well-formed status response +// message with Success for Read Client, we expect the error with CHIP_ERROR_INVALID_MESSAGE_TYPE void TestReadInteraction::TestSubscribeClientReceiveWellFormedStatusResponse(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -3915,7 +4011,7 @@ void TestReadInteraction::TestSubscribeClientReceiveWellFormedStatusResponse(nlT MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4007,7 +4103,7 @@ void TestReadInteraction::TestSubscribeClientReceiveInvalidReportMessage(nlTestS MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4099,7 +4195,7 @@ void TestReadInteraction::TestSubscribeClientReceiveUnsolicitedInvalidReportMess MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4158,8 +4254,8 @@ void TestReadInteraction::TestSubscribeClientReceiveUnsolicitedInvalidReportMess engine->Shutdown(); } -// Read Client sends the subscribe request, Read Handler drops the subscribe response, then test injects invalid subscribe response -// message +// Read Client sends the subscribe request, Read Handler drops the subscribe response, then test injects invalid subscribe +// response message void TestReadInteraction::TestSubscribeClientReceiveInvalidSubscribeResponseMessage(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4171,7 +4267,7 @@ void TestReadInteraction::TestSubscribeClientReceiveInvalidSubscribeResponseMess MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4265,7 +4361,7 @@ void TestReadInteraction::TestSubscribeClientReceiveUnsolicitedReportMessageWith MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4328,9 +4424,9 @@ void TestReadInteraction::TestSubscribeClientReceiveUnsolicitedReportMessageWith engine->Shutdown(); } -// TestReadChunkingInvalidSubscriptionId will try to read a few large attributes, the report won't fit into the MTU and result in -// chunking, second report has different subscription id from the first one, read client sends out the status report with invalid -// subscription +// TestReadChunkingInvalidSubscriptionId will try to read a few large attributes, the report won't fit into the MTU and result +// in chunking, second report has different subscription id from the first one, read client sends out the status report with +// invalid subscription void TestReadInteraction::TestReadChunkingInvalidSubscriptionId(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4344,7 +4440,7 @@ void TestReadInteraction::TestReadChunkingInvalidSubscriptionId(nlTestSuite * ap MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(apSuite, !delegate.mGotEventResponse); @@ -4407,9 +4503,9 @@ void TestReadInteraction::TestReadChunkingInvalidSubscriptionId(nlTestSuite * ap // TODO: Need to validate what status is being sent to the ReadHandler // The ReadHandler's exchange is still open when we synthesize the report data message. - // Since we synthesized the second report data message to the ReadClient with invalid subscription id, instead of sending it - // from the ReadHandler, the only messages here are the ReadClient's StatusResponse to the unexpected message and an MRP - // ack. + // Since we synthesized the second report data message to the ReadClient with invalid subscription id, instead of + // sending it from the ReadHandler, the only messages here are the ReadClient's StatusResponse to the unexpected message + // and an MRP ack. NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 2); NL_TEST_ASSERT(apSuite, delegate.mError == CHIP_ERROR_INVALID_SUBSCRIPTION); @@ -4435,7 +4531,7 @@ void TestReadInteraction::TestReadHandlerMalformedSubscribeRequest(nlTestSuite * MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4467,8 +4563,8 @@ void TestReadInteraction::TestReadHandlerMalformedSubscribeRequest(nlTestSuite * NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); } -// Read Client sends a malformed read request, interaction model engine fails to parse the request and generates a status report to -// client, and client is closed. +// Read Client sends a malformed read request, interaction model engine fails to parse the request and generates a status report +// to client, and client is closed. void TestReadInteraction::TestReadHandlerMalformedReadRequest1(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4480,7 +4576,7 @@ void TestReadInteraction::TestReadHandlerMalformedReadRequest1(nlTestSuite * apS MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4512,8 +4608,8 @@ void TestReadInteraction::TestReadHandlerMalformedReadRequest1(nlTestSuite * apS NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); } -// Read Client sends a malformed read request, read handler fails to parse the request and generates a status report to client, and -// client is closed. +// Read Client sends a malformed read request, read handler fails to parse the request and generates a status report to client, +// and client is closed. void TestReadInteraction::TestReadHandlerMalformedReadRequest2(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4525,7 +4621,7 @@ void TestReadInteraction::TestReadHandlerMalformedReadRequest2(nlTestSuite * apS MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4559,8 +4655,8 @@ void TestReadInteraction::TestReadHandlerMalformedReadRequest2(nlTestSuite * apS NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); } -// Read Client creates a subscription with the server, server sends chunked reports, after the handler sends out the first chunked -// report, client sends out invalid write request message, handler sends status report with invalid action and closes +// Read Client creates a subscription with the server, server sends chunked reports, after the handler sends out the first +// chunked report, client sends out invalid write request message, handler sends status report with invalid action and closes void TestReadInteraction::TestSubscribeSendUnknownMessage(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4572,7 +4668,7 @@ void TestReadInteraction::TestSubscribeSendUnknownMessage(nlTestSuite * apSuite, MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); chip::app::AttributePathParams attributePathParams[1]; @@ -4621,8 +4717,8 @@ void TestReadInteraction::TestSubscribeSendUnknownMessage(nlTestSuite * apSuite, err = readClient.mExchange->SendMessage(Protocols::InteractionModel::MsgType::WriteRequest, std::move(msgBuf)); ctx.DrainAndServiceIO(); - // client sends invalid write request, server sends out status report with invalid action and closes, client replies with - // status report server replies with MRP Ack + // client sends invalid write request, server sends out status report with invalid action and closes, client replies + // with status report server replies with MRP Ack NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 4); NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers() == 0); } @@ -4648,7 +4744,7 @@ void TestReadInteraction::TestSubscribeSendInvalidStatusReport(nlTestSuite * apS MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); chip::app::AttributePathParams attributePathParams[1]; @@ -4696,8 +4792,8 @@ void TestReadInteraction::TestSubscribeSendInvalidStatusReport(nlTestSuite * apS err = readClient.mExchange->SendMessage(Protocols::InteractionModel::MsgType::StatusResponse, std::move(msgBuf)); ctx.DrainAndServiceIO(); - // client sends malformed status response, server sends out status report with invalid action and close, client replies with - // status report server replies with MRP Ack + // client sends malformed status response, server sends out status report with invalid action and close, client replies + // with status report server replies with MRP Ack NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 4); NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers() == 0); } @@ -4723,7 +4819,7 @@ void TestReadInteraction::TestReadHandlerInvalidSubscribeRequest(nlTestSuite * a MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4769,7 +4865,7 @@ void TestReadInteraction::TestSubscribeInvalidateFabric(nlTestSuite * apSuite, v MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4828,7 +4924,7 @@ void TestReadInteraction::TestShutdownSubscription(nlTestSuite * apSuite, void * MockInteractionModelApp delegate; auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); @@ -4880,9 +4976,8 @@ void TestReadInteraction::TestSubscriptionReportWithDefunctSession(nlTestSuite * NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); MockInteractionModelApp delegate; - ReportSchedulerImpl * reportScheduler = app::reporting::GetDefaultReportScheduler(); - auto * engine = chip::app::InteractionModelEngine::GetInstance(); - err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), reportScheduler); + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), gReportScheduler); NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); AttributePathParams subscribePath(Test::kMockEndpoint3, Test::MockClusterId(2), Test::MockAttributeId(1)); @@ -4983,11 +5078,6 @@ const nlTest sTests[] = { NL_TEST_DEF("TestReadClientInvalidAttributeId", chip::app::TestReadInteraction::TestReadClientInvalidAttributeId), NL_TEST_DEF("TestReadHandlerInvalidAttributePath", chip::app::TestReadInteraction::TestReadHandlerInvalidAttributePath), NL_TEST_DEF("TestProcessSubscribeRequest", chip::app::TestReadInteraction::TestProcessSubscribeRequest), -/* - We need to figure out a way to run unit tests with an ICD build without affecting - all the standard unit tests - https://github.com/project-chip/connectedhomeip/issues/28446 -*/ #if CHIP_CONFIG_ENABLE_ICD_SERVER NL_TEST_DEF("TestICDProcessSubscribeRequestSupMaxIntervalCeiling", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestSupMaxIntervalCeiling), @@ -5062,6 +5152,16 @@ nlTestSuite sSuite = { TestContext::SetUp, TestContext::TearDown, }; + +nlTestSuite sSyncSuite = +{ + "TestSyncReadInteraction", + &sTests[0], + TestContext::SetUpSyncTestSuite, + TestContext::TearDownTestSuite, + TestContext::SetUp, + TestContext::TearDown, +}; // clang-format on } // namespace @@ -5071,4 +5171,10 @@ int TestReadInteraction() return chip::ExecuteTestsWithContext(&sSuite); } +int TestSyncReadInteraction() +{ + return chip::ExecuteTestsWithContext(&sSyncSuite); +} + CHIP_REGISTER_TEST_SUITE(TestReadInteraction) +CHIP_REGISTER_TEST_SUITE(TestSyncReadInteraction) diff --git a/src/app/tests/TestReportScheduler.cpp b/src/app/tests/TestReportScheduler.cpp index c626202f82f629..662be101d550bd 100644 --- a/src/app/tests/TestReportScheduler.cpp +++ b/src/app/tests/TestReportScheduler.cpp @@ -517,7 +517,7 @@ class TestReportScheduler NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 2); // Confirm that a report emission is scheduled - NL_TEST_ASSERT(aSuite, syncScheduler.IsReportScheduled()); + NL_TEST_ASSERT(aSuite, syncScheduler.IsReportScheduled(readHandler1)); // Validates that the lowest max is selected as the common max timestamp NL_TEST_ASSERT(aSuite, syncScheduler.mNextMaxTimestamp == node1->GetMaxTimestamp());