Skip to content

Commit 8860764

Browse files
committed
tests: refactor common utils
1 parent 3e678ce commit 8860764

File tree

7 files changed

+112
-41
lines changed

7 files changed

+112
-41
lines changed

test/__init__.py

Whitespace-only changes.

test/harmonic/test_follow_road.py

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,26 @@
99

1010
import launch_testing
1111
from launch import LaunchDescription
12-
from launch.actions import IncludeLaunchDescription
13-
from launch.launch_description_sources import PythonLaunchDescriptionSource
14-
import psutil
12+
from launch.actions import (
13+
IncludeLaunchDescription,
14+
)
15+
from launch.launch_description_sources import (
16+
PythonLaunchDescriptionSource,
17+
)
1518

1619
import rclpy
17-
from geometry_msgs.msg import Twist
20+
from std_msgs.msg import Bool
21+
from geometry_msgs.msg import Twist, PoseStamped
1822
from sensor_msgs.msg import Imu
1923

24+
from rclpy.qos import (
25+
QoSProfile,
26+
QoSReliabilityPolicy,
27+
QoSHistoryPolicy,
28+
)
29+
30+
from test.utils import stop_gazebo
31+
2032

2133
@pytest.mark.launch_test
2234
def generate_test_description():
@@ -62,7 +74,7 @@ def setUpClass(cls):
6274
cls.node = rclpy.create_node("test_node")
6375

6476
# wait for topics to be setup
65-
time.sleep(10)
77+
time.sleep(15)
6678

6779
@classmethod
6880
def tearDownClass(cls):
@@ -71,10 +83,7 @@ def tearDownClass(cls):
7183
cls.node.destroy_node()
7284
rclpy.shutdown()
7385
# Stop any running Gazebo processes
74-
for proc in psutil.process_iter(["name"]):
75-
if proc.info["name"] in ["gzserver", "gazebo", "ign gazebo", "gz"]:
76-
print(f"Stopping gzserver (PID={proc.pid})")
77-
proc.terminate()
86+
stop_gazebo()
7887

7988
def test_imu_works(self, proc_output):
8089
"""Test that the imu topic works."""
@@ -89,12 +98,19 @@ def test_imu_works(self, proc_output):
8998

9099
# Wait for messages (up to 1 second)
91100
for _ in range(10):
92-
rclpy.spin_once(self.__class__.node, timeout_sec=0.1)
101+
rclpy.spin_once(
102+
self.__class__.node,
103+
timeout_sec=0.1,
104+
)
93105
if msgs:
94106
break
95107

96108
# Check that we received msgs
97-
self.assertGreater(len(msgs), 0, msg="No IMU messages received.")
109+
self.assertGreater(
110+
len(msgs),
111+
0,
112+
msg="No IMU messages received.",
113+
)
98114

99115
# Clean up subscriptions
100116
self.__class__.node.destroy_subscription(sub)
@@ -103,33 +119,67 @@ def test_movement_works(self, proc_output):
103119
"""Test that the velocity topic works correctly."""
104120
msgs = []
105121

122+
sub_qos_profile = QoSProfile(
123+
reliability=QoSReliabilityPolicy.BEST_EFFORT,
124+
history=QoSHistoryPolicy.KEEP_LAST,
125+
depth=10,
126+
)
127+
128+
pub_qos_profile = QoSProfile(
129+
reliability=QoSReliabilityPolicy.RELIABLE,
130+
history=QoSHistoryPolicy.KEEP_LAST,
131+
depth=10,
132+
)
133+
106134
# Create a subscription to the velocity topic
107135
sub = self.__class__.node.create_subscription(
108-
Twist,
109-
"/gz/drone0/cmd_vel",
136+
PoseStamped,
137+
"/drone0/ground_truth/pose",
110138
lambda msg: msgs.append(msg),
111-
qos_profile=10,
139+
qos_profile=sub_qos_profile,
140+
)
141+
142+
arm_pub = self.__class__.node.create_publisher(
143+
Bool,
144+
"/gz/drone0/arm",
145+
qos_profile=pub_qos_profile,
112146
)
113147

114148
# Create a publisher to the velocity topic
115149
pub = self.__class__.node.create_publisher(
116150
Twist,
117151
"/gz/drone0/cmd_vel",
118-
qos_profile=10,
152+
qos_profile=pub_qos_profile,
119153
)
120154

121155
# Publish a high velocity message to move the vehicle
122156
twist_msg = Twist()
123-
twist_msg.linear.x = 45.0 # Move forward at 45 m/s
124-
twist_msg.angular.z = 120.0 # Turn at 120 rad/s
157+
twist_msg.linear.x = 1.0 # Move forward at 1 m/s
158+
twist_msg.angular.z = 0.1 # Turn at 10 rad/s
125159

160+
arm_pub.publish(Bool(data=True))
161+
rclpy.spin_once(
162+
self.__class__.node,
163+
timeout_sec=0.1,
164+
)
126165
# Wait for a short time to allow odometry updates
127-
for _ in range(15):
166+
for _ in range(100):
128167
pub.publish(twist_msg)
129-
rclpy.spin_once(self.__class__.node, timeout_sec=0.1)
130-
168+
rclpy.spin_once(
169+
self.__class__.node,
170+
timeout_sec=0.1,
171+
)
131172
# Check that we received some messages
132-
self.assertNotEqual(len(msgs), 0, msg="No cmd_vel messages received.")
173+
self.assertNotAlmostEqual(
174+
msgs[-1].pose.position.x,
175+
msgs[0].pose.position.x,
176+
msg="Drone did not move.",
177+
)
178+
self.assertNotEqual(
179+
len(msgs),
180+
0,
181+
msg="No cmd_vel messages received.",
182+
)
133183

