/* * * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * 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. */ /** * @file * This is a unit test suite for chip::System::Timer, * the part of the CHIP System Layer that implements timers. * */ #include #include #include #include #include #include #include #include #include #include #if CHIP_SYSTEM_CONFIG_USE_LWIP #include #include #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP using chip::ErrorStr; using namespace chip::System; 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 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 || CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT template class LayerEvents::value>::type> { public: static bool HasServiceEvents() { return true; } static void ServiceEvents(Layer & aLayer) { LayerImplFreeRTOS & layer = static_cast(aLayer); if (layer.IsInitialized()) { layer.HandlePlatformTimer(); } } }; #endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT // Test input vector format. static const uint32_t MAX_NUM_TIMERS = 1000; namespace chip { namespace System { class TestSystemTimer : public ::testing::Test { public: static void SetUpTestSuite() { ASSERT_EQ(::chip::Platform::MemoryInit(), CHIP_NO_ERROR); #if CHIP_SYSTEM_CONFIG_USE_LWIP && (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) && !(CHIP_SYSTEM_CONFIG_LWIP_SKIP_INIT) static sys_mbox_t * sLwIPEventQueue = NULL; sys_mbox_new(sLwIPEventQueue, 100); tcpip_init(NULL, NULL); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP && (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) && // !(CHIP_SYSTEM_CONFIG_LWIP_SKIP_INIT) mLayer.Init(); } static void TearDownTestSuite() { mLayer.Shutdown(); #if CHIP_SYSTEM_CONFIG_USE_LWIP && (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) && !(CHIP_SYSTEM_CONFIG_LWIP_SKIP_INIT) tcpip_finish(NULL, NULL); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP && (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) && // !(CHIP_SYSTEM_CONFIG_LWIP_SKIP_INIT) ::chip::Platform::MemoryShutdown(); } template static auto GetTimerPoolObject(T && pool) { return pool.mTimerPool; } static LayerImpl mLayer; }; LayerImpl TestSystemTimer::mLayer; static TestSystemTimer * gCurrentTestContext = nullptr; class ScopedGlobalTestContext { public: ScopedGlobalTestContext(TestSystemTimer * ctx) { gCurrentTestContext = ctx; } ~ScopedGlobalTestContext() { gCurrentTestContext = nullptr; } }; static volatile bool sOverflowTestDone; void TimerFailed(void * aState) { sOverflowTestDone = true; FAIL() << "Timer failed"; } void HandleTimerFailed(Layer * systemLayer, void * aState) { (void) systemLayer; TimerFailed(aState); } void HandleTimer10Success(Layer * systemLayer, void * aState) { EXPECT_TRUE(true); sOverflowTestDone = true; } TEST_F(TestSystemTimer, CheckOverflow) { 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); Layer & lSys = mLayer; sOverflowTestDone = false; lSys.StartTimer(timeout_overflow_0ms, HandleTimerFailed, this); lSys.StartTimer(timeout_10ms, HandleTimer10Success, this); while (!sOverflowTestDone) { LayerEvents::ServiceEvents(lSys); } lSys.CancelTimer(HandleTimerFailed, this); // cb timer is cancelled by destructor lSys.CancelTimer(HandleTimer10Success, this); } void HandleGreedyTimer(Layer * aLayer, void * aState) { static uint32_t sNumTimersHandled = 0; EXPECT_LT(sNumTimersHandled, MAX_NUM_TIMERS); if (sNumTimersHandled >= MAX_NUM_TIMERS) { return; } aLayer->StartTimer(chip::System::Clock::kZero, HandleGreedyTimer, aState); sNumTimersHandled++; } TEST_F(TestSystemTimer, CheckStarvation) { if (!LayerEvents::HasServiceEvents()) return; Layer & lSys = mLayer; lSys.StartTimer(chip::System::Clock::kZero, HandleGreedyTimer, this); LayerEvents::ServiceEvents(lSys); } TEST_F(TestSystemTimer, CheckOrder) { if (!LayerEvents::HasServiceEvents()) return; Layer & systemLayer = mLayer; 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; EXPECT_EQ(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); EXPECT_EQ(strcmp(testState.record, "A"), 0); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "AB"), 0); mockClock.AdvanceMonotonic(200_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "ABCD"), 0); Clock::Internal::SetSystemClockForTesting(savedClock); } TEST_F(TestSystemTimer, CheckCancellation) { if (!LayerEvents::HasServiceEvents()) return; Layer & systemLayer = mLayer; struct TestState { TestState(Layer & aSystemLayer) : mSystemLayer(aSystemLayer) {} 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) { auto self = static_cast(state); self->Record('A'); self->mSystemLayer.CancelTimer(B, state); self->mSystemLayer.CancelTimer(D, state); } static void B(Layer * layer, void * state) { static_cast(state)->Record('B'); } static void C(Layer * layer, void * state) { auto self = static_cast(state); self->Record('C'); self->mSystemLayer.CancelTimer(E, state); } static void D(Layer * layer, void * state) { static_cast(state)->Record('D'); } static void E(Layer * layer, void * state) { static_cast(state)->Record('E'); } char record[6] = { 0 }; Layer & mSystemLayer; }; TestState testState(systemLayer); EXPECT_EQ(testState.record[0], 0); Clock::ClockBase * const savedClock = &SystemClock(); Clock::Internal::MockClock mockClock; Clock::Internal::SetSystemClockForTesting(&mockClock); using namespace Clock::Literals; systemLayer.StartTimer(0_ms, TestState::A, &testState); systemLayer.StartTimer(0_ms, TestState::B, &testState); systemLayer.StartTimer(20_ms, TestState::C, &testState); systemLayer.StartTimer(30_ms, TestState::D, &testState); systemLayer.StartTimer(50_ms, TestState::E, &testState); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "AC"), 0); Clock::Internal::SetSystemClockForTesting(savedClock); } namespace { namespace CancelTimerTest { // A bit lower than maximum system timers just in case, for systems that // have some form of limit constexpr unsigned kCancelTimerCount = CHIP_SYSTEM_CONFIG_NUM_TIMERS - 4; int gCallbackProcessed[kCancelTimerCount]; /// Validates that gCallbackProcessed has valid values (0 or 1) void ValidateExecutedTimerCounts() { for (int processed : gCallbackProcessed) { EXPECT_TRUE((processed == 0) || (processed == 1)); } } unsigned ExecutedTimerCount() { unsigned count = 0; for (int processed : gCallbackProcessed) { if (processed != 0) { count++; } } return count; } void Callback(Layer * layer, void * state) { unsigned idx = static_cast(reinterpret_cast(state)); if (gCallbackProcessed[idx] != 0) { ChipLogError(Test, "UNEXPECTED EXECUTION at index %u", idx); } gCallbackProcessed[idx]++; if (ExecutedTimerCount() == kCancelTimerCount / 2) { ChipLogProgress(Test, "Cancelling timers"); for (unsigned i = 0; i < kCancelTimerCount; i++) { if (gCallbackProcessed[i] != 0) { continue; } ChipLogProgress(Test, "Timer %u is being cancelled", i); gCurrentTestContext->mLayer.CancelTimer(Callback, reinterpret_cast(static_cast(i))); gCallbackProcessed[i]++; // pretend executed. } } } TEST_F(TestSystemTimer, CancelTimerTest) { // Validates that timers can cancel other timers. Generally the test will // do the following: // - schedule several timers to start at the same time // - within each timers, after half of them have run, make one timer // cancel all the other ones // - assert that: // - timers will run if scheduled // - once cancelled, timers will NOT run (i.e. a timer can cancel // other timers, even if they are expiring at the same time) memset(gCallbackProcessed, 0, sizeof(gCallbackProcessed)); // TestContext & testContext = *static_cast(aContext); // ScopedGlobalTestContext testScope(&testContext); Layer & systemLayer = mLayer; Clock::ClockBase * const savedClock = &SystemClock(); Clock::Internal::MockClock mockClock; Clock::Internal::SetSystemClockForTesting(&mockClock); using namespace Clock::Literals; for (unsigned i = 0; i < kCancelTimerCount; i++) { EXPECT_EQ(systemLayer.StartTimer(10_ms, Callback, reinterpret_cast(static_cast(i))), CHIP_NO_ERROR); } LayerEvents::ServiceEvents(systemLayer); ValidateExecutedTimerCounts(); EXPECT_EQ(ExecutedTimerCount(), 0U); mockClock.AdvanceMonotonic(20_ms); LayerEvents::ServiceEvents(systemLayer); ValidateExecutedTimerCounts(); EXPECT_EQ(ExecutedTimerCount(), kCancelTimerCount); Clock::Internal::SetSystemClockForTesting(savedClock); } } // namespace CancelTimerTest } // namespace // Test the implementation helper classes TimerPool, TimerList, and TimerData. TEST_F(TestSystemTimer, CheckTimerPool) { using Timer = TimerList::Node; struct TestState { int count = 0; static void Increment(Layer * layer, void * state) { ++static_cast(state)->count; } static void Reset(Layer * layer, void * state) { static_cast(state)->count = 0; } }; TestState testState; using namespace Clock::Literals; struct { Clock::Timestamp awakenTime; TimerCompleteCallback onComplete; Timer * timer; } testTimer[] = { { 111_ms, TestState::Increment }, // 0 { 100_ms, TestState::Increment }, // 1 { 202_ms, TestState::Reset }, // 2 { 303_ms, TestState::Increment }, // 3 }; TimerPool pool; EXPECT_EQ(pool.mTimerPool.Allocated(), 0U); SYSTEM_STATS_RESET(Stats::kSystemLayer_NumTimers); SYSTEM_STATS_RESET_HIGH_WATER_MARK_FOR_TESTING(Stats::kSystemLayer_NumTimers); EXPECT_TRUE(SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 0)); EXPECT_TRUE(SYSTEM_STATS_TEST_HIGH_WATER_MARK(Stats::kSystemLayer_NumTimers, 0)); // Test TimerPool::Create() and TimerData accessors. for (auto & timer : testTimer) { timer.timer = pool.Create(mLayer, timer.awakenTime, timer.onComplete, &testState); } EXPECT_TRUE(SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 4)); for (auto & timer : testTimer) { ASSERT_NE(timer.timer, nullptr); EXPECT_EQ(timer.timer->AwakenTime(), timer.awakenTime); // TODO: Fix casting and use EXPECT_EQ EXPECT_TRUE(timer.timer->GetCallback().GetOnComplete() == timer.onComplete); EXPECT_EQ(timer.timer->GetCallback().GetAppState(), &testState); EXPECT_EQ(timer.timer->GetCallback().GetSystemLayer(), &mLayer); } // Test TimerList operations. TimerList list; EXPECT_EQ(list.Remove(nullptr), nullptr); EXPECT_EQ(list.Remove(nullptr, nullptr), nullptr); EXPECT_EQ(list.PopEarliest(), nullptr); EXPECT_EQ(list.PopIfEarlier(500_ms), nullptr); EXPECT_EQ(list.Earliest(), nullptr); EXPECT_TRUE(list.Empty()); Timer * earliest = list.Add(testTimer[0].timer); // list: () → (0) returns: 0 EXPECT_EQ(earliest, testTimer[0].timer); EXPECT_EQ(list.PopIfEarlier(10_ms), nullptr); EXPECT_EQ(list.Earliest(), testTimer[0].timer); EXPECT_FALSE(list.Empty()); earliest = list.Add(testTimer[1].timer); // list: (0) → (1 0) returns: 1 EXPECT_EQ(earliest, testTimer[1].timer); EXPECT_EQ(list.Earliest(), testTimer[1].timer); earliest = list.Add(testTimer[2].timer); // list: (1 0) → (1 0 2) returns: 1 EXPECT_EQ(earliest, testTimer[1].timer); EXPECT_EQ(list.Earliest(), testTimer[1].timer); earliest = list.Add(testTimer[3].timer); // list: (1 0 2) → (1 0 2 3) returns: 1 EXPECT_EQ(earliest, testTimer[1].timer); EXPECT_EQ(list.Earliest(), testTimer[1].timer); earliest = list.Remove(earliest); // list: (1 0 2 3) → (0 2 3) returns: 0 EXPECT_EQ(earliest, testTimer[0].timer); EXPECT_EQ(list.Earliest(), testTimer[0].timer); earliest = list.Remove(TestState::Reset, &testState); // list: (0 2 3) → (0 3) returns: 2 EXPECT_EQ(earliest, testTimer[2].timer); EXPECT_EQ(list.Earliest(), testTimer[0].timer); earliest = list.PopEarliest(); // list: (0 3) → (3) returns: 0 EXPECT_EQ(earliest, testTimer[0].timer); EXPECT_EQ(list.Earliest(), testTimer[3].timer); earliest = list.PopIfEarlier(10_ms); // list: (3) → (3) returns: nullptr EXPECT_EQ(earliest, nullptr); earliest = list.PopIfEarlier(500_ms); // list: (3) → () returns: 3 EXPECT_EQ(earliest, testTimer[3].timer); EXPECT_TRUE(list.Empty()); earliest = list.Add(testTimer[3].timer); // list: () → (3) returns: 3 list.Clear(); // list: (3) → () EXPECT_EQ(earliest, testTimer[3].timer); EXPECT_TRUE(list.Empty()); for (auto & timer : testTimer) { list.Add(timer.timer); } TimerList early = list.ExtractEarlier(200_ms); // list: (1 0 2 3) → (2 3) returns: (1 0) EXPECT_EQ(list.PopEarliest(), testTimer[2].timer); EXPECT_EQ(list.PopEarliest(), testTimer[3].timer); EXPECT_EQ(list.PopEarliest(), nullptr); EXPECT_EQ(early.PopEarliest(), testTimer[1].timer); EXPECT_EQ(early.PopEarliest(), testTimer[0].timer); EXPECT_EQ(early.PopEarliest(), nullptr); // Test TimerPool::Invoke() EXPECT_EQ(testState.count, 0); pool.Invoke(testTimer[0].timer); testTimer[0].timer = nullptr; EXPECT_EQ(testState.count, 1); EXPECT_EQ(pool.mTimerPool.Allocated(), 3U); EXPECT_TRUE(SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 3)); // Test TimerPool::Release() pool.Release(testTimer[1].timer); testTimer[1].timer = nullptr; EXPECT_EQ(testState.count, 1); EXPECT_EQ(pool.mTimerPool.Allocated(), 2U); EXPECT_TRUE(SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 2)); pool.ReleaseAll(); EXPECT_EQ(pool.mTimerPool.Allocated(), 0U); EXPECT_TRUE(SYSTEM_STATS_TEST_IN_USE(Stats::kSystemLayer_NumTimers, 0)); EXPECT_TRUE(SYSTEM_STATS_TEST_HIGH_WATER_MARK(Stats::kSystemLayer_NumTimers, 4)); } TEST_F(TestSystemTimer, ExtendTimerToTest) { if (!LayerEvents::HasServiceEvents()) return; Layer & systemLayer = mLayer; 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; EXPECT_EQ(testState.record[0], 0); Clock::ClockBase * const savedClock = &SystemClock(); Clock::Internal::MockClock mockClock; Clock::Internal::SetSystemClockForTesting(&mockClock); using namespace Clock::Literals; systemLayer.StartTimer(150_ms, TestState::B, &testState); systemLayer.StartTimer(200_ms, TestState::C, &testState); systemLayer.StartTimer(150_ms, TestState::D, &testState); // Timer wasn't started before. ExtendTimerTo will start it. systemLayer.ExtendTimerTo(100_ms, TestState::A, &testState); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "A"), 0); // Timer B as 50ms remaining. ExtendTimerTo 25 should have no effect // Timer C as 100ms remaining. ExtendTimerTo 75ms should have no effect // Timer D as 50ms remaining. Timer should be extend to a duration of 75ms systemLayer.ExtendTimerTo(25_ms, TestState::B, &testState); systemLayer.ExtendTimerTo(75_ms, TestState::D, &testState); systemLayer.ExtendTimerTo(75_ms, TestState::D, &testState); mockClock.AdvanceMonotonic(25_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "A"), 0); mockClock.AdvanceMonotonic(25_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "AB"), 0); // Timer D as 25ms remaining. Timer should be extend to a duration of 75ms systemLayer.ExtendTimerTo(75_ms, TestState::D, &testState); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_EQ(strcmp(testState.record, "ABCD"), 0); Clock::Internal::SetSystemClockForTesting(savedClock); // Extending a timer by 0 ms permitted EXPECT_EQ(systemLayer.ExtendTimerTo(0_ms, TestState::A, &testState), CHIP_ERROR_INVALID_ARGUMENT); } TEST_F(TestSystemTimer, IsTimerActiveTest) { if (!LayerEvents::HasServiceEvents()) return; Layer & systemLayer = mLayer; 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'); } char record[4] = { 0 }; }; TestState testState; EXPECT_EQ(testState.record[0], 0); Clock::ClockBase * const savedClock = &SystemClock(); Clock::Internal::MockClock mockClock; Clock::Internal::SetSystemClockForTesting(&mockClock); using namespace Clock::Literals; systemLayer.StartTimer(100_ms, TestState::A, &testState); systemLayer.StartTimer(200_ms, TestState::B, &testState); systemLayer.StartTimer(300_ms, TestState::C, &testState); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::A, &testState)); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::B, &testState)); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::C, &testState)); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_FALSE(systemLayer.IsTimerActive(TestState::A, &testState)); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::B, &testState)); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::C, &testState)); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_FALSE(systemLayer.IsTimerActive(TestState::B, &testState)); EXPECT_TRUE(systemLayer.IsTimerActive(TestState::C, &testState)); mockClock.AdvanceMonotonic(100_ms); LayerEvents::ServiceEvents(systemLayer); EXPECT_FALSE(systemLayer.IsTimerActive(TestState::C, &testState)); Clock::Internal::SetSystemClockForTesting(savedClock); } } // namespace System } // namespace chip