Skip to content

Commit 9d5aaf5

Browse files
authored
fix flakiness in TestTimersManager unit-test (#2468)
the previous version of the test was relying on the assumption that a timer with 1ms period gets called at least 6 times if the main thread waits 15ms. this is true most of the times, but it's not guaranteed, especially when running the test on windows CI servers. the new version of the test makes no assumptions on how much time it takes for the timers manager to invoke the timers, but rather focuses on ensuring that they are called the right amount of times, which is what's important for the purpose of the test Signed-off-by: Alberto Soragna <[email protected]>
1 parent bedc547 commit 9d5aaf5

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

rclcpp/test/rclcpp/test_timers_manager.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -367,22 +367,23 @@ TEST_F(TestTimersManager, check_one_timer_cancel_doesnt_affect_other_timers)
367367
auto timers_manager = std::make_shared<TimersManager>(
368368
rclcpp::contexts::get_global_default_context());
369369

370-
size_t t1_runs = 0;
370+
std::atomic<size_t> t1_runs = 0;
371+
const size_t cancel_iter = 5;
371372
std::shared_ptr<TimerT> t1;
372373
// After a while cancel t1. Don't remove it though.
373374
// Simulates typical usage in a Node where a timer is cancelled but not removed,
374375
// since typical users aren't going to mess around with the timer manager.
375376
t1 = TimerT::make_shared(
376377
1ms,
377-
[&t1_runs, &t1]() {
378+
[&t1_runs, &t1, cancel_iter]() {
378379
t1_runs++;
379-
if (t1_runs == 5) {
380+
if (t1_runs == cancel_iter) {
380381
t1->cancel();
381382
}
382383
},
383384
rclcpp::contexts::get_global_default_context());
384385

385-
size_t t2_runs = 0;
386+
std::atomic<size_t> t2_runs = 0;
386387
auto t2 = TimerT::make_shared(
387388
1ms,
388389
[&t2_runs]() {
@@ -397,11 +398,42 @@ TEST_F(TestTimersManager, check_one_timer_cancel_doesnt_affect_other_timers)
397398
// Start timers thread
398399
timers_manager->start();
399400

400-
std::this_thread::sleep_for(15ms);
401+
// Wait for t1 to be canceled
402+
auto loop_start_time = std::chrono::high_resolution_clock::now();
403+
while (!t1->is_canceled()) {
404+
auto now = std::chrono::high_resolution_clock::now();
405+
if (now - loop_start_time >= std::chrono::seconds(30)) {
406+
FAIL() << "timeout waiting for t1 to be canceled";
407+
break;
408+
}
409+
std::this_thread::sleep_for(3ms);
410+
}
411+
412+
EXPECT_TRUE(t1->is_canceled());
413+
EXPECT_FALSE(t2->is_canceled());
414+
EXPECT_EQ(t1_runs, cancel_iter);
415+
416+
// Verify that t2 is still being invoked
417+
const size_t start_t2_runs = t2_runs;
418+
const size_t num_t2_extra_runs = 6;
419+
loop_start_time = std::chrono::high_resolution_clock::now();
420+
while (t2_runs < start_t2_runs + num_t2_extra_runs) {
421+
auto now = std::chrono::high_resolution_clock::now();
422+
if (now - loop_start_time >= std::chrono::seconds(30)) {
423+
FAIL() << "timeout waiting for t2 to do some runs";
424+
break;
425+
}
426+
std::this_thread::sleep_for(3ms);
427+
}
428+
429+
EXPECT_TRUE(t1->is_canceled());
430+
EXPECT_FALSE(t2->is_canceled());
431+
// t1 hasn't run since before
432+
EXPECT_EQ(t1_runs, cancel_iter);
433+
// t2 has run the expected additional number of times
434+
EXPECT_GE(t2_runs, start_t2_runs + num_t2_extra_runs);
435+
// the t2 runs are strictly more than the t1 runs
436+
EXPECT_GT(t2_runs, t1_runs);
401437

402-
// t1 has stopped running
403-
EXPECT_NE(t1_runs, t2_runs);
404-
// Check that t2 has significantly more calls
405-
EXPECT_LT(t1_runs + 5, t2_runs);
406438
timers_manager->stop();
407439
}

0 commit comments

Comments
 (0)