Skip to content

Commit

Permalink
Merge pull request #589 from RI-SE/feature_integration-testing
Browse files Browse the repository at this point in the history
Integration testing
  • Loading branch information
samuelthoren authored Nov 20, 2023
2 parents 614c6e0 + eb00437 commit e52c0d8
Show file tree
Hide file tree
Showing 33 changed files with 707 additions and 56 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(WITH_OSI_ADAPTER ON CACHE BOOL "Enable OSIAdapter module")
set(WITH_ESMINI_ADAPTER ON CACHE BOOL "Enable EsminiAdapter module")
set(WITH_MQTT_BRIDGE ON CACHE BOOL "Enable MQTTBridge module")
set(WITH_POINTCLOUD_PUBLISHER ON CACHE BOOL "Enable PointcloudPublisher module")
set(WITH_INTEGRATION_TESTING ON CACHE BOOL "Enable IntegrationTesting module")
set(WITH_BACK_TO_START ON CACHE BOOL "Enable BackToStart module")

set(ENABLE_TESTS ON CACHE BOOL "Enable testing on build")
Expand Down Expand Up @@ -72,6 +73,9 @@ endif()
if(WITH_POINTCLOUD_PUBLISHER)
list(APPEND ENABLED_MODULES PointcloudPublisher)
endif()
if(WITH_INTEGRATION_TESTING)
list(APPEND ENABLED_MODULES IntegrationTesting)
endif()
if(WITH_BACK_TO_START)
list(APPEND ENABLED_MODULES BackToStart)
endif()
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
SHELL ["/bin/bash", "-c"]

WORKDIR /root/atos_git
COPY ./scripts/ ./scripts
COPY ./scripts/installation/ ./scripts/installation/
RUN --mount=type=cache,target=/var/cache/apt \
./scripts/install_deps.sh ${REPO_DIR}
./scripts/installation/install_deps.sh ${REPO_DIR}
COPY . .
RUN ./scripts/install_atos.sh ${REPO_DIR}
RUN ./scripts/installation/install_atos.sh ${REPO_DIR}
WORKDIR /root/atos_ws
23 changes: 14 additions & 9 deletions common/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,24 @@ class Module : public rclcpp::Node {
const std::string& topic,
const rclcpp::Logger& logger);

/*! \brief This helper function is performs a service call given a client and yields a response.
* \tparam Srv The name of the service to request.
* \param timeout The timeout for the service call.
* \param client The client to use to request the service.
* \param response The response of the service.
* \return The response of the service.
*/

