diff --git a/ros2controlcli/README.md b/ros2controlcli/README.md new file mode 100644 index 0000000000..29fd9f858d --- /dev/null +++ b/ros2controlcli/README.md @@ -0,0 +1,146 @@ +# ros2controlcli +Command line interface for controller manager + +## Verbs + + +### list + +```bash +$ ros2 control list -h +usage: ros2 control list [-h] [--spin-time SPIN_TIME] [--no-daemon] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] + +Output the list of loaded controllers, their type and status + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` + +```bash +$ ros2 control list +test_controller_name[test_controller] active +``` + +### list_types + +```bash +$ ros2 control list_types -h +usage: ros2 control list_types [-h] [--spin-time SPIN_TIME] [--no-daemon] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] + +Output the available controller types and their base classes + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` +```bash +$ ros2 control list_types +diff_drive_controller/DiffDriveController controller_interface::ControllerInterface +joint_state_controller/JointStateController controller_interface::ControllerInterface +joint_trajectory_controller/JointTrajectoryController controller_interface::ControllerInterface +test_controller +``` + +### load + +```bash +$ ros2 control load -h +usage: ros2 control load [-h] [--spin-time SPIN_TIME] [--no-daemon] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] controller_name + +Load a controller in a controller manager + +positional arguments: + controller_name Name of the controller + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` + +### reload_libraries + +```bash +$ ros2 control reload_libraries -h +usage: ros2 control reload_libraries [-h] [--spin-time SPIN_TIME] [--force-kill] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] + +Reload controller libraries + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + --force-kill Force stop of loaded controllers + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` + +### switch + +```bash +$ ros2 control switch -h +usage: ros2 control switch [-h] [--spin-time SPIN_TIME] [--no-daemon] [--stop-controllers [STOP_CONTROLLERS [STOP_CONTROLLERS ...]]] + [--start-controllers [START_CONTROLLERS [START_CONTROLLERS ...]]] [--strict] [--start-asap] + [--switch-timeout SWITCH_TIMEOUT] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] + +Switch controllers in a controller manager + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + --stop-controllers [STOP_CONTROLLERS [STOP_CONTROLLERS ...]] + Name of the controllers to be stopped + --start-controllers [START_CONTROLLERS [START_CONTROLLERS ...]] + Name of the controllers to be started + --strict Strict switch + --start-asap Start asap controllers + --switch-timeout SWITCH_TIMEOUT + Timeout for switching controllers + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` + + + + + +### unload + +```bash +$ ros2 control unload -h +usage: ros2 control unload [-h] [--spin-time SPIN_TIME] [--no-daemon] [-c CONTROLLER_MANAGER] [--include-hidden-nodes] controller_name + +Unload a controller in a controller manager + +positional arguments: + controller_name Name of the controller + +optional arguments: + -h, --help show this help message and exit + --spin-time SPIN_TIME + Spin time in seconds to wait for discovery (only applies when not using an already running daemon) + -c CONTROLLER_MANAGER, --controller-manager CONTROLLER_MANAGER + Name of the controller manager ROS node + --include-hidden-nodes + Consider hidden nodes as well +``` \ No newline at end of file diff --git a/ros2controlcli/package.xml b/ros2controlcli/package.xml new file mode 100644 index 0000000000..6cf4831b79 --- /dev/null +++ b/ros2controlcli/package.xml @@ -0,0 +1,27 @@ + + + + ros2controlcli + 0.0.0 + + The ROS 2 command line tools for ROS2 Control. + + Victor Lopez + Apache License 2.0 + + rclpy + ros2cli + ros2node + ros2param + controller_manager_msgs + rosidl_runtime_py + + ament_copyright + ament_flake8 + ament_pep257 + ament_xmllint + + + ament_python + + diff --git a/ros2controlcli/resource/ros2controlcli b/ros2controlcli/resource/ros2controlcli new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ros2controlcli/ros2controlcli/__init__.py b/ros2controlcli/ros2controlcli/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ros2controlcli/ros2controlcli/api/__init__.py b/ros2controlcli/ros2controlcli/api/__init__.py new file mode 100644 index 0000000000..8d589d7a82 --- /dev/null +++ b/ros2controlcli/ros2controlcli/api/__init__.py @@ -0,0 +1,138 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + + +from controller_manager_msgs.srv import ListControllers, ListControllerTypes, \ + LoadController, ReloadControllerLibraries, SwitchController, UnloadController +import rclpy +from ros2cli.node.direct import DirectNode +from ros2node.api import NodeNameCompleter +from ros2param.api import call_list_parameters + + +def service_caller(service_name, service_type, request): + try: + rclpy.init() + + node = rclpy.create_node( + 'ros2controlcli_{}_requester'.format( + service_name.replace( + '/', ''))) + + cli = node.create_client(service_type, service_name) + + if not cli.service_is_ready(): + node.get_logger().debug('waiting for service {} to become available...' + .format(service_name)) + if not cli.wait_for_service(0.2): + raise RuntimeError('Could not contact service {}'.format(service_name)) + + node.get_logger().debug('requester: making request: %r\n' % request) + future = cli.call_async(request) + rclpy.spin_until_future_complete(node, future) + if future.result() is not None: + return future.result() + else: + raise RuntimeError('Exception while calling service: %r' % future.exception()) + finally: + node.destroy_node() + rclpy.shutdown() + + +def list_controllers(controller_manager_name): + request = ListControllers.Request() + return service_caller('{}/list_controllers'.format(controller_manager_name), + ListControllers, request) + + +def list_controller_types(controller_manager_name): + request = ListControllerTypes.Request() + return service_caller( + '{}/list_controller_types'.format(controller_manager_name), ListControllerTypes, request) + + +def reload_controller_libraries(controller_manager_name, force_kill): + request = ReloadControllerLibraries.Request() + request.force_kill = force_kill + return service_caller( + '{}/reload_controller_libraries'.format(controller_manager_name), + ReloadControllerLibraries, + request) + + +def load_controller(controller_manager_name, controller_name): + request = LoadController.Request() + request.name = controller_name + return service_caller('{}/load_controller'.format(controller_manager_name), + LoadController, request) + + +def switch_controllers(controller_manager_name, stop_controllers, + start_controllers, strict, start_asap, timeout): + request = SwitchController.Request() + request.start_controllers = start_controllers + request.stop_controllers = stop_controllers + if strict: + request.strictness = SwitchController.Request.STRICT + else: + request.strictness = SwitchController.Request.BEST_EFFORT + request.start_asap = start_asap + request.timeout = rclpy.duration.Duration(seconds=timeout).to_msg() + return service_caller('{}/switch_controller'.format(controller_manager_name), + SwitchController, request) + + +def unload_controller(controller_manager_name, controller_name): + request = UnloadController.Request() + request.name = controller_name + return service_caller('{}/unload_controller'.format(controller_manager_name), + UnloadController, request) + + +class ControllerNameCompleter: + """Callable returning a list of controllers parameter names.""" + + def __call__(self, prefix, parsed_args, **kwargs): + with DirectNode(parsed_args) as node: + parameter_names = call_list_parameters( + node=node, node_name=parsed_args.controller_manager) + suffix = '.type' + return [ + n[:-len(suffix)] for n in parameter_names + if n.endswith(suffix)] + + +class LoadedControllerNameCompleter: + """Callable returning a list of loaded controllers.""" + + def __init__(self, valid_states=['active', 'inactive', 'configured', 'unconfigured']): + self.valid_states = valid_states + + def __call__(self, prefix, parsed_args, **kwargs): + controllers = list_controllers(parsed_args.controller_manager).controller + return [ + c.name for c in controllers + if c.state in self.valid_states] + + +def add_controller_mgr_parsers(parser): + """Parser arguments to get controller manager node name, defaults to /controller_manager.""" + arg = parser.add_argument( + '-c', '--controller-manager', help='Name of the controller manager ROS node', + default='/controller_manager', required=False) + arg.completer = NodeNameCompleter( + include_hidden_nodes_key='include_hidden_nodes') + parser.add_argument( + '--include-hidden-nodes', action='store_true', + help='Consider hidden nodes as well') diff --git a/ros2controlcli/ros2controlcli/command/__init__.py b/ros2controlcli/ros2controlcli/command/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ros2controlcli/ros2controlcli/command/control.py b/ros2controlcli/ros2controlcli/command/control.py new file mode 100644 index 0000000000..98c3a9d357 --- /dev/null +++ b/ros2controlcli/ros2controlcli/command/control.py @@ -0,0 +1,38 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + + +from ros2cli.command import add_subparsers_on_demand +from ros2cli.command import CommandExtension + + +class ControlCommand(CommandExtension): + """Various control related sub-commands.""" + + def add_arguments(self, parser, cli_name): + self._subparser = parser + # get verb extensions and let them add their arguments + add_subparsers_on_demand( + parser, cli_name, '_verb', 'ros2controlcli.verb', required=False) + + def main(self, *, parser, args): + if not hasattr(args, '_verb'): + # in case no verb was passed + self._subparser.print_help() + return 0 + + extension = getattr(args, '_verb') + + # call the verb's main method + return extension.main(args=args) diff --git a/ros2controlcli/ros2controlcli/verb/__init__.py b/ros2controlcli/ros2controlcli/verb/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ros2controlcli/ros2controlcli/verb/list.py b/ros2controlcli/ros2controlcli/verb/list.py new file mode 100644 index 0000000000..8dc1344a32 --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/list.py @@ -0,0 +1,30 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, list_controllers + + +class ListVerb(VerbExtension): + """Output the list of loaded controllers, their type and status.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + add_controller_mgr_parsers(parser) + + def main(self, *, args): + controllers = list_controllers(args.controller_manager).controller + for c in controllers: + print('{:20s}{:20s} {:10s}'.format(c.name, '[' + c.type + ']', c.state)) diff --git a/ros2controlcli/ros2controlcli/verb/list_types.py b/ros2controlcli/ros2controlcli/verb/list_types.py new file mode 100644 index 0000000000..af30d38c9c --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/list_types.py @@ -0,0 +1,31 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, list_controller_types + + +class ListTypesVerb(VerbExtension): + """Output the available controller types and theri base classes.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + add_controller_mgr_parsers(parser) + + def main(self, *, args): + response = list_controller_types(args.controller_manager) + types_and_classes = zip(response.types, response.base_classes) + for c in types_and_classes: + print('{:70s} {}'.format(c[0], c[1])) diff --git a/ros2controlcli/ros2controlcli/verb/load.py b/ros2controlcli/ros2controlcli/verb/load.py new file mode 100644 index 0000000000..0f3120704f --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/load.py @@ -0,0 +1,32 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, ControllerNameCompleter, load_controller + + +class LoadVerb(VerbExtension): + """Load a controller in a controller manager.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + arg = parser.add_argument( + 'controller_name', help='Name of the controller') + arg.completer = ControllerNameCompleter() + add_controller_mgr_parsers(parser) + + def main(self, *, args): + response = load_controller(args.controller_manager, args.controller_name) + return response.ok diff --git a/ros2controlcli/ros2controlcli/verb/reload_libraries.py b/ros2controlcli/ros2controlcli/verb/reload_libraries.py new file mode 100644 index 0000000000..df88a1e682 --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/reload_libraries.py @@ -0,0 +1,36 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, reload_controller_libraries + + +class ReloadLibrariesVerb(VerbExtension): + """Reload controller libraries.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + parser.add_argument( + '--force-kill', action='store_true', + help='Force stop of loaded controllers') + add_controller_mgr_parsers(parser) + + def main(self, *, args): + response = reload_controller_libraries(args.controller_manager, force_kill=args.force_kill) + if response.ok: + print('Reload successful') + else: + print('Error in reload, check controller manager logs') + return response.ok diff --git a/ros2controlcli/ros2controlcli/verb/switch.py b/ros2controlcli/ros2controlcli/verb/switch.py new file mode 100644 index 0000000000..30e20a1ac6 --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/switch.py @@ -0,0 +1,58 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, LoadedControllerNameCompleter, \ + switch_controllers + + +class SwitchVerb(VerbExtension): + """Switch controllers in a controller manager.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + arg = parser.add_argument( + '--stop-controllers', + nargs='*', + default=[], + help='Name of the controllers to be stopped') + arg.completer = LoadedControllerNameCompleter(['active']) + arg = parser.add_argument( + '--start-controllers', + nargs='*', + default=[], + help='Name of the controllers to be started') + arg.completer = LoadedControllerNameCompleter(['inactive']) + parser.add_argument( + '--strict', action='store_true', help='Strict switch') + parser.add_argument( + '--start-asap', action='store_true', help='Start asap controllers') + parser.add_argument( + '--switch-timeout', + default=5.0, + required=False, + help='Timeout for switching controllers') + arg.completer = LoadedControllerNameCompleter(['inactive']) + add_controller_mgr_parsers(parser) + + def main(self, *, args): + response = switch_controllers( + args.controller_manager, + args.stop_controllers, + args.start_controllers, + args.strict, + args.start_asap, + args.switch_timeout) + return response.ok diff --git a/ros2controlcli/ros2controlcli/verb/unload.py b/ros2controlcli/ros2controlcli/verb/unload.py new file mode 100644 index 0000000000..1c8f35a0da --- /dev/null +++ b/ros2controlcli/ros2controlcli/verb/unload.py @@ -0,0 +1,33 @@ +# Copyright 2020 PAL Robotics S.L. +# +# 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. + +from ros2cli.node.direct import add_arguments +from ros2cli.verb import VerbExtension +from ros2controlcli.api import add_controller_mgr_parsers, LoadedControllerNameCompleter, \ + unload_controller + + +class UnloadVerb(VerbExtension): + """Unload a controller in a controller manager.""" + + def add_arguments(self, parser, cli_name): + add_arguments(parser) + arg = parser.add_argument( + 'controller_name', help='Name of the controller') + arg.completer = LoadedControllerNameCompleter() + add_controller_mgr_parsers(parser) + + def main(self, *, args): + response = unload_controller(args.controller_manager, args.controller_name) + return response.ok diff --git a/ros2controlcli/setup.py b/ros2controlcli/setup.py new file mode 100644 index 0000000000..ef67145897 --- /dev/null +++ b/ros2controlcli/setup.py @@ -0,0 +1,47 @@ +from setuptools import find_packages +from setuptools import setup + +package_name = 'ros2controlcli' + +setup( + name=package_name, + version='0.0.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/' + package_name, ['package.xml']), + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ], + install_requires=['ros2cli'], + zip_safe=True, + author='Victor Lopez', + author_email='victor.lopez@pal-robotics.com', + maintainer='Victor Lopez', + maintainer_email='victor.lopez@pal-robotics.com', + url='https://github.com/ros-controls/ros2_control', + keywords=[], + classifiers=[ + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + ], + description='ROS2 Control command interface.', + long_description="""\ +ROS2 Control command interface.""", + license='Apache License, Version 2.0', + tests_require=['pytest'], + entry_points={ + 'ros2cli.command': [ + 'control = ros2controlcli.command.control:ControlCommand', + ], + 'ros2controlcli.verb': [ + 'list = ros2controlcli.verb.list:ListVerb', + 'list_types = ros2controlcli.verb.list_types:ListTypesVerb', + 'load = ros2controlcli.verb.load:LoadVerb', + 'reload_libraries = ros2controlcli.verb.reload_libraries:ReloadLibrariesVerb', + 'switch = ros2controlcli.verb.switch:SwitchVerb', + 'unload = ros2controlcli.verb.unload:UnloadVerb', + ], + } +) diff --git a/ros2controlcli/test/test_copyright.py b/ros2controlcli/test/test_copyright.py new file mode 100644 index 0000000000..cf0fae31fd --- /dev/null +++ b/ros2controlcli/test/test_copyright.py @@ -0,0 +1,23 @@ +# Copyright 2017 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. + +from ament_copyright.main import main +import pytest + + +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/ros2controlcli/test/test_flake8.py b/ros2controlcli/test/test_flake8.py new file mode 100644 index 0000000000..eff8299695 --- /dev/null +++ b/ros2controlcli/test/test_flake8.py @@ -0,0 +1,23 @@ +# Copyright 2017 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. + +from ament_flake8.main import main +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc = main(argv=[]) + assert rc == 0, 'Found errors' diff --git a/ros2controlcli/test/test_pep257.py b/ros2controlcli/test/test_pep257.py new file mode 100644 index 0000000000..0e38a6c605 --- /dev/null +++ b/ros2controlcli/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2017 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. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=[]) + assert rc == 0, 'Found code style errors / warnings' diff --git a/ros2controlcli/test/test_xmllint.py b/ros2controlcli/test/test_xmllint.py new file mode 100644 index 0000000000..f46285e717 --- /dev/null +++ b/ros2controlcli/test/test_xmllint.py @@ -0,0 +1,23 @@ +# Copyright 2019 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. + +from ament_xmllint.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.xmllint +def test_xmllint(): + rc = main(argv=[]) + assert rc == 0, 'Found errors'