|
17 | 17 | #include <algorithm> |
18 | 18 | #include <chrono> |
19 | 19 | #include <limits> |
| 20 | +#include <memory> |
20 | 21 | #include <string> |
21 | 22 |
|
22 | 23 | #include "rcl/error_handling.h" |
23 | 24 | #include "rcl/time.h" |
24 | 25 | #include "rclcpp/clock.hpp" |
25 | 26 | #include "rclcpp/rclcpp.hpp" |
26 | 27 | #include "rclcpp/time.hpp" |
| 28 | +#include "rclcpp/time_source.hpp" |
27 | 29 | #include "rclcpp/utilities.hpp" |
28 | 30 |
|
29 | 31 | #include "../utils/rclcpp_gtest_macros.hpp" |
@@ -447,3 +449,154 @@ TEST_F(TestTime, test_overflow_underflow_throws) { |
447 | 449 | test_time = rclcpp::Duration::from_nanoseconds(INT64_MIN) + rclcpp::Time(-1), |
448 | 450 | std::underflow_error("addition leads to int64_t underflow")); |
449 | 451 | } |
| 452 | + |
| 453 | +class TestClockSleep : public ::testing::Test |
| 454 | +{ |
| 455 | +protected: |
| 456 | + void SetUp() |
| 457 | + { |
| 458 | + // Shutdown in case there was a dangling global context from other test fixtures |
| 459 | + rclcpp::shutdown(); |
| 460 | + rclcpp::init(0, nullptr); |
| 461 | + node = std::make_shared<rclcpp::Node>("clock_sleep_node"); |
| 462 | + param_client = std::make_shared<rclcpp::SyncParametersClient>(node); |
| 463 | + ASSERT_TRUE(param_client->wait_for_service(5s)); |
| 464 | + } |
| 465 | + |
| 466 | + void TearDown() |
| 467 | + { |
| 468 | + node.reset(); |
| 469 | + rclcpp::shutdown(); |
| 470 | + } |
| 471 | + |
| 472 | + rclcpp::Node::SharedPtr node; |
| 473 | + rclcpp::SyncParametersClient::SharedPtr param_client; |
| 474 | +}; |
| 475 | + |
| 476 | +TEST_F(TestClockSleep, bad_clock_type) { |
| 477 | + rclcpp::Clock clock(RCL_SYSTEM_TIME); |
| 478 | + rclcpp::Time steady_until(12345, 0, RCL_STEADY_TIME); |
| 479 | + ASSERT_FALSE(clock.sleep_until(steady_until)); |
| 480 | + |
| 481 | + rclcpp::Time ros_until(54321, 0, RCL_ROS_TIME); |
| 482 | + ASSERT_FALSE(clock.sleep_until(ros_until)); |
| 483 | +} |
| 484 | + |
| 485 | +TEST_F(TestClockSleep, sleep_until_basic_system) { |
| 486 | + static const auto MILLION = 1000L * 1000L; |
| 487 | + const auto milliseconds = 300; |
| 488 | + rclcpp::Clock clock(RCL_SYSTEM_TIME); |
| 489 | + auto delay = rclcpp::Duration(0, milliseconds * MILLION); |
| 490 | + auto sleep_until = clock.now() + delay; |
| 491 | + |
| 492 | + auto start = std::chrono::system_clock::now(); |
| 493 | + ASSERT_TRUE(clock.sleep_until(sleep_until)); |
| 494 | + auto end = std::chrono::system_clock::now(); |
| 495 | + |
| 496 | + EXPECT_GE(clock.now(), sleep_until); |
| 497 | + EXPECT_GE(end - start, std::chrono::milliseconds(milliseconds)); |
| 498 | +} |
| 499 | + |
| 500 | +TEST_F(TestClockSleep, sleep_until_basic_steady) { |
| 501 | + static const auto MILLION = 1000L * 1000L; |
| 502 | + const auto milliseconds = 300; |
| 503 | + rclcpp::Clock clock(RCL_STEADY_TIME); |
| 504 | + auto delay = rclcpp::Duration(0, milliseconds * MILLION); |
| 505 | + auto sleep_until = clock.now() + delay; |
| 506 | + |
| 507 | + auto steady_start = std::chrono::steady_clock::now(); |
| 508 | + ASSERT_TRUE(clock.sleep_until(sleep_until)); |
| 509 | + auto steady_end = std::chrono::steady_clock::now(); |
| 510 | + |
| 511 | + EXPECT_GE(clock.now(), sleep_until); |
| 512 | + EXPECT_GE(steady_end - steady_start, std::chrono::milliseconds(milliseconds)); |
| 513 | +} |
| 514 | + |
| 515 | +TEST_F(TestClockSleep, sleep_until_steady_past_returns_immediately) { |
| 516 | + rclcpp::Clock clock(RCL_STEADY_TIME); |
| 517 | + auto until = clock.now() - rclcpp::Duration(1000, 0); |
| 518 | + // This should return immediately, other possible behavior might be sleep forever and timeout |
| 519 | + ASSERT_TRUE(clock.sleep_until(until)); |
| 520 | +} |
| 521 | + |
| 522 | +TEST_F(TestClockSleep, sleep_until_system_past_returns_immediately) { |
| 523 | + rclcpp::Clock clock(RCL_SYSTEM_TIME); |
| 524 | + auto until = clock.now() - rclcpp::Duration(1000, 0); |
| 525 | + // This should return immediately, other possible behavior might be sleep forever and timeout |
| 526 | + ASSERT_TRUE(clock.sleep_until(until)); |
| 527 | +} |
| 528 | + |
| 529 | +TEST_F(TestClockSleep, sleep_until_ros_time_enable_interrupt) { |
| 530 | + auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME); |
| 531 | + rclcpp::TimeSource time_source; |
| 532 | + time_source.attachNode(node); |
| 533 | + time_source.attachClock(clock); |
| 534 | + |
| 535 | + // 5 second timeout, but it should be interrupted right away |
| 536 | + const auto until = clock->now() + rclcpp::Duration(5, 0); |
| 537 | + |
| 538 | + // Try sleeping with ROS time off, then turn it on to interrupt |
| 539 | + bool sleep_succeeded = true; |
| 540 | + auto sleep_thread = std::thread( |
| 541 | + [clock, until, &sleep_succeeded]() { |
| 542 | + sleep_succeeded = clock->sleep_until(until); |
| 543 | + }); |
| 544 | + // yield execution long enough to let the sleep thread get to waiting on the condition variable |
| 545 | + std::this_thread::sleep_for(std::chrono::milliseconds(200)); |
| 546 | + auto set_parameters_results = param_client->set_parameters( |
| 547 | + {rclcpp::Parameter("use_sim_time", true)}); |
| 548 | + for (auto & result : set_parameters_results) { |
| 549 | + ASSERT_TRUE(result.successful); |
| 550 | + } |
| 551 | + sleep_thread.join(); |
| 552 | + EXPECT_FALSE(sleep_succeeded); |
| 553 | +} |
| 554 | + |
| 555 | +TEST_F(TestClockSleep, sleep_until_ros_time_disable_interrupt) { |
| 556 | + param_client->set_parameters({rclcpp::Parameter("use_sim_time", true)}); |
| 557 | + auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME); |
| 558 | + rclcpp::TimeSource time_source; |
| 559 | + time_source.attachNode(node); |
| 560 | + time_source.attachClock(clock); |
| 561 | + |
| 562 | + // /clock shouldn't be publishing, shouldn't be possible to reach timeout |
| 563 | + const auto until = clock->now() + rclcpp::Duration(600, 0); |
| 564 | + |
| 565 | + // Try sleeping with ROS time off, then turn it on to interrupt |
| 566 | + bool sleep_succeeded = true; |
| 567 | + auto sleep_thread = std::thread( |
| 568 | + [clock, until, &sleep_succeeded]() { |
| 569 | + sleep_succeeded = clock->sleep_until(until); |
| 570 | + }); |
| 571 | + // yield execution long enough to let the sleep thread get to waiting on the condition variable |
| 572 | + std::this_thread::sleep_for(std::chrono::milliseconds(200)); |
| 573 | + auto set_parameters_results = param_client->set_parameters( |
| 574 | + {rclcpp::Parameter("use_sim_time", false)}); |
| 575 | + for (auto & result : set_parameters_results) { |
| 576 | + ASSERT_TRUE(result.successful); |
| 577 | + } |
| 578 | + sleep_thread.join(); |
| 579 | + EXPECT_FALSE(sleep_succeeded); |
| 580 | +} |
| 581 | + |
| 582 | +TEST_F(TestClockSleep, sleep_until_shutdown_interrupt) { |
| 583 | + param_client->set_parameters({rclcpp::Parameter("use_sim_time", true)}); |
| 584 | + auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME); |
| 585 | + rclcpp::TimeSource time_source; |
| 586 | + time_source.attachNode(node); |
| 587 | + time_source.attachClock(clock); |
| 588 | + |
| 589 | + // the timeout doesn't matter here - no /clock is being published, so it should never wake |
| 590 | + const auto until = clock->now() + rclcpp::Duration(600, 0); |
| 591 | + |
| 592 | + bool sleep_succeeded = true; |
| 593 | + auto sleep_thread = std::thread( |
| 594 | + [clock, until, &sleep_succeeded]() { |
| 595 | + sleep_succeeded = clock->sleep_until(until); |
| 596 | + }); |
| 597 | + // yield execution long enough to let the sleep thread get to waiting on the condition variable |
| 598 | + std::this_thread::sleep_for(std::chrono::milliseconds(200)); |
| 599 | + rclcpp::shutdown(); |
| 600 | + sleep_thread.join(); |
| 601 | + EXPECT_FALSE(sleep_succeeded); |
| 602 | +} |
0 commit comments