Rewrite dyson vacuum test (#45265)

pull/45282/head
Xiaonan Shen 2021-01-18 22:56:54 +08:00 committed by GitHub
parent c621c0fa5d
commit 5ee4479151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 180 additions and 176 deletions

View File

@ -1,22 +1,27 @@
"""Common utils for Dyson tests."""
from typing import Optional, Type
from unittest import mock
from unittest.mock import MagicMock
from libpurecool.dyson_device import DysonDevice
from libpurecool.dyson_pure_cool import FanSpeed
from homeassistant.core import HomeAssistant
def load_mock_device(device):
SERIAL = "XX-XXXXX-XX"
NAME = "Temp Name"
ENTITY_NAME = "temp_name"
BASE_PATH = "homeassistant.components.dyson"
def load_mock_device(device: DysonDevice) -> None:
"""Load the mock with default values so it doesn't throw errors."""
device.serial = "XX-XXXXX-XX"
device.name = "Temp Name"
device.serial = SERIAL
device.name = NAME
device.connect = mock.Mock(return_value=True)
device.auto_connect = mock.Mock(return_value=True)
device.environmental_state.particulate_matter_25 = "0000"
device.environmental_state.particulate_matter_10 = "0000"
device.environmental_state.nitrogen_dioxide = "0000"
device.environmental_state.volatil_organic_compounds = "0000"
device.environmental_state.volatile_organic_compounds = "0000"
device.environmental_state.temperature = 250
device.state.hepa_filter_state = 0
device.state.carbon_filter_state = 0
device.state.speed = FanSpeed.FAN_SPEED_1.value
@ -24,3 +29,27 @@ def load_mock_device(device):
device.state.oscillation_angle_high = "000"
device.state.filter_life = "000"
device.state.heat_target = 200
if hasattr(device, "environmental_state"):
device.environmental_state.particulate_matter_25 = "0000"
device.environmental_state.particulate_matter_10 = "0000"
device.environmental_state.nitrogen_dioxide = "0000"
device.environmental_state.volatil_organic_compounds = "0000"
device.environmental_state.volatile_organic_compounds = "0000"
device.environmental_state.temperature = 250
def get_basic_device(spec: Type[DysonDevice]) -> DysonDevice:
"""Return a basic device with common fields filled out."""
device = MagicMock(spec=spec)
load_mock_device(device)
return device
async def async_update_device(
hass: HomeAssistant, device: DysonDevice, state_type: Optional[Type] = None
) -> None:
"""Update the device using callback function."""
callback = device.add_message_listener.call_args[0][0]
message = MagicMock(spec=state_type)
await hass.async_add_executor_job(callback, message)
await hass.async_block_till_done()

View File

@ -0,0 +1,46 @@
"""Configure pytest for Dyson tests."""
from unittest.mock import patch
from libpurecool.dyson_device import DysonDevice
import pytest
from homeassistant.components.dyson import CONF_LANGUAGE, DOMAIN
from homeassistant.const import CONF_DEVICES, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from .common import SERIAL
from tests.common import async_setup_component
BASE_PATH = "homeassistant.components.dyson"
@pytest.fixture
async def device(hass: HomeAssistant, request) -> DysonDevice:
"""Fixture to provide Dyson 360 Eye device."""
device = request.module.get_device()
platform = request.module.PLATFORM_DOMAIN
with patch(f"{BASE_PATH}.DysonAccount.login", return_value=True), patch(
f"{BASE_PATH}.DysonAccount.devices", return_value=[device]
), patch(f"{BASE_PATH}.DYSON_PLATFORMS", [platform]):
# DYSON_PLATFORMS is patched so that only the platform being tested is set up
await async_setup_component(
hass,
DOMAIN,
{
DOMAIN: {
CONF_USERNAME: "user@example.com",
CONF_PASSWORD: "password",
CONF_LANGUAGE: "US",
CONF_DEVICES: [
{
"device_id": SERIAL,
"device_ip": "0.0.0.0",
}
],
}
},
)
await hass.async_block_till_done()
return device

View File

@ -1,29 +1,43 @@
"""Test the Dyson 360 eye robot vacuum component."""
import unittest
from unittest import mock
from unittest.mock import MagicMock
from libpurecool.const import Dyson360EyeMode, PowerMode
from libpurecool.dyson_360_eye import Dyson360Eye
import pytest
from homeassistant.components.dyson import vacuum as dyson
from homeassistant.components.dyson.vacuum import Dyson360EyeDevice
from homeassistant.components.dyson.vacuum import ATTR_POSITION, SUPPORT_DYSON
from homeassistant.components.vacuum import (
ATTR_FAN_SPEED,
ATTR_FAN_SPEED_LIST,
ATTR_STATUS,
DOMAIN as PLATFORM_DOMAIN,
SERVICE_RETURN_TO_BASE,
SERVICE_SET_FAN_SPEED,
SERVICE_START_PAUSE,
SERVICE_STOP,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.const import (
ATTR_BATTERY_LEVEL,
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
STATE_OFF,
STATE_ON,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry
from tests.common import get_test_home_assistant
from .common import ENTITY_NAME, NAME, SERIAL, async_update_device, get_basic_device
ENTITY_ID = f"{PLATFORM_DOMAIN}.{ENTITY_NAME}"
def _get_non_vacuum_device():
"""Return a non vacuum device."""
device = mock.Mock()
device.name = "Device_Fan"
device.state = None
return device
def _get_vacuum_device_cleaning():
"""Return a vacuum device running."""
device = mock.Mock(spec=Dyson360Eye)
device.name = "Device_Vacuum"
device.state = mock.MagicMock()
@callback
def get_device() -> Dyson360Eye:
"""Return a Dyson 360 Eye device."""
device = get_basic_device(Dyson360Eye)
device.state = MagicMock()
device.state.state = Dyson360EyeMode.FULL_CLEAN_RUNNING
device.state.battery_level = 85
device.state.power_mode = PowerMode.QUIET
@ -31,159 +45,74 @@ def _get_vacuum_device_cleaning():
return device
def _get_vacuum_device_charging():
"""Return a vacuum device charging."""
device = mock.Mock(spec=Dyson360Eye)
device.name = "Device_Vacuum"
device.state = mock.MagicMock()
async def test_state(hass: HomeAssistant, device: Dyson360Eye) -> None:
"""Test the state of the vacuum."""
er = await entity_registry.async_get_registry(hass)
assert er.async_get(ENTITY_ID).unique_id == SERIAL
state = hass.states.get(ENTITY_ID)
assert state.name == NAME
assert state.state == STATE_ON
attributes = state.attributes
assert attributes[ATTR_STATUS] == "Cleaning"
assert attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_DYSON
assert attributes[ATTR_BATTERY_LEVEL] == 85
assert attributes[ATTR_POSITION] == "(0, 0)"
assert attributes[ATTR_FAN_SPEED] == "Quiet"
assert attributes[ATTR_FAN_SPEED_LIST] == ["Quiet", "Max"]
device.state.state = Dyson360EyeMode.INACTIVE_CHARGING
device.state.battery_level = 40
device.state.power_mode = PowerMode.QUIET
device.state.position = (0, 0)
return device
device.state.power_mode = PowerMode.MAX
await async_update_device(hass, device)
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.attributes[ATTR_STATUS] == "Stopped - Charging"
assert state.attributes[ATTR_FAN_SPEED] == "Max"
def _get_vacuum_device_pause():
"""Return a vacuum device in pause."""
device = mock.MagicMock(spec=Dyson360Eye)
device.name = "Device_Vacuum"
device.state = mock.MagicMock()
device.state.state = Dyson360EyeMode.FULL_CLEAN_PAUSED
device.state.battery_level = 40
device.state.power_mode = PowerMode.QUIET
device.state.position = (0, 0)
return device
await async_update_device(hass, device)
state = hass.states.get(ENTITY_ID)
assert state.state == STATE_OFF
assert state.attributes[ATTR_STATUS] == "Paused"
def _get_vacuum_device_unknown_state():
"""Return a vacuum device with unknown state."""
device = mock.Mock(spec=Dyson360Eye)
device.name = "Device_Vacuum"
device.state = mock.MagicMock()
device.state.state = "Unknown"
return device
@pytest.mark.parametrize(
"service,command,state",
[
(SERVICE_TURN_ON, "start", Dyson360EyeMode.INACTIVE_CHARGED),
(SERVICE_TURN_ON, "resume", Dyson360EyeMode.FULL_CLEAN_PAUSED),
(SERVICE_TURN_OFF, "pause", Dyson360EyeMode.FULL_CLEAN_RUNNING),
(SERVICE_STOP, "pause", Dyson360EyeMode.FULL_CLEAN_RUNNING),
(SERVICE_START_PAUSE, "pause", Dyson360EyeMode.FULL_CLEAN_RUNNING),
(SERVICE_START_PAUSE, "pause", Dyson360EyeMode.FULL_CLEAN_RUNNING),
(SERVICE_START_PAUSE, "start", Dyson360EyeMode.INACTIVE_CHARGED),
(SERVICE_START_PAUSE, "resume", Dyson360EyeMode.FULL_CLEAN_PAUSED),
(SERVICE_RETURN_TO_BASE, "abort", Dyson360EyeMode.FULL_CLEAN_PAUSED),
],
)
async def test_commands(
hass: HomeAssistant, device: Dyson360Eye, service: str, command: str, state: str
) -> None:
"""Test sending commands to the vacuum."""
device.state.state = state
await async_update_device(hass, device)
await hass.services.async_call(
PLATFORM_DOMAIN, service, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
)
getattr(device, command).assert_called_once_with()
class DysonTest(unittest.TestCase):
"""Dyson 360 eye robot vacuum component test class."""
def setUp(self): # pylint: disable=invalid-name
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.addCleanup(self.tear_down_cleanup)
def tear_down_cleanup(self):
"""Stop everything that was started."""
self.hass.stop()
def test_setup_component_with_no_devices(self):
"""Test setup component with no devices."""
self.hass.data[dyson.DYSON_DEVICES] = []
add_entities = mock.MagicMock()
dyson.setup_platform(self.hass, {}, add_entities)
add_entities.assert_called_with([])
def test_setup_component(self):
"""Test setup component with devices."""
def _add_device(devices):
assert len(devices) == 1
assert devices[0].name == "Device_Vacuum"
device_vacuum = _get_vacuum_device_cleaning()
device_non_vacuum = _get_non_vacuum_device()
self.hass.data[dyson.DYSON_DEVICES] = [device_vacuum, device_non_vacuum]
dyson.setup_platform(self.hass, {}, _add_device)
def test_on_message(self):
"""Test when message is received."""
device = _get_vacuum_device_cleaning()
component = Dyson360EyeDevice(device)
component.entity_id = "entity_id"
component.schedule_update_ha_state = mock.Mock()
component.on_message(mock.Mock())
assert component.schedule_update_ha_state.called
def test_should_poll(self):
"""Test polling is disable."""
device = _get_vacuum_device_cleaning()
component = Dyson360EyeDevice(device)
assert not component.should_poll
def test_properties(self):
"""Test component properties."""
device1 = _get_vacuum_device_cleaning()
device2 = _get_vacuum_device_unknown_state()
device3 = _get_vacuum_device_charging()
component = Dyson360EyeDevice(device1)
component2 = Dyson360EyeDevice(device2)
component3 = Dyson360EyeDevice(device3)
assert component.name == "Device_Vacuum"
assert component.is_on
assert component.status == "Cleaning"
assert component2.status == "Unknown"
assert component.battery_level == 85
assert component.fan_speed == "Quiet"
assert component.fan_speed_list == ["Quiet", "Max"]
assert component.device_state_attributes["position"] == "(0, 0)"
assert component.available
assert component.supported_features == 255
assert component.battery_icon == "mdi:battery-80"
assert component3.battery_icon == "mdi:battery-charging-40"
def test_turn_on(self):
"""Test turn on vacuum."""
device1 = _get_vacuum_device_charging()
component1 = Dyson360EyeDevice(device1)
component1.turn_on()
assert device1.start.called
device2 = _get_vacuum_device_pause()
component2 = Dyson360EyeDevice(device2)
component2.turn_on()
assert device2.resume.called
def test_turn_off(self):
"""Test turn off vacuum."""
device1 = _get_vacuum_device_cleaning()
component1 = Dyson360EyeDevice(device1)
component1.turn_off()
assert device1.pause.called
def test_stop(self):
"""Test stop vacuum."""
device1 = _get_vacuum_device_cleaning()
component1 = Dyson360EyeDevice(device1)
component1.stop()
assert device1.pause.called
def test_set_fan_speed(self):
"""Test set fan speed vacuum."""
device1 = _get_vacuum_device_cleaning()
component1 = Dyson360EyeDevice(device1)
component1.set_fan_speed("Max")
device1.set_power_mode.assert_called_with(PowerMode.MAX)
def test_start_pause(self):
"""Test start/pause."""
device1 = _get_vacuum_device_charging()
component1 = Dyson360EyeDevice(device1)
component1.start_pause()
assert device1.start.called
device2 = _get_vacuum_device_pause()
component2 = Dyson360EyeDevice(device2)
component2.start_pause()
assert device2.resume.called
device3 = _get_vacuum_device_cleaning()
component3 = Dyson360EyeDevice(device3)
component3.start_pause()
assert device3.pause.called
def test_return_to_base(self):
"""Test return to base."""
device = _get_vacuum_device_pause()
component = Dyson360EyeDevice(device)
component.return_to_base()
assert device.abort.called
async def test_set_fan_speed(hass: HomeAssistant, device: Dyson360Eye):
"""Test setting fan speed of the vacuum."""
fan_speed_map = {
"Max": PowerMode.MAX,
"Quiet": PowerMode.QUIET,
}
for service_speed, command_speed in fan_speed_map.items():
await hass.services.async_call(
PLATFORM_DOMAIN,
SERVICE_SET_FAN_SPEED,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_FAN_SPEED: service_speed},
blocking=True,
)
device.set_power_mode.assert_called_with(command_speed)