/* * * Copyright (c) 2020-2021 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 chip::System::WakeEvent * */ #include #include #include #include #include #include #include #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING #include #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING using namespace chip::System; #if CHIP_SYSTEM_CONFIG_USE_SOCKETS namespace chip { namespace System { class WakeEventTest { public: static int GetReadFD(const WakeEvent & wakeEvent) { return wakeEvent.GetReadFD(); } }; } // namespace System } // namespace chip namespace { class TestSystemWakeEvent : public ::testing::Test { public: void SetUp() { mSystemLayer.Init(); mWakeEvent.Open(mSystemLayer); } void TearDown() { mWakeEvent.Close(mSystemLayer); mSystemLayer.Shutdown(); } ::chip::System::LayerImpl mSystemLayer; WakeEvent mWakeEvent; fd_set mReadSet; fd_set mWriteSet; fd_set mErrorSet; int SelectWakeEvent(timeval timeout = {}) { // NOLINTBEGIN(clang-analyzer-security.insecureAPI.bzero) // // NOTE: darwin uses bzero to clear out FD sets. This is not a security concern. FD_ZERO(&mReadSet); FD_ZERO(&mWriteSet); FD_ZERO(&mErrorSet); // NOLINTEND(clang-analyzer-security.insecureAPI.bzero) FD_SET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet); return select(WakeEventTest::GetReadFD(mWakeEvent) + 1, &mReadSet, &mWriteSet, &mErrorSet, &timeout); } }; TEST_F(TestSystemWakeEvent, TestOpen) { EXPECT_GE(WakeEventTest::GetReadFD(mWakeEvent), 0); EXPECT_EQ(SelectWakeEvent(), 0); } TEST_F(TestSystemWakeEvent, TestNotify) { EXPECT_EQ(SelectWakeEvent(), 0); // Check that select() succeeds after Notify() has been called mWakeEvent.Notify(); EXPECT_EQ(SelectWakeEvent(), 1); EXPECT_TRUE(FD_ISSET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet)); // ...and state of the event is not cleared automatically EXPECT_EQ(SelectWakeEvent(), 1); EXPECT_TRUE(FD_ISSET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet)); } TEST_F(TestSystemWakeEvent, TestConfirm) { // Check that select() succeeds after Notify() has been called mWakeEvent.Notify(); EXPECT_EQ(SelectWakeEvent(), 1); EXPECT_TRUE(FD_ISSET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet)); // Check that Confirm() clears state of the event mWakeEvent.Confirm(); EXPECT_EQ(SelectWakeEvent(), 0); } #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING void * WaitForEvent(void * aContext) { // wait 5 seconds auto * context = static_cast(aContext); return reinterpret_cast(context->SelectWakeEvent(timeval{ 5, 0 })); } TEST_F(TestSystemWakeEvent, TestBlockingSelect) { // Spawn a thread waiting for the event pthread_t tid = 0; EXPECT_EQ(0, pthread_create(&tid, nullptr, WaitForEvent, this)); mWakeEvent.Notify(); void * selectResult = nullptr; EXPECT_EQ(0, pthread_join(tid, &selectResult)); EXPECT_EQ(selectResult, reinterpret_cast(1)); } #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING TEST_F(TestSystemWakeEvent, TestClose) { mWakeEvent.Close(mSystemLayer); const auto notifFD = WakeEventTest::GetReadFD(mWakeEvent); // Check that Close() has cleaned up itself and reopen is possible EXPECT_EQ(mWakeEvent.Open(mSystemLayer), CHIP_NO_ERROR); EXPECT_LT(notifFD, 0); } } // namespace #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS