core/tests/components/androidtv/test_remote.py

165 lines
5.6 KiB
Python

"""The tests for the androidtv remote platform."""
from typing import Any
from unittest.mock import call, patch
from androidtv.constants import KEYS
import pytest
from homeassistant.components.androidtv.const import (
CONF_TURN_OFF_COMMAND,
CONF_TURN_ON_COMMAND,
)
from homeassistant.components.remote import (
ATTR_NUM_REPEATS,
DOMAIN as REMOTE_DOMAIN,
SERVICE_SEND_COMMAND,
)
from homeassistant.const import (
ATTR_COMMAND,
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from . import patchers
from .common import (
CONFIG_ANDROID_DEFAULT,
CONFIG_FIRETV_DEFAULT,
SHELL_RESPONSE_OFF,
SHELL_RESPONSE_STANDBY,
setup_mock_entry,
)
from tests.common import MockConfigEntry
def _setup(config: dict[str, Any]) -> tuple[str, str, MockConfigEntry]:
"""Prepare mock entry for the media player tests."""
return setup_mock_entry(config, REMOTE_DOMAIN)
async def _test_service(
hass: HomeAssistant,
entity_id,
ha_service_name,
androidtv_method,
additional_service_data=None,
expected_call_args=None,
) -> None:
"""Test generic Android media player entity service."""
if expected_call_args is None:
expected_call_args = [None]
service_data = {ATTR_ENTITY_ID: entity_id}
if additional_service_data:
service_data.update(additional_service_data)
androidtv_patch = (
"androidtv.androidtv_async.AndroidTVAsync"
if "android" in entity_id
else "firetv.firetv_async.FireTVAsync"
)
with patch(f"androidtv.{androidtv_patch}.{androidtv_method}") as api_call:
await hass.services.async_call(
REMOTE_DOMAIN,
ha_service_name,
service_data=service_data,
blocking=True,
)
assert api_call.called
assert api_call.call_count == len(expected_call_args)
expected_calls = [call(s) if s else call() for s in expected_call_args]
assert api_call.call_args_list == expected_calls
@pytest.mark.parametrize("config", [CONFIG_ANDROID_DEFAULT, CONFIG_FIRETV_DEFAULT])
async def test_services_remote(hass: HomeAssistant, config) -> None:
"""Test services for remote entity."""
patch_key, entity_id, config_entry = _setup(config)
config_entry.add_to_hass(hass)
with patchers.patch_connect(True)[patch_key]:
with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]:
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
with (
patchers.patch_shell(SHELL_RESPONSE_STANDBY)[patch_key],
patchers.PATCH_SCREENCAP,
):
await _test_service(hass, entity_id, SERVICE_TURN_OFF, "turn_off")
await _test_service(hass, entity_id, SERVICE_TURN_ON, "turn_on")
await _test_service(
hass,
entity_id,
SERVICE_SEND_COMMAND,
"adb_shell",
{ATTR_COMMAND: ["BACK", "test"], ATTR_NUM_REPEATS: 2},
[
f"input keyevent {KEYS["BACK"]}",
"test",
f"input keyevent {KEYS["BACK"]}",
"test",
],
)
@pytest.mark.parametrize("config", [CONFIG_ANDROID_DEFAULT, CONFIG_FIRETV_DEFAULT])
async def test_services_remote_custom(hass: HomeAssistant, config) -> None:
"""Test services with custom options for remote entity."""
patch_key, entity_id, config_entry = _setup(config)
config_entry.add_to_hass(hass)
hass.config_entries.async_update_entry(
config_entry,
options={
CONF_TURN_OFF_COMMAND: "test off",
CONF_TURN_ON_COMMAND: "test on",
},
)
with patchers.patch_connect(True)[patch_key]:
with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]:
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
with (
patchers.patch_shell(SHELL_RESPONSE_STANDBY)[patch_key],
patchers.PATCH_SCREENCAP,
):
await _test_service(
hass, entity_id, SERVICE_TURN_OFF, "adb_shell", None, ["test off"]
)
await _test_service(
hass, entity_id, SERVICE_TURN_ON, "adb_shell", None, ["test on"]
)
async def test_remote_unicode_decode_error(hass: HomeAssistant) -> None:
"""Test sending a command via the send_command remote service that raises a UnicodeDecodeError exception."""
patch_key, entity_id, config_entry = _setup(CONFIG_ANDROID_DEFAULT)
config_entry.add_to_hass(hass)
response = b"test response"
with patchers.patch_connect(True)[patch_key]:
with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]:
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
with patch(
"androidtv.basetv.basetv_async.BaseTVAsync.adb_shell",
side_effect=UnicodeDecodeError("utf-8", response, 0, len(response), "TEST"),
) as api_call:
try:
await hass.services.async_call(
REMOTE_DOMAIN,
SERVICE_SEND_COMMAND,
service_data={ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: "BACK"},
blocking=True,
)
pytest.fail("Exception not raised")
except ServiceValidationError:
assert api_call.call_count == 1