Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions tests/devices/__snapshots__/test_v1_device.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# serializer version: 1
# name: test_device_trait_command_parsing[payload0-status-StatusTrait-get_status]
S7MaxVStatus(msg_ver=2, msg_seq=515, state=<RoborockStateCode.charging: 8>, battery=100, clean_time=5405, clean_area=91287500, square_meter_clean_area=91.3, error_code=<RoborockErrorCode.none: 0>, map_present=1, in_cleaning=<RoborockInCleaning.complete: 0>, in_returning=0, in_fresh_state=1, lab_status=1, water_box_status=0, back_type=None, wash_phase=None, wash_ready=None, fan_power=<RoborockFanSpeedS7MaxV.custom: 106>, dnd_enabled=1, map_status=3, is_locating=0, lock_status=0, water_box_mode=<RoborockMopIntensityS7.custom: 204>, water_box_carriage_status=0, mop_forbidden_enable=0, camera_status=None, is_exploring=None, home_sec_status=None, home_sec_enable_password=None, adbumper_status=None, water_shortage_status=None, dock_type=None, dust_collection_status=None, auto_dust_collection=None, avoid_count=None, mop_mode=None, debug_mode=None, collision_avoid_status=None, switch_map_mode=None, dock_error_status=None, charge_status=None, unsave_map_reason=4, unsave_map_flag=0, wash_status=None, distance_off=0, in_warmup=None, dry_status=None, rdt=None, clean_percent=None, rss=None, dss=None, common_status=None, corner_clean_mode=None, error_code_name='none', state_name='charging', water_box_mode_name='custom', fan_power_options=['off', 'quiet', 'balanced', 'turbo', 'max', 'custom', 'max_plus'], fan_power_name='custom', mop_mode_name=None)
# ---
# name: test_device_trait_command_parsing[payload1-do_not_disturb-DoNotDisturbTrait-get_dnd_timer]
list([
dict({
'enabled': 1,
'end_hour': 8,
'end_minute': 0,
'start_hour': 22,
'start_minute': 0,
}),
])
# ---
# name: test_device_trait_command_parsing[payload2-clean_summary-CleanSummaryTrait-get_clean_summary]
CleanSummary(clean_time=1442559, clean_area=24258125000, square_meter_clean_area=24258.1, clean_count=296, dust_collection_count=None, records=[1756848207, 1754930385, 1753203976, 1752183435, 1747427370, 1746204046, 1745601543, 1744387080, 1743528522, 1742489154, 1741022299, 1740433682, 1739902516, 1738875106, 1738864366, 1738620067, 1736873889, 1736197544, 1736121269, 1734458038], last_clean_t=None)
# ---
# name: test_device_trait_command_parsing[payload3-sound_volume-SoundVolumeTrait-get_volume]
90
# ---
73 changes: 56 additions & 17 deletions tests/devices/test_v1_device.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
"""Tests for the Device class."""

import pathlib
from collections.abc import Awaitable, Callable
from unittest.mock import AsyncMock, Mock

import pytest
from syrupy import SnapshotAssertion

from roborock.containers import HomeData, S7MaxVStatus, UserData
from roborock.devices.device import RoborockDevice
from roborock.devices.traits.clean_summary import CleanSummaryTrait
from roborock.devices.traits.dnd import DoNotDisturbTrait
from roborock.devices.traits.sound_volume import SoundVolumeTrait
from roborock.devices.traits.status import StatusTrait
from roborock.devices.traits.trait import Trait
from roborock.devices.v1_rpc_channel import decode_rpc_response
from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol

from .. import mock_data

USER_DATA = UserData.from_dict(mock_data.USER_DATA)
HOME_DATA = HomeData.from_dict(mock_data.HOME_DATA_RAW)
STATUS = S7MaxVStatus.from_dict(mock_data.STATUS)

TESTDATA = pathlib.Path("tests/protocols/testdata/v1_protocol/")


@pytest.fixture(autouse=True, name="channel")
def device_channel_fixture() -> AsyncMock:
Expand Down Expand Up @@ -45,7 +55,10 @@ def traits_fixture(rpc_channel: AsyncMock) -> list[Trait]:
StatusTrait(
product_info=HOME_DATA.products[0],
rpc_channel=rpc_channel,
)
),
CleanSummaryTrait(rpc_channel=rpc_channel),
DoNotDisturbTrait(rpc_channel=rpc_channel),
SoundVolumeTrait(rpc_channel=rpc_channel),
]


Expand All @@ -70,19 +83,45 @@ async def test_device_connection(device: RoborockDevice, channel: AsyncMock) ->
assert unsub.called


async def test_device_get_status_command(device: RoborockDevice, rpc_channel: AsyncMock) -> None:
"""Test the device get_status command."""
# Mock response for get_status command
rpc_channel.send_command.return_value = [STATUS.as_dict()]

# Test get_status and verify the command was sent
status_api = device.traits["status"]
assert isinstance(status_api, StatusTrait)
assert status_api is not None
status = await status_api.get_status()
assert rpc_channel.send_command.called

# Verify the result
assert status is not None
assert status.battery == 100
assert status.state == 8
@pytest.fixture(name="setup_rpc_channel")
def setup_rpc_channel_fixture(rpc_channel: AsyncMock, payload: pathlib.Path) -> AsyncMock:
"""Fixture to set up the RPC channel for the tests."""
# The values other than the payload are arbitrary
message = RoborockMessage(
protocol=RoborockMessageProtocol.GENERAL_RESPONSE,
payload=payload.read_bytes(),
seq=12750,
version=b"1.0",
random=97431,
timestamp=1652547161,
)
response_message = decode_rpc_response(message)
rpc_channel.send_command.return_value = response_message.data
return rpc_channel


@pytest.mark.parametrize(
("payload", "trait_name", "trait_type", "trait_method"),
[
(TESTDATA / "get_status.json", "status", StatusTrait, StatusTrait.get_status),
(TESTDATA / "get_dnd.json", "do_not_disturb", DoNotDisturbTrait, DoNotDisturbTrait.get_dnd_timer),
(TESTDATA / "get_clean_summary.json", "clean_summary", CleanSummaryTrait, CleanSummaryTrait.get_clean_summary),
(TESTDATA / "get_volume.json", "sound_volume", SoundVolumeTrait, SoundVolumeTrait.get_volume),
],
)
async def test_device_trait_command_parsing(
device: RoborockDevice,
setup_rpc_channel: AsyncMock,
snapshot: SnapshotAssertion,
trait_name: str,
trait_type: type[Trait],
trait_method: Callable[..., Awaitable[object]],
) -> None:
"""Test the device trait command."""
trait = device.traits[trait_name]
assert trait
assert isinstance(trait, trait_type)
result = await trait_method(trait)
assert setup_rpc_channel.send_command.called

assert result == snapshot