Skip to content

Commit 2b258e8

Browse files
committed
Add rosbridge_server tests for action feedback and goal sending
1 parent 20d8212 commit 2b258e8

File tree

7 files changed

+174
-13
lines changed

7 files changed

+174
-13
lines changed

rosbridge_library/src/rosbridge_library/capabilities/send_action_goal.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ def cancel_action_goal(self, message):
133133
cid = message.get("id", None)
134134
action = message["action"]
135135

136+
# Typecheck the args
137+
self.basic_type_check(message, self.cancel_action_goal_msg_fields)
138+
136139
# Pull out the ID
137140
# Check for deprecated action ID, eg. /rosbridge/topics#33
138141
cid = extract_id(action, cid)

rosbridge_library/src/rosbridge_library/capability.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ def basic_type_check(self, msg, types_info):
9494
"""
9595
for mandatory, fieldname, fieldtypes in types_info:
9696
if mandatory and fieldname not in msg:
97-
raise MissingArgumentException(
98-
"Expected a %s field but none was found." % fieldname
99-
)
97+
raise MissingArgumentException(f"Expected a {fieldname} field but none was found.")
10098
elif fieldname in msg:
10199
if not isinstance(fieldtypes, tuple):
102100
fieldtypes = (fieldtypes,)

rosbridge_library/test/capabilities/test_action_capabilities.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def test_execute_advertised_action(self):
157157
"op": "action_feedback",
158158
"action": action_path,
159159
"id": self.received_message["id"],
160-
"values": {"sequence": [1, 1, 2]},
160+
"values": {"sequence": [0, 1, 1]},
161161
}
162162
)
163163
)
@@ -170,7 +170,7 @@ def test_execute_advertised_action(self):
170170
self.fail("Timed out waiting for action feedback message.")
171171

172172
self.assertIsNotNone(self.latest_feedback)
173-
self.assertEqual(list(self.latest_feedback.feedback.sequence), [1, 1, 2])
173+
self.assertEqual(list(self.latest_feedback.feedback.sequence), [0, 1, 1])
174174

175175
# Now send the result
176176
result_msg = loads(
@@ -179,7 +179,7 @@ def test_execute_advertised_action(self):
179179
"op": "action_result",
180180
"action": action_path,
181181
"id": self.received_message["id"],
182-
"values": {"sequence": [1, 1, 2, 3, 5]},
182+
"values": {"sequence": [0, 1, 1, 2, 3, 5]},
183183
"result": True,
184184
}
185185
)
@@ -196,7 +196,7 @@ def test_execute_advertised_action(self):
196196

197197
self.assertIsNotNone(self.received_message)
198198
self.assertEqual(self.received_message["op"], "action_result")
199-
self.assertEqual(self.received_message["values"]["result"]["sequence"], [1, 1, 2, 3, 5])
199+
self.assertEqual(self.received_message["values"]["result"]["sequence"], [0, 1, 1, 2, 3, 5])
200200

201201
def test_cancel_advertised_action(self):
202202
# Advertise the action
@@ -218,7 +218,7 @@ def test_cancel_advertised_action(self):
218218
goal_msg = loads(
219219
dumps(
220220
{
221-
"op": "call_service",
221+
"op": "send_action_goal",
222222
"id": "foo",
223223
"action": action_path,
224224
"action_type": "example_interfaces/Fibonacci",
@@ -286,7 +286,7 @@ def test_unadvertise_action(self):
286286
goal_msg = loads(
287287
dumps(
288288
{
289-
"op": "call_service",
289+
"op": "send_action_goal",
290290
"id": "foo",
291291
"action": action_path,
292292
"action_type": "example_interfaces/Fibonacci",

rosbridge_server/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ install(FILES
2424
if(BUILD_TESTING)
2525
find_package(launch_testing_ament_cmake REQUIRED)
2626
add_launch_test(test/websocket/advertise_action.test.py)
27+
add_launch_test(test/websocket/advertise_action_feedback.test.py)
2728
add_launch_test(test/websocket/advertise_service.test.py)
2829
add_launch_test(test/websocket/call_service.test.py)
30+
add_launch_test(test/websocket/send_action_goal.test.py)
2931
add_launch_test(test/websocket/smoke.test.py)
3032
add_launch_test(test/websocket/transient_local_publisher.test.py)
3133
add_launch_test(test/websocket/best_effort_publisher.test.py)

rosbridge_server/test/websocket/advertise_action.test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ async def test_two_concurrent_calls(self, node: Node, make_client):
6767
{
6868
"op": "action_result",
6969
"action": "/test_fibonacci_action",
70-
"values": {"sequence": [1, 1, 2]},
70+
"values": {"sequence": [0, 1, 1, 2]},
7171
"id": requests[0]["id"],
7272
"result": True,
7373
}
@@ -81,15 +81,15 @@ async def test_two_concurrent_calls(self, node: Node, make_client):
8181
{
8282
"op": "action_result",
8383
"action": "/test_fibonacci_action",
84-
"values": {"sequence": [1, 1, 2, 3, 5]},
84+
"values": {"sequence": [0, 1, 1, 2, 3, 5]},
8585
"id": requests[1]["id"],
8686
"result": True,
8787
}
8888
)
8989

9090
result1 = await self.goal1_result_future
91-
self.assertEqual(result1.result, Fibonacci.Result(sequence=[1, 1, 2]))
91+
self.assertEqual(result1.result, Fibonacci.Result(sequence=[0, 1, 1, 2]))
9292
result2 = await self.goal2_result_future
93-
self.assertEqual(result2.result, Fibonacci.Result(sequence=[1, 1, 2, 3, 5]))
93+
self.assertEqual(result2.result, Fibonacci.Result(sequence=[0, 1, 1, 2, 3, 5]))
9494

9595
node.destroy_client(client)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
import unittest
5+
6+
from example_interfaces.action import Fibonacci
7+
from rclpy.action import ActionClient
8+
from rclpy.node import Node
9+
from twisted.python import log
10+
11+
sys.path.append(os.path.dirname(__file__)) # enable importing from common.py in this directory
12+
13+
import common # noqa: E402
14+
from common import expect_messages, websocket_test # noqa: E402
15+
16+
log.startLogging(sys.stderr)
17+
18+
generate_test_description = common.generate_test_description
19+
20+
21+
class TestActionFeedback(unittest.TestCase):
22+
def goal_response_callback(self, future):
23+
goal_handle = future.result()
24+
if not goal_handle.accepted:
25+
return
26+
self.goal_result_future = goal_handle.get_result_async()
27+
28+
def feedback_callback(self, msg):
29+
self.latest_feedback = msg
30+
31+
@websocket_test
32+
async def test_feedback(self, node: Node, make_client):
33+
ws_client = await make_client()
34+
ws_client.sendJson(
35+
{
36+
"op": "advertise_action",
37+
"action": "/test_fibonacci_action",
38+
"type": "example_interfaces/Fibonacci",
39+
}
40+
)
41+
client = ActionClient(node, Fibonacci, "/test_fibonacci_action")
42+
client.wait_for_server()
43+
44+
requests_future, ws_client.message_handler = expect_messages(
45+
1, "WebSocket", node.get_logger()
46+
)
47+
requests_future.add_done_callback(lambda _: node.executor.wake())
48+
49+
self.goal_result_future = None
50+
goal_future = client.send_goal_async(
51+
Fibonacci.Goal(order=5),
52+
feedback_callback=self.feedback_callback,
53+
)
54+
goal_future.add_done_callback(self.goal_response_callback)
55+
56+
requests = await requests_future
57+
58+
self.assertEqual(requests[0]["op"], "send_action_goal")
59+
self.assertEqual(requests[0]["action"], "/test_fibonacci_action")
60+
self.assertEqual(requests[0]["action_type"], "example_interfaces/Fibonacci")
61+
self.assertEqual(requests[0]["args"], {"order": 5})
62+
63+
self.latest_feedback = None
64+
ws_client.sendJson(
65+
{
66+
"op": "action_feedback",
67+
"action": "/test_fibonacci_action",
68+
"values": {"sequence": [0, 1, 1, 2]},
69+
"id": requests[0]["id"],
70+
}
71+
)
72+
ws_client.sendJson(
73+
{
74+
"op": "action_result",
75+
"action": "/test_fibonacci_action",
76+
"values": {"sequence": [0, 1, 1, 2, 3, 5]},
77+
"id": requests[0]["id"],
78+
"result": True,
79+
}
80+
)
81+
82+
result = await self.goal_result_future
83+
self.assertIsNotNone(self.latest_feedback)
84+
self.assertEqual(self.latest_feedback.feedback, Fibonacci.Feedback(sequence=[0, 1, 1, 2]))
85+
self.assertEqual(result.result, Fibonacci.Result(sequence=[0, 1, 1, 2, 3, 5]))
86+
87+
node.destroy_client(client)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
import time
5+
import unittest
6+
7+
from example_interfaces.action import Fibonacci
8+
from rclpy.action import ActionServer
9+
from rclpy.callback_groups import ReentrantCallbackGroup
10+
from rclpy.node import Node
11+
from twisted.python import log
12+
13+
sys.path.append(os.path.dirname(__file__)) # enable importing from common.py in this directory
14+
15+
import common # noqa: E402
16+
from common import expect_messages, websocket_test # noqa: E402
17+
18+
log.startLogging(sys.stderr)
19+
20+
generate_test_description = common.generate_test_description
21+
22+
23+
class TestSendActionGoal(unittest.TestCase):
24+
def execute_callback(self, goal):
25+
feedback_msg = Fibonacci.Feedback()
26+
feedback_msg.sequence = [0, 1]
27+
28+
for i in range(1, goal.request.order):
29+
feedback_msg.sequence.append(feedback_msg.sequence[i] + feedback_msg.sequence[i - 1])
30+
goal.publish_feedback(feedback_msg)
31+
time.sleep(0.1)
32+
33+
goal.succeed()
34+
result = Fibonacci.Result()
35+
result.sequence = feedback_msg.sequence
36+
return result
37+
38+
@websocket_test
39+
async def test_one_call(self, node: Node, make_client):
40+
action_server = ActionServer(
41+
node,
42+
Fibonacci,
43+
"/test_fibonacci_action",
44+
self.execute_callback,
45+
callback_group=ReentrantCallbackGroup(),
46+
)
47+
48+
ws_client = await make_client()
49+
responses_future, ws_client.message_handler = expect_messages(
50+
1, "WebSocket", node.get_logger()
51+
)
52+
responses_future.add_done_callback(lambda _: node.executor.wake())
53+
54+
ws_client.sendJson(
55+
{
56+
"op": "send_action_goal",
57+
"action": "/test_fibonacci_action",
58+
"action_type": "example_interfaces/Fibonacci",
59+
"args": {"order": 5},
60+
"feedback": True,
61+
}
62+
)
63+
64+
responses = await responses_future
65+
self.assertEqual(len(responses), 1)
66+
self.assertEqual(responses[0]["op"], "action_result")
67+
self.assertEqual(responses[0]["action"], "/test_fibonacci_action")
68+
self.assertEqual(responses[0]["values"]["result"]["sequence"], [0, 1, 1, 2, 3, 5])
69+
self.assertEqual(responses[0]["result"], True)
70+
71+
action_server.destroy()

0 commit comments

Comments
 (0)