"""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