| /* |
| * |
| * Copyright (c) 2023 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <app/InteractionModelEngine.h> |
| #include <app/reporting/ReportSchedulerImpl.h> |
| #include <app/reporting/SynchronizedReportSchedulerImpl.h> |
| #include <app/tests/AppTestContext.h> |
| #include <lib/support/UnitTestContext.h> |
| #include <lib/support/UnitTestRegistration.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <nlunit-test.h> |
| |
| namespace { |
| |
| class TestContext : public chip::Test::AppContext |
| { |
| public: |
| static int Initialize(void * context) |
| { |
| if (AppContext::Initialize(context) != SUCCESS) |
| return FAILURE; |
| |
| auto * ctx = static_cast<TestContext *>(context); |
| |
| if (ctx->mEventCounter.Init(0) != CHIP_NO_ERROR) |
| { |
| return FAILURE; |
| } |
| |
| return SUCCESS; |
| } |
| |
| static int Finalize(void * context) |
| { |
| chip::app::EventManagement::DestroyEventManagement(); |
| |
| if (AppContext::Finalize(context) != SUCCESS) |
| return FAILURE; |
| |
| return SUCCESS; |
| } |
| |
| private: |
| chip::MonotonicallyIncreasingCounter<chip::EventNumber> mEventCounter; |
| }; |
| |
| class NullReadHandlerCallback : public chip::app::ReadHandler::ManagementCallback |
| { |
| public: |
| void OnDone(chip::app::ReadHandler & apReadHandlerObj) override {} |
| chip::app::ReadHandler::ApplicationCallback * GetAppCallback() override { return nullptr; } |
| }; |
| |
| } // namespace |
| |
| namespace chip { |
| namespace app { |
| namespace reporting { |
| |
| using ReportScheduler = reporting::ReportScheduler; |
| using ReportSchedulerImpl = reporting::ReportSchedulerImpl; |
| using ReadHandlerNode = reporting::ReportScheduler::ReadHandlerNode; |
| using Milliseconds64 = System::Clock::Milliseconds64; |
| |
| static const size_t kNumMaxReadHandlers = 16; |
| |
| class TestTimerDelegate : public ReportScheduler::TimerDelegate |
| { |
| public: |
| struct NodeTimeoutPair |
| { |
| ReadHandlerNode * node; |
| System::Clock::Timeout timeout; |
| }; |
| |
| NodeTimeoutPair mPairArray[kNumMaxReadHandlers]; |
| size_t mPairArraySize = 0; |
| System::Clock::Timestamp mMockSystemTimestamp = System::Clock::Milliseconds64(0); |
| |
| NodeTimeoutPair * FindPair(ReadHandlerNode * node, size_t & position) |
| { |
| for (size_t i = 0; i < mPairArraySize; i++) |
| { |
| if (mPairArray[i].node == node) |
| { |
| position = i; |
| return &mPairArray[i]; |
| } |
| } |
| return nullptr; |
| } |
| |
| CHIP_ERROR insertPair(ReadHandlerNode * node, System::Clock::Timeout timeout) |
| { |
| VerifyOrReturnError(mPairArraySize < kNumMaxReadHandlers, CHIP_ERROR_NO_MEMORY); |
| mPairArray[mPairArraySize].node = node; |
| mPairArray[mPairArraySize].timeout = timeout; |
| mPairArraySize++; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void removePair(ReadHandlerNode * node) |
| { |
| size_t position; |
| NodeTimeoutPair * pair = FindPair(node, position); |
| VerifyOrReturn(pair != nullptr); |
| |
| size_t nextPos = static_cast<size_t>(position + 1); |
| size_t moveNum = static_cast<size_t>(mPairArraySize - nextPos); |
| |
| // Compress array after removal, if the removed position is not the last |
| if (moveNum) |
| { |
| memmove(&mPairArray[position], &mPairArray[nextPos], sizeof(NodeTimeoutPair) * moveNum); |
| } |
| |
| mPairArraySize--; |
| } |
| |
| static void TimerCallbackInterface(System::Layer * aLayer, void * aAppState) |
| { |
| // Normaly we would call the callback here, thus scheduling an engine run, but we don't need it for this test as we simulate |
| // all the callbacks related to report emissions. The actual callback should look like this: |
| // |
| TimerContext * context = static_cast<TimerContext *>(aAppState); |
| context->TimerFired(); |
| ChipLogProgress(DataManagement, "Simluating engine run for Handler: %p", aAppState); |
| } |
| virtual CHIP_ERROR StartTimer(TimerContext * context, System::Clock::Timeout aTimeout) override |
| { |
| return insertPair(static_cast<ReadHandlerNode *>(context), aTimeout + mMockSystemTimestamp); |
| } |
| virtual void CancelTimer(TimerContext * context) override { removePair(static_cast<ReadHandlerNode *>(context)); } |
| virtual bool IsTimerActive(TimerContext * context) override |
| { |
| size_t position; |
| NodeTimeoutPair * pair = FindPair(static_cast<ReadHandlerNode *>(context), position); |
| VerifyOrReturnValue(pair != nullptr, false); |
| |
| return pair->timeout > mMockSystemTimestamp; |
| } |
| |
| virtual System::Clock::Timestamp GetCurrentMonotonicTimestamp() override { return mMockSystemTimestamp; } |
| |
| void SetMockSystemTimestamp(System::Clock::Timestamp aMockTimestamp) { mMockSystemTimestamp = aMockTimestamp; } |
| |
| // Increment the mock timestamp by aTime and call callbacks for timers that have expired. Checks if the timeout expired after |
| // incrementing |
| void IncrementMockTimestamp(System::Clock::Milliseconds64 aTime) |
| { |
| mMockSystemTimestamp = mMockSystemTimestamp + aTime; |
| for (size_t i = 0; i < mPairArraySize; i++) |
| { |
| if (mPairArray[i].timeout <= mMockSystemTimestamp) |
| { |
| TimerCallbackInterface(nullptr, mPairArray[i].node); |
| } |
| } |
| } |
| }; |
| |
| /// @brief TestTimerSynchronizedDelegate is a mock of the TimerDelegate interface that allows to control the time without dependency |
| /// on the system layer. This also simulates the system timer by verifying if the timeout expired when incrementing the mock |
| /// timestamp. only one timer can be active at a time, which is the one has the earliest timeout. |
| /// It is used to test the SynchronizedReportSchedulerImpl. |
| class TestTimerSynchronizedDelegate : public ReportScheduler::TimerDelegate |
| { |
| public: |
| static void TimerCallbackInterface(System::Layer * aLayer, void * aAppState) |
| { |
| TimerContext * context = static_cast<TimerContext *>(aAppState); |
| context->TimerFired(); |
| } |
| virtual CHIP_ERROR StartTimer(TimerContext * context, System::Clock::Timeout aTimeout) override |
| { |
| if (nullptr == context) |
| { |
| return CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| mTimerContext = context; |
| mTimerTimeout = mMockSystemTimestamp + aTimeout; |
| return CHIP_NO_ERROR; |
| } |
| virtual void CancelTimer(TimerContext * context) override |
| { |
| VerifyOrReturn(nullptr != mTimerContext); |
| mTimerContext = nullptr; |
| mTimerTimeout = System::Clock::Milliseconds64(0x7FFFFFFFFFFFFFFF); |
| } |
| virtual bool IsTimerActive(TimerContext * context) override |
| { |
| return (nullptr != mTimerContext) && (mTimerTimeout > mMockSystemTimestamp); |
| } |
| |
| virtual System::Clock::Timestamp GetCurrentMonotonicTimestamp() override { return mMockSystemTimestamp; } |
| |
| void SetMockSystemTimestamp(System::Clock::Timestamp aMockTimestamp) { mMockSystemTimestamp = aMockTimestamp; } |
| |
| // Increment the mock timestamp one milisecond at a time for a total of aTime miliseconds. Checks if the timeout expired when |
| // incrementing |
| void IncrementMockTimestamp(System::Clock::Milliseconds64 aTime) |
| { |
| for (System::Clock::Milliseconds64 i = System::Clock::Milliseconds64(0); i < aTime; i++) |
| { |
| mMockSystemTimestamp++; |
| if (mMockSystemTimestamp == mTimerTimeout) |
| { |
| TimerCallbackInterface(nullptr, mTimerContext); |
| } |
| } |
| |
| if (aTime == System::Clock::Milliseconds64(0)) |
| { |
| if (mMockSystemTimestamp == mTimerTimeout) |
| { |
| TimerCallbackInterface(nullptr, mTimerContext); |
| } |
| } |
| } |
| |
| TimerContext * mTimerContext = nullptr; |
| System::Clock::Timeout mTimerTimeout = System::Clock::Milliseconds64(0x7FFFFFFFFFFFFFFF); |
| System::Clock::Timestamp mMockSystemTimestamp = System::Clock::Milliseconds64(0); |
| }; |
| |
| TestTimerDelegate sTestTimerDelegate; |
| ReportSchedulerImpl sScheduler(&sTestTimerDelegate); |
| |
| TestTimerSynchronizedDelegate sTestTimerSynchronizedDelegate; |
| SynchronizedReportSchedulerImpl syncScheduler(&sTestTimerSynchronizedDelegate); |
| |
| class TestReportScheduler |
| { |
| public: |
| static ReadHandler * GetReadHandlerFromPool(ReportScheduler * scheduler, uint32_t target) |
| { |
| uint32_t i = 0; |
| ReadHandler * ret = nullptr; |
| |
| scheduler->mNodesPool.ForEachActiveObject([target, &i, &ret](ReadHandlerNode * node) { |
| if (i == target) |
| { |
| ret = node->GetReadHandler(); |
| return Loop::Break; |
| } |
| |
| i++; |
| return Loop::Continue; |
| }); |
| |
| return ret; |
| } |
| |
| static void TestReadHandlerList(nlTestSuite * aSuite, void * aContext) |
| { |
| TestContext & ctx = *static_cast<TestContext *>(aContext); |
| NullReadHandlerCallback nullCallback; |
| // exchange context |
| Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); |
| |
| // Read handler pool |
| ObjectPool<ReadHandler, kNumMaxReadHandlers> readHandlerPool; |
| |
| // Initialize mock timestamp |
| sTestTimerDelegate.SetMockSystemTimestamp(Milliseconds64(0)); |
| |
| for (size_t i = 0; i < kNumMaxReadHandlers; i++) |
| { |
| ReadHandler * readHandler = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); |
| NL_TEST_ASSERT(aSuite, nullptr != readHandler); |
| VerifyOrReturn(nullptr != readHandler); |
| NL_TEST_ASSERT(aSuite, nullptr != sScheduler.FindReadHandlerNode(readHandler)); |
| } |
| |
| NL_TEST_ASSERT(aSuite, readHandlerPool.Allocated() == kNumMaxReadHandlers); |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == kNumMaxReadHandlers); |
| NL_TEST_ASSERT(aSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 1); |
| |
| // Test unregister first ReadHandler |
| uint32_t target = 0; |
| ReadHandler * firstReadHandler = GetReadHandlerFromPool(&sScheduler, target); |
| |
| NL_TEST_ASSERT(aSuite, nullptr != firstReadHandler); |
| sScheduler.OnReadHandlerDestroyed(firstReadHandler); |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == kNumMaxReadHandlers - 1); |
| NL_TEST_ASSERT(aSuite, nullptr == sScheduler.FindReadHandlerNode(firstReadHandler)); |
| |
| // Test unregister middle ReadHandler |
| target = static_cast<uint32_t>(sScheduler.GetNumReadHandlers() / 2); |
| ReadHandler * middleReadHandler = GetReadHandlerFromPool(&sScheduler, target); |
| |
| NL_TEST_ASSERT(aSuite, nullptr != middleReadHandler); |
| sScheduler.OnReadHandlerDestroyed(middleReadHandler); |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == kNumMaxReadHandlers - 2); |
| NL_TEST_ASSERT(aSuite, nullptr == sScheduler.FindReadHandlerNode(middleReadHandler)); |
| |
| // Test unregister last ReadHandler |
| target = static_cast<uint32_t>(sScheduler.GetNumReadHandlers() - 1); |
| ReadHandler * lastReadHandler = GetReadHandlerFromPool(&sScheduler, target); |
| sScheduler.OnReadHandlerDestroyed(lastReadHandler); |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == kNumMaxReadHandlers - 3); |
| NL_TEST_ASSERT(aSuite, nullptr == sScheduler.FindReadHandlerNode(lastReadHandler)); |
| |
| sScheduler.UnregisterAllHandlers(); |
| // Confirm all ReadHandlers are unregistered from the scheduler |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == 0); |
| readHandlerPool.ForEachActiveObject([&](ReadHandler * handler) { |
| NL_TEST_ASSERT(aSuite, nullptr == sScheduler.FindReadHandlerNode(handler)); |
| return Loop::Continue; |
| }); |
| |
| readHandlerPool.ReleaseAll(); |
| exchangeCtx->Close(); |
| NL_TEST_ASSERT(aSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); |
| } |
| |
| static void TestReportTiming(nlTestSuite * aSuite, void * aContext) |
| { |
| TestContext & ctx = *static_cast<TestContext *>(aContext); |
| NullReadHandlerCallback nullCallback; |
| // exchange context |
| Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); |
| |
| // Read handler pool |
| ObjectPool<ReadHandler, kNumMaxReadHandlers> readHandlerPool; |
| |
| // Initialize mock timestamp |
| sTestTimerDelegate.SetMockSystemTimestamp(Milliseconds64(0)); |
| |
| // Dirty read handler, will be triggered at min interval |
| // Test OnReadHandler created |
| ReadHandler * readHandler1 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); |
| |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(1)); |
| ReadHandlerNode * node = sScheduler.FindReadHandlerNode(readHandler1); |
| node->SetIntervalTimeStamps(readHandler1); |
| readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| readHandler1->ForceDirtyState(); |
| |
| // Clean read handler, will be triggered at max interval |
| ReadHandler * readHandler2 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(3)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(0)); |
| node = sScheduler.FindReadHandlerNode(readHandler2); |
| node->SetIntervalTimeStamps(readHandler2); |
| readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| // Clean read handler, will be triggered at max interval, but will be cancelled before |
| ReadHandler * readHandler3 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMaxReportingInterval(3)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMinReportingIntervalForTests(0)); |
| node = sScheduler.FindReadHandlerNode(readHandler3); |
| node->SetIntervalTimeStamps(readHandler3); |
| readHandler3->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| // Confirms that none of the ReadHandlers are currently reportable |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler3)); |
| |
| // Simulate system clock increment |
| sTestTimerDelegate.IncrementMockTimestamp(Milliseconds64(1100)); |
| |
| // Checks that the first ReadHandler is reportable after 1 second since it is dirty and min interval has expired |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportableNow(readHandler3)); |
| |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportScheduled(readHandler3)); |
| sScheduler.CancelReport(readHandler3); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler3)); |
| |
| // Simulate system clock increment |
| sTestTimerDelegate.IncrementMockTimestamp(Milliseconds64(2000)); |
| |
| // Checks that all ReadHandlers are reportable |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportableNow(readHandler2)); |
| // Even if its timer got cancelled, readHandler3 should still be considered reportable as the max interval has expired |
| // and it is in generating report state |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportableNow(readHandler3)); |
| |
| // Clear dirty flag on readHandler1 and confirm it is still reportable by time |
| readHandler1->ClearForceDirtyFlag(); |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportableNow(readHandler1)); |
| |
| sScheduler.UnregisterAllHandlers(); |
| readHandlerPool.ReleaseAll(); |
| exchangeCtx->Close(); |
| NL_TEST_ASSERT(aSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); |
| } |
| |
| static void TestObserverCallbacks(nlTestSuite * aSuite, void * aContext) |
| { |
| TestContext & ctx = *static_cast<TestContext *>(aContext); |
| NullReadHandlerCallback nullCallback; |
| // exchange context |
| Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); |
| |
| // Read handler pool |
| ObjectPool<ReadHandler, kNumMaxReadHandlers> readHandlerPool; |
| |
| // Initialize mock timestamp |
| sTestTimerDelegate.SetMockSystemTimestamp(Milliseconds64(0)); |
| |
| ReadHandler * readHandler = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &sScheduler); |
| // Test OnReadHandler created registered the ReadHandler in the scheduler |
| NL_TEST_ASSERT(aSuite, nullptr != sScheduler.FindReadHandlerNode(readHandler)); |
| |
| // Should have registered the read handler in the scheduler and scheduled a report |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == 1); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler->SetMaxReportingInterval(2)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler->SetMinReportingIntervalForTests(1)); |
| ReadHandlerNode * node = sScheduler.FindReadHandlerNode(readHandler); |
| node->SetIntervalTimeStamps(readHandler); |
| |
| // Test OnReportingIntervalsChanged modified the intervals and re-scheduled a report |
| NL_TEST_ASSERT(aSuite, node->GetMinTimestamp().count() == 1000); |
| NL_TEST_ASSERT(aSuite, node->GetMaxTimestamp().count() == 2000); |
| |
| // Do those manually to avoid scheduling an engine run |
| readHandler->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportScheduled(readHandler)); |
| |
| NL_TEST_ASSERT(aSuite, nullptr != node); |
| VerifyOrReturn(nullptr != node); |
| NL_TEST_ASSERT(aSuite, node->GetReadHandler() == readHandler); |
| |
| // Test OnBecameReportable |
| readHandler->ForceDirtyState(); |
| // Should have changed the scheduled timeout to the handler's min interval, to check, we wait for the min interval to |
| // expire |
| // Simulate system clock increment |
| sTestTimerDelegate.IncrementMockTimestamp(Milliseconds64(1100)); |
| |
| // Check that no report is scheduled since the min interval has expired, the timer should now be stopped |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler)); |
| |
| // Test OnSubscriptionAction |
| readHandler->ClearForceDirtyFlag(); |
| readHandler->mObserver->OnSubscriptionAction(readHandler); |
| // Should have changed the scheduled timeout to the handlers max interval, to check, we wait for the min interval to |
| // confirm it is not expired yet so the report should still be scheduled |
| |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportScheduled(readHandler)); |
| // Simulate system clock increment |
| sTestTimerDelegate.IncrementMockTimestamp(Milliseconds64(1100)); |
| |
| // Check that the report is still scheduled as the max interval has not expired yet and the dirty flag was cleared |
| NL_TEST_ASSERT(aSuite, sScheduler.IsReportScheduled(readHandler)); |
| // Simulate system clock increment |
| sTestTimerDelegate.IncrementMockTimestamp(Milliseconds64(2100)); |
| |
| // Check that no report is scheduled since the max interval should have expired, the timer should now be stopped |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler)); |
| |
| // Test OnReadHandlerDestroyed |
| readHandler->mObserver->OnReadHandlerDestroyed(readHandler); |
| // Should have unregistered the read handler in the scheduler and cancelled the report |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler)); |
| NL_TEST_ASSERT(aSuite, sScheduler.GetNumReadHandlers() == 0); |
| NL_TEST_ASSERT(aSuite, nullptr == sScheduler.FindReadHandlerNode(readHandler)); |
| |
| readHandlerPool.ReleaseAll(); |
| exchangeCtx->Close(); |
| NL_TEST_ASSERT(aSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); |
| } |
| |
| static void TestSynchronizedScheduler(nlTestSuite * aSuite, void * aContext) |
| { |
| TestContext & ctx = *static_cast<TestContext *>(aContext); |
| NullReadHandlerCallback nullCallback; |
| // exchange context |
| Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); |
| |
| // First test: ReadHandler 2 merge on ReadHandler 1 max interval |
| // Read handler pool |
| ObjectPool<ReadHandler, kNumMaxReadHandlers> readHandlerPool; |
| |
| // Initialize the mock system time |
| sTestTimerSynchronizedDelegate.SetMockSystemTimestamp(System::Clock::Milliseconds64(0)); |
| |
| ReadHandler * readHandler1 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(0)); |
| ReadHandlerNode * node1 = syncScheduler.FindReadHandlerNode(readHandler1); |
| node1->SetIntervalTimeStamps(readHandler1); |
| readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| ReadHandler * readHandler2 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(3)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(1)); |
| ReadHandlerNode * node2 = syncScheduler.FindReadHandlerNode(readHandler2); |
| node2->SetIntervalTimeStamps(readHandler2); |
| readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| // Confirm all handler are currently registered in the scheduler |
| NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 2); |
| |
| // Confirm that a report emission is scheduled |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportScheduled()); |
| |
| // Validates that the lowest max is selected as the common max timestamp |
| NL_TEST_ASSERT(aSuite, syncScheduler.mNextMaxTimestamp == node1->GetMaxTimestamp()); |
| // Validates that the highest reportable min is selected as the common min interval (0 here) |
| NL_TEST_ASSERT(aSuite, syncScheduler.mNextMinTimestamp == node1->GetMinTimestamp()); |
| // Validates that the next report emission is scheduled on the common max timestamp |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == syncScheduler.mNextMaxTimestamp); |
| |
| // Simulate waiting for the max interval to expire (2s) |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(2000)); |
| |
| // Confirm that both handlers are now reportable since the timer has expired (readHandler1 from its max and readHandler2 |
| // from its sync) |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| // Confirm timeout has expired and no report is scheduled, an engine run would typically happen here |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !sScheduler.IsReportScheduled(readHandler2)); |
| |
| // Simulate a report emission for readHandler1 |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| // Simulate a report emission for readHandler2 |
| readHandler2->mObserver->OnSubscriptionAction(readHandler2); |
| |
| // Validate that the max timestamp for both readhandlers got updated and that the next report emission is scheduled on |
| // the new max timestamp for readhandler1 |
| NL_TEST_ASSERT(aSuite, node1->GetMaxTimestamp() > sTestTimerSynchronizedDelegate.GetCurrentMonotonicTimestamp()); |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| |
| // Confirm behavior when a read handler becomes dirty |
| readHandler2->ForceDirtyState(); |
| // OnBecomeReportable should have been called on ForceDirtyState because readHandler callbacks are now integrated |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| // Simulate wait enough for min timestamp of readHandler2 to be reached (1s) |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| |
| // confirm report scheduled now |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node2->GetMinTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node2->GetMinTimestamp()); |
| |
| // Confirm that the next report emission is scheduled on the min timestamp of readHandler2 as it is the highest reportable |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node2->GetMinTimestamp()); |
| |
| // Simulate a report emission for readHandler1 |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| |
| // ReadHandler 2 should still be reportable since it hasn't emitted a report yet |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| readHandler2->ClearForceDirtyFlag(); |
| readHandler2->mObserver->OnSubscriptionAction(readHandler2); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| // Validate next report scheduled on the max timestamp of readHandler1 |
| NL_TEST_ASSERT(aSuite, node1->GetMaxTimestamp() > sTestTimerSynchronizedDelegate.GetCurrentMonotonicTimestamp()); |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| |
| // Simulate readHandler1 becoming dirty after less than 1 seconds, since it is reportable now, this will Schedule an Engin |
| // run immediately |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(900)); |
| readHandler1->ForceDirtyState(); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| // Simulate a report emission for readHandler1 |
| readHandler1->ClearForceDirtyFlag(); |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| // The next report should be scheduler on the max timestamp of readHandler1 and readHandler2 should be synced |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node1->GetMaxTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(2000)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| readHandler2->mObserver->OnSubscriptionAction(readHandler2); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| |
| // Simulate a new ReadHandler being added with a min timestamp that will force a conflict |
| |
| // Wait for 1 second, nothing should happen here |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| |
| ReadHandler * readHandler3 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMaxReportingInterval(3)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler3->SetMinReportingIntervalForTests(2)); |
| ReadHandlerNode * node3 = syncScheduler.FindReadHandlerNode(readHandler3); |
| node3->SetIntervalTimeStamps(readHandler3); |
| readHandler3->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| // Confirm all handler are currently registered in the scheduler |
| NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 3); |
| |
| // Since the min interval on readHandler3 is 2, it should be above the current max timestamp, therefore the next report |
| // should still happen on the max timestamp of readHandler1 and the sync should be done on future reports |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| // The min timestamp should also not have changed since the min of readhandler3 is higher than the current max |
| NL_TEST_ASSERT(aSuite, syncScheduler.mNextMinTimestamp == node2->GetMinTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| |
| // Confirm that readHandler1 and readHandler 2 are now reportable, whilst readHandler3 is not |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3)); |
| readHandler1->mObserver->OnBecameReportable(readHandler1); |
| readHandler2->mObserver->OnBecameReportable(readHandler2); |
| |
| // Simulate a report emission for readHandler1 and readHandler2 |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| readHandler1->mObserver->OnSubscriptionAction(readHandler2); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| // Confirm that next report is scheduled on the max timestamp of readHandler3 and other 2 readHandlers are synced |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node3->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node3->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node3->GetMaxTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(2000)); |
| |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler3)); |
| readHandler1->mObserver->OnBecameReportable(readHandler1); |
| readHandler2->mObserver->OnBecameReportable(readHandler2); |
| readHandler3->mObserver->OnBecameReportable(readHandler3); |
| // Engine run should happen here and send all reports |
| readHandler1->mObserver->OnSubscriptionAction(readHandler1); |
| readHandler2->mObserver->OnSubscriptionAction(readHandler2); |
| readHandler3->mObserver->OnSubscriptionAction(readHandler3); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node1->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node1->GetMaxTimestamp()); |
| |
| // Now simulate a new readHandler being added with a max forcing a conflict |
| ReadHandler * readHandler4 = |
| readHandlerPool.CreateObject(nullCallback, exchangeCtx, ReadHandler::InteractionType::Subscribe, &syncScheduler); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler4->SetMaxReportingInterval(1)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler4->SetMinReportingIntervalForTests(0)); |
| ReadHandlerNode * node4 = syncScheduler.FindReadHandlerNode(readHandler4); |
| node4->SetIntervalTimeStamps(readHandler4); |
| readHandler4->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| |
| // Confirm all handler are currently registered in the scheduler |
| NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 4); |
| |
| // Confirm next report is scheduled on the max timestamp of readHandler4 and other handlers 1 and 2 are synced |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node4->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node4->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node4->GetMaxTimestamp()); |
| |
| // Confirm handler 3 is synched on a later timestamp since its min is higher than the max of readHandler4 |
| NL_TEST_ASSERT(aSuite, node3->GetSyncTimestamp() == node1->GetMaxTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1100)); |
| |
| // Confirm readHandler1, 2 and 4 are reportable |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler4)); |
| |
| // Confirm readHandler3 is not reportable |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3)); |
| readHandler4->mObserver->OnBecameReportable(readHandler1); |
| readHandler4->mObserver->OnBecameReportable(readHandler2); |
| readHandler4->mObserver->OnBecameReportable(readHandler4); |
| readHandler4->mObserver->OnSubscriptionAction(readHandler1); |
| readHandler4->mObserver->OnSubscriptionAction(readHandler2); |
| readHandler4->mObserver->OnSubscriptionAction(readHandler4); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler4)); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| |
| // Confirm readHandler3 is reportable and other handlers are synced |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler3)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler4)); |
| syncScheduler.OnBecameReportable(readHandler1); |
| syncScheduler.OnBecameReportable(readHandler2); |
| syncScheduler.OnBecameReportable(readHandler3); |
| syncScheduler.OnBecameReportable(readHandler4); |
| syncScheduler.OnSubscriptionAction(readHandler1); |
| syncScheduler.OnSubscriptionAction(readHandler2); |
| syncScheduler.OnSubscriptionAction(readHandler3); |
| syncScheduler.OnSubscriptionAction(readHandler4); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler4)); |
| |
| // Next emission should be scheduled on the max timestamp of readHandler4 as it is the most restrictive, and handlers 1 and |
| // 2 should be synced to handler 4 |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node4->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node4->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node4->GetMaxTimestamp()); |
| // handler 3 should have a sync on a different point as its min is higher, in this case it is the max timestamp of handler 1 |
| NL_TEST_ASSERT(aSuite, node3->GetSyncTimestamp() == node1->GetMaxTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| |
| // Confirm readHandler 1-2-4 are reportable |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler4)); |
| |
| // Confirm readHandler3 is not reportable because of its min interval |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler3)); |
| |
| syncScheduler.OnReadHandlerDestroyed(readHandler1); |
| syncScheduler.OnReadHandlerDestroyed(readHandler2); |
| syncScheduler.OnReadHandlerDestroyed(readHandler3); |
| syncScheduler.OnReadHandlerDestroyed(readHandler4); |
| |
| // Reset all handlers |
| // Test case: Scheduler 1 and 2 are reportable but min2 > max1, they should sync only when possible (min2 = 3, max1 = 2) |
| NL_TEST_ASSERT(aSuite, syncScheduler.GetNumReadHandlers() == 0); |
| |
| readHandler1->MoveToState(ReadHandler::HandlerState::Idle); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMaxReportingInterval(2)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler1->SetMinReportingIntervalForTests(0)); |
| readHandler1->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| syncScheduler.OnReadHandlerCreated(readHandler1); |
| // Forcing the dirty flag to make the scheduler call Engine::ScheduleRun() immediately |
| readHandler1->ForceDirtyState(); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| |
| readHandler2->MoveToState(ReadHandler::HandlerState::Idle); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMaxReportingInterval(4)); |
| NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == readHandler2->SetMinReportingIntervalForTests(3)); |
| readHandler2->MoveToState(ReadHandler::HandlerState::GeneratingReports); |
| syncScheduler.OnReadHandlerCreated(readHandler2); |
| readHandler2->ForceDirtyState(); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| node1 = syncScheduler.FindReadHandlerNode(readHandler1); |
| node2 = syncScheduler.FindReadHandlerNode(readHandler2); |
| |
| readHandler1->ClearForceDirtyFlag(); // report got emited so clear dirty flag |
| syncScheduler.OnSubscriptionAction(readHandler1); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| |
| // Confirm next report is scheduled on the max timestamp of readHandler1 and readhandler2 is not synced |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| // Node 2's sync timestamp should have remained unaffected since its min is higher |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node2->GetMaxTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(2000)); |
| // Verify handler 1 became reportable |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| syncScheduler.OnBecameReportable(readHandler1); |
| |
| // simulate run with only readhandler1 reportable |
| syncScheduler.OnSubscriptionAction(readHandler1); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, !syncScheduler.IsReportableNow(readHandler2)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node2->GetMinTimestamp()); |
| NL_TEST_ASSERT(aSuite, node1->GetSyncTimestamp() == node2->GetMinTimestamp()); |
| |
| sTestTimerSynchronizedDelegate.IncrementMockTimestamp(System::Clock::Milliseconds64(1000)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler1)); |
| NL_TEST_ASSERT(aSuite, syncScheduler.IsReportableNow(readHandler2)); |
| |
| readHandler2->ClearForceDirtyFlag(); |
| syncScheduler.OnSubscriptionAction(readHandler1); |
| syncScheduler.OnSubscriptionAction(readHandler2); |
| |
| NL_TEST_ASSERT(aSuite, syncScheduler.mTestNextReportTimestamp == node1->GetMaxTimestamp()); |
| NL_TEST_ASSERT(aSuite, node2->GetSyncTimestamp() == node2->GetMaxTimestamp()); |
| |
| syncScheduler.UnregisterAllHandlers(); |
| readHandlerPool.ReleaseAll(); |
| exchangeCtx->Close(); |
| NL_TEST_ASSERT(aSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); |
| } |
| }; |
| |
| } // namespace reporting |
| } // namespace app |
| } // namespace chip |
| |
| namespace { |
| |
| /** |
| * Test Suite. It lists all the test functions. |
| */ |
| |
| static nlTest sTests[] = { |
| NL_TEST_DEF("TestReadHandlerList", chip::app::reporting::TestReportScheduler::TestReadHandlerList), |
| NL_TEST_DEF("TestReportTiming", chip::app::reporting::TestReportScheduler::TestReportTiming), |
| NL_TEST_DEF("TestObserverCallbacks", chip::app::reporting::TestReportScheduler::TestObserverCallbacks), |
| NL_TEST_DEF("TestSynchronizedScheduler", chip::app::reporting::TestReportScheduler::TestSynchronizedScheduler), |
| NL_TEST_SENTINEL(), |
| }; |
| |
| nlTestSuite sSuite = { "TestReportScheduler", &sTests[0], TestContext::Initialize, TestContext::Finalize }; |
| |
| } // namespace |
| |
| int TestReportScheduler() |
| { |
| return chip::ExecuteTestsWithContext<TestContext>(&sSuite); |
| } |
| |
| CHIP_REGISTER_TEST_SUITE(TestReportScheduler); |