diff --git a/.env b/.env index a251207c1..0c2e23e01 100644 --- a/.env +++ b/.env @@ -1,6 +1,6 @@ PROJECT_NAME="airstack" -PROJECT_VERSION="1.0.2" +PROJECT_VERSION="1.0.3" # can replace with your docker hub username PROJECT_DOCKER_REGISTRY="airlab-storage.andrew.cmu.edu:5001/shared" DEFAULT_ISAAC_SCENE="omniverse://airlab-storage.andrew.cmu.edu:8443/Projects/AirStack/fire_academy.scene.usd" -PLAY_SIM_ON_START="true" \ No newline at end of file +PLAY_SIM_ON_START="true" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..b5fd24905 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +# Pull Request Title + +## What does this pull request do? + +Which issue number does this address? + +Add videos and images if possible. + +## How did you implement it? + +## Testing +**How do you run the tests?** + +**What do the tests do?** + +**What are the expected results of the tests?** + +## Did you update the docs (and where)? +Docs are updated via mkdocs.yml and markdown files under `docs/`. It should render at localhost:8000 when you run `docker compose up docs`. diff --git a/docs/development/testing/testing_frameworks.md b/docs/development/testing/testing_frameworks.md index 60ba2d659..9b629ac7f 100644 --- a/docs/development/testing/testing_frameworks.md +++ b/docs/development/testing/testing_frameworks.md @@ -1 +1,112 @@ -# Testing Frameworks \ No newline at end of file +# Testing Frameworks + +## Testing Categories +Testing is organized into four main categories: + +1. **Integration Testing (End-to-End Testing):** + Validates the entire system within its operational environment, whether in simulation or on hardware. + +2. **System Testing:** + Tests interactions between system components, such as communication between ROS nodes. + +3. **Node Testing:** + Focuses on verifying the functionality of individual nodes, from initialization to execution. + +4. **Unit Testing:** + Tests specific functions or business logic to ensure the correctness of the smallest units of code. + +--- + +## Testing Utilities +Since our autonomy system primarily relies on ROS 2, we use the `colcon test` framework to run tests. Most tests are written in Python using the `pytest` package, as demonstrated in the example below. + +--- + +## Testing Structure +All tests should be included in a ```tests/``` folder in their respective heirarchy of the architecture. For example, integration testing should on the same level as the ```robot/``` and ```simulation/``` folders, where a node test should reside in the ros package directoy. ```colcon test``` will search through the workspace to find all testing packages, provided they are specified in the Cmake.txt or setup.py files. + +## Example Testing Script + +Below is an example of a systems test that can give the general structure of a testing script. + +``` +import os +import sys +import time +import unittest +import uuid + +import launch +from launch.launch_service import LaunchService +import launch_ros +import launch_ros.actions +import launch_testing.actions +from launch_testing.io_handler import ActiveIoHandler +import launch_testing_ros + +import pytest + +import rclpy +from rclpy.node import Node + +import std_msgs.msg +from std_msgs.msg import String +import mavros_msgs.srv + +import time + +@pytest.mark.rostest +# this is the test descriptioon used to launch the full robot system with launch_robot_headless.yaml +def generate_test_description(): + robot_launch_path = 'path/to/launch/file' + + gui_arg = launch.actions.DeclareLaunchArgument('use_gui', default_value='false', description='Whether to launch the GUI') + + robot_launch = launch.actions.IncludeLaunchDescription( launch.launch_description_sources.AnyLaunchDescriptionSource(robot_launch_path), + launch_arguments={'use_gui': launch.substitutions.LaunchConfiguration('use_gui')}.items()) + return ( + launch.LaunchDescription([ + gui_arg, + robot_launch, + launch_testing.actions.ReadyToTest(), + ]), + {} + ) + +class TestRobotSystem(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initialize the ROS context for the test node + rclpy.init() + + @classmethod + def tearDownClass(cls): + # Shutdown the ROS context + rclpy.shutdown() + + def setUp(self): + # Create a ROS node for tests + self.node = rclpy.create_node('robot_tester_node') + self.service_timeout = 2 + + def tearDown(self): + self.node.destroy_node() + + def test_set_mode(self): + client = self.node.create_client(mavros_msgs.srv.SetMode, '/mavros/set_mode') + self.node.get_logger().info("Waiting for service to be available...") + accum_time = 0 + while not client.wait_for_service(timeout_sec=1.0): + print('service not available, waiting again...') + accum_time += 1 + if accum_time > self.service_timeout: + print('service not available, aborting test...') + self.assertTrue(False) + request = mavros_msgs.srv.SetMode.Request() + request.custom_mode = "GUIDED" + print("Sending request to set mode to GUIDED") + future = client.call_async(request) + rclpy.spin_until_future_complete(self.node, future) + response = future.result() + self.assertTrue(response.mode_sent) +``` \ No newline at end of file diff --git a/ground_control_station/docker/docker-compose.yaml b/ground_control_station/docker/docker-compose.yaml index de1d37dd2..2345ffbd0 100644 --- a/ground_control_station/docker/docker-compose.yaml +++ b/ground_control_station/docker/docker-compose.yaml @@ -11,7 +11,9 @@ services: command: > bash -c "ssh service restart; tmux new -d -s gcs_bringup - && tmux send-keys -t gcs_bringup 'ros2 launch gcs_bringup gcs.launch.xml' ENTER + && tmux send-keys -t gcs_bringup + 'if [ ! -f "/root/ros_ws/install/setup.bash" ]; then bws && sws; fi; + ros2 launch gcs_bringup gcs.launch.xml' ENTER && sleep infinity" # Interactive shell stdin_open: true # docker run -i diff --git a/robot/docker/robot-base-docker-compose.yaml b/robot/docker/robot-base-docker-compose.yaml index c7c60343d..176c49839 100644 --- a/robot/docker/robot-base-docker-compose.yaml +++ b/robot/docker/robot-base-docker-compose.yaml @@ -7,7 +7,7 @@ services: bash -c "ssh service restart; tmux new -d -s robot_bringup && tmux send-keys -t robot_bringup - 'if [ ! -d "~/ros_ws/install" ]; then bws && sws; fi; + 'if [ ! -f "/root/ros_ws/install/setup.bash" ]; then bws && sws; fi; ros2 launch robot_bringup robot.launch.xml' ENTER && sleep infinity" # Interactive shell