-
Notifications
You must be signed in to change notification settings - Fork 51
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
Sloretz/test discovery #512
Draft
sloretz
wants to merge
21
commits into
rolling
Choose a base branch
from
sloretz/test_discovery
base: rolling
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
a6eb611
First pass discovery tests
sloretz 01ac3c5
Rename to rootests
sloretz 7b659b0
run_tests -> run_root_tests
sloretz 8b3c560
Add thishost tests that don't use mininet
sloretz 2153480
Minor tweaks
sloretz 3ad7bf5
Linting
sloretz dec1548
Update test expectations for changed matrix
sloretz c1ec828
increase test timeout
sloretz 4df83da
Improve package.xml
sloretz 371ec23
Use extend and lists more than append()
sloretz 1d0cd6a
Commands are required
sloretz 710592a
Don't need samehost root tests because non-root tests cover that already
sloretz 1c0f82d
Test case when RANGE is not set
sloretz 24f5193
More stuff to readme, not sure if table will render
sloretz 6cbc122
More readme edits
sloretz 8a063c7
Check that node was successfully created
sloretz 06a2ce8
Disable testing range not set to save time
sloretz 2e970ec
Fix flake8 issues
sloretz 2143918
Skip subnet samehost tests with static peer
sloretz f10b1fb
single quotes
sloretz 30eb7e3
Merge branch 'rolling' into sloretz/test_discovery
sloretz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
cmake_minimum_required(VERSION 3.5) | ||
project(test_discovery) | ||
|
||
find_package(ament_cmake_ros REQUIRED) | ||
find_package(rclcpp REQUIRED) | ||
find_package(test_msgs REQUIRED) | ||
|
||
add_executable(publish_once src/publish_once.cpp) | ||
target_compile_features(publish_once PUBLIC cxx_std_17) | ||
target_link_libraries(publish_once PRIVATE | ||
rclcpp::rclcpp | ||
${test_msgs_TARGETS} | ||
) | ||
|
||
add_executable(subscribe_once src/subscribe_once.cpp) | ||
target_compile_features(subscribe_once PUBLIC cxx_std_17) | ||
target_link_libraries(subscribe_once PRIVATE | ||
rclcpp::rclcpp | ||
${test_msgs_TARGETS} | ||
) | ||
|
||
install(TARGETS publish_once subscribe_once | ||
DESTINATION lib/${PROJECT_NAME}) | ||
|
||
install(FILES | ||
roottests/test_discovery.py | ||
roottests/conftest.py | ||
DESTINATION share/${PROJECT_NAME}/roottests/) | ||
|
||
install(PROGRAMS | ||
scripts/run_root_tests.py | ||
DESTINATION lib/${PROJECT_NAME}/) | ||
|
||
if(BUILD_TESTING) | ||
find_package(ament_lint_auto REQUIRED) | ||
ament_lint_auto_find_test_dependencies() | ||
|
||
ament_add_pytest_test(test_discovery tests/test_discovery.py TIMEOUT 1600) | ||
endif() | ||
|
||
ament_package() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# test_discovery | ||
|
||
This package tests the use of `ROS_AUTOMATIC_DISCOVERY_RANGE` and `ROS_STATIC_PEERS` environment variables. | ||
There are two sets of tests: automated tests that run when testing this package, and semiautomated tests that must be run manually after building this package. | ||
|
||
# Automated tests | ||
|
||
The automated tests run when testing this package. | ||
They test only the cases that apply when two processes are on the same host. | ||
|
||
## Semi-automated tests | ||
|
||
The semiautomated tests use `mininet` to test discovery behavior across two different (virtual) hosts. | ||
These tests require `root` access, and a working `mininet` install. | ||
|
||
### Installing prerequisites | ||
|
||
A working `mininet` install has only been tested on an Ubuntu based machine. | ||
If you're running in a container, that container will need root priviledges. | ||
|
||
First install the necessary dependencies: | ||
|
||
```bash | ||
sudo apt install \ | ||
iputils-ping \ | ||
iproute2 \ | ||
mininet \ | ||
openvswitch-switch \ | ||
openvswitch-testcontroller | ||
``` | ||
|
||
Next, make sure the openvswitch service is running | ||
|
||
```bash | ||
sudo service openvswitch-switch start | ||
``` | ||
|
||
You're now ready to run the tests. | ||
|
||
### Running the semi-automated tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?xml version="1.0"?> | ||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> | ||
<package format="2"> | ||
<name>test_discovery</name> | ||
<version>0.14.0</version> | ||
<description> | ||
Test discovery behaviors in ROS 2 using semiautomated tests. | ||
</description> | ||
|
||
<maintainer email="[email protected]">Brandon Ong</maintainer> | ||
|
||
<license>Apache License 2.0</license> | ||
|
||
<author email="[email protected]">Shane Loretz</author> | ||
|
||
<buildtool_depend>ament_cmake_ros</buildtool_depend> | ||
|
||
<!-- <depend>mininet</depend> TODO make this a rosdep key --> | ||
<depend>rclcpp</depend> | ||
<depend>test_msgs</depend> | ||
<depend>pytest</depend> | ||
|
||
<test_depend>ament_lint_auto</test_depend> | ||
<test_depend>ament_lint_common</test_depend> | ||
<test_depend>ament_cmake_pytest</test_depend> | ||
|
||
<test_depend>test_msgs</test_depend> | ||
|
||
<exec_depend>ros2cli</exec_depend> | ||
|
||
<export> | ||
<build_type>ament_cmake</build_type> | ||
</export> | ||
</package> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Copyright 2023 Open Source Robotics Foundation, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
def pytest_addoption(parser): | ||
parser.addoption('--ros-workspaces', action='store') | ||
parser.addoption('--rmws', action='store') | ||
|
||
|
||
def pytest_generate_tests(metafunc): | ||
if 'rmw' in metafunc.fixturenames: | ||
metafunc.parametrize('rmw', metafunc.config.option.rmws.split(':')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Copyright 2023 Open Source Robotics Foundation, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
# Run discovery tests | ||
# 1. Install dependencies | ||
# sudo apt install iputils-ping iproute2 mininet openvswitch-switch openvswitch-testcontroller | ||
# 2. Start openvswitch service if not already running | ||
# sudo service openvswitch-switch start | ||
|
||
from mininet.net import Mininet | ||
from mininet.topo import MinimalTopo | ||
import pytest | ||
|
||
|
||
RANGES = [ | ||
# None, # idential to LOCALHOST, but takes too long to test here | ||
'OFF', | ||
'SUBNET', | ||
'LOCALHOST', | ||
] | ||
|
||
|
||
class MininetFixture: | ||
__slots__ = ( | ||
'net', | ||
'h1', | ||
'h2', | ||
) | ||
|
||
|
||
def h1_ipv4(net: MininetFixture) -> str: | ||
return net.h1.IP() | ||
|
||
|
||
def h2_ipv4(net: MininetFixture) -> str: | ||
return net.h2.IP() | ||
|
||
|
||
def no_peer(net: MininetFixture) -> str: | ||
return '' | ||
|
||
|
||
@pytest.fixture() | ||
def mn(): | ||
f = MininetFixture() | ||
f.net = Mininet(topo=MinimalTopo()) | ||
f.h1 = f.net.getNodeByName('h1') | ||
f.h2 = f.net.getNodeByName('h2') | ||
|
||
f.net.start() | ||
yield f | ||
f.net.stop() | ||
|
||
|
||
# TODO(sloretz) figure out ROS workspace path from environment variables | ||
@pytest.fixture(scope='session') | ||
def ros_ws(pytestconfig): | ||
return pytestconfig.getoption('ros_workspaces').split(':') | ||
|
||
|
||
def make_env_str(ros_ws, rmw, discovery_range, peer): | ||
cmd = [] | ||
for ws in ros_ws: | ||
cmd.extend([ | ||
'.', | ||
f'"{ws}/setup.bash"', | ||
'&&', | ||
]) | ||
cmd.extend([ | ||
f'RMW_IMPLEMENTATION={rmw}', | ||
f'ROS_STATIC_PEERS="{peer}"', | ||
]) | ||
if discovery_range is not None: | ||
cmd.append(f'ROS_AUTOMATIC_DISCOVERY_RANGE={discovery_range}') | ||
return ' '.join(cmd) | ||
|
||
|
||
@pytest.mark.parametrize('sub_peer', (no_peer, h1_ipv4)) | ||
@pytest.mark.parametrize('sub_range', RANGES) | ||
@pytest.mark.parametrize('pub_peer', (no_peer, h2_ipv4)) | ||
@pytest.mark.parametrize('pub_range', RANGES) | ||
def test_differenthost(mn, ros_ws, rmw, pub_range, pub_peer, sub_range, sub_peer): | ||
pub_peer = pub_peer(mn) | ||
sub_peer = sub_peer(mn) | ||
|
||
pub_env = make_env_str(ros_ws, rmw, pub_range, pub_peer) | ||
sub_env = make_env_str(ros_ws, rmw, sub_range, sub_peer) | ||
pub_cmd = pub_env + ' ros2 run test_discovery publish_once > /dev/null &' | ||
sub_cmd = sub_env + ' ros2 run test_discovery subscribe_once' | ||
print('$', pub_cmd) | ||
print('$', sub_cmd) | ||
|
||
mn.h1.cmd(pub_cmd) | ||
result = mn.h2.cmd(sub_cmd) | ||
|
||
# Invalid node configuration could make OFF tests appear to succeed | ||
assert 'test_discovery: node successfully created' in result.strip() | ||
|
||
message_received = 'test_discovery: message was received' in result.strip() | ||
|
||
if 'OFF' in (pub_range, sub_range): | ||
# If either has discovery off then it won't succeed | ||
assert not message_received, result.strip() | ||
elif pub_peer or sub_peer: | ||
# if either has a static peer set, discovery should succeed | ||
assert message_received, result.strip() | ||
elif 'SUBNET' == pub_range and 'SUBNET' == sub_range: | ||
# With no static peer, succeed only if both are set to SUBNET | ||
assert message_received, result.strip() | ||
else: | ||
# All other cases don't discover each other | ||
assert not message_received, result.strip() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# Copyright 2023 Open Source Robotics Foundation, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import argparse | ||
import os | ||
import sys | ||
|
||
from ament_index_python import get_package_share_path | ||
from ament_index_python import get_resources | ||
|
||
|
||
def get_rmw_implementations(): | ||
resources = list(get_resources('rmw_typesupport').keys()) | ||
if 'rmw_implementation' in resources: | ||
resources.remove('rmw_implementation') | ||
return tuple(resources) | ||
|
||
|
||
def get_tests_dir(): | ||
pkg_path = get_package_share_path('test_discovery') | ||
return pkg_path / 'roottests' | ||
|
||
|
||
def get_workspaces(): | ||
# Get an ordered list of workspaces that are sourced | ||
prefixes = os.environ['AMENT_PREFIX_PATH'] | ||
if not prefixes: | ||
raise ValueError('No ROS/Colcon workspace sourced') | ||
workspaces = set() | ||
for prefix in prefixes.split(':'): | ||
if not prefix: | ||
# env var might have began or ended with a ':' | ||
continue | ||
# If there exists a parent folder containing a setup.bash | ||
# then assume this is an isolated colcon workspace | ||
if os.path.exists(os.path.join(prefix, '../setup.bash')): | ||
workspaces.add(os.path.dirname(prefix)) | ||
else: | ||
# Assume a merged ament/colcon workspace | ||
workspaces.add(prefix) | ||
return tuple(workspaces) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--rmw', default=None) | ||
parser.add_argument('--select', default=None) | ||
args = parser.parse_args() | ||
|
||
rmw_implementations = get_rmw_implementations() | ||
if args.rmw: | ||
if args.rmw not in rmw_implementations: | ||
raise ValueError(f'{args.rmw} is not an installed rmw: {rmw_implementations}') | ||
rmw_implementations = [args.rmw] | ||
|
||
cmd = [ | ||
'sudo', | ||
sys.executable, | ||
'-m', | ||
'pytest', | ||
'-c', | ||
str(get_tests_dir() / 'conftest.py'), | ||
] | ||
if args.select: | ||
cmd.extend([ | ||
'-k', | ||
args.select, | ||
]) | ||
cmd.extend([ | ||
f'--rmws={":".join(rmw_implementations)}', | ||
f'--ros-workspaces={":".join(get_workspaces())}', | ||
str(get_tests_dir() / 'test_discovery.py'), | ||
]) | ||
|
||
print('Executing the following command:') | ||
print('================================') | ||
print('$', *cmd) | ||
print('================================') | ||
|
||
os.execvp(cmd[0], cmd) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improved package.xml in 4df83da
ament_cmake_pytest
is atest_depend
, butpytest
is still a run depend for the tests that run as root