-
Notifications
You must be signed in to change notification settings - Fork 293
Running integration tests with mock hardware #1226
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
URJala
wants to merge
22
commits into
UniversalRobots:main
Choose a base branch
from
URJala:mock_hardware_tests
base: main
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.
Open
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
1848f8b
Revert "Apply renaming of member variables of JTC (#1275)"
URJala d7de8a7
Reapply "Apply renaming of member variables of JTC (#1275)"
URJala 9cd437a
Running integration tests with mock hardware
URJala f1389a6
Robot driver tests pass with mock hardware
URJala 4d55d75
something got duplicated, removing
URJala a848909
Split passthrough controller tests to its own file.
URJala 664b50c
Split integration tests out to one file per controller
URJala 33ce7ed
finish splitting passthrough controller tests
URJala 45b22f1
Add the separate tests to cmake file
URJala 09b36ea
formatting
URJala 45dc4b7
Add mock hardware test description
URJala 96f550f
Move all mock harrdware tests in to their own file
URJala c868514
Remove controller switching tests
URJala 6aa4b59
add mock hardware test to build tests
URJala d1de97b
Remove passthrough test from sjtc test script
URJala f0721c0
Clean up test description generators
URJala f217984
fix config controller test
URJala 08af3b1
Clean up all the test setups and remove irrelevant tests
URJala 782264f
remove mock hardware parameter from setUpClass
URJala 08c71ed
Move sjtc trajectory and illegal trajectory test to test_common, so i…
URJala 238c04e
Apply suggestions from code review
URJala 3201f73
Apply renaming to the last few test classes
URJala 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 hidden or 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
89 changes: 89 additions & 0 deletions
89
ur_robot_driver/test/integration_test_config_controller.py
This file contains hidden or 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,89 @@ | ||
#!/usr/bin/env python | ||
# Copyright 2025, Universal Robots A/S | ||
# | ||
# Redistribution and use in source and binary forms, with or without | ||
# modification, are permitted provided that the following conditions are met: | ||
# | ||
# * Redistributions of source code must retain the above copyright | ||
# notice, this list of conditions and the following disclaimer. | ||
# | ||
# * Redistributions in binary form must reproduce the above copyright | ||
# notice, this list of conditions and the following disclaimer in the | ||
# documentation and/or other materials provided with the distribution. | ||
# | ||
# * Neither the name of the {copyright_holder} nor the names of its | ||
# contributors may be used to endorse or promote products derived from | ||
# this software without specific prior written permission. | ||
# | ||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
# POSSIBILITY OF SUCH DAMAGE. | ||
|
||
import os | ||
import sys | ||
import time | ||
import unittest | ||
|
||
import launch_testing | ||
import pytest | ||
import rclpy | ||
from rclpy.node import Node | ||
|
||
sys.path.append(os.path.dirname(__file__)) | ||
from test_common import ( # noqa: E402 | ||
ControllerManagerInterface, | ||
DashboardInterface, | ||
IoStatusInterface, | ||
ConfigurationInterface, | ||
generate_driver_test_description, | ||
) | ||
|
||
|
||
@pytest.mark.launch_test | ||
@launch_testing.parametrize("tf_prefix", [(""), ("my_ur_")]) | ||
def generate_test_description(tf_prefix): | ||
return generate_driver_test_description(tf_prefix=tf_prefix) | ||
|
||
|
||
class ConfigControllerTest(unittest.TestCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
# Initialize the ROS context | ||
rclpy.init() | ||
cls.node = Node("config_controller_test") | ||
time.sleep(1) | ||
cls.init_robot(cls) | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
# Shutdown the ROS context | ||
cls.node.destroy_node() | ||
rclpy.shutdown() | ||
|
||
def init_robot(self): | ||
self._dashboard_interface = DashboardInterface(self.node) | ||
self._controller_manager_interface = ControllerManagerInterface(self.node) | ||
self._io_status_controller_interface = IoStatusInterface(self.node) | ||
self._configuration_controller_interface = ConfigurationInterface(self.node) | ||
|
||
def setUp(self): | ||
self._dashboard_interface.start_robot() | ||
time.sleep(1) | ||
self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) | ||
|
||
# | ||
# Test functions | ||
# | ||
|
||
def test_get_robot_software_version(self): | ||
self.assertGreater( | ||
self._configuration_controller_interface.get_robot_software_version().major, 1 | ||
) |
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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,132 @@ | ||
#!/usr/bin/env python | ||
# Copyright 2025, Universal Robots A/S | ||
# | ||
# Redistribution and use in source and binary forms, with or without | ||
# modification, are permitted provided that the following conditions are met: | ||
# | ||
# * Redistributions of source code must retain the above copyright | ||
# notice, this list of conditions and the following disclaimer. | ||
# | ||
# * Redistributions in binary form must reproduce the above copyright | ||
# notice, this list of conditions and the following disclaimer in the | ||
# documentation and/or other materials provided with the distribution. | ||
# | ||
# * Neither the name of the {copyright_holder} nor the names of its | ||
# contributors may be used to endorse or promote products derived from | ||
# this software without specific prior written permission. | ||
# | ||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
# POSSIBILITY OF SUCH DAMAGE. | ||
|
||
import logging | ||
import os | ||
import sys | ||
import time | ||
import unittest | ||
|
||
import launch_testing | ||
import pytest | ||
import rclpy | ||
from rclpy.node import Node | ||
from ur_msgs.msg import IOStates | ||
|
||
sys.path.append(os.path.dirname(__file__)) | ||
from test_common import ( # noqa: E402 | ||
ControllerManagerInterface, | ||
DashboardInterface, | ||
IoStatusInterface, | ||
generate_driver_test_description, | ||
) | ||
|
||
|
||
@pytest.mark.launch_test | ||
@launch_testing.parametrize("tf_prefix", [(""), ("my_ur_")]) | ||
def generate_test_description(tf_prefix): | ||
return generate_driver_test_description(tf_prefix=tf_prefix) | ||
|
||
|
||
class IOControllerTest(unittest.TestCase): | ||
@classmethod | ||
def setUpClass(cls): | ||
# Initialize the ROS context | ||
rclpy.init() | ||
cls.node = Node("io_controller_test") | ||
time.sleep(1) | ||
cls.init_robot(cls) | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
# Shutdown the ROS context | ||
cls.node.destroy_node() | ||
rclpy.shutdown() | ||
|
||
def init_robot(self): | ||
self._dashboard_interface = DashboardInterface(self.node) | ||
self._controller_manager_interface = ControllerManagerInterface(self.node) | ||
self._io_status_controller_interface = IoStatusInterface(self.node) | ||
|
||
def setUp(self): | ||
self._dashboard_interface.start_robot() | ||
time.sleep(1) | ||
self.assertTrue(self._io_status_controller_interface.resend_robot_program().success) | ||
|
||
# | ||
# Test functions | ||
# | ||
|
||
def test_set_io(self): | ||
"""Test to set an IO and check whether it has been set.""" | ||
# Create io callback to verify result | ||
io_msg = None | ||
|
||
def io_msg_cb(msg): | ||
nonlocal io_msg | ||
io_msg = msg | ||
|
||
io_states_sub = self.node.create_subscription( | ||
IOStates, | ||
"/io_and_status_controller/io_states", | ||
io_msg_cb, | ||
rclpy.qos.qos_profile_system_default, | ||
) | ||
|
||
# Set pin 0 to 1.0 | ||
test_pin = 0 | ||
|
||
logging.info("Setting pin %d to 1.0", test_pin) | ||
self._io_status_controller_interface.set_io(fun=1, pin=test_pin, state=1.0) | ||
|
||
# Wait until the pin state has changed | ||
pin_state = False | ||
end_time = time.time() + 5 | ||
while not pin_state and time.time() < end_time: | ||
rclpy.spin_once(self.node, timeout_sec=0.1) | ||
if io_msg is not None: | ||
pin_state = io_msg.digital_out_states[test_pin].state | ||
|
||
self.assertEqual(pin_state, 1.0) | ||
|
||
# Set pin 0 to 0.0 | ||
logging.info("Setting pin %d to 0.0", test_pin) | ||
self._io_status_controller_interface.set_io(fun=1, pin=test_pin, state=0.0) | ||
|
||
# Wait until the pin state has changed back | ||
end_time = time.time() + 5 | ||
while pin_state and time.time() < end_time: | ||
rclpy.spin_once(self.node, timeout_sec=0.1) | ||
if io_msg is not None: | ||
pin_state = io_msg.digital_out_states[test_pin].state | ||
|
||
self.assertEqual(pin_state, 0.0) | ||
|
||
# Clean up io subscription | ||
self.node.destroy_subscription(io_states_sub) |
Oops, something went wrong.
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.
Shouldn't this test also be able to run on mock hardware? I see, it doesn't, but I don't see a conceptual reason why it shouldn't.
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 is no real reason that it shouldn't, but with the way the controller sets io's, it apparently cannot verify if a pin was set, and fails the test because the pins indeed end up at the wrong value. The controller also prints a little warning that this can happen with mock hardware, so I don't think it should be tested, as it doesn't behave correctly.