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