blob: 15a6a6e225dc361361f458807f329637fab6d1ed [file] [log] [blame]
/*
*
* Copyright (c) 2025 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.
*/
/**
* @file
* This is a unit test suite for <tt>chip::System::SystemLayerImplSelect</tt>
*
*/
#include <gtest/gtest.h>
#include <lib/support/CodeUtils.h>
#include <pw_unit_test/framework.h>
#include <system/SystemClock.h>
#include <system/SystemConfig.h>
#include <system/SystemError.h>
#include <system/SystemLayer.h>
#include <system/SystemLayerImpl.h>
#include <platform/CHIPDeviceLayer.h>
using namespace chip;
using namespace chip::System;
using namespace chip::System::Clock::Literals;
// EventSources are only supported by a select-based LayerSelectLoop
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
// The fake PlatformManagerImpl does not drive the system layer event loop
#if !CHIP_DEVICE_LAYER_TARGET_FAKE
struct TestSource : public LayerImplSelect::EventSource
{
TestSource() { EXPECT_EQ(wakeEvent.Open(), CHIP_NO_ERROR); }
~TestSource() { wakeEvent.Close(); }
void PrepareEvents(int & maxfd, fd_set & readfds, fd_set & writefds, fd_set & exceptfds, struct timeval & timeout) override
{
maxfd = std::max(maxfd, wakeEvent.GetReadFD());
FD_SET(wakeEvent.GetReadFD(), &readfds);
if (timeout.tv_sec > delaySeconds)
{
timeout.tv_sec = delaySeconds;
timeout.tv_usec = 0;
}
else if (timeout.tv_sec == delaySeconds)
{
timeout.tv_usec = 0;
}
prepareCalled++;
}
void ProcessEvents(const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds) override
{
if (FD_ISSET(wakeEvent.GetReadFD(), &readfds))
{
wakeEvent.Confirm();
handlerCalled++;
}
}
CHIP_ERROR Notify() { return wakeEvent.Notify(); }
int prepareCalled = 0;
int handlerCalled = 0;
int delaySeconds = 0;
WakeEvent wakeEvent;
};
struct TestSystemEventSource : public ::testing::Test
{
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();
}
};
TEST_F(TestSystemEventSource, OneEventSource)
{
auto & impl = static_cast<LayerImplSelect &>(chip::DeviceLayer::SystemLayer());
TestSource source1;
// Add for the first time
impl.EventSourceAdd(&source1);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.prepareCalled, 1);
ASSERT_EQ(source1.handlerCalled, 0);
// Add the same source for the second time
impl.EventSourceAdd(&source1);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.prepareCalled, 2);
ASSERT_EQ(source1.handlerCalled, 0);
// Notify the source
ASSERT_EQ(source1.Notify(), CHIP_NO_ERROR);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.prepareCalled, 3);
ASSERT_EQ(source1.handlerCalled, 1);
impl.EventSourceClear();
}
TEST_F(TestSystemEventSource, MultipleEventSources)
{
auto & impl = static_cast<LayerImplSelect &>(chip::DeviceLayer::SystemLayer());
TestSource source1;
TestSource source2;
ASSERT_EQ(source1.Notify(), CHIP_NO_ERROR);
impl.EventSourceAdd(&source1);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.handlerCalled, 1);
ASSERT_EQ(source2.Notify(), CHIP_NO_ERROR);
impl.EventSourceAdd(&source2);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source2.handlerCalled, 1);
ASSERT_EQ(source1.Notify(), CHIP_NO_ERROR);
ASSERT_EQ(source2.Notify(), CHIP_NO_ERROR);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.handlerCalled, 2);
ASSERT_EQ(source2.handlerCalled, 2);
impl.EventSourceClear();
}
TEST_F(TestSystemEventSource, RemoveSomeEventSource)
{
auto & impl = static_cast<LayerImplSelect &>(chip::DeviceLayer::SystemLayer());
TestSource source1;
TestSource source2;
ASSERT_EQ(source1.Notify(), CHIP_NO_ERROR);
impl.EventSourceAdd(&source1);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source2.Notify(), CHIP_NO_ERROR);
impl.EventSourceAdd(&source2);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source2.Notify(), CHIP_NO_ERROR);
impl.EventSourceRemove(&source1);
impl.PrepareEvents();
impl.WaitForEvents();
impl.HandleEvents();
ASSERT_EQ(source1.handlerCalled, 1);
ASSERT_EQ(source2.handlerCalled, 2);
impl.EventSourceClear();
}
TEST_F(TestSystemEventSource, MultipleEventSourcesOfDifferentTimeout)
{
auto & impl = static_cast<LayerImplSelect &>(chip::DeviceLayer::SystemLayer());
TestSource source1;
TestSource source2;
source1.delaySeconds = 2;
source2.delaySeconds = 3;
impl.EventSourceAdd(&source1);
impl.EventSourceAdd(&source2);
impl.PrepareEvents();
auto start = System::SystemClock().GetMonotonicTimestamp();
impl.WaitForEvents();
auto end = System::SystemClock().GetMonotonicTimestamp();
impl.HandleEvents();
auto duration = end - start;
ASSERT_GE(duration, 2000_ms);
ASSERT_LT(duration, 3000_ms);
impl.EventSourceClear();
}
#endif // !CHIP_DEVICE_LAYER_TARGET_FAKE
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH