Rewrite dyson vacuum test (#45265)
parent
c621c0fa5d
commit
5ee4479151
|
@ -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()
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue