145 lines
4.3 KiB
Python
145 lines
4.3 KiB
Python
"""高层 MQTT 控制测试 (用 unittest.mock 替换 paho.mqtt, 不需要真实 broker)."""
|
|
import struct
|
|
from unittest.mock import MagicMock, patch
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_mqtt():
|
|
"""打补丁让 Go1MQTT 用 mock client, 不实际连接."""
|
|
with patch('paho.mqtt.client.Client') as MockClient:
|
|
instance = MagicMock()
|
|
MockClient.return_value = instance
|
|
yield instance
|
|
|
|
|
|
def test_velocity_default():
|
|
from go1_pro_sdk import Velocity
|
|
v = Velocity()
|
|
assert v.vx == 0.0
|
|
assert v.vy == 0.0
|
|
assert v.vz == 0.0
|
|
|
|
|
|
def test_pose_named_arg():
|
|
from go1_pro_sdk import Pose
|
|
p = Pose(lean_left_right=0.5)
|
|
assert p.lean_left_right == 0.5
|
|
assert p.twist_left_right == 0.0
|
|
|
|
|
|
def test_mode_enum_string():
|
|
from go1_pro_sdk import Mode
|
|
assert Mode.STAND_UP.value == 'standUp'
|
|
assert Mode.DANCE_1.value == 'dance1'
|
|
assert Mode.DAMPING.value == 'damping'
|
|
|
|
|
|
def test_go1mqtt_connect_and_disconnect(mock_mqtt):
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT(host='localhost', port=1883)
|
|
mock_mqtt.connect.assert_called_once_with('localhost', 1883, 60)
|
|
mock_mqtt.loop_start.assert_called_once()
|
|
|
|
client.disconnect()
|
|
mock_mqtt.loop_stop.assert_called()
|
|
mock_mqtt.disconnect.assert_called()
|
|
|
|
|
|
def test_switch_mode_publishes_to_action(mock_mqtt):
|
|
from go1_pro_sdk import Mode
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.switch_mode(Mode.STAND_UP)
|
|
|
|
# 应该发送 controller/action = "standUp"
|
|
mock_mqtt.publish.assert_called_with('controller/action', 'standUp', qos=1)
|
|
|
|
|
|
def test_switch_mode_string_also_works(mock_mqtt):
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
client.switch_mode('walk')
|
|
mock_mqtt.publish.assert_called_with('controller/action', 'walk', qos=1)
|
|
|
|
|
|
def test_send_cmd_vel_publishes_4_floats(mock_mqtt):
|
|
from go1_pro_sdk import Velocity
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.send_cmd_vel(Velocity(vx=0.3, vy=0.1, vz=-0.2))
|
|
|
|
# 应该有 2 次 publish: 先归零再发实际
|
|
assert mock_mqtt.publish.call_count >= 2
|
|
last_call = mock_mqtt.publish.call_args_list[-1]
|
|
topic, payload = last_call.args[0], last_call.args[1]
|
|
assert topic == 'controller/stick'
|
|
# 协议: (vy, vz, 0, vx) — unitree 反直觉的顺序
|
|
vy, vz, _, vx = struct.unpack('ffff', payload)
|
|
assert abs(vy - 0.1) < 1e-5
|
|
assert abs(vz - (-0.2)) < 1e-5
|
|
assert abs(vx - 0.3) < 1e-5
|
|
|
|
|
|
def test_send_cmd_vel_clips_to_1(mock_mqtt):
|
|
from go1_pro_sdk import Velocity
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.send_cmd_vel(Velocity(vx=5.0, vy=-3.0, vz=0.0)) # 超界
|
|
last_payload = mock_mqtt.publish.call_args_list[-1].args[1]
|
|
vy, vz, _, vx = struct.unpack('ffff', last_payload)
|
|
assert vx == 1.0
|
|
assert vy == -1.0
|
|
|
|
|
|
def test_send_cmd_pose(mock_mqtt):
|
|
from go1_pro_sdk import Pose
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.send_cmd_pose(Pose(lean_left_right=0.3, twist_left_right=0.0,
|
|
look_up_down=0.2, extend_squat=-0.5))
|
|
payload = mock_mqtt.publish.call_args.args[1]
|
|
ll, tw, lu, es = struct.unpack('ffff', payload)
|
|
assert abs(ll - 0.3) < 1e-5
|
|
assert abs(lu - 0.2) < 1e-5
|
|
assert abs(es - (-0.5)) < 1e-5
|
|
|
|
|
|
def test_set_led_publishes_3_bytes(mock_mqtt):
|
|
from go1_pro_sdk import LED
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.set_led(LED(255, 128, 0))
|
|
last_call = mock_mqtt.publish.call_args
|
|
assert last_call.args[0] == 'face_light/color'
|
|
assert last_call.args[1] == bytes([255, 128, 0])
|
|
|
|
|
|
def test_set_led_clips_0_255(mock_mqtt):
|
|
from go1_pro_sdk import LED
|
|
from go1_pro_sdk.highlevel import Go1MQTT
|
|
client = Go1MQTT()
|
|
|
|
client.set_led(LED(300, -10, 100))
|
|
assert mock_mqtt.publish.call_args.args[1] == bytes([255, 0, 100])
|
|
|
|
|
|
def test_go1_high_level_api_calls_mqtt(mock_mqtt):
|
|
"""Go1 类的方法应该都委托给 Go1MQTT."""
|
|
from go1_pro_sdk import Go1, Velocity, LED
|
|
|
|
with Go1() as dog:
|
|
dog.stand_up()
|
|
dog.dance_1()
|
|
dog.walk(Velocity(vx=0.5))
|
|
dog.set_led(LED(0, 255, 0))
|
|
dog.stand_down()
|
|
|
|
# 应该有多次 publish
|
|
assert mock_mqtt.publish.call_count >= 5
|