* add media player test and update media player logic to qualify vizio for platinum quality score * add SCAN_INTERVAL and log message once when device goes from available to unavailable and vice versa * move test constants into one file to avoid defining dupes in each test file * move constant to shrink diff * move pytest fixtures to conftest.py * remove commented out code * move unload test to test_init * updates to tests and logging based on review * bypass notification service * add fixture so component setup makes it through config flow * split failure tests into two * fix setup and entity check for test_init and setup failures in test_media_player * make domain references consistent across test files * remove logging steps that were introduced to help debug * move common patches out to new fixture and use config entry everywhere appropriate * fix docstring * add test for update options to increase code coverage * add one more assert * refactor test_media_player to move boiler plate logic out of each test into _test_init function * final refactor of test_media_player to move repeat logic into separate function * update docstrings * refactor setup failure tests to move shared logic into private function * fix last new functions code to use variable instead of static config variable * remove trailing comma * test that volume_step gets properly passed to Vizio volume function * fix comment language * assert with unittest.mock.call in _test_service and use config_entries.async_setup instead of config_entries.async_add * replace config_entries.async_add with config_entries.async_setup everywhere * simplify if statement for argument assertion * fix logging based on style guide * remove accidentally committed changes * update scan interval to something more reasonable and add tests for availability changes * change filter function to list comprehension, simplify log messages, remove default entity id from logs since it is user configurable, fix docstrings
297 lines
10 KiB
Python
297 lines
10 KiB
Python
"""Tests for Vizio config flow."""
|
|
from datetime import timedelta
|
|
from unittest.mock import call
|
|
|
|
from asynctest import patch
|
|
import pytest
|
|
from pyvizio.const import (
|
|
DEVICE_CLASS_SPEAKER as VIZIO_DEVICE_CLASS_SPEAKER,
|
|
DEVICE_CLASS_TV as VIZIO_DEVICE_CLASS_TV,
|
|
)
|
|
from pyvizio.vizio import MAX_VOLUME
|
|
|
|
from homeassistant.components.media_player import (
|
|
ATTR_INPUT_SOURCE,
|
|
ATTR_MEDIA_VOLUME_LEVEL,
|
|
ATTR_MEDIA_VOLUME_MUTED,
|
|
DEVICE_CLASS_SPEAKER,
|
|
DEVICE_CLASS_TV,
|
|
DOMAIN as MP_DOMAIN,
|
|
SERVICE_MEDIA_NEXT_TRACK,
|
|
SERVICE_MEDIA_PREVIOUS_TRACK,
|
|
SERVICE_SELECT_SOURCE,
|
|
SERVICE_TURN_OFF,
|
|
SERVICE_TURN_ON,
|
|
SERVICE_VOLUME_DOWN,
|
|
SERVICE_VOLUME_MUTE,
|
|
SERVICE_VOLUME_SET,
|
|
SERVICE_VOLUME_UP,
|
|
)
|
|
from homeassistant.components.vizio.const import CONF_VOLUME_STEP, DOMAIN
|
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE
|
|
from homeassistant.helpers.typing import HomeAssistantType
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from .const import (
|
|
CURRENT_INPUT,
|
|
ENTITY_ID,
|
|
INPUT_LIST,
|
|
MOCK_SPEAKER_CONFIG,
|
|
MOCK_USER_VALID_TV_CONFIG,
|
|
NAME,
|
|
UNIQUE_ID,
|
|
VOLUME_STEP,
|
|
)
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
|
|
async def _test_setup(
|
|
hass: HomeAssistantType, ha_device_class: str, vizio_power_state: bool
|
|
) -> None:
|
|
"""Test Vizio Device entity setup."""
|
|
if vizio_power_state:
|
|
ha_power_state = STATE_ON
|
|
elif vizio_power_state is False:
|
|
ha_power_state = STATE_OFF
|
|
else:
|
|
ha_power_state = STATE_UNAVAILABLE
|
|
|
|
if ha_device_class == DEVICE_CLASS_SPEAKER:
|
|
vizio_device_class = VIZIO_DEVICE_CLASS_SPEAKER
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN, data=MOCK_SPEAKER_CONFIG, unique_id=UNIQUE_ID
|
|
)
|
|
else:
|
|
vizio_device_class = VIZIO_DEVICE_CLASS_TV
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN, data=MOCK_USER_VALID_TV_CONFIG, unique_id=UNIQUE_ID
|
|
)
|
|
|
|
with patch(
|
|
"homeassistant.components.vizio.media_player.VizioAsync.get_current_volume",
|
|
return_value=int(MAX_VOLUME[vizio_device_class] / 2),
|
|
), patch(
|
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
|
return_value=vizio_power_state,
|
|
):
|
|
config_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
attr = hass.states.get(ENTITY_ID).attributes
|
|
assert attr["friendly_name"] == NAME
|
|
assert attr["device_class"] == ha_device_class
|
|
|
|
assert hass.states.get(ENTITY_ID).state == ha_power_state
|
|
if ha_power_state == STATE_ON:
|
|
assert attr["source_list"] == INPUT_LIST
|
|
assert attr["source"] == CURRENT_INPUT
|
|
assert (
|
|
attr["volume_level"]
|
|
== float(int(MAX_VOLUME[vizio_device_class] / 2))
|
|
/ MAX_VOLUME[vizio_device_class]
|
|
)
|
|
|
|
|
|
async def _test_setup_failure(hass: HomeAssistantType, config: str) -> None:
|
|
"""Test generic Vizio entity setup failure."""
|
|
with patch(
|
|
"homeassistant.components.vizio.media_player.VizioAsync.can_connect",
|
|
return_value=False,
|
|
):
|
|
config_entry = MockConfigEntry(domain=DOMAIN, data=config, unique_id=UNIQUE_ID)
|
|
config_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
assert len(hass.states.async_entity_ids(MP_DOMAIN)) == 0
|
|
|
|
|
|
async def _test_service(
|
|
hass: HomeAssistantType,
|
|
vizio_func_name: str,
|
|
ha_service_name: str,
|
|
additional_service_data: dict = None,
|
|
*args,
|
|
**kwargs,
|
|
) -> None:
|
|
"""Test generic Vizio media player entity service."""
|
|
service_data = {ATTR_ENTITY_ID: ENTITY_ID}
|
|
if additional_service_data:
|
|
service_data.update(additional_service_data)
|
|
|
|
with patch(
|
|
f"homeassistant.components.vizio.media_player.VizioAsync.{vizio_func_name}"
|
|
) as service_call:
|
|
await hass.services.async_call(
|
|
MP_DOMAIN, ha_service_name, service_data=service_data, blocking=True,
|
|
)
|
|
assert service_call.called
|
|
|
|
if args or kwargs:
|
|
assert service_call.call_args == call(*args, **kwargs)
|
|
|
|
|
|
async def test_speaker_on(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio Speaker entity setup when on."""
|
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, True)
|
|
|
|
|
|
async def test_speaker_off(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio Speaker entity setup when off."""
|
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, False)
|
|
|
|
|
|
async def test_speaker_unavailable(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio Speaker entity setup when unavailable."""
|
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, None)
|
|
|
|
|
|
async def test_init_tv_on(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio TV entity setup when on."""
|
|
await _test_setup(hass, DEVICE_CLASS_TV, True)
|
|
|
|
|
|
async def test_init_tv_off(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio TV entity setup when off."""
|
|
await _test_setup(hass, DEVICE_CLASS_TV, False)
|
|
|
|
|
|
async def test_init_tv_unavailable(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test Vizio TV entity setup when unavailable."""
|
|
await _test_setup(hass, DEVICE_CLASS_TV, None)
|
|
|
|
|
|
async def test_setup_failure_speaker(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture
|
|
) -> None:
|
|
"""Test speaker entity setup failure."""
|
|
await _test_setup_failure(hass, MOCK_SPEAKER_CONFIG)
|
|
|
|
|
|
async def test_setup_failure_tv(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture
|
|
) -> None:
|
|
"""Test TV entity setup failure."""
|
|
await _test_setup_failure(hass, MOCK_USER_VALID_TV_CONFIG)
|
|
|
|
|
|
async def test_services(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test all Vizio media player entity services."""
|
|
await _test_setup(hass, DEVICE_CLASS_TV, True)
|
|
|
|
await _test_service(hass, "pow_on", SERVICE_TURN_ON)
|
|
await _test_service(hass, "pow_off", SERVICE_TURN_OFF)
|
|
await _test_service(
|
|
hass, "mute_on", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: True}
|
|
)
|
|
await _test_service(
|
|
hass, "mute_off", SERVICE_VOLUME_MUTE, {ATTR_MEDIA_VOLUME_MUTED: False}
|
|
)
|
|
await _test_service(
|
|
hass, "input_switch", SERVICE_SELECT_SOURCE, {ATTR_INPUT_SOURCE: "USB"}, "USB"
|
|
)
|
|
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP)
|
|
await _test_service(hass, "vol_down", SERVICE_VOLUME_DOWN)
|
|
await _test_service(
|
|
hass, "vol_up", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 1}
|
|
)
|
|
await _test_service(
|
|
hass, "vol_down", SERVICE_VOLUME_SET, {ATTR_MEDIA_VOLUME_LEVEL: 0}
|
|
)
|
|
await _test_service(hass, "ch_up", SERVICE_MEDIA_NEXT_TRACK)
|
|
await _test_service(hass, "ch_down", SERVICE_MEDIA_PREVIOUS_TRACK)
|
|
|
|
|
|
async def test_options_update(
|
|
hass: HomeAssistantType, vizio_connect: pytest.fixture, vizio_update: pytest.fixture
|
|
) -> None:
|
|
"""Test when config entry update event fires."""
|
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, True)
|
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
|
assert config_entry.options
|
|
new_options = config_entry.options.copy()
|
|
updated_options = {CONF_VOLUME_STEP: VOLUME_STEP}
|
|
new_options.update(updated_options)
|
|
hass.config_entries.async_update_entry(
|
|
entry=config_entry, options=new_options,
|
|
)
|
|
assert config_entry.options == updated_options
|
|
await _test_service(hass, "vol_up", SERVICE_VOLUME_UP, num=VOLUME_STEP)
|
|
|
|
|
|
async def _test_update_availability_switch(
|
|
hass: HomeAssistantType,
|
|
initial_power_state: bool,
|
|
final_power_state: bool,
|
|
caplog: pytest.fixture,
|
|
) -> None:
|
|
now = dt_util.utcnow()
|
|
future_interval = timedelta(minutes=1)
|
|
|
|
# Setup device as if time is right now
|
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
|
await _test_setup(hass, DEVICE_CLASS_SPEAKER, initial_power_state)
|
|
|
|
# Clear captured logs so that only availability state changes are captured for
|
|
# future assertion
|
|
caplog.clear()
|
|
|
|
# Fast forward time to future twice to trigger update and assert vizio log message
|
|
for i in range(1, 3):
|
|
future = now + (future_interval * i)
|
|
with patch(
|
|
"homeassistant.components.vizio.media_player.VizioAsync.get_power_state",
|
|
return_value=final_power_state,
|
|
), patch("homeassistant.util.dt.utcnow", return_value=future), patch(
|
|
"homeassistant.util.utcnow", return_value=future
|
|
):
|
|
async_fire_time_changed(hass, future)
|
|
await hass.async_block_till_done()
|
|
if final_power_state is None:
|
|
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
|
else:
|
|
assert hass.states.get(ENTITY_ID).state != STATE_UNAVAILABLE
|
|
|
|
# Ensure connection status messages from vizio.media_player appear exactly once
|
|
# (on availability state change)
|
|
vizio_log_list = [
|
|
log
|
|
for log in caplog.records
|
|
if log.name == "homeassistant.components.vizio.media_player"
|
|
]
|
|
assert len(vizio_log_list) == 1
|
|
|
|
|
|
async def test_update_unavailable_to_available(
|
|
hass: HomeAssistantType,
|
|
vizio_connect: pytest.fixture,
|
|
vizio_update: pytest.fixture,
|
|
caplog: pytest.fixture,
|
|
) -> None:
|
|
"""Test device becomes available after being unavailable."""
|
|
await _test_update_availability_switch(hass, None, True, caplog)
|
|
|
|
|
|
async def test_update_available_to_unavailable(
|
|
hass: HomeAssistantType,
|
|
vizio_connect: pytest.fixture,
|
|
vizio_update: pytest.fixture,
|
|
caplog: pytest.fixture,
|
|
) -> None:
|
|
"""Test device becomes unavailable after being available."""
|
|
await _test_update_availability_switch(hass, True, None, caplog)
|