/** * Copyright © 2017 IBM Corporation * * 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 #include #include "event.hpp" #include "timer.hpp" /** * Testcases for the Timer class */ using namespace phosphor::fan::util; using namespace std::chrono; /** * Class to ensure sd_events are correctly * setup and destroyed. */ class TimerTest : public ::testing::Test { public: // systemd event handler phosphor::fan::event::EventPtr events; // Need this so that events can be initialized. int rc; // Gets called as part of each TEST_F construction TimerTest() { sd_event* event = nullptr; auto rc = sd_event_default(&event); EXPECT_GE(rc, 0); events.reset(event); } }; /** * Helper class to hande tracking timer expirations * via callback functions. */ class CallbackTester { public: CallbackTester() {} size_t getCount() { return _count; } void callbackFunction() { _count++; _gotCallback = true; } bool gotCallback() { return _gotCallback; } private: bool _gotCallback = false; size_t _count = 0; }; /** * Helper class that more closely mimics real usage, * which is another class containing a timer and using * one of its member functions as the callback. */ class CallbackTesterWithTimer : public CallbackTester { public: CallbackTesterWithTimer(phosphor::fan::event::EventPtr& events) : _timer(events, std::bind(&CallbackTesterWithTimer::callbackFunction, this)) { } void callbackFunction() { //restart the timer once from the callback if (!_restarted) { _restarted = true; auto time = duration_cast(seconds(1)); _timer.start(time, Timer::TimerType::oneshot); } CallbackTester::callbackFunction(); } Timer& getTimer() { return _timer; } inline bool restarted() const { return _restarted; } private: Timer _timer; bool _restarted = false; }; /** * Test that a callback will occur after 2 seconds. */ TEST_F(TimerTest, timerExpiresAfter2seconds) { CallbackTester tester; Timer timer(events, std::bind(&CallbackTester::callbackFunction, &tester)); auto time = duration_cast(seconds(2)); EXPECT_EQ(false, timer.running()); timer.start(time, Timer::TimerType::oneshot); EXPECT_EQ(false, tester.gotCallback()); EXPECT_EQ(true, timer.running()); int count = 0; auto sleepTime = duration_cast(seconds(1)); //Wait for 2 1s timeouts while (count < 2) { // Returns 0 on timeout and positive number on dispatch if (sd_event_run(events.get(), sleepTime.count()) == 0) { count++; } } EXPECT_EQ(true, tester.gotCallback()); EXPECT_EQ(1, tester.getCount()); EXPECT_EQ(false, timer.running()); } /** * Test that a timer can be restarted. */ TEST_F(TimerTest, timerRestart) { CallbackTester tester; Timer timer(events, std::bind(&CallbackTester::callbackFunction, &tester)); auto time = duration_cast(seconds(2)); timer.start(time, Timer::TimerType::oneshot); //wait for a second auto sleepTime = duration_cast(seconds(1)); auto rc = sd_event_run(events.get(), sleepTime.count()); //expect the timeout, not the dispatch //and the timer should still be running EXPECT_EQ(0, rc); EXPECT_EQ(true, timer.running()); //Restart it timer.start(time, Timer::TimerType::oneshot); //Wait just 1s, make sure not done rc = sd_event_run(events.get(), sleepTime.count()); EXPECT_EQ(0, rc); EXPECT_EQ(true, timer.running()); EXPECT_EQ(false, tester.gotCallback()); //Wait 1 more second, this time expecting a dispatch int count = 0; while (count < 1) { // Returns 0 on timeout and positive number on dispatch if (sd_event_run(events.get(), sleepTime.count()) == 0) { count++; } } EXPECT_EQ(true, tester.gotCallback()); EXPECT_EQ(1, tester.getCount()); EXPECT_EQ(false, timer.running()); } /** * Test that a timer can be stopped. */ TEST_F(TimerTest, timerStop) { CallbackTester tester; Timer timer(events, std::bind(&CallbackTester::callbackFunction, &tester)); auto time = duration_cast(seconds(2)); timer.start(time, Timer::TimerType::oneshot); auto sleepTime = duration_cast(seconds(1)); //wait 1s auto rc = sd_event_run(events.get(), sleepTime.count()); //expect the timeout, not the dispatch EXPECT_EQ(rc, 0); EXPECT_EQ(true, timer.running()); timer.stop(); EXPECT_EQ(false, timer.running()); EXPECT_EQ(false, tester.gotCallback()); //Wait another 2s, make sure no callbacks happened sleepTime = duration_cast(seconds(2)); rc = sd_event_run(events.get(), sleepTime.count()); EXPECT_EQ(rc, 0); EXPECT_EQ(false, timer.running()); EXPECT_EQ(false, tester.gotCallback()); } /** * Test that the timer can be restarted from within * a callback function. */ TEST_F(TimerTest, timerRestartFromCallback) { CallbackTesterWithTimer tester(events); auto& timer = tester.getTimer(); auto time = duration_cast(seconds(2)); timer.start(time, Timer::TimerType::oneshot); //after running for 2 seconds, the timer will get restarted //for another 1s int count = 0; auto sleepTime = duration_cast(seconds(1)); while (count < 3) { // Returns 0 on timeout and positive number on dispatch if (sd_event_run(events.get(), sleepTime.count()) == 0) { count++; } } EXPECT_EQ(false, timer.running()); EXPECT_EQ(true, tester.gotCallback()); EXPECT_EQ(2, tester.getCount()); //2 callbacks EXPECT_EQ(true, tester.restarted()); } /** * This shows what happens when the timer expires but * sd_event_run never got called. */ TEST_F(TimerTest, timerNoEventRun) { CallbackTester tester; Timer timer(events, std::bind(&CallbackTester::callbackFunction, &tester)); auto time = duration_cast(milliseconds(500)); timer.start(time, Timer::TimerType::oneshot); sleep(1); //The timer should have expired, but with no event processing //it will still think it's running. EXPECT_EQ(true, timer.running()); EXPECT_EQ(false, tester.gotCallback()); //Now process an event auto sleepTime = duration_cast(milliseconds(5)); auto rc = sd_event_run(events.get(), sleepTime.count()); EXPECT_GT(rc, 0); EXPECT_EQ(false, timer.running()); EXPECT_EQ(true, tester.gotCallback()); } /** * Tests that a timer in repeating mode will keep calling * the callback. */ TEST_F(TimerTest, RepeatingTimer) { CallbackTester tester; Timer timer(events, std::bind(&CallbackTester::callbackFunction, &tester)); auto time = duration_cast(seconds(1)); timer.start(time, Timer::TimerType::repeating); int count = 0; auto sleepTime = duration_cast(milliseconds(500)); while (count < 5) { if (sd_event_run(events.get(), sleepTime.count()) == 0) { count++; } } EXPECT_EQ(true, timer.running()); EXPECT_EQ(true, tester.gotCallback()); EXPECT_EQ(4, tester.getCount()); timer.stop(); EXPECT_EQ(false, timer.running()); }