Skip to content

Commit c2760b4

Browse files
[CI] Backport #1636 #1668 and fix coverage on jammy (#1677) (#1693)
* Add a pytest launch file to test ros2_control_node (#1636) * [CI] Add coveragepy and remove ignore: test (#1668) (cherry picked from commit 8b32c33) Co-authored-by: Christoph Fröhlich <[email protected]>
1 parent 1634124 commit c2760b4

File tree

6 files changed

+123
-6
lines changed

6 files changed

+123
-6
lines changed

codecov.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ fixes:
1414
comment:
1515
layout: "diff, flags, files"
1616
behavior: default
17-
ignore:
18-
- "**/test"
1917
flags:
2018
unittests:
2119
paths:

controller_manager/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ if(BUILD_TESTING)
204204
ament_target_dependencies(test_hardware_management_srvs
205205
controller_manager_msgs
206206
)
207+
208+
find_package(ament_cmake_pytest REQUIRED)
209+
install(FILES test/test_ros2_control_node.yaml
210+
DESTINATION test)
211+
ament_add_pytest_test(test_ros2_control_node test/test_ros2_control_node_launch.py)
207212
endif()
208213

209214
install(

controller_manager/package.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
<depend>std_msgs</depend>
3030

3131
<test_depend>ament_cmake_gmock</test_depend>
32+
<test_depend>ament_cmake_pytest</test_depend>
33+
<test_depend>python3-coverage</test_depend>
3234
<test_depend>hardware_interface_testing</test_depend>
3335
<test_depend>ros2_control_test_assets</test_depend>
3436

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
controller_manager:
2+
ros__parameters:
3+
update_rate: 100 # Hz
4+
5+
ctrl_with_parameters_and_type:
6+
ros__parameters:
7+
type: "controller_manager/test_controller"
8+
joint_names: ["joint0"]
9+
10+
joint_state_broadcaster:
11+
type: joint_state_broadcaster/JointStateBroadcaster
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright (c) 2024 AIT - Austrian Institute of Technology GmbH
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions are met:
5+
#
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
#
9+
# * Redistributions in binary form must reproduce the above copyright
10+
# notice, this list of conditions and the following disclaimer in the
11+
# documentation and/or other materials provided with the distribution.
12+
#
13+
# * Neither the name of the {copyright_holder} nor the names of its
14+
# contributors may be used to endorse or promote products derived from
15+
# this software without specific prior written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
# POSSIBILITY OF SUCH DAMAGE.
28+
#
29+
# Author: Christoph Froehlich
30+
31+
import pytest
32+
import unittest
33+
import time
34+
35+
from launch import LaunchDescription
36+
from launch.substitutions import PathJoinSubstitution
37+
from launch_ros.substitutions import FindPackageShare
38+
from launch_testing.actions import ReadyToTest
39+
40+
import launch_testing.markers
41+
import rclpy
42+
import launch_ros.actions
43+
from rclpy.node import Node
44+
45+
46+
# Executes the given launch file and checks if all nodes can be started
47+
@pytest.mark.launch_test
48+
def generate_test_description():
49+
50+
robot_controllers = PathJoinSubstitution(
51+
[
52+
FindPackageShare("controller_manager"),
53+
"test",
54+
"test_ros2_control_node.yaml",
55+
]
56+
)
57+
58+
control_node = launch_ros.actions.Node(
59+
package="controller_manager",
60+
executable="ros2_control_node",
61+
parameters=[robot_controllers],
62+
output="both",
63+
)
64+
65+
return LaunchDescription([control_node, ReadyToTest()])
66+
67+
68+
# This is our test fixture. Each method is a test case.
69+
# These run alongside the processes specified in generate_test_description()
70+
class TestFixture(unittest.TestCase):
71+
72+
def setUp(self):
73+
rclpy.init()
74+
self.node = Node("test_node")
75+
76+
def tearDown(self):
77+
self.node.destroy_node()
78+
rclpy.shutdown()
79+
80+
def test_node_start(self):
81+
start = time.time()
82+
found = False
83+
while time.time() - start < 2.0 and not found:
84+
found = "controller_manager" in self.node.get_node_names()
85+
time.sleep(0.1)
86+
assert found, "controller_manager not found!"
87+
88+
89+
@launch_testing.post_shutdown_test()
90+
# These tests are run after the processes in generate_test_description() have shutdown.
91+
class TestDescriptionCraneShutdown(unittest.TestCase):
92+
93+
def test_exit_codes(self, proc_info):
94+
"""Check if the processes exited normally."""
95+
launch_testing.asserts.assertExitCodes(proc_info)

controller_manager/test/test_spawner_unspawner.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
using ::testing::_;
3030
using ::testing::Return;
31+
const char coveragepy_script[] = "python3 -m coverage run --append --branch";
3132

3233
using namespace std::chrono_literals;
3334
class TestLoadController : public ControllerManagerFixture<controller_manager::ControllerManager>
@@ -68,14 +69,18 @@ class TestLoadController : public ControllerManagerFixture<controller_manager::C
6869

6970
int call_spawner(const std::string extra_args)
7071
{
71-
std::string spawner_script = "ros2 run controller_manager spawner ";
72+
std::string spawner_script =
73+
std::string(coveragepy_script) +
74+
" $(ros2 pkg prefix controller_manager)/lib/controller_manager/spawner ";
7275
return std::system((spawner_script + extra_args).c_str());
7376
}
7477

7578
int call_unspawner(const std::string extra_args)
7679
{
77-
std::string spawner_script = "ros2 run controller_manager unspawner ";
78-
return std::system((spawner_script + extra_args).c_str());
80+
std::string unspawner_script =
81+
std::string(coveragepy_script) +
82+
" $(ros2 pkg prefix controller_manager)/lib/controller_manager/unspawner ";
83+
return std::system((unspawner_script + extra_args).c_str());
7984
}
8085

8186
TEST_F(TestLoadController, spawner_with_no_arguments_errors)
@@ -267,7 +272,8 @@ TEST_F(TestLoadController, unload_on_kill)
267272
// timeout command will kill it after the specified time with signal SIGINT
268273
std::stringstream ss;
269274
ss << "timeout --signal=INT 5 "
270-
<< "ros2 run controller_manager spawner "
275+
<< std::string(coveragepy_script) +
276+
" $(ros2 pkg prefix controller_manager)/lib/controller_manager/spawner "
271277
<< "ctrl_3 -c test_controller_manager -t "
272278
<< std::string(test_controller::TEST_CONTROLLER_CLASS_NAME) << " --unload-on-kill";
273279

0 commit comments

Comments
 (0)