diff options
Diffstat (limited to 'test/timertest.cpp')
-rw-r--r-- | test/timertest.cpp | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/test/timertest.cpp b/test/timertest.cpp new file mode 100644 index 0000000..40b2ddf --- /dev/null +++ b/test/timertest.cpp @@ -0,0 +1,359 @@ +/** + * 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 <iostream> +#include <chrono> +#include <gtest/gtest.h> +#include "timer.hpp" + +/** + * Testcases for the Timer class + */ + +using namespace phosphor::fan::util; +using namespace std::chrono; + +void EventDeleter(sd_event* events) +{ + sd_event_unref(events); +} + +using EventPtr = std::shared_ptr<sd_event>; + +/** + * Class to ensure sd_events are correctly + * setup and destroyed. + */ +class TimerTest : public ::testing::Test +{ + public: + // systemd event handler + 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, EventDeleter); + } +}; + +/** + * 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(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<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(seconds(2)); + timer.start(time, Timer::TimerType::oneshot); + + //wait for a second + auto sleepTime = duration_cast<microseconds>(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<microseconds>(seconds(2)); + timer.start(time, Timer::TimerType::oneshot); + + auto sleepTime = duration_cast<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(seconds(1)); + timer.start(time, Timer::TimerType::repeating); + + int count = 0; + auto sleepTime = duration_cast<microseconds>(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()); +} |