From 59c29c79ec29dc29f3a32daabe299a80d0c8a49e Mon Sep 17 00:00:00 2001 From: Matt Spinler Date: Thu, 27 Apr 2017 11:17:28 -0500 Subject: phosphor-fan: Testcases for Timer class Change-Id: Id8b752cc6e494dcf76d9668553292a65a3fd19d6 Signed-off-by: Matt Spinler --- test/Makefile.am | 13 ++ test/timertest.cpp | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 test/Makefile.am create mode 100644 test/timertest.cpp (limited to 'test') diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..16a3252 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(top_srcdir) + +# Run all 'check' test programs +TESTS = $(check_PROGRAMS) + +# # Build/add timertest to test suite +check_PROGRAMS = timertest +timertest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) +timertest_CXXFLAGS = $(PTHREAD_CFLAGS) +timertest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) \ + $(SYSTEMD_LIBS) ${SDBUSPLUS_LIBS} +timertest_SOURCES = timertest.cpp +timertest_LDADD = $(top_builddir)/timer.o 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 +#include +#include +#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; + +/** + * 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(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()); +} -- cgit v1.2.1