1 #include <chrono> 2 #include <gmock/gmock.h> 3 #include <gtest/gtest.h> 4 #include <memory> 5 #include <optional> 6 #include <sdeventplus/clock.hpp> 7 #include <sdeventplus/event.hpp> 8 #include <sdeventplus/test/sdevent.hpp> 9 #include <sdeventplus/utility/timer.hpp> 10 #include <stdexcept> 11 #include <systemd/sd-event.h> 12 13 namespace sdeventplus 14 { 15 namespace utility 16 { 17 namespace 18 { 19 20 constexpr ClockId testClock = ClockId::Monotonic; 21 22 using std::chrono::microseconds; 23 using std::chrono::milliseconds; 24 using testing::DoAll; 25 using testing::Return; 26 using testing::SaveArg; 27 using testing::SetArgPointee; 28 using TestTimer = Timer<testClock>; 29 30 ssize_t event_ref_times = 0; 31 32 ACTION(EventRef) 33 { 34 event_ref_times++; 35 } 36 37 ACTION(EventUnref) 38 { 39 ASSERT_LT(0, event_ref_times); 40 event_ref_times--; 41 } 42 43 class TimerTest : public testing::Test 44 { 45 protected: 46 testing::StrictMock<test::SdEventMock> mock; 47 sd_event* const expected_event = reinterpret_cast<sd_event*>(1234); 48 sd_event_source* const expected_source = 49 reinterpret_cast<sd_event_source*>(2345); 50 const milliseconds interval{134}; 51 const milliseconds starting_time{10}; 52 sd_event_time_handler_t handler = nullptr; 53 void* handler_userdata; 54 std::unique_ptr<Event> event; 55 std::unique_ptr<TestTimer> timer; 56 std::function<void()> callback; 57 58 void expectNow(microseconds ret) 59 { 60 EXPECT_CALL(mock, 61 sd_event_now(expected_event, 62 static_cast<clockid_t>(testClock), testing::_)) 63 .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0))); 64 } 65 66 void expectSetTime(microseconds time) 67 { 68 EXPECT_CALL(mock, 69 sd_event_source_set_time(expected_source, time.count())) 70 .WillOnce(Return(0)); 71 } 72 73 void expectSetEnabled(source::Enabled enabled) 74 { 75 EXPECT_CALL(mock, sd_event_source_set_enabled( 76 expected_source, static_cast<int>(enabled))) 77 .WillOnce(Return(0)); 78 } 79 80 void expectGetEnabled(source::Enabled enabled) 81 { 82 EXPECT_CALL(mock, 83 sd_event_source_get_enabled(expected_source, testing::_)) 84 .WillOnce( 85 DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0))); 86 } 87 88 void resetTimer() 89 { 90 if (timer) 91 { 92 expectSetEnabled(source::Enabled::Off); 93 timer.reset(); 94 } 95 } 96 97 void expireTimer() 98 { 99 const milliseconds new_time(90); 100 expectNow(new_time); 101 expectSetTime(new_time + interval); 102 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 103 EXPECT_TRUE(timer->hasExpired()); 104 EXPECT_EQ(interval, timer->getInterval()); 105 } 106 107 void SetUp() 108 { 109 EXPECT_CALL(mock, sd_event_ref(expected_event)) 110 .WillRepeatedly(DoAll(EventRef(), Return(expected_event))); 111 EXPECT_CALL(mock, sd_event_unref(expected_event)) 112 .WillRepeatedly(DoAll(EventUnref(), Return(nullptr))); 113 event = std::make_unique<Event>(expected_event, &mock); 114 EXPECT_CALL(mock, sd_event_source_unref(expected_source)) 115 .WillRepeatedly(Return(nullptr)); 116 EXPECT_CALL(mock, 117 sd_event_source_set_userdata(expected_source, testing::_)) 118 .WillRepeatedly( 119 DoAll(SaveArg<1>(&handler_userdata), Return(nullptr))); 120 121 // Having a callback proxy allows us to update the test callback 122 // dynamically, without changing it inside the timer 123 auto runCallback = [&](TestTimer&) { 124 if (callback) 125 { 126 callback(); 127 } 128 }; 129 expectNow(starting_time); 130 EXPECT_CALL(mock, sd_event_add_time( 131 expected_event, testing::_, 132 static_cast<clockid_t>(testClock), 133 microseconds(starting_time + interval).count(), 134 1000, testing::_, nullptr)) 135 .WillOnce(DoAll(SetArgPointee<1>(expected_source), 136 SaveArg<5>(&handler), Return(0))); 137 expectSetEnabled(source::Enabled::On); 138 timer = std::make_unique<TestTimer>(*event, runCallback, interval); 139 EXPECT_EQ(expected_event, timer->get_event().get()); 140 } 141 142 void TearDown() 143 { 144 resetTimer(); 145 event.reset(); 146 EXPECT_EQ(0, event_ref_times); 147 } 148 }; 149 150 TEST_F(TimerTest, NoCallback) 151 { 152 resetTimer(); 153 expectNow(starting_time); 154 EXPECT_CALL( 155 mock, sd_event_add_time(expected_event, testing::_, 156 static_cast<clockid_t>(testClock), 157 microseconds(starting_time + interval).count(), 158 1000, testing::_, nullptr)) 159 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler), 160 Return(0))); 161 expectSetEnabled(source::Enabled::On); 162 timer = std::make_unique<TestTimer>(*event, nullptr, interval); 163 164 expectNow(starting_time); 165 expectSetTime(starting_time + interval); 166 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 167 } 168 169 TEST_F(TimerTest, NoInterval) 170 { 171 resetTimer(); 172 expectNow(starting_time); 173 EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_, 174 static_cast<clockid_t>(testClock), 175 microseconds(starting_time).count(), 176 1000, testing::_, nullptr)) 177 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler), 178 Return(0))); 179 expectSetEnabled(source::Enabled::Off); 180 timer = std::make_unique<TestTimer>(*event, nullptr); 181 182 EXPECT_EQ(std::nullopt, timer->getInterval()); 183 EXPECT_THROW(timer->setEnabled(true), std::runtime_error); 184 } 185 186 TEST_F(TimerTest, NewTimer) 187 { 188 EXPECT_FALSE(timer->hasExpired()); 189 EXPECT_EQ(interval, timer->getInterval()); 190 } 191 192 TEST_F(TimerTest, IsEnabled) 193 { 194 expectGetEnabled(source::Enabled::On); 195 EXPECT_TRUE(timer->isEnabled()); 196 expectGetEnabled(source::Enabled::Off); 197 EXPECT_FALSE(timer->isEnabled()); 198 } 199 200 TEST_F(TimerTest, GetRemainingDisabled) 201 { 202 expectGetEnabled(source::Enabled::Off); 203 EXPECT_THROW(timer->getRemaining(), std::runtime_error); 204 } 205 206 TEST_F(TimerTest, GetRemainingNegative) 207 { 208 milliseconds now(675), end(453); 209 expectGetEnabled(source::Enabled::On); 210 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 211 .WillOnce( 212 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); 213 expectNow(now); 214 EXPECT_EQ(milliseconds(0), timer->getRemaining()); 215 } 216 217 TEST_F(TimerTest, GetRemainingPositive) 218 { 219 milliseconds now(453), end(675); 220 expectGetEnabled(source::Enabled::On); 221 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 222 .WillOnce( 223 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); 224 expectNow(now); 225 EXPECT_EQ(end - now, timer->getRemaining()); 226 } 227 228 TEST_F(TimerTest, SetEnabled) 229 { 230 expectSetEnabled(source::Enabled::On); 231 timer->setEnabled(true); 232 EXPECT_FALSE(timer->hasExpired()); 233 // Value should always be passed through regardless of current state 234 expectSetEnabled(source::Enabled::On); 235 timer->setEnabled(true); 236 EXPECT_FALSE(timer->hasExpired()); 237 238 expectSetEnabled(source::Enabled::Off); 239 timer->setEnabled(false); 240 EXPECT_FALSE(timer->hasExpired()); 241 // Value should always be passed through regardless of current state 242 expectSetEnabled(source::Enabled::Off); 243 timer->setEnabled(false); 244 EXPECT_FALSE(timer->hasExpired()); 245 } 246 247 TEST_F(TimerTest, SetEnabledUnsetTimer) 248 { 249 // Force the timer to become unset 250 expectSetEnabled(source::Enabled::Off); 251 timer->restart(std::nullopt); 252 253 // Setting an interval should not update the timer directly 254 timer->setInterval(milliseconds(90)); 255 256 expectSetEnabled(source::Enabled::Off); 257 timer->setEnabled(false); 258 EXPECT_THROW(timer->setEnabled(true), std::runtime_error); 259 } 260 261 TEST_F(TimerTest, SetEnabledOneshot) 262 { 263 // Timer effectively becomes oneshot if it gets initialized but has 264 // the interval removed 265 timer->setInterval(std::nullopt); 266 267 expectSetEnabled(source::Enabled::Off); 268 timer->setEnabled(false); 269 expectSetEnabled(source::Enabled::On); 270 timer->setEnabled(true); 271 } 272 273 TEST_F(TimerTest, SetRemaining) 274 { 275 const milliseconds now(90), remaining(30); 276 expectNow(now); 277 expectSetTime(now + remaining); 278 timer->setRemaining(remaining); 279 EXPECT_EQ(interval, timer->getInterval()); 280 EXPECT_FALSE(timer->hasExpired()); 281 } 282 283 TEST_F(TimerTest, ResetRemaining) 284 { 285 const milliseconds now(90); 286 expectNow(now); 287 expectSetTime(now + interval); 288 timer->resetRemaining(); 289 EXPECT_EQ(interval, timer->getInterval()); 290 EXPECT_FALSE(timer->hasExpired()); 291 } 292 293 TEST_F(TimerTest, SetInterval) 294 { 295 const milliseconds new_interval(40); 296 timer->setInterval(new_interval); 297 EXPECT_EQ(new_interval, timer->getInterval()); 298 EXPECT_FALSE(timer->hasExpired()); 299 } 300 301 TEST_F(TimerTest, SetIntervalEmpty) 302 { 303 timer->setInterval(std::nullopt); 304 EXPECT_EQ(std::nullopt, timer->getInterval()); 305 EXPECT_FALSE(timer->hasExpired()); 306 } 307 308 TEST_F(TimerTest, CallbackHappensLast) 309 { 310 const milliseconds new_time(90); 311 expectNow(new_time); 312 expectSetTime(new_time + interval); 313 callback = [&]() { 314 EXPECT_TRUE(timer->hasExpired()); 315 expectSetEnabled(source::Enabled::On); 316 timer->setEnabled(true); 317 timer->clearExpired(); 318 timer->setInterval(std::nullopt); 319 }; 320 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 321 EXPECT_FALSE(timer->hasExpired()); 322 EXPECT_EQ(std::nullopt, timer->getInterval()); 323 expectSetEnabled(source::Enabled::On); 324 timer->setEnabled(true); 325 } 326 327 TEST_F(TimerTest, CallbackOneshot) 328 { 329 // Make sure we try a one shot so we can test the callback 330 // correctly 331 timer->setInterval(std::nullopt); 332 333 expectSetEnabled(source::Enabled::Off); 334 callback = [&]() { 335 EXPECT_TRUE(timer->hasExpired()); 336 EXPECT_THROW(timer->setEnabled(true), std::runtime_error); 337 timer->setInterval(interval); 338 }; 339 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 340 EXPECT_THROW(timer->setEnabled(true), std::runtime_error); 341 } 342 343 TEST_F(TimerTest, SetValuesExpiredTimer) 344 { 345 const milliseconds new_time(90); 346 expectNow(new_time); 347 expectSetTime(new_time + interval); 348 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 349 EXPECT_TRUE(timer->hasExpired()); 350 EXPECT_EQ(interval, timer->getInterval()); 351 352 // Timer should remain expired unless clearExpired() or reset() 353 expectSetEnabled(source::Enabled::On); 354 timer->setEnabled(true); 355 EXPECT_TRUE(timer->hasExpired()); 356 expectNow(milliseconds(20)); 357 expectSetTime(milliseconds(50)); 358 timer->setRemaining(milliseconds(30)); 359 EXPECT_TRUE(timer->hasExpired()); 360 timer->setInterval(milliseconds(10)); 361 EXPECT_TRUE(timer->hasExpired()); 362 expectNow(milliseconds(20)); 363 expectSetTime(milliseconds(30)); 364 timer->resetRemaining(); 365 EXPECT_TRUE(timer->hasExpired()); 366 367 timer->clearExpired(); 368 EXPECT_FALSE(timer->hasExpired()); 369 } 370 371 TEST_F(TimerTest, Restart) 372 { 373 expireTimer(); 374 375 const milliseconds new_interval(471); 376 expectNow(starting_time); 377 expectSetTime(starting_time + new_interval); 378 expectSetEnabled(source::Enabled::On); 379 timer->restart(new_interval); 380 EXPECT_FALSE(timer->hasExpired()); 381 EXPECT_EQ(new_interval, timer->getInterval()); 382 expectSetEnabled(source::Enabled::On); 383 timer->setEnabled(true); 384 } 385 386 TEST_F(TimerTest, RestartEmpty) 387 { 388 expireTimer(); 389 390 expectSetEnabled(source::Enabled::Off); 391 timer->restart(std::nullopt); 392 EXPECT_FALSE(timer->hasExpired()); 393 EXPECT_EQ(std::nullopt, timer->getInterval()); 394 EXPECT_THROW(timer->setEnabled(true), std::runtime_error); 395 } 396 397 TEST_F(TimerTest, RestartOnce) 398 { 399 expireTimer(); 400 401 const milliseconds remaining(471); 402 expectNow(starting_time); 403 expectSetTime(starting_time + remaining); 404 expectSetEnabled(source::Enabled::On); 405 timer->restartOnce(remaining); 406 EXPECT_FALSE(timer->hasExpired()); 407 EXPECT_EQ(std::nullopt, timer->getInterval()); 408 expectSetEnabled(source::Enabled::On); 409 timer->setEnabled(true); 410 } 411 412 } // namespace 413 } // namespace utility 414 } // namespace sdeventplus 415