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