Skip to content

Add Launch Tests and enforce in CI #549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 51 commits into
base: humble-devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e1d9f16
tests: update ignore file
Apolo151 Jul 25, 2025
44d4726
tests: setup testing structure and docs
Apolo151 Jul 29, 2025
515c9b2
tests: update test README and deps
Apolo151 Jul 30, 2025
45e003f
tests: add 3d_reconstruction launch test
Apolo151 Jul 30, 2025
aad1105
chore: reformat python files
Apolo151 Jul 30, 2025
29ea7e7
tests: update test with gazebo termination
Apolo151 Jul 30, 2025
ef67bb6
update tests timeout in CMake
Apolo151 Jul 30, 2025
69c61e6
tests: follow_person_following_cam test
Apolo151 Jul 30, 2025
73c8987
ci: add launch tests workflow
Apolo151 Jul 30, 2025
514e525
ci: correct workflow location
Apolo151 Jul 30, 2025
69ce2ac
ci: remove artifact step from launch testing workflow
Apolo151 Jul 30, 2025
faf6cae
chore: reformat
Apolo151 Jul 30, 2025
012121d
update test requirements
Apolo151 Jul 31, 2025
5a10243
ci: update test workflow
Apolo151 Jul 31, 2025
27e7c01
ci: update test workflow
Apolo151 Jul 31, 2025
5d49f88
ci: update ros2 setup step
Apolo151 Jul 31, 2025
b84222a
ci: update gazebo setup setp
Apolo151 Jul 31, 2025
03585e2
ci: update ws build step
Apolo151 Jul 31, 2025
d559fc1
increase wait time for camera test
Apolo151 Jul 31, 2025
efa6e01
ci: update tests workflow with gazebo headless setup
Apolo151 Jul 31, 2025
efc3bdf
tests: increase wait time for 3d_reconstruction test
Apolo151 Jul 31, 2025
5259730
ci: update gazebo mocking for headless setup
Apolo151 Jul 31, 2025
ee49b81
ci: update gazebo mocking for headless setup
Apolo151 Jul 31, 2025
4a509af
ci: update 3d reconstruction test timing and QoS
Apolo151 Jul 31, 2025
459c4ff
ci: update to skip camera tests in CI
Apolo151 Jul 31, 2025
c445e82
ci: update to skip camera tests in CI
Apolo151 Jul 31, 2025
5fa8abb
tests: add launch test for simple circuit line following exercise
Apolo151 Aug 9, 2025
2f63362
tests: add test for vaccum cleaner exercise
Apolo151 Aug 9, 2025
ae0e850
tests: update deps
Apolo151 Aug 10, 2025
06e5d74
tests: add test for follow road gazebo harmonic exercise
Apolo151 Aug 11, 2025
208a399
CI: add gazebo harmonic testing workflow
Apolo151 Aug 11, 2025
7c40c48
CI: update gz harmonic testing workflow
Apolo151 Aug 11, 2025
d89e4cb
CI: update harmonic workflow: install xacro package
Apolo151 Aug 14, 2025
9cd0e85
CI: harmonic workflow: install gazebo
Apolo151 Aug 14, 2025
ae40d47
CI: harmonic workflow: build all packages
Apolo151 Aug 14, 2025
390be29
CI: harmonic workflow: add gz_sim
Apolo151 Aug 14, 2025
0f9898d
CI: harmonic workflow: add gz bridge packages
Apolo151 Aug 14, 2025
2962748
CI: harmonic workflow: add state estimator package
Apolo151 Aug 14, 2025
f442964
CI: harmonic workflow: add motion controller package
Apolo151 Aug 14, 2025
8eb18c6
CI: harmonic workflow: add gazebo assets package
Apolo151 Aug 14, 2025
8ee0e34
CI: retry
Apolo151 Aug 14, 2025
8fc6fdf
CI: harmonic workflow, add drones package world
Apolo151 Aug 14, 2025
9699701
CI: harmonic workflow, rename launch files
Apolo151 Aug 14, 2025
3fca101
CI: harmonic workflow, rename launch files
Apolo151 Aug 14, 2025
1d61280
CI: remove test skipping
Apolo151 Aug 14, 2025
fe96637
docs: add docstrings to vaccum clean launchers
Apolo151 Aug 14, 2025
1eb1dc2
docs: remove unused imports in vaccum cleaner launchers
Apolo151 Aug 14, 2025
4fb205b
docs: add docstrings and remove unused imports in simple circuit laun…
Apolo151 Aug 14, 2025
17a530d
chore: reformat simple circuit and vaccum cleaner launchers
Apolo151 Aug 14, 2025
141341d
docs: add docstrings and remove unused imports in follow person and 3…
Apolo151 Aug 14, 2025
7fe02a2
CI: update gz harmonic workflow to enforce using harmonic version
Apolo151 Aug 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions .github/workflows/ros2_gz_harmonic_launch_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: ROS 2 Launch Tests (Gazebo Harmonic)

