diff options
| author | William A. Kennington III <wak@google.com> | 2018-09-19 18:28:37 -0700 |
|---|---|---|
| committer | William A. Kennington III <wak@google.com> | 2018-09-19 23:30:27 -0700 |
| commit | 81282e1a2b8565dc4e005684bbb923f67e9a6b9d (patch) | |
| tree | a90e97f1e1e997811038828db65a1e9336b20743 /test/utility/timer.cpp | |
| parent | 7f58b2a1ec99272e0c192a1981df4ea13284bc1f (diff) | |
| download | sdeventplus-81282e1a2b8565dc4e005684bbb923f67e9a6b9d.tar.gz sdeventplus-81282e1a2b8565dc4e005684bbb923f67e9a6b9d.zip | |
utility/timer: Implement
We often need a continually ticking timer for our daemons. This utility
wraps an sd_event time source as a convenience. This is meant to be a
usable replacement for the timer.hpp found in other openbmc projects.
Tested:
New tests pass with full coverage. Changes to the phosphor-watchdog
that rely on this utility work as expected.
Change-Id: Id12aed9e5b018e7eca825c4a7ac7b4f46e2f04c6
Signed-off-by: William A. Kennington III <wak@google.com>
Diffstat (limited to 'test/utility/timer.cpp')
| -rw-r--r-- | test/utility/timer.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/test/utility/timer.cpp b/test/utility/timer.cpp new file mode 100644 index 0000000..c919f7e --- /dev/null +++ b/test/utility/timer.cpp @@ -0,0 +1,263 @@ +#include <chrono> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <memory> +#include <sdeventplus/clock.hpp> +#include <sdeventplus/event.hpp> +#include <sdeventplus/test/sdevent.hpp> +#include <sdeventplus/utility/timer.hpp> +#include <stdexcept> +#include <systemd/sd-event.h> + +namespace sdeventplus +{ +namespace utility +{ +namespace +{ + +constexpr ClockId testClock = ClockId::Monotonic; + +using std::chrono::microseconds; +using std::chrono::milliseconds; +using testing::DoAll; +using testing::Return; +using testing::SaveArg; +using testing::SetArgPointee; +using TestTimer = Timer<testClock>; + +ssize_t event_ref_times = 0; + +ACTION(EventRef) +{ + event_ref_times++; +} + +ACTION(EventUnref) +{ + ASSERT_LT(0, event_ref_times); + event_ref_times--; +} + +class TimerTest : public testing::Test +{ + protected: + testing::StrictMock<test::SdEventMock> mock; + sd_event* const expected_event = reinterpret_cast<sd_event*>(1234); + sd_event_source* const expected_source = + reinterpret_cast<sd_event_source*>(2345); + const milliseconds interval{134}; + const milliseconds starting_time{10}; + sd_event_time_handler_t handler = nullptr; + void* handler_userdata; + std::unique_ptr<TestTimer> timer; + std::function<void()> callback; + + void expectNow(microseconds ret) + { + EXPECT_CALL(mock, + sd_event_now(expected_event, + static_cast<clockid_t>(testClock), testing::_)) + .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0))); + } + + void expectSetTime(microseconds time) + { + EXPECT_CALL(mock, + sd_event_source_set_time(expected_source, time.count())) + .WillOnce(Return(0)); + } + + void expectSetEnabled(source::Enabled enabled) + { + EXPECT_CALL(mock, sd_event_source_set_enabled( + expected_source, static_cast<int>(enabled))) + .WillOnce(Return(0)); + } + + void expectGetEnabled(source::Enabled enabled) + { + EXPECT_CALL(mock, + sd_event_source_get_enabled(expected_source, testing::_)) + .WillOnce( + DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0))); + } + + void SetUp() + { + EXPECT_CALL(mock, sd_event_ref(expected_event)) + .WillRepeatedly(DoAll(EventRef(), Return(expected_event))); + EXPECT_CALL(mock, sd_event_unref(expected_event)) + .WillRepeatedly(DoAll(EventUnref(), Return(nullptr))); + Event event(expected_event, &mock); + + auto runCallback = [&]() { + if (callback) + { + callback(); + } + }; + expectNow(starting_time); + EXPECT_CALL(mock, sd_event_add_time( + expected_event, testing::_, + static_cast<clockid_t>(testClock), + microseconds(starting_time + interval).count(), + 1000, testing::_, nullptr)) + .WillOnce(DoAll(SetArgPointee<1>(expected_source), + SaveArg<5>(&handler), Return(0))); + EXPECT_CALL(mock, + sd_event_source_set_userdata(expected_source, testing::_)) + .WillOnce(DoAll(SaveArg<1>(&handler_userdata), Return(nullptr))); + // Timer always enables the source to keep ticking + expectSetEnabled(source::Enabled::On); + timer = std::make_unique<TestTimer>(event, runCallback, interval); + } + + void TearDown() + { + expectSetEnabled(source::Enabled::Off); + EXPECT_CALL(mock, sd_event_source_unref(expected_source)) + .WillOnce(Return(nullptr)); + timer.reset(); + EXPECT_EQ(0, event_ref_times); + } +}; + +TEST_F(TimerTest, NewTimer) +{ + EXPECT_FALSE(timer->hasExpired()); + EXPECT_EQ(interval, timer->getInterval()); +} + +TEST_F(TimerTest, IsEnabled) +{ + expectGetEnabled(source::Enabled::On); + EXPECT_TRUE(timer->isEnabled()); + expectGetEnabled(source::Enabled::Off); + EXPECT_FALSE(timer->isEnabled()); +} + +TEST_F(TimerTest, GetRemainingDisabled) +{ + expectGetEnabled(source::Enabled::Off); + EXPECT_THROW(timer->getRemaining(), std::runtime_error); +} + +TEST_F(TimerTest, GetRemainingNegative) +{ + milliseconds now(675), end(453); + expectGetEnabled(source::Enabled::On); + EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) + .WillOnce( + DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); + expectNow(now); + EXPECT_EQ(milliseconds(0), timer->getRemaining()); +} + +TEST_F(TimerTest, GetRemainingPositive) +{ + milliseconds now(453), end(675); + expectGetEnabled(source::Enabled::On); + EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) + .WillOnce( + DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); + expectNow(now); + EXPECT_EQ(end - now, timer->getRemaining()); +} + +TEST_F(TimerTest, SetEnabled) +{ + expectSetEnabled(source::Enabled::On); + timer->setEnabled(true); + EXPECT_FALSE(timer->hasExpired()); + // Value should always be passed through regardless of current state + expectSetEnabled(source::Enabled::On); + timer->setEnabled(true); + EXPECT_FALSE(timer->hasExpired()); + + expectSetEnabled(source::Enabled::Off); + timer->setEnabled(false); + EXPECT_FALSE(timer->hasExpired()); + // Value should always be passed through regardless of current state + expectSetEnabled(source::Enabled::Off); + timer->setEnabled(false); + EXPECT_FALSE(timer->hasExpired()); +} + +TEST_F(TimerTest, SetRemaining) +{ + const milliseconds now(90), remaining(30); + expectNow(now); + expectSetTime(now + remaining); + timer->setRemaining(remaining); + EXPECT_EQ(interval, timer->getInterval()); + EXPECT_FALSE(timer->hasExpired()); +} + +TEST_F(TimerTest, ResetRemaining) +{ + const milliseconds now(90); + expectNow(now); + expectSetTime(now + interval); + timer->resetRemaining(); + EXPECT_EQ(interval, timer->getInterval()); + EXPECT_FALSE(timer->hasExpired()); +} + +TEST_F(TimerTest, SetInterval) +{ + const milliseconds new_interval(40); + timer->setInterval(new_interval); + EXPECT_EQ(new_interval, timer->getInterval()); + EXPECT_FALSE(timer->hasExpired()); +} + +TEST_F(TimerTest, SetValuesExpiredTimer) +{ + const milliseconds new_time(90); + expectNow(new_time); + expectSetTime(new_time + interval); + EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); + EXPECT_TRUE(timer->hasExpired()); + EXPECT_EQ(interval, timer->getInterval()); + + // Timer should remain expired unless clearExpired() or reset() + expectSetEnabled(source::Enabled::On); + timer->setEnabled(true); + EXPECT_TRUE(timer->hasExpired()); + expectNow(milliseconds(20)); + expectSetTime(milliseconds(50)); + timer->setRemaining(milliseconds(30)); + EXPECT_TRUE(timer->hasExpired()); + timer->setInterval(milliseconds(10)); + EXPECT_TRUE(timer->hasExpired()); + expectNow(milliseconds(20)); + expectSetTime(milliseconds(30)); + timer->resetRemaining(); + EXPECT_TRUE(timer->hasExpired()); + + timer->clearExpired(); + EXPECT_FALSE(timer->hasExpired()); +} + +TEST_F(TimerTest, Restart) +{ + const milliseconds new_time(90); + expectNow(new_time); + expectSetTime(new_time + interval); + EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); + EXPECT_TRUE(timer->hasExpired()); + EXPECT_EQ(interval, timer->getInterval()); + + const milliseconds new_interval(471); + expectNow(starting_time); + expectSetTime(starting_time + new_interval); + expectSetEnabled(source::Enabled::On); + timer->restart(new_interval); + EXPECT_FALSE(timer->hasExpired()); + EXPECT_EQ(new_interval, timer->getInterval()); +} + +} // namespace +} // namespace utility +} // namespace sdeventplus |