134184
# Clean up subscriptions
135185
self.__class__.node.destroy_subscription(sub)

test/test_3d_reconstruction.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
from launch import LaunchDescription
1212
from launch.actions import IncludeLaunchDescription
1313
from launch.launch_description_sources import PythonLaunchDescriptionSource
14-
import psutil
1514

1615
import rclpy
1716
from sensor_msgs.msg import CameraInfo, Image
1817

18+
from test.utils import stop_gazebo
19+
1920

2021
@pytest.mark.launch_test
2122
def generate_test_description():
@@ -60,10 +61,7 @@ def tearDownClass(cls):
6061
cls.node.destroy_node()
6162
rclpy.shutdown()
6263
# Stop any running Gazebo processes
63-
for proc in psutil.process_iter(["name"]):
64-
if proc.info["name"] == "gzserver":
65-
print(f"Stopping gzserver (PID={proc.pid})")
66-
proc.terminate()
64+
stop_gazebo()
6765

6866
@unittest.skipIf(
6967
os.environ.get("CI") == "true" or os.environ.get("GITHUB_ACTIONS") == "true",

test/test_follow_person_followingcam.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import os
66
import time
77
import unittest
8-
import psutil
98
import pytest
109

1110
import launch_testing
@@ -17,6 +16,8 @@
1716
from sensor_msgs.msg import Imu, JointState
1817
from nav_msgs.msg import Odometry
1918

19+
from test.utils import stop_gazebo
20+
2021

2122
@pytest.mark.launch_test
2223
def generate_test_description():
@@ -60,10 +61,7 @@ def tearDownClass(cls):
6061
cls.node.destroy_node()
6162
rclpy.shutdown()
6263
# Stop the gzserver process if it is running
63-
for proc in psutil.process_iter(["name"]):
64-
if proc.info["name"] == "gzserver":
65-
print(f"Stopping gzserver (PID={proc.pid})")
66-
proc.terminate()
64+
stop_gazebo()
6765

6866
def test_joint_state_topics(self, proc_output):
6967
"""Test that the joint state topics are published correctly."""

test/test_simple_circuit.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
from launch import LaunchDescription
1212
from launch.actions import IncludeLaunchDescription
1313
from launch.launch_description_sources import PythonLaunchDescriptionSource
14-
import psutil
1514

1615
import rclpy
1716
from sensor_msgs.msg import Image
1817
from nav_msgs.msg import Odometry
1918
from geometry_msgs.msg import Twist
2019

20+
from test.utils import stop_gazebo
21+
2122

2223
@pytest.mark.launch_test
2324
def generate_test_description():
@@ -62,10 +63,7 @@ def tearDownClass(cls):
6263
cls.node.destroy_node()
6364
rclpy.shutdown()
6465
# Stop any running Gazebo processes
65-
for proc in psutil.process_iter(["name"]):
66-
if proc.info["name"] == "gzserver":
67-
print(f"Stopping gzserver (PID={proc.pid})")
68-
proc.terminate()
66+
stop_gazebo()
6967

7068
def test_odom_works(self, proc_output):
7169
"""Test that the odometry state is updated when the vehicle moves."""

test/test_vacuum_cleaner.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
from launch import LaunchDescription
1212
from launch.actions import IncludeLaunchDescription
1313
from launch.launch_description_sources import PythonLaunchDescriptionSource
14-
import psutil
1514

1615
import rclpy
1716
from nav_msgs.msg import Odometry
1817
from geometry_msgs.msg import Twist
1918
from sensor_msgs.msg import LaserScan
2019

20+
from test.utils import stop_gazebo
21+
2122

2223
@pytest.mark.launch_test
2324
def generate_test_description():
@@ -62,10 +63,7 @@ def tearDownClass(cls):
6263
cls.node.destroy_node()
6364
rclpy.shutdown()
6465
# Stop any running Gazebo processes
65-
for proc in psutil.process_iter(["name"]):
66-
if proc.info["name"] == "gzserver":
67-
print(f"Stopping gzserver (PID={proc.pid})")
68-
proc.terminate()
66+
stop_gazebo()
6967

7068
def test_odom_works(self, proc_output):
7169
"""Test that the odometry state is updated when the vehicle moves."""

test/utils.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Utility functions with common usage in tests."""
2+
3+
4+
import psutil
5+
6+
7+
def stop_gazebo():
8+
"""
9+
Terminates any running Gazebo-related processes.
10+
11+
Iterates through all running processes and terminates those whose names or cmds
12+
match Gazebo-related executables.
13+
"""
14+
# Stop any running Gazebo processes
15+
for proc in psutil.process_iter(["name", "cmdline"]):
16+
cmdline_list = proc.info.get("cmdline")
17+
cmdline = " ".join(cmdline_list) if cmdline_list else ""
18+
if (
19+
proc.info["name"]
20+
in [
21+
"gzserver",
22+
"gazebo",
23+
"ign gazebo",
24+
"gz",
25+
]
26+
or "gz sim" in cmdline
27+
):
28+
print(f"Stopping Gazebo process (PID={proc.pid}, CMD={cmdline})")
29+
proc.terminate()

0 commit comments

Comments
 (0)