on:
push:
branches: [main, testing]
pull_request:
branches: [main, testing]
workflow_dispatch:

env:
WORLDS_DIR: /opt/jderobot
ROS_DISTRO: humble

jobs:
launch-tests:
runs-on: ubuntu-22.04
timeout-minutes: 30

steps:
- name: Create checkout directory with proper permissions
run: |
sudo mkdir -p ${{ env.WORLDS_DIR }}
sudo chmod -R 777 ${{ env.WORLDS_DIR }}

- name: Checkout repository
uses: actions/checkout@v3

- name: copy worlds
run: |
sudo cp -r ${{ github.workspace }}/Worlds ${{ env.WORLDS_DIR }}
sudo cp -r ${{ github.workspace }}/jderobot_drones ${{ env.WORLDS_DIR }}

- name: Setup ROS 2 Humble
run: |
# Add the ROS 2 apt repository with updated key
sudo apt-get update && sudo apt-get install -y curl gnupg lsb-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt-get update
# Install ROS 2 packages
sudo apt-get install -y ros-humble-ros-base python3-colcon-common-extensions

- name: "Setup Gazebo"
uses: gazebo-tooling/[email protected]
with:
required-gazebo-distributions: harmonic
- name: "Test Gazebo installation"
run: "gz sim --versions"

# - name: Install Gazebo and dependencies
# run: |
# # First install libunwind-dev to satisfy the dependency requirements
# sudo apt-get update
# sudo apt-get install -y libunwind-dev

# # Then install the rest of the packages
# sudo apt-get install -y python3-pip python3-pytest-timeout python3-pytest-cov \
# ros-humble-launch-testing ros-humble-launch-testing-ament-cmake \
# ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros ros-humble-xacro \
# ros-humble-joint-state-publisher ros-humble-robot-state-publisher \
# ros-humble-nav2-msgs ros-humble-turtlebot3-msgs

# # Install Gazebo separately with --fix-broken option
# sudo apt-get install -y --fix-broken gazebo libgazebo-dev

- name: Install Gazebo Harmonic and dependencies
run: |
sudo apt-get install -y ros-humble-xacro ros-humble-ros-gz-sim
sudo apt remove ros-humble-ros-gzharmonic-bridge ros-humble-ros-gzharmonic-interfaces ros-humble-ros-gzharmonic-sim
sudo apt-get install ros-humble-as2-gazebo-assets ros-humble-as2-platform-gazebo ros-humble-ros-gz-image
sudo apt-get install -y ros-humble-as2-gazebo-assets ros-humble-as2-state-estimator
sudo apt-get install -y ros-humble-as2-motion-controller ros-humble-as2-behaviors-motion

- name: Install Gazebo classic and resolve version conflicts
run: |
sudo add-apt-repository ppa:openrobotics/gazebo11-gz-cli
sudo apt update
sudo apt-get install gazebo11

# Resolve version conflicts
sudo apt-get remove libgazebo11
sudo apt-get install libgazebo11=11.10.2+dfsg-1
sudo apt-get install libgazebo-dev

- name: "Test Gazebo installation"
run: "gz sim --versions"

- name: Rename Launch Files
run: |
sudo mv /opt/ros/humble/share/as2_state_estimator/launch/ground_truth_state_estimator.launch.py /opt/ros/humble/share/as2_state_estimator/launch/ground_truth-state_estimator.launch.py
sudo mv /opt/ros/humble/share/as2_motion_controller/launch/pid_speed_controller.launch.py /opt/ros/humble/share/as2_motion_controller/launch/pid_speed_controller-motion_controller.launch.py

- name: Setup Python dependencies
run: |
python -m pip install --upgrade pip
if [ -f test/requirements.txt ]; then
pip install -r test/requirements.txt
fi
pip install pytest pytest-timeout pytest-cov

