ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

[ROS2] Testing launch of Node

asked 2023-08-08 16:14:11 -0600

slim71 gravatar image

I've been trying to write some tests for my package, but with not much success until now...

I have a "PelicanUnit" node and in its constructor I require a path to a .sdf model: if that's not given, everything shuts down with a std::runtime_error. Now, using colcon test and Google Test, I've been trying to write sort of like smoketests for successful and unsuccessful launches. The failing case has been a little tricky, but in the end I've managed with this:

TEST(SmokeTests, ParseModelFailing) {
     // Initialize RCLCPP
     rclcpp::init(0, nullptr);
     EXPECT_THROW(
         {
             try {
                 PelicanUnit pelican;
             } catch (const std::exception& e) {
                 EXPECT_STREQ("Agent model could not be parsed!", e.what());
                 throw; // Re-throw the exception for Google Test to catch
             }
         }, 
         std::runtime_error);
     // Shutdown RCLCPP
     rclcpp::shutdown(); 
}

Unfortunately, I cannot find a way for the other test.

Up until now I've tried:

  • launching the node from the test itself but I could not pass the config file I normally use with the standard launch file

    TEST(SmokeTests, Main) {
        // Useless, since the node itself does not parse arguments (?)
        char  arg0[] = "--params-file";
        char  arg1[] = "src/pelican/config/copter1.yaml"; // TODO: use package share directory
        char* argv[] = { &arg0[0], &arg1[0], NULL };
        int   argc   = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
    
        // Initialization
        rclcpp::init(argc, argv);
    
        // Instantiation
        rclcpp::Node::SharedPtr node = std::make_shared<PelicanUnit>();
        rclcpp::executors::MultiThreadedExecutor executor;
    
        executor.add_node(node);
        executor.spin();
    
        rclcpp::shutdown();
    }
    
  • using a test launch_file, which has been somewhat almost good, but the config file used has not been found and, most importantly, I'm not sure I can use this as a proper test. I can only test that the command is launched, not that the node itself fires up smoothly...

    TEST(SmokeTests, NodeStartingCorrectly) {
        // Set the launch file path and arguments
        const std::string pwd = "source ";
        const std::string launch_command = "ros2 launch pelican test_launch.launch.py";
    
        // Run the launch command and check its exit status
        std::system(pwd.c_str());
        int exit_status = std::system(launch_command.c_str());
        ASSERT_EQ(exit_status, 0);
    }
    

(launch file)

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
from launch.substitutions import PathJoinSubstitution, ThisLaunchFileDir


def generate_launch_description():
    config_pkg_share = FindPackageShare('pelican')
    config_middleware = 'config/'

    ld =  LaunchDescription()

    config_file = 'copter1.yaml'

    node = Node(
            package='pelican',
            executable='pelican_unit',
            ros_arguments=['--params-file', 
                           PathJoinSubstitution([config_pkg_share, config_middleware, config_file])]
        )

    ld.add_action(node)

    return ld

Is there anyone that can provide some assistance or suggestions for this? Thank you!

edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2023-08-11 15:20:04 -0600

slim71 gravatar image

In the end I've managed to do it like this:

TEST(SmokeTests, NodeStartingCorrectly) {
    char  arg0[] = "--ros-args";
    char  arg1[] = "--params-file";
    char  arg2[] = "test.yaml";
    char* argv[] = { &arg0[0], &arg1[0], &arg2[0], NULL };
    int   argc   = (int)(sizeof(argv) / sizeof(argv[0])) - 1;

    // Initialization
    rclcpp::init(argc, argv);

    // Instantiation
    rclcpp::Node::SharedPtr node = std::make_shared<PelicanUnit>();
    rclcpp::executors::MultiThreadedExecutor executor;

    executor.add_node(node);

    std::thread t(
        [&](){
            ASSERT_NO_THROW(executor.spin(););
        }
    );

    std::this_thread::sleep_for(std::chrono::seconds(5));
    executor.cancel();
    t.join(); // Wait for thread completion


    rclcpp::shutdown();
}

I have not found another way with spin_some or spin_until_future_complete, probably due to something related to these issues:

edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2023-08-08 16:14:11 -0600

Seen: 2,898 times

Last updated: Aug 11 '23