From 13186080743ec5f6c1c95ba80651b19517e5efe4 Mon Sep 17 00:00:00 2001 From: Kevin Schoedel <67607049+kpschoedel@users.noreply.github.com> Date: Fri, 10 Dec 2021 14:34:05 -0500 Subject: [PATCH] Add a unit test of System::Layer timer order (#12895) #### Problem Nothing explicitly tests that `System::Layer` timers fire in chronological order. Since we can now have a mock system clock, this is testable. #### Change overview - Add a unit test verifying that timer callbacks run in order. - Add unit test assertions for timer statistics. - Revise implementation selection in TestSystemTimer to use the `Layer` class rather than only '#if', since not every configuration with sockets/LwIP necessarily needs to use the provided `Layer` implementations. - Provide a common MockClock implementation. #### Testing Res ipsa loquitur. --- .../tests/TestActiveResolveAttempts.cpp | 56 +++----- src/system/SystemClock.h | 30 +++++ src/system/SystemStats.h | 6 + src/system/tests/TestSystemClock.cpp | 11 +- src/system/tests/TestSystemTimer.cpp | 127 +++++++++++++++--- 5 files changed, 166 insertions(+), 64 deletions(-) diff --git a/src/lib/dnssd/minimal_mdns/tests/TestActiveResolveAttempts.cpp b/src/lib/dnssd/minimal_mdns/tests/TestActiveResolveAttempts.cpp index 970a9a896a2b17..2792284f8d6226 100644 --- a/src/lib/dnssd/minimal_mdns/tests/TestActiveResolveAttempts.cpp +++ b/src/lib/dnssd/minimal_mdns/tests/TestActiveResolveAttempts.cpp @@ -26,18 +26,6 @@ using namespace chip; using namespace chip::System::Clock::Literals; using chip::System::Clock::Timeout; -class MockClock : public System::Clock::ClockImpl -{ -public: - System::Clock::Microseconds64 GetMonotonicMicroseconds64() override { return mMsec; } - System::Clock::Milliseconds64 GetMonotonicMilliseconds64() override { return mMsec; } - - void Advance(System::Clock::Milliseconds32 ms) { mMsec += ms; } - -private: - System::Clock::Milliseconds64 mMsec; -}; - PeerId MakePeerId(NodeId nodeId) { PeerId peerId; @@ -46,10 +34,10 @@ PeerId MakePeerId(NodeId nodeId) void TestSinglePeerAddRemove(nlTestSuite * inSuite, void * inContext) { - MockClock mockClock; + System::Clock::Internal::MockClock mockClock; mdns::Minimal::ActiveResolveAttempts attempts(&mockClock); - mockClock.Advance(1234_ms32); + mockClock.AdvanceMonotonic(1234_ms32); // Starting up, no scheduled peers are expected NL_TEST_ASSERT(inSuite, !attempts.GetTimeUntilNextExpectedResponse().HasValue()); @@ -65,12 +53,12 @@ void TestSinglePeerAddRemove(nlTestSuite * inSuite, void * inContext) // one Next schedule is called, expect to have a delay of 1000 ms NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(1000_ms32)); - mockClock.Advance(500_ms32); + mockClock.AdvanceMonotonic(500_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(500_ms32)); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); // past due date: timeout should be 0 - mockClock.Advance(800_ms32); + mockClock.AdvanceMonotonic(800_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(0_ms32)); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(1))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); @@ -78,7 +66,7 @@ void TestSinglePeerAddRemove(nlTestSuite * inSuite, void * inContext) // one Next schedule is called, expect to have a delay of 2000 ms // sincve the timeout doubles every time NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(2000_ms32)); - mockClock.Advance(100_ms32); + mockClock.AdvanceMonotonic(100_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(1900_ms32)); // once complete, nothing to schedule @@ -89,10 +77,10 @@ void TestSinglePeerAddRemove(nlTestSuite * inSuite, void * inContext) void TestRescheduleSamePeerId(nlTestSuite * inSuite, void * inContext) { - MockClock mockClock; + System::Clock::Internal::MockClock mockClock; mdns::Minimal::ActiveResolveAttempts attempts(&mockClock); - mockClock.Advance(112233_ms32); + mockClock.AdvanceMonotonic(112233_ms32); attempts.MarkPending(MakePeerId(1)); @@ -104,7 +92,7 @@ void TestRescheduleSamePeerId(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(1000_ms32)); // 2nd try goes to 2 seconds (once at least 1 second passes) - mockClock.Advance(1234_ms32); + mockClock.AdvanceMonotonic(1234_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(0_ms32)); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(1))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); @@ -122,21 +110,21 @@ void TestRescheduleSamePeerId(nlTestSuite * inSuite, void * inContext) void TestLRU(nlTestSuite * inSuite, void * inContext) { // validates that the LRU logic is working - MockClock mockClock; + System::Clock::Internal::MockClock mockClock; mdns::Minimal::ActiveResolveAttempts attempts(&mockClock); - mockClock.Advance(334455_ms32); + mockClock.AdvanceMonotonic(334455_ms32); // add a single very old peer attempts.MarkPending(MakePeerId(9999)); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(9999))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); - mockClock.Advance(1000_ms32); + mockClock.AdvanceMonotonic(1000_ms32); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(9999))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); - mockClock.Advance(2000_ms32); + mockClock.AdvanceMonotonic(2000_ms32); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(9999))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); @@ -145,7 +133,7 @@ void TestLRU(nlTestSuite * inSuite, void * inContext) for (uint32_t i = 1; i < mdns::Minimal::ActiveResolveAttempts::kRetryQueueSize; i++) { attempts.MarkPending(MakePeerId(i)); - mockClock.Advance(1_ms32); + mockClock.AdvanceMonotonic(1_ms32); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(i))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); @@ -159,7 +147,7 @@ void TestLRU(nlTestSuite * inSuite, void * inContext) // add another element - this should overwrite peer 9999 attempts.MarkPending(MakePeerId(mdns::Minimal::ActiveResolveAttempts::kRetryQueueSize)); - mockClock.Advance(32_s16); + mockClock.AdvanceMonotonic(32_s16); for (Optional peerId = attempts.NextScheduledPeer(); peerId.HasValue(); peerId = attempts.NextScheduledPeer()) { @@ -182,7 +170,7 @@ void TestLRU(nlTestSuite * inSuite, void * inContext) break; } - mockClock.Advance(ms.Value()); + mockClock.AdvanceMonotonic(ms.Value()); Optional peerId = attempts.NextScheduledPeer(); while (peerId.HasValue()) @@ -196,10 +184,10 @@ void TestLRU(nlTestSuite * inSuite, void * inContext) void TestNextPeerOrdering(nlTestSuite * inSuite, void * inContext) { - MockClock mockClock; + System::Clock::Internal::MockClock mockClock; mdns::Minimal::ActiveResolveAttempts attempts(&mockClock); - mockClock.Advance(123321_ms32); + mockClock.AdvanceMonotonic(123321_ms32); // add a single peer that will be resolved quickly attempts.MarkPending(MakePeerId(1)); @@ -207,7 +195,7 @@ void TestNextPeerOrdering(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(1))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(1000_ms32)); - mockClock.Advance(20_ms32); + mockClock.AdvanceMonotonic(20_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(980_ms32)); // expect peerid to be resolve within 1 second from now @@ -216,13 +204,13 @@ void TestNextPeerOrdering(nlTestSuite * inSuite, void * inContext) // mock that we are querying 2 as well NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(2))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); - mockClock.Advance(80_ms32); + mockClock.AdvanceMonotonic(80_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(900_ms32)); // Peer 1 is done, now peer2 should be pending (in 980ms) attempts.Complete(MakePeerId(1)); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(920_ms32)); - mockClock.Advance(20_ms32); + mockClock.AdvanceMonotonic(20_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(900_ms32)); // Once peer 3 is added, queue should be @@ -236,14 +224,14 @@ void TestNextPeerOrdering(nlTestSuite * inSuite, void * inContext) // After the clock advance // - 400 ms until peer id 2 is pending // - 500 ms until peer id 3 is pending - mockClock.Advance(500_ms32); + mockClock.AdvanceMonotonic(500_ms32); NL_TEST_ASSERT(inSuite, attempts.GetTimeUntilNextExpectedResponse() == Optional(400_ms32)); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); // advancing the clock 'too long' will return both other entries, in reverse order due to how // the internal cache is built - mockClock.Advance(500_ms32); + mockClock.AdvanceMonotonic(500_ms32); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(3))); NL_TEST_ASSERT(inSuite, attempts.NextScheduledPeer() == Optional::Value(MakePeerId(2))); NL_TEST_ASSERT(inSuite, !attempts.NextScheduledPeer().HasValue()); diff --git a/src/system/SystemClock.h b/src/system/SystemClock.h index a65941ef2dbe69..6202ea8b6861af 100644 --- a/src/system/SystemClock.h +++ b/src/system/SystemClock.h @@ -303,6 +303,36 @@ inline void SetSystemClockForTesting(Clock::ClockBase * clock) Clock::Internal::gClockBase = clock; } +// Provide a mock implementation for use by unit tests. +class MockClock : public ClockImpl +{ +public: + Microseconds64 GetMonotonicMicroseconds64() override { return mSystemTime; } + Milliseconds64 GetMonotonicMilliseconds64() override { return std::chrono::duration_cast(mSystemTime); } + CHIP_ERROR GetClock_RealTime(Microseconds64 & aCurTime) override + { + aCurTime = mRealTime; + return CHIP_NO_ERROR; + } + CHIP_ERROR GetClock_RealTimeMS(Milliseconds64 & aCurTime) override + { + aCurTime = std::chrono::duration_cast(mRealTime); + return CHIP_NO_ERROR; + } + CHIP_ERROR SetClock_RealTime(Microseconds64 aNewCurTime) override + { + mRealTime = aNewCurTime; + return CHIP_NO_ERROR; + } + + void SetMonotonic(Milliseconds64 timestamp) { mSystemTime = timestamp; } + void AdvanceMonotonic(Milliseconds64 increment) { mSystemTime += increment; } + void AdvanceRealTime(Milliseconds64 increment) { mRealTime += increment; } + + Microseconds64 mSystemTime = Clock::kZero; + Microseconds64 mRealTime = Clock::kZero; +}; + } // namespace Internal #if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/system/SystemStats.h b/src/system/SystemStats.h index ec6784094bda1d..9d95e47d58f204 100644 --- a/src/system/SystemStats.h +++ b/src/system/SystemStats.h @@ -156,6 +156,11 @@ const Label * GetStrings(); // Additional macros for testing. #define SYSTEM_STATS_TEST_IN_USE(entry, expected) (chip::System::Stats::GetResourcesInUse()[entry] == (expected)) #define SYSTEM_STATS_TEST_HIGH_WATER_MARK(entry, expected) (chip::System::Stats::GetHighWatermarks()[entry] == (expected)) +#define SYSTEM_STATS_RESET_HIGH_WATER_MARK_FOR_TESTING(entry) \ + do \ + { \ + chip::System::Stats::GetHighWatermarks()[entry] = 0; \ + } while (0) #else // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS @@ -171,5 +176,6 @@ const Label * GetStrings(); #define SYSTEM_STATS_TEST_IN_USE(entry, expected) (true) #define SYSTEM_STATS_TEST_HIGH_WATER_MARK(entry, expected) (true) +#define SYSTEM_STATS_RESET_HIGH_WATER_MARK_FOR_TESTING(entry) #endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS diff --git a/src/system/tests/TestSystemClock.cpp b/src/system/tests/TestSystemClock.cpp index 62ff182841dfc2..0853973b4f5846 100644 --- a/src/system/tests/TestSystemClock.cpp +++ b/src/system/tests/TestSystemClock.cpp @@ -85,14 +85,7 @@ void TestRealClock(nlTestSuite * inSuite, void * inContext) void TestMockClock(nlTestSuite * inSuite, void * inContext) { - class MockClock : public Clock::ClockImpl - { - public: - Clock::Microseconds64 GetMonotonicMicroseconds64() override { return mTime; } - Clock::Milliseconds64 GetMonotonicMilliseconds64() override { return mTime; } - Clock::Milliseconds64 mTime = Clock::kZero; - }; - MockClock clock; + Clock::Internal::MockClock clock; Clock::ClockBase * savedRealClock = &SystemClock(); Clock::Internal::SetSystemClockForTesting(&clock); @@ -101,7 +94,7 @@ void TestMockClock(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, SystemClock().GetMonotonicMicroseconds64() == Clock::kZero); constexpr Clock::Milliseconds64 k1234 = Clock::Milliseconds64(1234); - clock.mTime = k1234; + clock.SetMonotonic(k1234); NL_TEST_ASSERT(inSuite, SystemClock().GetMonotonicMilliseconds64() == k1234); NL_TEST_ASSERT(inSuite, SystemClock().GetMonotonicMicroseconds64() == k1234); diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp index 6520be21959bfa..5ee0f9a2cd1534 100644 --- a/src/system/tests/TestSystemTimer.cpp +++ b/src/system/tests/TestSystemTimer.cpp @@ -49,21 +49,50 @@ using chip::ErrorStr; using namespace chip::System; -static void ServiceEvents(Layer & aLayer) +template +class LayerEvents { +public: + static bool HasServiceEvents() { return false; } + static void ServiceEvents(Layer & aLayer) {} +}; + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - static_cast(aLayer).PrepareEvents(); - static_cast(aLayer).WaitForEvents(); - static_cast(aLayer).HandleEvents(); + +template +class LayerEvents::value>::type> +{ +public: + static bool HasServiceEvents() { return true; } + static void ServiceEvents(Layer & aLayer) + { + LayerSocketsLoop & layer = static_cast(aLayer); + layer.PrepareEvents(); + layer.WaitForEvents(); + layer.HandleEvents(); + } +}; + #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #if CHIP_SYSTEM_CONFIG_USE_LWIP - if (aLayer.IsInitialized()) + +template +class LayerEvents::value>::type> +{ +public: + static bool HasServiceEvents() { return true; } + static void ServiceEvents(Layer & aLayer) { - static_cast(aLayer).HandlePlatformTimer(); + LayerImplLwIP & layer = static_cast(aLayer); + if (layer.IsInitialized()) + { + layer.HandlePlatformTimer(); + } } +}; + #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -} // Test input vector format. static const uint32_t MAX_NUM_TIMERS = 1000; @@ -122,6 +151,9 @@ void HandleTimer10Success(Layer * systemLayer, void * aState) static void CheckOverflow(nlTestSuite * inSuite, void * aContext) { + if (!LayerEvents::HasServiceEvents()) + return; + chip::System::Clock::Milliseconds32 timeout_overflow_0ms = chip::System::Clock::Milliseconds32(652835029); chip::System::Clock::Milliseconds32 timeout_10ms = chip::System::Clock::Milliseconds32(10); @@ -135,7 +167,7 @@ static void CheckOverflow(nlTestSuite * inSuite, void * aContext) while (!sOverflowTestDone) { - ServiceEvents(lSys); + LayerEvents::ServiceEvents(lSys); } lSys.CancelTimer(HandleTimerFailed, aContext); @@ -160,12 +192,68 @@ void HandleGreedyTimer(Layer * aLayer, void * aState) static void CheckStarvation(nlTestSuite * inSuite, void * aContext) { + if (!LayerEvents::HasServiceEvents()) + return; + TestContext & lContext = *static_cast(aContext); Layer & lSys = *lContext.mLayer; lSys.StartTimer(chip::System::Clock::kZero, HandleGreedyTimer, aContext); - ServiceEvents(lSys); + LayerEvents::ServiceEvents(lSys); +} + +void CheckOrder(nlTestSuite * inSuite, void * aContext) +{ + if (!LayerEvents::HasServiceEvents()) + return; + + TestContext & testContext = *static_cast(aContext); + Layer & systemLayer = *testContext.mLayer; + nlTestSuite * const suite = testContext.mTestSuite; + + struct TestState + { + void Record(char c) + { + size_t n = strlen(record); + if (n + 1 < sizeof(record)) + { + record[n++] = c; + record[n] = 0; + } + } + static void A(Layer * layer, void * state) { static_cast(state)->Record('A'); } + static void B(Layer * layer, void * state) { static_cast(state)->Record('B'); } + static void C(Layer * layer, void * state) { static_cast(state)->Record('C'); } + static void D(Layer * layer, void * state) { static_cast(state)->Record('D'); } + char record[5] = { 0 }; + }; + TestState testState; + NL_TEST_ASSERT(suite, testState.record[0] == 0); + + Clock::ClockBase * const savedClock = &SystemClock(); + Clock::Internal::MockClock mockClock; + Clock::Internal::SetSystemClockForTesting(&mockClock); + + using namespace Clock::Literals; + systemLayer.StartTimer(300_ms, TestState::D, &testState); + systemLayer.StartTimer(100_ms, TestState::B, &testState); + systemLayer.StartTimer(200_ms, TestState::C, &testState); + systemLayer.StartTimer(0_ms, TestState::A, &testState); + + LayerEvents::ServiceEvents(systemLayer); + NL_TEST_ASSERT(suite, strcmp(testState.record, "A") == 0); + + mockClock.AdvanceMonotonic(100_ms); + LayerEvents::ServiceEvents(systemLayer); + NL_TEST_ASSERT(suite, strcmp(testState.record, "AB") == 0); + + mockClock.AdvanceMonotonic(200_ms); + LayerEvents::ServiceEvents(systemLayer); + NL_TEST_ASSERT(suite, strcmp(testState.record, "ABCD") == 0); + + Clock::Internal::SetSystemClockForTesting(savedClock); } // Test the implementation helper classes TimerPool, TimerList, and TimerData. @@ -210,6 +298,9 @@ void chip::System::TestTimer::CheckTimerPool(nlTestSuite * inSuite, void * aCont TimerPool pool; NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 0); SYSTEM_STATS_RESET(Stats::kSystemLayer_NumTimers); + SYSTEM_STATS_RESET_HIGH_WATER_MARK_FOR_TESTING(Stats::kSystemLayer_NumTimers); + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 0)); + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_HIGH_WATER_MARK(Stats::kSystemLayer_NumTimers, 0)); // Test TimerPool::Create() and TimerData accessors. @@ -217,9 +308,7 @@ void chip::System::TestTimer::CheckTimerPool(nlTestSuite * inSuite, void * aCont { timer.timer = pool.Create(systemLayer, timer.awakenTime, timer.onComplete, &testState); } -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 4); -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 4)); for (auto & timer : testTimer) { @@ -300,24 +389,19 @@ void chip::System::TestTimer::CheckTimerPool(nlTestSuite * inSuite, void * aCont testTimer[0].timer = nullptr; NL_TEST_ASSERT(suite, testState.count == 1); NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 3); -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 3); -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 3)); // Test TimerPool::Release() pool.Release(testTimer[1].timer); testTimer[1].timer = nullptr; NL_TEST_ASSERT(suite, testState.count == 1); NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 2); -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 2); -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 2)); pool.ReleaseAll(); NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 0); -#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS - NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 0); -#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 0)); + NL_TEST_ASSERT(suite, SYSTEM_STATS_TEST_HIGH_WATER_MARK(Stats::kSystemLayer_NumTimers, 4)); } // Test Suite @@ -330,6 +414,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Timer::TestOverflow", CheckOverflow), NL_TEST_DEF("Timer::TestTimerStarvation", CheckStarvation), + NL_TEST_DEF("Timer::TestTimerOrder", CheckOrder), NL_TEST_DEF("Timer::TestTimerPool", chip::System::TestTimer::CheckTimerPool), NL_TEST_SENTINEL() };