- name: Configure Gazebo for headless rendering
run: |
# Configure display for headless rendering
echo "export GAZEBO_IP=127.0.0.1" >> $GITHUB_ENV
echo "export OGRE_RTT_MODE=Copy" >> $GITHUB_ENV
echo "export DISPLAY=:1" >> $GITHUB_ENV

# Configure audio to use null devices
echo "export AUDIODEV=null" >> $GITHUB_ENV
echo "export SDL_AUDIODRIVER=dummy" >> $GITHUB_ENV

# Create dummy ALSA config
echo 'pcm.!default { type null; }' > ~/.asoundrc
echo 'ctl.!default { type null; }' >> ~/.asoundrc

# Set up virtual framebuffer for visual rendering
sudo apt-get install -y xvfb
Xvfb :1 -screen 0 1280x1024x24 &

# Give Xvfb and audio config time to initialize
sleep 3

- name: add only Harmonic tests to ros package
run: sed -i 's|file(GLOB TEST_FILES "${RI_ROOT_DIR}/test/test\*\.py")|file(GLOB TEST_FILES "${RI_ROOT_DIR}/test/harmonic/test_*.py")|' CustomRobots/CMakeLists.txt

- name: Build workspace
run: |
source /opt/ros/humble/setup.bash
colcon build --symlink-install

- name: Run launch tests
run: |
source /opt/ros/humble/setup.bash
source install/setup.bash
colcon test --packages-select custom_robots --event-handlers console_direct+ --return-code-on-test-failure

- name: Show test results
if: always()
run: |
colcon test-result --verbose
104 changes: 104 additions & 0 deletions .github/workflows/ros2_launch_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: ROS 2 Launch Tests

on:
push:
branches: [main, testing]
pull_request:
branches: [main, testing]
workflow_dispatch:

env:
WORLDS_DIR: /opt/jderobot
ROS_DISTRO: humble

jobs:
launch-tests:
runs-on: ubuntu-22.04
timeout-minutes: 30

steps:
- name: Create checkout directory with proper permissions
run: |
sudo mkdir -p ${{ env.WORLDS_DIR }}
sudo chmod -R 777 ${{ env.WORLDS_DIR }}

- name: Checkout repository
uses: actions/checkout@v3

- name: copy worlds
run: |
sudo cp -r ${{ github.workspace }}/Worlds ${{ env.WORLDS_DIR }}

- name: Setup ROS 2 Humble
run: |
# Add the ROS 2 apt repository with updated key
sudo apt-get update && sudo apt-get install -y curl gnupg lsb-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt-get update
# Install ROS 2 packages
sudo apt-get install -y ros-humble-ros-base python3-colcon-common-extensions

- name: Install Gazebo and dependencies
run: |
# First install libunwind-dev to satisfy dependency requirements
sudo apt-get update
sudo apt-get install -y libunwind-dev

# Then install the rest of the packages
sudo apt-get install -y python3-pip python3-pytest-timeout python3-pytest-cov \
ros-humble-launch-testing ros-humble-launch-testing-ament-cmake \
ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros ros-humble-xacro \
ros-humble-joint-state-publisher ros-humble-robot-state-publisher \
ros-humble-nav2-msgs ros-humble-turtlebot3-msgs

# Install Gazebo separately with --fix-broken option
sudo apt-get install -y --fix-broken gazebo libgazebo-dev

- name: Setup Python dependencies
run: |
python -m pip install --upgrade pip
if [ -f test/requirements.txt ]; then
pip install -r test/requirements.txt
fi
pip install pytest pytest-timeout pytest-cov

- name: Configure Gazebo for headless rendering
run: |
# Configure display for headless rendering
echo "export GAZEBO_IP=127.0.0.1" >> $GITHUB_ENV
echo "export OGRE_RTT_MODE=Copy" >> $GITHUB_ENV
echo "export DISPLAY=:1" >> $GITHUB_ENV

# Configure audio to use null devices
echo "export AUDIODEV=null" >> $GITHUB_ENV
echo "export SDL_AUDIODRIVER=dummy" >> $GITHUB_ENV

# Create dummy ALSA config
echo 'pcm.!default { type null; }' > ~/.asoundrc
echo 'ctl.!default { type null; }' >> ~/.asoundrc

