/* * Copyright (c) 2024 Project CHIP Authors * * 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 #include // EventLoopHandlers are only supported by a select-based LayerSocketsLoop #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && !CHIP_SYSTEM_CONFIG_USE_DISPATCH // The fake PlatformManagerImpl does not drive the system layer event loop #if !CHIP_DEVICE_LAYER_TARGET_FAKE #include #include #include #include using namespace chip; using namespace chip::System::Clock; using namespace chip::System::Clock::Literals; class TestEventLoopHandler : public ::testing::Test { public: static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); ASSERT_EQ(DeviceLayer::PlatformMgr().InitChipStack(), CHIP_NO_ERROR); } static void TearDownTestSuite() { DeviceLayer::PlatformMgr().Shutdown(); Platform::MemoryShutdown(); } static System::LayerSocketsLoop & SystemLayer() { return static_cast(DeviceLayer::SystemLayer()); } // Schedules a call to the provided lambda and returns a cancel function. template static std::function Schedule(Timeout delay, Lambda lambda) { System::TimerCompleteCallback callback = [](System::Layer * layer, void * ctx) { auto * function = static_cast *>(ctx); (*function)(); delete function; }; auto * function = new std::function(lambda); SystemLayer().StartTimer(delay, callback, function); return [=] { SystemLayer().CancelTimer(callback, function); delete function; }; } template static void ScheduleNextTick(Lambda lambda) { // ScheduleLambda is based on device events, which are greedily processed until the // queue is empty, so we can't use it to wait for the next event loop tick. Just use // a timer with a very short delay. Schedule(1_ms, lambda); } }; TEST_F(TestEventLoopHandler, EventLoopHandlerSequence) { struct : public System::EventLoopHandler { std::string trace; Timestamp PrepareEvents(Timestamp now) override { trace.append("P"); return Timestamp::max(); } void HandleEvents() override { trace.append("H"); } } loopHandler; ScheduleNextTick([&] { loopHandler.trace.append("1"); SystemLayer().AddLoopHandler(loopHandler); loopHandler.trace.append("A"); ScheduleNextTick([&] { // "P" loopHandler.trace.append("2"); ScheduleNextTick([&] { // "H", "P" loopHandler.trace.append("3"); SystemLayer().RemoveLoopHandler(loopHandler); loopHandler.trace.append("R"); ScheduleNextTick([&] { loopHandler.trace.append("4"); DeviceLayer::PlatformMgr().StopEventLoopTask(); }); }); }); }); chip::DeviceLayer::PlatformMgr().RunEventLoop(); EXPECT_EQ(loopHandler.trace, std::string("1AP2HP3R4")); } TEST_F(TestEventLoopHandler, EventLoopHandlerWake) { struct : public System::EventLoopHandler { Timestamp startTimestamp = System::SystemClock().GetMonotonicTimestamp(); Timestamp wakeTimestamp = Timestamp::max(); Timestamp PrepareEvents(Timestamp now) override { return now + 400_ms; } void HandleEvents() override { // StartTimer() (called by Schedule()) is liable to causes an immediate // wakeup via Signal(), so ignore this call if it's only been a few ms. auto now = System::SystemClock().GetMonotonicTimestamp(); if (now - startTimestamp >= 100_ms) { wakeTimestamp = now; DeviceLayer::PlatformMgr().StopEventLoopTask(); } } } loopHandler; // Schedule a fallback timer to ensure the test stops auto cancelFallback = Schedule(1000_ms, [] { DeviceLayer::PlatformMgr().StopEventLoopTask(); }); SystemLayer().AddLoopHandler(loopHandler); chip::DeviceLayer::PlatformMgr().RunEventLoop(); SystemLayer().RemoveLoopHandler(loopHandler); cancelFallback(); // avoid leaking the fallback timer Timestamp sleepDuration = loopHandler.wakeTimestamp - loopHandler.startTimestamp; EXPECT_GE(sleepDuration.count(), 400u); // loopHandler requested wake-up after 400ms EXPECT_LE(sleepDuration.count(), 500u); // allow some slack for test machine load } #endif // !CHIP_DEVICE_LAYER_TARGET_FAKE #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && !CHIP_SYSTEM_CONFIG_USE_DISPATCH