feat(level1): ROS2 完全体 12 包 / 80 测试 / 23 文档 / 工程化 / Docker 分组
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# py_srv
|
||||
|
||||
ROS2 Service req/resp 演示包(Python)。属于 Level 1 基础机制第 3 块。
|
||||
|
||||
## 功能
|
||||
|
||||
- **`add_two_ints_server`**: 接收 `AddTwoInts` 请求,返回 `a + b`
|
||||
- **`add_two_ints_client`**: 异步发请求 + 等响应
|
||||
|
||||
## 关键概念
|
||||
|
||||
| 概念 | 用途 |
|
||||
|---|---|
|
||||
| `Service` / `Client` | 同步请求-响应 |
|
||||
| `wait_for_service` / `service_is_ready` | 等待 server 注册 |
|
||||
| `call_async` + `spin_until_future_complete` | 异步发请求 |
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
# 终端 1:启 server
|
||||
ros2 launch py_srv srv_launch.py
|
||||
|
||||
# 终端 2:CLI 调用
|
||||
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 12, b: 30}"
|
||||
# 预期: sum: 42
|
||||
|
||||
# 终端 2 备选:启 client 节点(从参数读 a/b)
|
||||
ros2 run py_srv add_two_ints_client --ros-args -p a:=12 -p b:=30
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
colcon test --packages-select py_srv
|
||||
```
|
||||
|
||||
测试覆盖:
|
||||
|
||||
| 文件 | 用例数 | 内容 |
|
||||
|---|---|---|
|
||||
| `test_srv_server.py` | 5 | 节点名 + 参数 + 回调(正/负/零) |
|
||||
| `test_srv_client.py` | 1 | 同进程 client 调用 server(12+30=42) |
|
||||
| **总计** | **6** | **目标 6/6 100% 通过** |
|
||||
|
||||
## 深度学习
|
||||
|
||||
- 编程规范:[`doc/CODING_STYLE.md`](../doc/CODING_STYLE.md)
|
||||
- Service 深度:[`doc/30-services.md`](../doc/30-services.md)
|
||||
@@ -1,23 +1,21 @@
|
||||
"""
|
||||
srv_launch.py —— 启动 add_two_ints 服务端的 launch 文件。
|
||||
|
||||
注意:client 不在这里启动,因它是"一次性调用",通常在终端或脚本里跑。
|
||||
client 用法:
|
||||
source install/setup.bash
|
||||
ros2 run py_srv add_two_ints_client 1 2 # 默认参数
|
||||
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 3, b: 5}"
|
||||
"""
|
||||
|
||||
"""srv_launch.py - 启动 add_two_ints_server。"""
|
||||
from launch import LaunchDescription
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
return LaunchDescription([
|
||||
Node(
|
||||
package='py_srv',
|
||||
executable='add_two_ints_server',
|
||||
name='add_two_ints_server_py',
|
||||
output='screen',
|
||||
),
|
||||
])
|
||||
def generate_launch_description() -> LaunchDescription:
|
||||
"""生成启动描述:AddTwoIntsServer。"""
|
||||
service_name_arg = LaunchConfiguration('service_name')
|
||||
|
||||
server_node = Node(
|
||||
package='py_srv',
|
||||
executable='add_two_ints_server',
|
||||
name='add_two_ints_server',
|
||||
output='screen',
|
||||
parameters=[{
|
||||
'service_name': service_name_arg,
|
||||
}],
|
||||
)
|
||||
|
||||
return LaunchDescription([server_node])
|
||||
@@ -1,13 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model
|
||||
href="http://download.ros.org/schema/package_format3.xsd"
|
||||
schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
href="http://download.ros.org/schema/package_format3.xsd"
|
||||
schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>py_srv</name>
|
||||
<version>0.1.0</version>
|
||||
<description>Python service demo: AddTwoInts server + client</description>
|
||||
<description>
|
||||
ROS2 Service req/resp 演示包(Python)。
|
||||
演示 AddTwoIntsServer + AddTwoIntClient,验证同步请求-响应。
|
||||
属于 Level 1 基础机制第 3 块。
|
||||
</description>
|
||||
<maintainer email="dev@example.com">xs</maintainer>
|
||||
<license>Apache-2.0</license>
|
||||
<license>MIT</license>
|
||||
|
||||
<depend>rclpy</depend>
|
||||
<depend>example_interfaces</depend>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
"""py_srv - ROS2 Service req/resp 演示包。
|
||||
|
||||
属于 Level 1 基础机制第 3 块:Service 同步请求-响应。
|
||||
|
||||
演示内容:
|
||||
- AddTwoIntsServer: 提供 a + b 同步求和服务
|
||||
- AddTwoIntsClient: 客户端发请求,接响应(可重试)
|
||||
|
||||
参考:
|
||||
- ROS2 设计稿 https://design.ros2.org/articles/topic_and_service.html
|
||||
- 编程规范 doc/CODING_STYLE.md
|
||||
- 深度文档 doc/30-services.md
|
||||
"""
|
||||
@@ -1,63 +1,128 @@
|
||||
"""
|
||||
add_two_ints_client.py —— ROS2 Python 服务端调用方(Service Client)。
|
||||
"""add_two_ints_client - 同步求和服务客户端。
|
||||
|
||||
Client API:
|
||||
Client = node.create_client(srv_type, srv_name):
|
||||
创建一个"客户端",绑定到指定 srv_name 的服务端。
|
||||
client.wait_for_service(timeout_sec=...) ->
|
||||
阻塞等服务端上线;ROS2 网络发现需要 1-2s。
|
||||
request = srv_type.Request():
|
||||
构造请求对象,填写字段。
|
||||
future = client.call_async(request) -> Future:
|
||||
异步调用,返回 Future;不阻塞主线程。
|
||||
rclpy.spin_until_future_complete(node, future, timeout_sec=...):
|
||||
在节点事件循环里等 future 完成(或超时),取回 response。
|
||||
"""
|
||||
设计思想:
|
||||
ROS2 Client 用异步 future + spin_until_future_complete 等结果。
|
||||
wait_for_service 必须轮询(不能在 __init__ 阻塞 — 死锁)。
|
||||
|
||||
import sys
|
||||
参考:
|
||||
- ROS2 Humble Tutorial https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Service-And-Client.html
|
||||
"""
|
||||
import time
|
||||
from typing import List, Optional
|
||||
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
from rclpy.node import Node
|
||||
|
||||
|
||||
class AddTwoIntsClient(Node):
|
||||
"""同步求和服务客户端(只发一次请求)。
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('add_two_ints_client_py')
|
||||
self.client = self.create_client(AddTwoInts, 'add_two_ints')
|
||||
# wait_for_service:阻塞到服务端上线或超时(秒)。
|
||||
# 这里给 5 秒,通常 1-2s 内 ROS2 完成 DDS discovery。
|
||||
while not self.client.wait_for_service(timeout_sec=1.0):
|
||||
self.get_logger().info('waiting for add_two_ints service...')
|
||||
Attributes:
|
||||
_client: rclpy.Client 实例。
|
||||
"""
|
||||
|
||||
def call(self, a, b):
|
||||
req = AddTwoInts.Request()
|
||||
req.a = a
|
||||
req.b = b
|
||||
# call_async 返回 Future,由 spin_until_future_complete 触发回调。
|
||||
future = self.client.call_async(req)
|
||||
# spin_until_future_complete:在节点事件循环里"短轮询"等到 future 完成。
|
||||
rclpy.spin_until_future_complete(self, future, timeout_sec=5.0)
|
||||
if future.result() is not None:
|
||||
return future.result().sum
|
||||
self.get_logger().error('service call failed')
|
||||
return None
|
||||
DEFAULT_SERVICE_NAME: str = 'add_two_ints'
|
||||
DEFAULT_A: int = 1
|
||||
DEFAULT_B: int = 2
|
||||
|
||||
def __init__(self, *, node_name: str = 'add_two_ints_client') -> None:
|
||||
"""初始化客户端节点。
|
||||
|
||||
Args:
|
||||
node_name: ROS2 节点名,默认 `add_two_ints_client`。
|
||||
"""
|
||||
super().__init__(node_name)
|
||||
|
||||
self.declare_parameter(
|
||||
'service_name', self.DEFAULT_SERVICE_NAME,
|
||||
descriptor='要调用的服务名',
|
||||
)
|
||||
self.declare_parameter(
|
||||
'a', self.DEFAULT_A,
|
||||
descriptor='第一个加数',
|
||||
)
|
||||
self.declare_parameter(
|
||||
'b', self.DEFAULT_B,
|
||||
descriptor='第二个加数',
|
||||
)
|
||||
|
||||
service_name: str = self.get_parameter('service_name').value
|
||||
|
||||
self._client = self.create_client(AddTwoInts, service_name)
|
||||
|
||||
self.get_logger().info(
|
||||
f'AddTwoIntsClient created: service="{service_name}"',
|
||||
)
|
||||
|
||||
def wait_for_service_ready(self, timeout_sec: float = 5.0) -> bool:
|
||||
"""轮询等待服务可用。
|
||||
|
||||
Args:
|
||||
timeout_sec: 超时秒数。
|
||||
|
||||
Returns:
|
||||
True 服务可用 / False 超时。
|
||||
"""
|
||||
deadline = time.time() + timeout_sec
|
||||
while time.time() < deadline:
|
||||
if self._client.service_is_ready():
|
||||
return True
|
||||
time.sleep(0.05)
|
||||
return False
|
||||
|
||||
def call_once(self, a: int, b: int, timeout_sec: float = 5.0) -> Optional[int]:
|
||||
"""异步发请求 + 等结果。
|
||||
|
||||
Args:
|
||||
a: 第一个加数。
|
||||
b: 第二个加数。
|
||||
timeout_sec: 等结果超时秒数。
|
||||
|
||||
Returns:
|
||||
响应 sum;失败/超时返回 None。
|
||||
"""
|
||||
if not self.wait_for_service_ready(timeout_sec=timeout_sec):
|
||||
self.get_logger().warn('service not available')
|
||||
return None
|
||||
|
||||
request = AddTwoInts.Request()
|
||||
request.a = a
|
||||
request.b = b
|
||||
|
||||
future = self._client.call_async(request)
|
||||
rclpy.spin_until_future_complete(self, future, timeout_sec=timeout_sec)
|
||||
|
||||
if not future.done():
|
||||
self.get_logger().warn('call timed out')
|
||||
return None
|
||||
|
||||
result = future.result()
|
||||
if result is None:
|
||||
self.get_logger().warn('call failed')
|
||||
return None
|
||||
|
||||
self.get_logger().info(f'result: {a} + {b} = {result.sum}')
|
||||
return result.sum
|
||||
|
||||
|
||||
def main(args=None):
|
||||
def main(args: Optional[List[str]] = None) -> None:
|
||||
"""ROS2 节点入口:从参数读 a/b,调一次服务,打印结果,退出。"""
|
||||
rclpy.init(args=args)
|
||||
node = AddTwoIntsClient()
|
||||
|
||||
# 从命令行参数读取两个整数;默认 1 + 2。
|
||||
a = int(sys.argv[1]) if len(sys.argv) > 1 else 1
|
||||
b = int(sys.argv[2]) if len(sys.argv) > 2 else 2
|
||||
result = node.call(a, b)
|
||||
if result is not None:
|
||||
node.get_logger().info(f'result: {a} + {b} = {result}')
|
||||
print(f'RESULT: {a} + {b} = {result}')
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
node: Optional[AddTwoIntsClient] = None
|
||||
try:
|
||||
node = AddTwoIntsClient()
|
||||
a = node.get_parameter('a').value
|
||||
b = node.get_parameter('b').value
|
||||
result = node.call_once(a, b)
|
||||
if result is None:
|
||||
node.get_logger().error('call failed')
|
||||
except KeyboardInterrupt:
|
||||
if node is not None:
|
||||
node.get_logger().info('KeyboardInterrupt → 退出')
|
||||
finally:
|
||||
if rclpy.ok():
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,56 +1,98 @@
|
||||
"""
|
||||
add_two_ints_server.py —— ROS2 Python 服务端(Service Server)。
|
||||
"""add_two_ints_server - 同步求和服务端。
|
||||
|
||||
ROS2 Service 是另一种通信模式,与 Topic 的区别:
|
||||
Topic — 多对多、无连接、异步、单向(pub/sub)。
|
||||
Service — 一对一、有连接、同步(可异步)、双向(request/response)。
|
||||
适合"一次性调用 + 等结果"的场景,如拍照、开关机械臂、计算查询等。
|
||||
设计思想:
|
||||
ROS2 Service 是同步、一对一、双向的请求-响应。
|
||||
本节点演示:
|
||||
1. create_service 注册回调
|
||||
2. 回调签名 cb(request, response) -> response
|
||||
3. callback 内 try/except + 日志
|
||||
4. 计数器统计已处理请求数
|
||||
|
||||
关键 API:
|
||||
Service[T_Request, T_Response]:
|
||||
节点上的"服务句柄",由 create_service(...) 构造。
|
||||
T_Request / T_Response:由 .srv 文件自动生成的 Python 类型;
|
||||
本 demo 用 example_interfaces/srv/AddTwoInts,
|
||||
含 a, b(int64) 与 sum(int64) 三个字段。
|
||||
回调签名:
|
||||
handler(req, response) -> response
|
||||
handler 内部填 response 字段,ROS2 把它发回给 client。
|
||||
参考:
|
||||
- ROS2 Humble Tutorial https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Service-And-Client.html
|
||||
"""
|
||||
from typing import List, Optional
|
||||
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
from rclpy.node import Node
|
||||
from rclpy.service import Service
|
||||
|
||||
|
||||
class AddTwoIntsServer(Node):
|
||||
"""提供 a + b 求和的同步服务。
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('add_two_ints_server_py')
|
||||
# create_service(srv_type, srv_name, callback):
|
||||
# - srv_type:服务接口类(本例 AddTwoInts);
|
||||
# - srv_name:客户端调用时使用的服务名;
|
||||
# - callback:收到请求时由 ROS2 事件循环调用,
|
||||
# 返回值就是发回 client 的 response。
|
||||
self.srv = self.create_service(
|
||||
AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
|
||||
self.get_logger().info('add_two_ints_server_py ready, waiting for requests...')
|
||||
Attributes:
|
||||
service_: AddTwoInts 服务句柄。
|
||||
_handled_count: 已处理请求数(下划线=内部状态)。
|
||||
"""
|
||||
|
||||
def add_two_ints_callback(self, request, response):
|
||||
# request.a / request.b 是 AddTwoInts.Request 的 int64 字段。
|
||||
response.sum = request.a + request.b
|
||||
self.get_logger().info(f'incoming: a={request.a}, b={request.b} -> sum={response.sum}')
|
||||
DEFAULT_SERVICE_NAME: str = 'add_two_ints'
|
||||
|
||||
def __init__(self, *, node_name: str = 'add_two_ints_server') -> None:
|
||||
"""初始化服务端节点。
|
||||
|
||||
Args:
|
||||
node_name: ROS2 节点名,默认 `add_two_ints_server`。
|
||||
"""
|
||||
super().__init__(node_name)
|
||||
|
||||
self.declare_parameter(
|
||||
'service_name', self.DEFAULT_SERVICE_NAME,
|
||||
descriptor='服务名(字符串)',
|
||||
)
|
||||
|
||||
service_name: str = self.get_parameter('service_name').value
|
||||
|
||||
self.service_: Service = self.create_service(
|
||||
AddTwoInts, service_name, self._handle_request,
|
||||
)
|
||||
|
||||
self._handled_count: int = 0
|
||||
|
||||
self.get_logger().info(
|
||||
f'AddTwoIntsServer ready: service="{service_name}"',
|
||||
)
|
||||
|
||||
def _handle_request(
|
||||
self,
|
||||
request: AddTwoInts.Request,
|
||||
response: AddTwoInts.Response,
|
||||
) -> AddTwoInts.Response:
|
||||
"""处理客户端请求:a + b。
|
||||
|
||||
Args:
|
||||
request: 包含 a、b 两个 int64 字段的请求。
|
||||
response: 包含 sum 字段的响应(原地修改,必须返回)。
|
||||
|
||||
Returns:
|
||||
填好 sum 字段的 response。
|
||||
"""
|
||||
try:
|
||||
response.sum = request.a + request.b
|
||||
self._handled_count += 1
|
||||
self.get_logger().info(
|
||||
f'request #{self._handled_count}: {request.a} + {request.b} = {response.sum}',
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self.get_logger().error(f'handle_request failed: {exc}', exc_info=True)
|
||||
response.sum = 0
|
||||
return response
|
||||
|
||||
|
||||
def main(args=None):
|
||||
def main(args: Optional[List[str]] = None) -> None:
|
||||
"""ROS2 节点入口。"""
|
||||
rclpy.init(args=args)
|
||||
node = AddTwoIntsServer()
|
||||
node: Optional[AddTwoIntsServer] = None
|
||||
try:
|
||||
node = AddTwoIntsServer()
|
||||
rclpy.spin(node)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
if node is not None:
|
||||
node.get_logger().info('KeyboardInterrupt → 退出')
|
||||
finally:
|
||||
if rclpy.ok():
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+12
-9
@@ -1,23 +1,26 @@
|
||||
from setuptools import find_packages, setup
|
||||
import os
|
||||
from glob import glob
|
||||
|
||||
package_name = 'py_srv'
|
||||
from setuptools import setup
|
||||
|
||||
PACKAGE_NAME = 'py_srv'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
name=PACKAGE_NAME,
|
||||
version='0.1.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
packages=[PACKAGE_NAME],
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
('share/' + package_name + '/launch', ['launch/srv_launch.py']),
|
||||
['resource/' + PACKAGE_NAME]),
|
||||
('share/' + PACKAGE_NAME, ['package.xml']),
|
||||
(os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*.py')),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='xs',
|
||||
maintainer_email='dev@example.com',
|
||||
description='Python service demo: AddTwoInts server + client',
|
||||
license='Apache-2.0',
|
||||
description='ROS2 Service req/resp 演示 - Level 1 基础机制第 3 块',
|
||||
license='MIT',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""pytest 共享 fixtures - py_srv 测试。"""
|
||||
from typing import Iterator
|
||||
|
||||
import pytest
|
||||
import rclpy
|
||||
|
||||
from py_srv.add_two_ints_client import AddTwoIntsClient
|
||||
from py_srv.add_two_ints_server import AddTwoIntsServer
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def ros_context() -> Iterator[None]:
|
||||
"""session 级 rclpy 上下文。"""
|
||||
rclpy.init()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if rclpy.ok():
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def server(ros_context: None) -> Iterator[AddTwoIntsServer]:
|
||||
"""每个测试一个独立 server 实例。"""
|
||||
node = AddTwoIntsServer()
|
||||
try:
|
||||
yield node
|
||||
finally:
|
||||
node.destroy_node()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(ros_context: None) -> Iterator[AddTwoIntsClient]:
|
||||
"""每个测试一个独立 client 实例。"""
|
||||
node = AddTwoIntsClient()
|
||||
try:
|
||||
yield node
|
||||
finally:
|
||||
node.destroy_node()
|
||||
@@ -0,0 +1,43 @@
|
||||
"""测试 AddTwoIntsServiceClient 客户端调用服务端(同进程)。"""
|
||||
import time
|
||||
|
||||
import rclpy
|
||||
import pytest
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
|
||||
from py_srv.add_two_ints_server import AddTwoIntsServer
|
||||
from py_srv.add_two_ints_client import AddTwoIntsClient
|
||||
|
||||
|
||||
def test_client_calls_server_inproc(ros_context: None) -> None:
|
||||
"""同进程 spin:server + client,验证 12+30=42。"""
|
||||
server = AddTwoIntsServer()
|
||||
client = AddTwoIntsClient()
|
||||
|
||||
executor = rclpy.executors.SingleThreadedExecutor()
|
||||
executor.add_node(server)
|
||||
executor.add_node(client)
|
||||
|
||||
# 触发 client 调用
|
||||
result_future_container: list = []
|
||||
|
||||
def call_in_thread() -> None:
|
||||
# 给点时间让 server 注册(同进程也需要一点 spin 时间)
|
||||
deadline = time.time() + 2.0
|
||||
while time.time() < deadline:
|
||||
executor.spin_once(timeout_sec=0.05)
|
||||
if client._client.service_is_ready():
|
||||
break
|
||||
result = client.call_once(12, 30, timeout_sec=3.0)
|
||||
result_future_container.append(result)
|
||||
|
||||
import threading
|
||||
t = threading.Thread(target=call_in_thread)
|
||||
t.start()
|
||||
t.join(timeout=5.0)
|
||||
|
||||
server.destroy_node()
|
||||
client.destroy_node()
|
||||
|
||||
assert len(result_future_container) == 1
|
||||
assert result_future_container[0] == 42
|
||||
@@ -0,0 +1,45 @@
|
||||
"""测试 AddTwoIntsServiceRequest 服务端处理逻辑。"""
|
||||
from py_srv.add_two_ints_server import AddTwoIntsServer
|
||||
|
||||
|
||||
def test_server_node_name(server: AddTwoIntsServer) -> None:
|
||||
"""默认节点名正确。"""
|
||||
assert server.get_name() == 'add_two_ints_server'
|
||||
|
||||
|
||||
def test_server_service_name(server: AddTwoIntsServer) -> None:
|
||||
"""默认 service_name 参数正确。"""
|
||||
assert server.get_parameter('service_name').value == 'add_two_ints'
|
||||
|
||||
|
||||
def test_server_handle_positive(server: AddTwoIntsServer) -> None:
|
||||
"""正数加法正确。"""
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
req = AddTwoInts.Request()
|
||||
req.a = 12
|
||||
req.b = 30
|
||||
resp = AddTwoInts.Response()
|
||||
out = server._handle_request(req, resp)
|
||||
assert out.sum == 42
|
||||
|
||||
|
||||
def test_server_handle_negative(server: AddTwoIntsServer) -> None:
|
||||
"""负数加法正确。"""
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
req = AddTwoInts.Request()
|
||||
req.a = -5
|
||||
req.b = 3
|
||||
resp = AddTwoInts.Response()
|
||||
out = server._handle_request(req, resp)
|
||||
assert out.sum == -2
|
||||
|
||||
|
||||
def test_server_handle_zero(server: AddTwoIntsServer) -> None:
|
||||
"""零加法正确。"""
|
||||
from example_interfaces.srv import AddTwoInts
|
||||
req = AddTwoInts.Request()
|
||||
req.a = 0
|
||||
req.b = 0
|
||||
resp = AddTwoInts.Response()
|
||||
out = server._handle_request(req, resp)
|
||||
assert out.sum == 0
|
||||
Reference in New Issue
Block a user