# Set up virtual framebuffer for visual rendering
sudo apt-get install -y xvfb
Xvfb :1 -screen 0 1280x1024x24 &

# Give Xvfb and audio config time to initialize
sleep 3

- name: Build workspace
run: |
source /opt/ros/humble/setup.bash
source /usr/share/gazebo/setup.sh
colcon build --symlink-install --packages-up-to custom_robots

- name: Run launch tests
run: |
source /opt/ros/humble/setup.bash
source install/setup.bash
source /usr/share/gazebo/setup.bash
colcon test --packages-select custom_robots --event-handlers console_direct+ --return-code-on-test-failure

- name: Show test results
if: always()
run: |
colcon test-result --verbose
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ __pycache__/

# IDEs
.vscode

# Tests
build
install
test/__pycache__/

# Logs
**log**

20 changes: 18 additions & 2 deletions CustomRobots/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.8)
project(custom_robots)

# Define project root directory
set(RI_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)

# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
Expand Down Expand Up @@ -136,9 +139,22 @@ install(
pick_place/urdf
DESTINATION share/${PROJECT_NAME})

install(
FILES ${RI_ROOT_DIR}/Launchers/follow_road.launch.py
${RI_ROOT_DIR}/Launchers/rescue_people.launch.py
DESTINATION share/${PROJECT_NAME}/launch)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
# find_package(ament_lint_auto REQUIRED)
# ament_lint_auto_find_test_dependencies()

find_package(launch_testing_ament_cmake REQUIRED)
# Use file(GLOB...) to find all test files matching the pattern
file(GLOB TEST_FILES "${RI_ROOT_DIR}/test/test*.py")
# Loop through each test file and add it as a launch test
foreach(test_file ${TEST_FILES})
add_launch_test(${test_file} TIMEOUT 90)
endforeach()
endif()

ament_export_libraries(personplugin)
Expand Down
8 changes: 7 additions & 1 deletion CustomRobots/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
<exec_depend>urdf</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>

<!-- launch testing -->
<test_depend>ament_cmake_ros</test_depend>
<test_depend>launch_testing</test_depend>
<test_depend>launch_testing_ament_cmake</test_depend>
<test_depend>rclpy</test_depend>

<!-- Gz Harmonic -->
<exec_depend>ros-humble-ros-gzharmonic-bridge</exec_depend>
<exec_depend>ros-humble-ros-gzharmonic-sim</exec_depend>
Expand All @@ -42,4 +48,4 @@
<build_type>ament_cmake</build_type>
<gazebo_ros plugin_path="${prefix}/lib" gazebo_media_path="${prefix}" />
</export>
</package>
</package>
8 changes: 5 additions & 3 deletions Launchers/3d_reconstruction.launch.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Launch file for 3D reconstruction simulation using Gazebo and custom robots."""

import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition, UnlessCondition
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
"""Generate the launch description for the 3D reconstruction Gazebo simulation."""

# Set the path to the Gazebo ROS package
pkg_gazebo_ros = FindPackageShare(package="gazebo_ros").find("gazebo_ros")
Expand Down
12 changes: 9 additions & 3 deletions Launchers/follow_person_followingcam.launch.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
"""Launch file for following person with following camera in Gazebo simulation."""

import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition, UnlessCondition
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
"""
Generate the launch description for the following person exercise.

With a following camera in Gazebo simulation.
"""

# Set the path to the Gazebo ROS package
pkg_gazebo_ros = FindPackageShare(package="gazebo_ros").find("gazebo_ros")
Expand Down
8 changes: 5 additions & 3 deletions Launchers/simple_circuit.launch.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Launch file for starting the simple circuit world in Gazebo with custom robots."""

import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition, UnlessCondition
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
"""Generate the launch description for starting the simple circuit world."""

# Set the path to the Gazebo ROS package
pkg_gazebo_ros = FindPackageShare(package="gazebo_ros").find("gazebo_ros")
Expand Down
8 changes: 5 additions & 3 deletions Launchers/simple_circuit_ackermann.launch.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Launch file for the simple circuit Ackermann simulation using Gazebo."""

import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition, UnlessCondition
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
"""Generate the launch description for the simple circuit Ackermann simulation."""

# Set the path to the Gazebo ROS package
pkg_gazebo_ros = FindPackageShare(package="gazebo_ros").find("gazebo_ros")
Expand Down
Loading