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