返回教程正文

配套源码

test_websocket_client.py

ros2/test/test_websocket_client.py
ros_gateway Brick:在 App Lab 中建立可靠的 WebSocket 通道ros2/test/test_websocket_client.py
Python113 行
  1. # SPDX-License-Identifier: MIT
  2. import json
  3. import unittest
  4. from unittest.mock import Mock
  5. from ventuno_app_bridge.websocket_client import WebSocketBridgeClient
  6. class TestWebSocketBridgeClient(unittest.TestCase):
  7. """验证 ROS 2 WebSocket 客户端的本地队列与协议编码。"""
  8. def setUp(self):
  9. """
  10. @description : 为每个测试创建相互隔离的客户端和回调记录器
  11. @param : 无参数
  12. @return : 无返回值
  13. """
  14. self.messages = []
  15. self.connection_changes = []
  16. self.logs = []
  17. self.client = WebSocketBridgeClient(
  18. websocket_url="ws://127.0.0.1:8765/ros",
  19. reconnect_interval=0.1,
  20. heartbeat_interval=1.0,
  21. command_timeout=0.3,
  22. message_callback=self.messages.append,
  23. connection_callback=self.connection_changes.append,
  24. log_callback=lambda level, message: self.logs.append((level, message)),
  25. )
  26. def test_latest_velocity_replaces_older_command(self):
  27. """
  28. @description : 验证速度队列仅保留最新命令并编码协议公共字段
  29. @param : 无参数
  30. @return : 无返回值
  31. """
  32. websocket = Mock()
  33. self.client.send_cmd_vel(0.1, 0.2, 0.3)
  34. self.client.send_cmd_vel(-0.4, 0.5, -0.6)
  35. self.client._send_latest_command(websocket)
  36. websocket.send.assert_called_once()
  37. message = json.loads(websocket.send.call_args.args[0])
  38. self.assertEqual(message["version"], 1)
  39. self.assertEqual(message["type"], "cmd_vel")
  40. self.assertEqual(message["seq"], 1)
  41. self.assertEqual(message["vx"], -0.4)
  42. self.assertEqual(message["vy"], 0.5)
  43. self.assertEqual(message["wz"], -0.6)
  44. def test_stale_velocity_is_not_sent(self):
  45. """
  46. @description : 验证超过本地时限的速度命令会被丢弃
  47. @param : 无参数
  48. @return : 无返回值
  49. """
  50. websocket = Mock()
  51. self.client._latest_command.put_nowait(
  52. {
  53. "timestamp_ms": self.client._now_ms() - 1000,
  54. "vx": 0.1,
  55. "vy": 0.0,
  56. "wz": 0.0,
  57. }
  58. )
  59. self.client._send_latest_command(websocket)
  60. websocket.send.assert_not_called()
  61. self.assertTrue(any("stale local cmd_vel" in item[1] for item in self.logs))
  62. def test_connection_callback_only_reports_changes(self):
  63. """
  64. @description : 验证连接状态回调不会重复发布相同状态
  65. @param : 无参数
  66. @return : 无返回值
  67. """
  68. self.client._set_connected(True)
  69. self.client._set_connected(True)
  70. self.client._set_connected(False)
  71. self.client._set_connected(False)
  72. self.assertEqual(self.connection_changes, [True, False])
  73. def test_mode_queue_is_bounded(self):
  74. """
  75. @description : 验证控制请求队列达到上限后拒绝继续增长
  76. @param : 无参数
  77. @return : 无返回值
  78. """
  79. results = [self.client.request_mode("ROS_TELEOP") for _ in range(9)]
  80. self.assertEqual(results, ([True] * 8) + [False])
  81. self.assertEqual(self.client._control_queue.qsize(), 8)
  82. def test_receive_requires_json_object(self):
  83. """
  84. @description : 验证服务端消息根节点必须是 JSON 对象
  85. @param : 无参数
  86. @return : 无返回值
  87. """
  88. websocket = Mock()
  89. websocket.recv.return_value = "[]"
  90. with self.assertRaisesRegex(RuntimeError, "JSON root"):
  91. self.client._receive_json(websocket, timeout=0.1)
  92. if __name__ == "__main__":
  93. unittest.main()