From 27c9d75bf6b34ac9e83877b4db0931efd6143c5f Mon Sep 17 00:00:00 2001 From: Milan Vukov Date: Tue, 15 Oct 2024 21:01:43 +0200 Subject: [PATCH] Clean up testing infra (#388) --- .bazelrc | 9 ++++--- repositories/ament_cmake_ros.BUILD.bazel | 11 -------- repositories/ros2_repo_mappings.yaml | 6 ----- repositories/ros2_repositories_impl.bzl | 18 ------------- repositories/ros_testing.BUILD.bazel | 19 -------------- ros2/cc_defs.bzl | 3 +++ ros2/py_defs.bzl | 3 +++ ros2/pytest_wrapper.py.tpl | 32 +++++++++--------------- ros2/test.bzl | 11 ++++---- ros2/test.py.tpl | 20 +++++---------- 10 files changed, 36 insertions(+), 96 deletions(-) delete mode 100644 repositories/ament_cmake_ros.BUILD.bazel delete mode 100644 repositories/ros_testing.BUILD.bazel diff --git a/.bazelrc b/.bazelrc index c9ae3431..532306c2 100644 --- a/.bazelrc +++ b/.bazelrc @@ -16,9 +16,6 @@ build --noenable_bzlmod # This is a mandatory flag. build --incompatible_default_to_explicit_init_py -# Don't let local Python site packages leak into the build and cause problems -common --action_env=PYTHONNOUSERSITE=1 - # All code will be compiled with C++17 flag. # Users can naturally override copts for cc_* targets. # NOTE: googletest >=v1.13.0 requires min C++14. @@ -28,7 +25,13 @@ build --cxxopt=-std=c++17 # Ensure that you don't accidentally make non-hermetic actions/tests # which depend on remote services. Tag an individual target with # tags=["requires-network"] to opt-out of the enforcement. +# In ROS context this is important such that each test target is executed +# in a separate network environment. +# This is a mandatory flag. build --sandbox_default_allow_network=false + +# Don't let local Python site packages leak into the build and cause problems +common --action_env=PYTHONNOUSERSITE=1 # Don't let environment variables like $PATH sneak into the build, # which can cause massive cache misses when they change. common --incompatible_strict_action_env diff --git a/repositories/ament_cmake_ros.BUILD.bazel b/repositories/ament_cmake_ros.BUILD.bazel deleted file mode 100644 index a584f0b7..00000000 --- a/repositories/ament_cmake_ros.BUILD.bazel +++ /dev/null @@ -1,11 +0,0 @@ -""" Builds ament_cmake_ros. -""" - -load("@rules_python//python:defs.bzl", "py_library") - -py_library( - name = "domain_coordinator", - srcs = glob(["domain_coordinator/domain_coordinator/**/*.py"]), - imports = ["domain_coordinator"], - visibility = ["//visibility:public"], -) diff --git a/repositories/ros2_repo_mappings.yaml b/repositories/ros2_repo_mappings.yaml index f6b09d23..990a2bb6 100644 --- a/repositories/ros2_repo_mappings.yaml +++ b/repositories/ros2_repo_mappings.yaml @@ -1,8 +1,5 @@ # Maps names of ROS repositories to Bazel' http_archive arguments. repositories: - ament_cmake_ros: - name: ros2_ament_cmake_ros - build_file: "@com_github_mvukov_rules_ros2//repositories:ament_cmake_ros.BUILD.bazel" ament_index: name: ros2_ament_index build_file: "@com_github_mvukov_rules_ros2//repositories:ament_index.BUILD.bazel" @@ -110,9 +107,6 @@ repositories: build_file: "@com_github_mvukov_rules_ros2//repositories:ros2cli.BUILD.bazel" patches: - "@com_github_mvukov_rules_ros2//repositories/patches:ros2cli_replace-netifaces.patch" - ros_testing: - name: ros2_ros_testing - build_file: "@com_github_mvukov_rules_ros2//repositories:ros_testing.BUILD.bazel" rosbag2: name: ros2_rosbag2 build_file: "@com_github_mvukov_rules_ros2//repositories:rosbag2.BUILD.bazel" diff --git a/repositories/ros2_repositories_impl.bzl b/repositories/ros2_repositories_impl.bzl index c23d75c8..15ff8310 100644 --- a/repositories/ros2_repositories_impl.bzl +++ b/repositories/ros2_repositories_impl.bzl @@ -3,15 +3,6 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") def ros2_repositories_impl(): - maybe( - http_archive, - name = "ros2_ament_cmake_ros", - build_file = "@com_github_mvukov_rules_ros2//repositories:ament_cmake_ros.BUILD.bazel", - sha256 = "01c778f18315ad13efd02e24200ff04f1e72359096c0967dba45e45bc479b3c6", - strip_prefix = "ament_cmake_ros-0.10.0", - url = "https://github.com/ros2/ament_cmake_ros/archive/refs/tags/0.10.0.tar.gz", - ) - maybe( http_archive, name = "ros2_ament_index", @@ -290,15 +281,6 @@ def ros2_repositories_impl(): url = "https://github.com/ros2/ros2cli/archive/refs/tags/0.18.10.tar.gz", ) - maybe( - http_archive, - name = "ros2_ros_testing", - build_file = "@com_github_mvukov_rules_ros2//repositories:ros_testing.BUILD.bazel", - sha256 = "f52dc8d48e3e525597e96e5316e882a03cbed6b2d3024699219c0afc0283a38b", - strip_prefix = "ros_testing-0.4.0", - url = "https://github.com/ros2/ros_testing/archive/refs/tags/0.4.0.tar.gz", - ) - maybe( http_archive, name = "ros2_rosbag2", diff --git a/repositories/ros_testing.BUILD.bazel b/repositories/ros_testing.BUILD.bazel deleted file mode 100644 index 092cf325..00000000 --- a/repositories/ros_testing.BUILD.bazel +++ /dev/null @@ -1,19 +0,0 @@ -""" Builds ros_testing. -""" - -load("@rules_python//python:defs.bzl", "py_library") - -py_library( - name = "ros2test", - srcs = glob(["ros2test/ros2test/**/*.py"]), - imports = ["ros2test"], - visibility = ["//visibility:public"], - deps = [ - "@ros2_ament_cmake_ros//:domain_coordinator", - "@ros2_launch//:launch", - "@ros2_launch//:launch_testing", - "@ros2_launch_ros//:launch_ros", - "@ros2_launch_ros//:launch_testing_ros", - "@ros2cli", - ], -) diff --git a/ros2/cc_defs.bzl b/ros2/cc_defs.bzl index 19921e86..d05f2a51 100644 --- a/ros2/cc_defs.bzl +++ b/ros2/cc_defs.bzl @@ -119,6 +119,9 @@ def ros2_cpp_test(name, ros2_package_name = None, set_up_ament = True, idl_deps Adds common ROS 2 C++ definitions on top of a cc_test. + Please make sure that --sandbox_default_allow_network=false is set in .bazelrc. + This ensures proper network isolation. + Args: name: A unique target name. ros2_package_name: If given, defines a ROS package name for the target. diff --git a/ros2/py_defs.bzl b/ros2/py_defs.bzl index e0f7901a..6b2c24f1 100644 --- a/ros2/py_defs.bzl +++ b/ros2/py_defs.bzl @@ -60,6 +60,9 @@ def ros2_py_binary(name, srcs, main, set_up_ament = False, **kwargs): def ros2_py_test(name, srcs, main, set_up_ament = True, **kwargs): """ Defines a ROS 2 Python test. + Please make sure that --sandbox_default_allow_network=false is set in .bazelrc. + This ensures proper network isolation. + Args: name: A unique target name. srcs: List of source files. diff --git a/ros2/pytest_wrapper.py.tpl b/ros2/pytest_wrapper.py.tpl index 441a0958..b3b895ca 100644 --- a/ros2/pytest_wrapper.py.tpl +++ b/ros2/pytest_wrapper.py.tpl @@ -1,12 +1,10 @@ # Adapted from https://gist.github.com/betaboon/c1dd785b5ba468b4df4e382eafff969a -import contextlib import os import pathlib import sys import coverage -import domain_coordinator import pytest {ament_setup} @@ -41,28 +39,22 @@ def main() -> None: if 'ROS_LOG_DIR' not in os.environ: os.environ['ROS_LOG_DIR'] = bazel_test_output_dir - with contextlib.ExitStack() as stack: - if 'ROS_DOMAIN_ID' not in os.environ: - domain_id = stack.enter_context(domain_coordinator.domain_id()) - os.environ['ROS_DOMAIN_ID'] = str(domain_id) - print(f'Running with ROS_DOMAIN_ID {os.environ["ROS_DOMAIN_ID"]}') + bazel_coverage = os.getenv('COVERAGE') == '1' - bazel_coverage = os.getenv('COVERAGE') == '1' + coverage_session = None + if bazel_coverage: + coverage_session = start_coverage_session() - coverage_session = None - if bazel_coverage: - coverage_session = start_coverage_session() + args = [ + '-ra', '-vv', '-p', 'launch_pytest.plugin', + f'--junitxml={os.environ["XML_OUTPUT_FILE"]}' + ] + sys.argv[1:] + pytest_exit_code = pytest.main(args) - args = [ - '-ra', '-vv', '-p', 'launch_pytest.plugin', - f'--junitxml={os.environ["XML_OUTPUT_FILE"]}' - ] + sys.argv[1:] - pytest_exit_code = pytest.main(args) + if coverage_session: + finalize_coverage_session(coverage_session) - if coverage_session: - finalize_coverage_session(coverage_session) - - sys.exit(pytest_exit_code) + sys.exit(pytest_exit_code) if __name__ == '__main__': diff --git a/ros2/test.bzl b/ros2/test.bzl index df964399..538e1cbd 100644 --- a/ros2/test.bzl +++ b/ros2/test.bzl @@ -8,9 +8,12 @@ load("@rules_ros2_pip_deps//:requirements.bzl", "requirement") def ros2_test(name, launch_file, nodes = None, deps = None, data = None, idl_deps = None, use_pytest = False, **kwargs): """ Defines a ROS 2 test. - In case you don't need ROS 2 nodes for tests, but need ament setup such - that e.g. plugins can work: use a lightweight macro ros2_cpp_test - from //ros2:cc_defs.bzl. + For lighter options, e.g. you don't need a launch file, please take a look at: + * ros2_cpp_test in //ros2:cc_defs.bzl and + * ros2_py_test in //ros2:py_defs.bzl + + Please make sure that --sandbox_default_allow_network=false is set in .bazelrc. + This ensures proper network isolation. Args: name: A unique target name. @@ -51,7 +54,6 @@ def _ros2_launch_testing_test(name, nodes, launch_file, deps, data, idl_deps, ** data = nodes + [launch_file] + data, main = launch_script, deps = [ - "@ros2_ament_cmake_ros//:domain_coordinator", "@ros2_launch//:launch_testing", "@ros2_launch_ros//:launch_testing_ros", ] + deps, @@ -78,7 +80,6 @@ def _ros2_launch_pytest_test(name, nodes, launch_file, deps, data, idl_deps, **k args = kwargs.pop("args", []) + ["$(rootpath :%s)" % launch_file], deps = [ "@ros2_launch//:launch_pytest", - "@ros2_ament_cmake_ros//:domain_coordinator", requirement("coverage"), requirement("pytest"), requirement("pytest-cov"), diff --git a/ros2/test.py.tpl b/ros2/test.py.tpl index cd6917e4..eca00b3e 100644 --- a/ros2/test.py.tpl +++ b/ros2/test.py.tpl @@ -1,8 +1,6 @@ -import contextlib import os import sys -import domain_coordinator import launch_testing.launch_test import launch_testing_ros @@ -10,8 +8,8 @@ import launch_testing_ros LAUNCH_FILE = '{launch_file}' -# The package name is intentionally undefined such that ros2test picks up -# the given launch file. +# The package name is intentionally undefined such that +# launch_testing.launch_test picks up the given launch file. sys.argv = sys.argv[:1] + [ f'--junit-xml={os.environ["XML_OUTPUT_FILE"]}', LAUNCH_FILE, @@ -25,13 +23,7 @@ if 'ROS_HOME' not in os.environ: if 'ROS_LOG_DIR' not in os.environ: os.environ['ROS_LOG_DIR'] = bazel_test_output_dir -with contextlib.ExitStack() as stack: - if 'ROS_DOMAIN_ID' not in os.environ: - domain_id = stack.enter_context(domain_coordinator.domain_id()) - os.environ['ROS_DOMAIN_ID'] = str(domain_id) - print(f'Running with ROS_DOMAIN_ID {os.environ["ROS_DOMAIN_ID"]}') - - parser, args = launch_testing.launch_test.parse_arguments() - exit_code = launch_testing.launch_test.run( - parser, args, test_runner_cls=launch_testing_ros.LaunchTestRunner) - sys.exit(exit_code) +parser, args = launch_testing.launch_test.parse_arguments() +exit_code = launch_testing.launch_test.run( + parser, args, test_runner_cls=launch_testing_ros.LaunchTestRunner) +sys.exit(exit_code)