diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | test/Makefile.am | 13 | ||||
-rw-r--r-- | test/timertest.cpp | 359 |
4 files changed, 395 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index 61c6782..e118816 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ libfan_la_SOURCES = \ utility.cpp \ timer.cpp -SUBDIRS = . +SUBDIRS = . test if WANT_PRESENCE SUBDIRS += presence diff --git a/configure.ac b/configure.ac index 4a1cecd..1b6df27 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,27 @@ PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging], , # Checks for library functions. LT_INIT # Required for systemd linking +# Check/set gtest specific functions. +AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"]) +AC_SUBST(GTEST_CPPFLAGS) +AC_ARG_ENABLE([oe-sdk], + AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.]) +) +AC_ARG_VAR(OECORE_TARGET_SYSROOT, + [Path to the OE SDK SYSROOT]) +AS_IF([test "x$enable_oe_sdk" == "xyes"], + AS_IF([test "x$OECORE_TARGET_SYSROOT" == "x"], + AC_MSG_ERROR([OECORE_TARGET_SYSROOT must be set with --enable-oe-sdk]) + ) + AC_MSG_NOTICE([Enabling OE-SDK at $OECORE_TARGET_SYSROOT]) + [ + testcase_flags="-Wl,-rpath,\${OECORE_TARGET_SYSROOT}/lib" + testcase_flags="${testcase_flags} -Wl,-rpath,\${OECORE_TARGET_SYSROOT}/usr/lib" + testcase_flags="${testcase_flags} -Wl,-dynamic-linker,`find \${OECORE_TARGET_SYSROOT}/lib/ld-*.so | sort -r -n | head -n1`" + ] + AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags]) +) + AC_ARG_ENABLE([presence], AS_HELP_STRING([--disable-presence], [Disable fan presence package.])) AC_ARG_ENABLE([control], @@ -94,5 +115,5 @@ AS_IF([test "x$enable_cooling-type" != "xno"], [ ]) # Create configured output -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile test/Makefile]) AC_OUTPUT 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 <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()); +} |