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