summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac23
-rw-r--r--test/Makefile.am13
-rw-r--r--test/timertest.cpp359
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());
+}
OpenPOWER on IntegriCloud