blob: c919f7ed6a9c9a904a08c013a513c4adeee02f61 [file] [log] [blame]
William A. Kennington III81282e12018-09-19 18:28:37 -07001#include <chrono>
2#include <gmock/gmock.h>
3#include <gtest/gtest.h>
4#include <memory>
5#include <sdeventplus/clock.hpp>
6#include <sdeventplus/event.hpp>
7#include <sdeventplus/test/sdevent.hpp>
8#include <sdeventplus/utility/timer.hpp>
9#include <stdexcept>
10#include <systemd/sd-event.h>
11
12namespace sdeventplus
13{
14namespace utility
15{
16namespace
17{
18
19constexpr ClockId testClock = ClockId::Monotonic;
20
21using std::chrono::microseconds;
22using std::chrono::milliseconds;
23using testing::DoAll;
24using testing::Return;
25using testing::SaveArg;
26using testing::SetArgPointee;
27using TestTimer = Timer<testClock>;
28
29ssize_t event_ref_times = 0;
30
31ACTION(EventRef)
32{
33 event_ref_times++;
34}
35
36ACTION(EventUnref)
37{
38 ASSERT_LT(0, event_ref_times);
39 event_ref_times--;
40}
41
42class TimerTest : public testing::Test
43{
44 protected:
45 testing::StrictMock<test::SdEventMock> mock;
46 sd_event* const expected_event = reinterpret_cast<sd_event*>(1234);
47 sd_event_source* const expected_source =
48 reinterpret_cast<sd_event_source*>(2345);
49 const milliseconds interval{134};
50 const milliseconds starting_time{10};
51 sd_event_time_handler_t handler = nullptr;
52 void* handler_userdata;
53 std::unique_ptr<TestTimer> timer;
54 std::function<void()> callback;
55
56 void expectNow(microseconds ret)
57 {
58 EXPECT_CALL(mock,
59 sd_event_now(expected_event,
60 static_cast<clockid_t>(testClock), testing::_))
61 .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0)));
62 }
63
64 void expectSetTime(microseconds time)
65 {
66 EXPECT_CALL(mock,
67 sd_event_source_set_time(expected_source, time.count()))
68 .WillOnce(Return(0));
69 }
70
71 void expectSetEnabled(source::Enabled enabled)
72 {
73 EXPECT_CALL(mock, sd_event_source_set_enabled(
74 expected_source, static_cast<int>(enabled)))
75 .WillOnce(Return(0));
76 }
77
78 void expectGetEnabled(source::Enabled enabled)
79 {
80 EXPECT_CALL(mock,
81 sd_event_source_get_enabled(expected_source, testing::_))
82 .WillOnce(
83 DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0)));
84 }
85
86 void SetUp()
87 {
88 EXPECT_CALL(mock, sd_event_ref(expected_event))
89 .WillRepeatedly(DoAll(EventRef(), Return(expected_event)));
90 EXPECT_CALL(mock, sd_event_unref(expected_event))
91 .WillRepeatedly(DoAll(EventUnref(), Return(nullptr)));
92 Event event(expected_event, &mock);
93
94 auto runCallback = [&]() {
95 if (callback)
96 {
97 callback();
98 }
99 };
100 expectNow(starting_time);
101 EXPECT_CALL(mock, sd_event_add_time(
102 expected_event, testing::_,
103 static_cast<clockid_t>(testClock),
104 microseconds(starting_time + interval).count(),
105 1000, testing::_, nullptr))
106 .WillOnce(DoAll(SetArgPointee<1>(expected_source),
107 SaveArg<5>(&handler), Return(0)));
108 EXPECT_CALL(mock,
109 sd_event_source_set_userdata(expected_source, testing::_))
110 .WillOnce(DoAll(SaveArg<1>(&handler_userdata), Return(nullptr)));
111 // Timer always enables the source to keep ticking
112 expectSetEnabled(source::Enabled::On);
113 timer = std::make_unique<TestTimer>(event, runCallback, interval);
114 }
115
116 void TearDown()
117 {
118 expectSetEnabled(source::Enabled::Off);
119 EXPECT_CALL(mock, sd_event_source_unref(expected_source))
120 .WillOnce(Return(nullptr));
121 timer.reset();
122 EXPECT_EQ(0, event_ref_times);
123 }
124};
125
126TEST_F(TimerTest, NewTimer)
127{
128 EXPECT_FALSE(timer->hasExpired());
129 EXPECT_EQ(interval, timer->getInterval());
130}
131
132TEST_F(TimerTest, IsEnabled)
133{
134 expectGetEnabled(source::Enabled::On);
135 EXPECT_TRUE(timer->isEnabled());
136 expectGetEnabled(source::Enabled::Off);
137 EXPECT_FALSE(timer->isEnabled());
138}
139
140TEST_F(TimerTest, GetRemainingDisabled)
141{
142 expectGetEnabled(source::Enabled::Off);
143 EXPECT_THROW(timer->getRemaining(), std::runtime_error);
144}
145
146TEST_F(TimerTest, GetRemainingNegative)
147{
148 milliseconds now(675), end(453);
149 expectGetEnabled(source::Enabled::On);
150 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
151 .WillOnce(
152 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
153 expectNow(now);
154 EXPECT_EQ(milliseconds(0), timer->getRemaining());
155}
156
157TEST_F(TimerTest, GetRemainingPositive)
158{
159 milliseconds now(453), end(675);
160 expectGetEnabled(source::Enabled::On);
161 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_))
162 .WillOnce(
163 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0)));
164 expectNow(now);
165 EXPECT_EQ(end - now, timer->getRemaining());
166}
167
168TEST_F(TimerTest, SetEnabled)
169{
170 expectSetEnabled(source::Enabled::On);
171 timer->setEnabled(true);
172 EXPECT_FALSE(timer->hasExpired());
173 // Value should always be passed through regardless of current state
174 expectSetEnabled(source::Enabled::On);
175 timer->setEnabled(true);
176 EXPECT_FALSE(timer->hasExpired());
177
178 expectSetEnabled(source::Enabled::Off);
179 timer->setEnabled(false);
180 EXPECT_FALSE(timer->hasExpired());
181 // Value should always be passed through regardless of current state
182 expectSetEnabled(source::Enabled::Off);
183 timer->setEnabled(false);
184 EXPECT_FALSE(timer->hasExpired());
185}
186
187TEST_F(TimerTest, SetRemaining)
188{
189 const milliseconds now(90), remaining(30);
190 expectNow(now);
191 expectSetTime(now + remaining);
192 timer->setRemaining(remaining);
193 EXPECT_EQ(interval, timer->getInterval());
194 EXPECT_FALSE(timer->hasExpired());
195}
196
197TEST_F(TimerTest, ResetRemaining)
198{
199 const milliseconds now(90);
200 expectNow(now);
201 expectSetTime(now + interval);
202 timer->resetRemaining();
203 EXPECT_EQ(interval, timer->getInterval());
204 EXPECT_FALSE(timer->hasExpired());
205}
206
207TEST_F(TimerTest, SetInterval)
208{
209 const milliseconds new_interval(40);
210 timer->setInterval(new_interval);
211 EXPECT_EQ(new_interval, timer->getInterval());
212 EXPECT_FALSE(timer->hasExpired());
213}
214
215TEST_F(TimerTest, SetValuesExpiredTimer)
216{
217 const milliseconds new_time(90);
218 expectNow(new_time);
219 expectSetTime(new_time + interval);
220 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
221 EXPECT_TRUE(timer->hasExpired());
222 EXPECT_EQ(interval, timer->getInterval());
223
224 // Timer should remain expired unless clearExpired() or reset()
225 expectSetEnabled(source::Enabled::On);
226 timer->setEnabled(true);
227 EXPECT_TRUE(timer->hasExpired());
228 expectNow(milliseconds(20));
229 expectSetTime(milliseconds(50));
230 timer->setRemaining(milliseconds(30));
231 EXPECT_TRUE(timer->hasExpired());
232 timer->setInterval(milliseconds(10));
233 EXPECT_TRUE(timer->hasExpired());
234 expectNow(milliseconds(20));
235 expectSetTime(milliseconds(30));
236 timer->resetRemaining();
237 EXPECT_TRUE(timer->hasExpired());
238
239 timer->clearExpired();
240 EXPECT_FALSE(timer->hasExpired());
241}
242
243TEST_F(TimerTest, Restart)
244{
245 const milliseconds new_time(90);
246 expectNow(new_time);
247 expectSetTime(new_time + interval);
248 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata));
249 EXPECT_TRUE(timer->hasExpired());
250 EXPECT_EQ(interval, timer->getInterval());
251
252 const milliseconds new_interval(471);
253 expectNow(starting_time);
254 expectSetTime(starting_time + new_interval);
255 expectSetEnabled(source::Enabled::On);
256 timer->restart(new_interval);
257 EXPECT_FALSE(timer->hasExpired());
258 EXPECT_EQ(new_interval, timer->getInterval());
259}
260
261} // namespace
262} // namespace utility
263} // namespace sdeventplus