/**
* @brief This helper function performs a service call given a client and yields a response. This
* function is used when you want to specify the request instead of sending an empty request.
*
* @tparam Srv Srv The name of the service to request.
* @param timeout The timeout for the service call.
* @param client The client to use to request the service.
* @param response The response of the service.
* @param request The request of the service, with the data to be sent. Defaults to an empty request.
* @return The response of the service.
*/
template <typename Srv>
bool callService( const std::chrono::duration< double > &timeout,
std::shared_ptr<rclcpp::Client<Srv>> &client,
std::shared_ptr<typename Srv::Response> &response)
std::shared_ptr<typename Srv::Response> &response,
std::shared_ptr<typename Srv::Request> request = std::make_shared<typename Srv::Request>())
{
auto request = std::make_shared<typename Srv::Request>();
auto promise = client->async_send_request(request);
if (rclcpp::spin_until_future_complete(get_node_base_interface(), promise, timeout) ==
rclcpp::FutureReturnCode::SUCCESS) {
Expand Down
5 changes: 4 additions & 1 deletion conf/params.yaml → conf/conf/params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ atos:
chunk_duration: 2.0
pointcloud_publisher:
ros__parameters:
pointcloud_files: [""]
pointcloud_files: [""]
integration_testing_handler:
ros__parameters:
scenario_execution: true
10 changes: 0 additions & 10 deletions dependencies.txt

This file was deleted.

7 changes: 3 additions & 4 deletions docker-compose-demo.yml → docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ services:
context: .
dockerfile: ./Dockerfile
ipc: host
stop_signal: SIGINT
stop_grace_period: 15s
privileged: true
stdin_open: true
tty: true
volumes:
- ~/.astazero/ATOS/:/root/.astazero/ATOS/
- "./conf:/root/.astazero/ATOS"
- "./conf/param.yaml:/root/.astazero/conf/params.yaml"
ports:
- "80:80"
- "8080:8080"
Expand All @@ -23,7 +22,7 @@ services:
- "55555:55555"
- "443:443"
- "9090:9090"
command: bash -c "source /root/atos_ws/install/setup.sh ; ros2 launch atos launch_basic.py"
command: bash -c "source /root/atos_ws/install/setup.sh ; python3 -m pytest /root/atos_ws/src/atos/scripts/integration_testing/run_scenario_test.py
isoObject:
image: astazero/iso_object_demo:latest
privileged: true
Expand Down
17 changes: 16 additions & 1 deletion docs/Contributing/Testing/testing.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Testing

## Unit Tests
Unit tests for C++ code in ATOS are written in gtest. New modules must have unit tests covering at least the core functionality. All unit tests for a each module are executed automatically each time a pull request is submitted.
Unit tests for C++ code in ATOS are written in gtest. New modules must have unit tests covering at least the core functionality. All unit tests for each module are executed automatically each time a pull request is submitted.

To run individual unit tests, run the following command from the ATOS build folder in your workspace:
```bash
Expand All @@ -20,5 +20,20 @@ colcon and cmake switches if test should be built with the BUILD_TESTING flag. T
colcon build --cmake-args -DBUILD_TESTING=ON
```

## Integration testing
The docker compose file docker-compose-test.yml is used to run integration tests towards one or more simulated ISO22133 test objects. These test are used to "smoke test" ATOS and makes sure that no changes
breaks any core functionality. You can start these test with the command
```bash
docker compose -f docker-compose-test.yml up --abort-on-container-exit
```

If you wish to run these test manually you can find the entry point script at scripts/integration_testing/run_scenario_test.py. Run the test with
```bash
python3 -m pytest run_scenario_test.py
```
!!! note

Running the integration test manually requires you to set up any necessary test objects yourself!

## Sample code
In the Sample Module you will find a sample node and a some tests that triggers message callbacks and service routines. Use this as inspiration when testing your modules.
4 changes: 2 additions & 2 deletions docs/Usage/How-to/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## The test directory
After starting ATOS the first time, you will find the test directory `.astazero/ATOS` in your home folder on the host machine (or where you have specified it to be).
This directory contains all configuration settings and journals which are located in the the following directories:
This directory contains all configuration settings and journals which are located in the following directories:

- **Catalogs**
- Explanation: Catalog directory containing various OpenSCENARIO-files with settings and parameters used by esmini.
Expand All @@ -11,7 +11,7 @@ This directory contains all configuration settings and journals which are locate
- **certs**
- Explanation: Directory containing the certificates used by e.g. the web-gui.
- **conf**
- Explanation: Directory containing the configuration files used to configure ATOS, i.e. the ROS parameters located in the file params.yaml. Find more information below.
- Explanation: Directory containing the configuration files used to configure ATOS, i.e. the ROS parameters located in the file params.yaml. Find more information below.
- **journal**
- Explanation: Directory containing the journal files, i.e. recorded configuration/state/position of each object for the duration of a test.
- **objects**
Expand Down
2 changes: 1 addition & 1 deletion docs/Usage/Modules/ATOSBase.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ATOSBase

## About the module
`ATOSBase` is a node for ATOS and is responsible for suppling the object ID for the objects that are used in the test.
`ATOSBase` is a node for ATOS and is responsible for supplying the object ID for the objects that are used in the test.
19 changes: 19 additions & 0 deletions docs/Usage/Modules/IntegrationTesting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# IntegrationTesting

## About the module
`IntegrationTesting` is used for integration tests. The module launches an integration testing handler, which reads from `params.yaml` which integration tests we want to execute. A node for each integration
test is then launched, and the node performs the specified integration tests. An integration tests can for instance be if an object reports all states correctly during a run, or if it follows a trajectory
correctly. This module can be run both offline and through GitHub Actions.

!!! note

You can read more about integration testing in the section [Testing](../../Contributing/Testing/testing.md).

## ROS parameters
The integration tests are specified in `params.yaml`, and can be set to true of false depending on if we want to run them or not. The following ROS parameters can be set for `IntegrationTesting`:

```yaml
integration_testing_handler:
ros__parameters:
scenario_execution: true # set to true or false depending if you want to run this integration test
```
2 changes: 1 addition & 1 deletion docs/Usage/Modules/MQTTBridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ to JSON which is then published over MQTT to a specified topic.

Note! The module will shut if no broker ip is specified in the params.yaml
## Integration with EsminiAdapter
The module can be used togheter with the EsminiAdapter module to trigger V2X while running a OpenScenario file in ATOS. You can find more information how to set this up at [EsminiAdapter](./EsminiAdapter.md).
The module can be used together with the EsminiAdapter module to trigger V2X while running a OpenScenario file in ATOS. You can find more information how to set this up at [EsminiAdapter](./EsminiAdapter.md).

## ROS parameters
The following ROS parameters should be set in the params.yaml file:
Expand Down
2 changes: 1 addition & 1 deletion docs/Usage/Modules/PointcloudPublisher.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
`PointcloudPublisher` is used to publish site scans. This module supports pointclouds that have the file type `.pcd`. If you have a very large pointcloud, it is recommended to downsample it before inputting it into the module.

## ROS parameters
The follwing ROS parameters can be set for `PointcloudPublisher`:
The following ROS parameters can be set for `PointcloudPublisher`:

```yaml
atos:
Expand Down
6 changes: 3 additions & 3 deletions docs/Usage/Modules/SampleModule.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The sample module is a ros2 node that features some basic publishers and subscri
It also features a TCPServer running in a separate thread.

## Usage
In order to compile and launch this module (or any other module created from the template) you need to go to the outer-most CMakeLists.txt file in the root of the repository and add the following line:
In order to compile and launch this module (or any other module created from the template) you need to go to the outermost CMakeLists.txt file in the root of the repository and add the following line:
```
set(WITH_MODULE_X ON CACHE BOOL "Enable ModuleX module")
```
Expand All @@ -21,7 +21,7 @@ if(WITH_MODULE_X)
endif()
```

Note: When switching ON/OFF certain modules, it might be nessesscary to remove the CMakeCache.txt file in ~/atos_ws/install/atos/.
Note: When switching ON/OFF certain modules, it might be necessary to remove the CMakeCache.txt file in ~/atos_ws/install/atos/.

It is also necessary to add the module to a launch file, located in the launch directory. This is done by adding the following line to the list of nodes in the appropriate launch file:
```
Expand All @@ -37,7 +37,7 @@ Then you can compile and launch the module by running the following commands:
```
MAKEFLAGS=-j5 colcon build --packages-up-to atos
```
(tune -j5 to an approperiate number depending on your availiable RAM memory and CPU cores)
(tune -j5 to an appropriate number depending on your available RAM memory and CPU cores)
```
ros2 launch atos launch_basic.py
```
2 changes: 1 addition & 1 deletion docs/Usage/Modules/TrajectoryletStreamer.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# TrajectoryletStreamer
An experimental module for generating piecewise trajectory "chunks" based on a known trajectory. It is indended for testing dynamic trajectory functionality in other modules with a trajectory that is known before hand.
An experimental module for generating piece-wise trajectory "chunks" based on a known trajectory. It is intended for testing dynamic trajectory functionality in other modules with a trajectory that is known beforehand.

# ROS parameters
The following ROS parameters can be set for `TrajectoryletStreamer`:
Expand Down
31 changes: 31 additions & 0 deletions launch/launch_integration_testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import sys
import os
from ament_index_python.packages import get_package_prefix
sys.path.insert(0, os.path.join( # Need to modify the sys.path since we launch from the ros2 installed path
get_package_prefix('atos'),
'share', 'atos', 'launch'))
from launch_ros.actions import Node
from launch import LaunchDescription
import launch_utils.launch_base as launch_base


def get_integration_test_nodes():
files = launch_base.get_files()
return [
Node(
package='atos',
namespace='atos',
executable='integration_testing_handler',
parameters=[files["params"]]
)
]

def generate_launch_description():
base_nodes = launch_base.get_base_nodes()

integration_test_nodes = get_integration_test_nodes()

for node in integration_test_nodes:
base_nodes.append(node)

return LaunchDescription(base_nodes)
2 changes: 1 addition & 1 deletion launch/launch_utils/validate_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def validate_files():

# Params
params = validate_file(atos_dir / Path("conf") / Path("params.yaml"),
get_package_prefix('atos') / Path("etc") / Path("params.yaml"))
get_package_prefix('atos') / Path("etc") / Path("conf") / Path("params.yaml"))

return {
"params": params,
Expand Down
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ nav:
- "Usage/Modules/BackToStart.md"
- "Usage/Modules/DirectControl.md"
- "Usage/Modules/EsminiAdapter.md"
- "Usage/Modules/IntegrationTesting.md"
- "Usage/Modules/JournalControl.md"
- "Usage/Modules/MQTTBridge.md"
- "Usage/Modules/OSIAdapter.md"
Expand All @@ -35,3 +36,4 @@ nav:
- "Contributing/Feature Requests/Feature_requests.md"
- "Contributing/Issues/issues.md"
- "Contributing/Pull Requests/pull_requests.md"
- "Contributing/Testing/testing.md"
59 changes: 59 additions & 0 deletions modules/IntegrationTesting/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

project(integration_testing_handler)

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(atos_interfaces REQUIRED)

# Define target names
set(INTEGRATION_TESTING_TARGET ${PROJECT_NAME})

set(ATOS_COMMON_LIBRARY ATOSCommon)
get_target_property(COMMON_HEADERS ${ATOS_COMMON_LIBRARY} INCLUDE_DIRECTORIES)

include(GNUInstallDirs)

# Create project main executable target
add_executable(${INTEGRATION_TESTING_TARGET}
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/integrationtestinghandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/integrationtestingfactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/integrationtesting.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scenarioexecution.cpp
)
# Link project executable to util libraries
target_link_libraries(${INTEGRATION_TESTING_TARGET}
${ATOS_COMMON_LIBRARY}
)

# ROS specific settings
ament_target_dependencies(${INTEGRATION_TESTING_TARGET}
rclcpp
std_msgs
sensor_msgs
atos_interfaces
)

target_include_directories(${INTEGRATION_TESTING_TARGET} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/inc
${COMMON_HEADERS}
)

# Installation rules
install(CODE "MESSAGE(STATUS \"Installing target ${OSI_ADAPTER_TARGET}\")")
install(TARGETS ${INTEGRATION_TESTING_TARGET}
RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}/atos"
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
38 changes: 38 additions & 0 deletions modules/IntegrationTesting/inc/integrationtesting.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once

#include "module.hpp"
#include "roschannels/commandchannels.hpp"
#include "atos_interfaces/srv/get_object_control_state.hpp"


class IntegrationTesting : public Module {

public:
IntegrationTesting(const std::string& moduleName);
~IntegrationTesting();

static inline std::string const testName = "scenario_execution";
virtual void runIntegrationTest() = 0;

protected:
const std::string atosNamespace = "/atos/";
const std::string initTopic = atosNamespace + ROSChannels::Init::topicName;
const std::string connectTopic = atosNamespace + ROSChannels::Connect::topicName;
const std::string armTopic = atosNamespace + ROSChannels::Arm::topicName;
const std::string startTopic = atosNamespace + ROSChannels::Start::topicName;
std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Empty>> initPub;
std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Empty>> connectPub;
std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Empty>> armPub;
std::shared_ptr<rclcpp::Publisher<std_msgs::msg::Empty>> startPub;
std::shared_ptr<rclcpp::Client<atos_interfaces::srv::GetObjectControlState>> getObjectControlStateClient;
std::vector<std::pair<int, int>> stateResult;

virtual void printResult() = 0;
int getObjectControlState();
void checkState(const std::string& command);
};
Loading

0 comments on commit e52c0d8

Please sign in to comment.