2019-08-29 10:03:03 +00:00
""" The tests for the androidtv platform. """
import logging
2020-01-06 22:10:13 +00:00
from unittest . mock import patch
from androidtv . exceptions import LockNotAcquiredException
2019-08-29 10:03:03 +00:00
from homeassistant . components . androidtv . media_player import (
2019-09-02 20:08:01 +00:00
ANDROIDTV_DOMAIN ,
2020-01-06 22:10:13 +00:00
ATTR_COMMAND ,
ATTR_DEVICE_PATH ,
ATTR_LOCAL_PATH ,
2019-09-02 20:08:01 +00:00
CONF_ADB_SERVER_IP ,
2019-10-17 13:33:20 +00:00
CONF_ADBKEY ,
2019-11-07 22:04:59 +00:00
CONF_APPS ,
2020-01-29 20:13:09 +00:00
CONF_EXCLUDE_UNNAMED_APPS ,
2020-01-06 22:10:13 +00:00
KEYS ,
SERVICE_ADB_COMMAND ,
SERVICE_DOWNLOAD ,
SERVICE_UPLOAD ,
2019-11-07 22:04:59 +00:00
)
from homeassistant . components . media_player . const import (
ATTR_INPUT_SOURCE ,
DOMAIN ,
SERVICE_SELECT_SOURCE ,
2019-09-02 20:08:01 +00:00
)
from homeassistant . const import (
2019-11-07 22:04:59 +00:00
ATTR_ENTITY_ID ,
2019-09-02 20:08:01 +00:00
CONF_DEVICE_CLASS ,
CONF_HOST ,
CONF_NAME ,
CONF_PLATFORM ,
2020-01-26 09:39:19 +00:00
SERVICE_VOLUME_SET ,
2019-09-02 20:08:01 +00:00
STATE_IDLE ,
STATE_OFF ,
2019-11-07 22:04:59 +00:00
STATE_PLAYING ,
2019-09-02 20:08:01 +00:00
STATE_UNAVAILABLE ,
2019-08-29 10:03:03 +00:00
)
2019-12-09 12:57:24 +00:00
from homeassistant . setup import async_setup_component
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
from . import patchers
# Android TV device with Python ADB implementation
CONFIG_ANDROIDTV_PYTHON_ADB = {
DOMAIN : {
CONF_PLATFORM : ANDROIDTV_DOMAIN ,
CONF_HOST : " 127.0.0.1 " ,
CONF_NAME : " Android TV " ,
2019-12-14 15:54:41 +00:00
CONF_DEVICE_CLASS : " androidtv " ,
2019-09-02 20:08:01 +00:00
}
}
# Android TV device with ADB server
CONFIG_ANDROIDTV_ADB_SERVER = {
DOMAIN : {
CONF_PLATFORM : ANDROIDTV_DOMAIN ,
CONF_HOST : " 127.0.0.1 " ,
CONF_NAME : " Android TV " ,
2019-12-14 15:54:41 +00:00
CONF_DEVICE_CLASS : " androidtv " ,
2019-09-02 20:08:01 +00:00
CONF_ADB_SERVER_IP : " 127.0.0.1 " ,
}
}
# Fire TV device with Python ADB implementation
CONFIG_FIRETV_PYTHON_ADB = {
DOMAIN : {
CONF_PLATFORM : ANDROIDTV_DOMAIN ,
CONF_HOST : " 127.0.0.1 " ,
CONF_NAME : " Fire TV " ,
CONF_DEVICE_CLASS : " firetv " ,
}
}
# Fire TV device with ADB server
CONFIG_FIRETV_ADB_SERVER = {
DOMAIN : {
CONF_PLATFORM : ANDROIDTV_DOMAIN ,
CONF_HOST : " 127.0.0.1 " ,
CONF_NAME : " Fire TV " ,
CONF_DEVICE_CLASS : " firetv " ,
CONF_ADB_SERVER_IP : " 127.0.0.1 " ,
}
}
2020-01-06 22:10:13 +00:00
def _setup ( config ) :
2019-10-17 13:33:20 +00:00
""" Perform common setup tasks for the tests. """
2019-09-02 20:08:01 +00:00
if CONF_ADB_SERVER_IP not in config [ DOMAIN ] :
patch_key = " python "
else :
patch_key = " server "
if config [ DOMAIN ] . get ( CONF_DEVICE_CLASS ) != " firetv " :
entity_id = " media_player.android_tv "
else :
entity_id = " media_player.fire_tv "
2019-10-17 13:33:20 +00:00
return patch_key , entity_id
async def _test_reconnect ( hass , caplog , config ) :
""" Test that the error and reconnection attempts are logged correctly.
" Handles device/service unavailable. Log a warning once when
unavailable , log once when reconnected . "
https : / / developers . home - assistant . io / docs / en / integration_quality_scale_index . html
"""
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-10-17 13:33:20 +00:00
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
2019-09-27 05:53:26 +00:00
patch_key
2019-10-17 13:33:20 +00:00
] , patchers . patch_shell ( " " ) [
patch_key
] , patchers . PATCH_KEYGEN , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
2019-09-02 20:08:01 +00:00
assert await async_setup_component ( hass , DOMAIN , config )
2019-10-17 13:33:20 +00:00
2019-09-02 20:08:01 +00:00
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
caplog . clear ( )
caplog . set_level ( logging . WARNING )
with patchers . patch_connect ( False ) [ patch_key ] , patchers . patch_shell ( error = True ) [
patch_key
2019-10-17 13:33:20 +00:00
] , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
2019-09-02 20:08:01 +00:00
for _ in range ( 5 ) :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_UNAVAILABLE
assert len ( caplog . record_tuples ) == 2
assert caplog . record_tuples [ 0 ] [ 1 ] == logging . ERROR
assert caplog . record_tuples [ 1 ] [ 1 ] == logging . WARNING
caplog . set_level ( logging . DEBUG )
2019-10-17 13:33:20 +00:00
with patchers . patch_connect ( True ) [ patch_key ] , patchers . patch_shell ( " 1 " ) [
patch_key
] , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
2019-09-02 20:08:01 +00:00
# Update 1 will reconnect
await hass . helpers . entity_component . async_update_entity ( entity_id )
# If using an ADB server, the state will get updated; otherwise, the
# state will be the last known state
state = hass . states . get ( entity_id )
if patch_key == " server " :
assert state . state == STATE_IDLE
else :
assert state . state == STATE_OFF
# Update 2 will update the state, regardless of which ADB connection
# method is used
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_IDLE
if patch_key == " python " :
assert (
" ADB connection to 127.0.0.1:5555 successfully established "
in caplog . record_tuples [ 2 ]
)
else :
assert (
" ADB connection to 127.0.0.1:5555 via ADB server 127.0.0.1:5037 successfully established "
in caplog . record_tuples [ 2 ]
)
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
return True
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def _test_adb_shell_returns_none ( hass , config ) :
""" Test the case that the ADB shell command returns `None`.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
The state should be ` None ` and the device should be unavailable .
"""
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-08-29 10:03:03 +00:00
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
2019-09-27 05:53:26 +00:00
patch_key
2019-10-17 13:33:20 +00:00
] , patchers . patch_shell ( " " ) [
patch_key
] , patchers . PATCH_KEYGEN , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
2019-09-02 20:08:01 +00:00
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state != STATE_UNAVAILABLE
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
with patchers . patch_shell ( None ) [ patch_key ] , patchers . patch_shell ( error = True ) [
patch_key
2019-10-17 13:33:20 +00:00
] , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
2019-09-02 20:08:01 +00:00
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_UNAVAILABLE
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
return True
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_reconnect_androidtv_python_adb ( hass , caplog ) :
""" Test that the error and reconnection attempts are logged correctly.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Android TV
* ADB connection method : Python ADB implementation
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_reconnect ( hass , caplog , CONFIG_ANDROIDTV_PYTHON_ADB )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_adb_shell_returns_none_androidtv_python_adb ( hass ) :
""" Test the case that the ADB shell command returns `None`.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Android TV
* ADB connection method : Python ADB implementation
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_adb_shell_returns_none ( hass , CONFIG_ANDROIDTV_PYTHON_ADB )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_reconnect_firetv_python_adb ( hass , caplog ) :
""" Test that the error and reconnection attempts are logged correctly.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Fire TV
* ADB connection method : Python ADB implementation
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_reconnect ( hass , caplog , CONFIG_FIRETV_PYTHON_ADB )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_adb_shell_returns_none_firetv_python_adb ( hass ) :
""" Test the case that the ADB shell command returns `None`.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Fire TV
* ADB connection method : Python ADB implementation
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_adb_shell_returns_none ( hass , CONFIG_FIRETV_PYTHON_ADB )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_reconnect_androidtv_adb_server ( hass , caplog ) :
""" Test that the error and reconnection attempts are logged correctly.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Android TV
* ADB connection method : ADB server
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_reconnect ( hass , caplog , CONFIG_ANDROIDTV_ADB_SERVER )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_adb_shell_returns_none_androidtv_adb_server ( hass ) :
""" Test the case that the ADB shell command returns `None`.
* Device type : Android TV
* ADB connection method : ADB server
"""
assert await _test_adb_shell_returns_none ( hass , CONFIG_ANDROIDTV_ADB_SERVER )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_reconnect_firetv_adb_server ( hass , caplog ) :
""" Test that the error and reconnection attempts are logged correctly.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Fire TV
* ADB connection method : ADB server
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_reconnect ( hass , caplog , CONFIG_FIRETV_ADB_SERVER )
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
async def test_adb_shell_returns_none_firetv_adb_server ( hass ) :
""" Test the case that the ADB shell command returns `None`.
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
* Device type : Fire TV
* ADB connection method : ADB server
2019-08-29 10:03:03 +00:00
2019-09-02 20:08:01 +00:00
"""
assert await _test_adb_shell_returns_none ( hass , CONFIG_FIRETV_ADB_SERVER )
2019-10-17 13:33:20 +00:00
async def test_setup_with_adbkey ( hass ) :
""" Test that setup succeeds when using an ADB key. """
config = CONFIG_ANDROIDTV_PYTHON_ADB . copy ( )
config [ DOMAIN ] [ CONF_ADBKEY ] = hass . config . path ( " user_provided_adbkey " )
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-10-17 13:33:20 +00:00
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
2019-10-17 13:33:20 +00:00
patch_key
] , patchers . patch_shell ( " " ) [
patch_key
] , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER , patchers . PATCH_ISFILE , patchers . PATCH_ACCESS :
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
2019-11-07 22:04:59 +00:00
2019-12-14 15:54:41 +00:00
async def _test_sources ( hass , config0 ) :
""" Test that sources (i.e., apps) are handled correctly for Android TV and Fire TV devices. """
config = config0 . copy ( )
2020-01-29 20:13:09 +00:00
config [ DOMAIN ] [ CONF_APPS ] = {
" com.app.test1 " : " TEST 1 " ,
" com.app.test3 " : None ,
" com.app.test4 " : " " ,
}
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-11-07 22:04:59 +00:00
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
2019-11-07 22:04:59 +00:00
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
2019-12-14 15:54:41 +00:00
if config [ DOMAIN ] . get ( CONF_DEVICE_CLASS ) != " firetv " :
patch_update = patchers . patch_androidtv_update (
" playing " ,
" com.app.test1 " ,
2020-01-29 20:13:09 +00:00
[ " com.app.test1 " , " com.app.test2 " , " com.app.test3 " , " com.app.test4 " ] ,
2019-12-14 15:54:41 +00:00
" hdmi " ,
False ,
1 ,
)
else :
patch_update = patchers . patch_firetv_update (
2020-01-29 20:13:09 +00:00
" playing " ,
" com.app.test1 " ,
[ " com.app.test1 " , " com.app.test2 " , " com.app.test3 " , " com.app.test4 " ] ,
2019-12-14 15:54:41 +00:00
)
with patch_update :
2019-11-07 22:04:59 +00:00
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_PLAYING
assert state . attributes [ " source " ] == " TEST 1 "
2020-01-29 20:13:09 +00:00
assert sorted ( state . attributes [ " source_list " ] ) == [ " TEST 1 " , " com.app.test2 " ]
2019-11-07 22:04:59 +00:00
2019-12-14 15:54:41 +00:00
if config [ DOMAIN ] . get ( CONF_DEVICE_CLASS ) != " firetv " :
patch_update = patchers . patch_androidtv_update (
" playing " ,
" com.app.test2 " ,
2020-01-29 20:13:09 +00:00
[ " com.app.test2 " , " com.app.test1 " , " com.app.test3 " , " com.app.test4 " ] ,
2019-12-14 15:54:41 +00:00
" hdmi " ,
True ,
0 ,
)
else :
patch_update = patchers . patch_firetv_update (
2020-01-29 20:13:09 +00:00
" playing " ,
" com.app.test2 " ,
[ " com.app.test2 " , " com.app.test1 " , " com.app.test3 " , " com.app.test4 " ] ,
2019-12-14 15:54:41 +00:00
)
with patch_update :
2019-11-07 22:04:59 +00:00
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_PLAYING
assert state . attributes [ " source " ] == " com.app.test2 "
2020-01-29 20:13:09 +00:00
assert sorted ( state . attributes [ " source_list " ] ) == [ " TEST 1 " , " com.app.test2 " ]
2019-11-07 22:04:59 +00:00
2019-12-14 15:54:41 +00:00
return True
2019-11-07 22:04:59 +00:00
2019-12-14 15:54:41 +00:00
async def test_androidtv_sources ( hass ) :
""" Test that sources (i.e., apps) are handled correctly for Android TV devices. """
assert await _test_sources ( hass , CONFIG_ANDROIDTV_ADB_SERVER )
async def test_firetv_sources ( hass ) :
""" Test that sources (i.e., apps) are handled correctly for Fire TV devices. """
assert await _test_sources ( hass , CONFIG_FIRETV_ADB_SERVER )
2020-01-29 20:13:09 +00:00
async def _test_exclude_sources ( hass , config0 , expected_sources ) :
""" Test that sources (i.e., apps) are handled correctly when the `exclude_unnamed_apps` config parameter is provided. """
config = config0 . copy ( )
config [ DOMAIN ] [ CONF_APPS ] = {
" com.app.test1 " : " TEST 1 " ,
" com.app.test3 " : None ,
" com.app.test4 " : " " ,
}
patch_key , entity_id = _setup ( config )
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
if config [ DOMAIN ] . get ( CONF_DEVICE_CLASS ) != " firetv " :
patch_update = patchers . patch_androidtv_update (
" playing " ,
" com.app.test1 " ,
[
" com.app.test1 " ,
" com.app.test2 " ,
" com.app.test3 " ,
" com.app.test4 " ,
" com.app.test5 " ,
] ,
" hdmi " ,
False ,
1 ,
)
else :
patch_update = patchers . patch_firetv_update (
" playing " ,
" com.app.test1 " ,
[
" com.app.test1 " ,
" com.app.test2 " ,
" com.app.test3 " ,
" com.app.test4 " ,
" com.app.test5 " ,
] ,
)
with patch_update :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_PLAYING
assert state . attributes [ " source " ] == " TEST 1 "
assert sorted ( state . attributes [ " source_list " ] ) == expected_sources
return True
async def test_androidtv_exclude_sources ( hass ) :
""" Test that sources (i.e., apps) are handled correctly for Android TV devices when the `exclude_unnamed_apps` config parameter is provided as true. """
config = CONFIG_ANDROIDTV_ADB_SERVER . copy ( )
config [ DOMAIN ] [ CONF_EXCLUDE_UNNAMED_APPS ] = True
assert await _test_exclude_sources ( hass , config , [ " TEST 1 " ] )
async def test_firetv_exclude_sources ( hass ) :
""" Test that sources (i.e., apps) are handled correctly for Fire TV devices when the `exclude_unnamed_apps` config parameter is provided as true. """
config = CONFIG_FIRETV_ADB_SERVER . copy ( )
config [ DOMAIN ] [ CONF_EXCLUDE_UNNAMED_APPS ] = True
assert await _test_exclude_sources ( hass , config , [ " TEST 1 " ] )
2019-12-14 15:54:41 +00:00
async def _test_select_source ( hass , config0 , source , expected_arg , method_patch ) :
""" Test that the methods for launching and stopping apps are called correctly when selecting a source. """
config = config0 . copy ( )
2020-01-29 20:13:09 +00:00
config [ DOMAIN ] [ CONF_APPS ] = { " com.app.test1 " : " TEST 1 " , " com.app.test3 " : None }
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-11-07 22:04:59 +00:00
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
2019-11-07 22:04:59 +00:00
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
with method_patch as method_patch_ :
await hass . services . async_call (
DOMAIN ,
SERVICE_SELECT_SOURCE ,
{ ATTR_ENTITY_ID : entity_id , ATTR_INPUT_SOURCE : source } ,
blocking = True ,
)
method_patch_ . assert_called_with ( expected_arg )
return True
2019-12-14 15:54:41 +00:00
async def test_androidtv_select_source_launch_app_id ( hass ) :
""" Test that an app can be launched using its app ID. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" com.app.test1 " ,
" com.app.test1 " ,
patchers . PATCH_LAUNCH_APP ,
)
async def test_androidtv_select_source_launch_app_name ( hass ) :
""" Test that an app can be launched using its friendly name. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" TEST 1 " ,
" com.app.test1 " ,
patchers . PATCH_LAUNCH_APP ,
)
async def test_androidtv_select_source_launch_app_id_no_name ( hass ) :
""" Test that an app can be launched using its app ID when it has no friendly name. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" com.app.test2 " ,
" com.app.test2 " ,
patchers . PATCH_LAUNCH_APP ,
)
2020-01-29 20:13:09 +00:00
async def test_androidtv_select_source_launch_app_hidden ( hass ) :
""" Test that an app can be launched using its app ID when it is hidden from the sources list. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" com.app.test3 " ,
" com.app.test3 " ,
patchers . PATCH_LAUNCH_APP ,
)
2019-12-14 15:54:41 +00:00
async def test_androidtv_select_source_stop_app_id ( hass ) :
""" Test that an app can be stopped using its app ID. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" !com.app.test1 " ,
" com.app.test1 " ,
patchers . PATCH_STOP_APP ,
)
async def test_androidtv_select_source_stop_app_name ( hass ) :
""" Test that an app can be stopped using its friendly name. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" !TEST 1 " ,
" com.app.test1 " ,
patchers . PATCH_STOP_APP ,
)
async def test_androidtv_select_source_stop_app_id_no_name ( hass ) :
""" Test that an app can be stopped using its app ID when it has no friendly name. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" !com.app.test2 " ,
" com.app.test2 " ,
patchers . PATCH_STOP_APP ,
)
2020-01-29 20:13:09 +00:00
async def test_androidtv_select_source_stop_app_hidden ( hass ) :
""" Test that an app can be stopped using its app ID when it is hidden from the sources list. """
assert await _test_select_source (
hass ,
CONFIG_ANDROIDTV_ADB_SERVER ,
" !com.app.test3 " ,
" com.app.test3 " ,
patchers . PATCH_STOP_APP ,
)
2019-11-07 22:04:59 +00:00
async def test_firetv_select_source_launch_app_id ( hass ) :
""" Test that an app can be launched using its app ID. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" com.app.test1 " ,
" com.app.test1 " ,
patchers . PATCH_LAUNCH_APP ,
2019-11-07 22:04:59 +00:00
)
async def test_firetv_select_source_launch_app_name ( hass ) :
""" Test that an app can be launched using its friendly name. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" TEST 1 " ,
" com.app.test1 " ,
patchers . PATCH_LAUNCH_APP ,
2019-11-07 22:04:59 +00:00
)
async def test_firetv_select_source_launch_app_id_no_name ( hass ) :
""" Test that an app can be launched using its app ID when it has no friendly name. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" com.app.test2 " ,
" com.app.test2 " ,
patchers . PATCH_LAUNCH_APP ,
2019-11-07 22:04:59 +00:00
)
2020-01-29 20:13:09 +00:00
async def test_firetv_select_source_launch_app_hidden ( hass ) :
""" Test that an app can be launched using its app ID when it is hidden from the sources list. """
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" com.app.test3 " ,
" com.app.test3 " ,
patchers . PATCH_LAUNCH_APP ,
)
2019-11-07 22:04:59 +00:00
async def test_firetv_select_source_stop_app_id ( hass ) :
""" Test that an app can be stopped using its app ID. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" !com.app.test1 " ,
" com.app.test1 " ,
patchers . PATCH_STOP_APP ,
2019-11-07 22:04:59 +00:00
)
async def test_firetv_select_source_stop_app_name ( hass ) :
""" Test that an app can be stopped using its friendly name. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" !TEST 1 " ,
" com.app.test1 " ,
patchers . PATCH_STOP_APP ,
2019-11-07 22:04:59 +00:00
)
async def test_firetv_select_source_stop_app_id_no_name ( hass ) :
""" Test that an app can be stopped using its app ID when it has no friendly name. """
2019-12-14 15:54:41 +00:00
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" !com.app.test2 " ,
" com.app.test2 " ,
patchers . PATCH_STOP_APP ,
2019-11-07 22:04:59 +00:00
)
2019-12-15 10:31:59 +00:00
2020-01-29 20:13:09 +00:00
async def test_firetv_select_source_stop_hidden ( hass ) :
""" Test that an app can be stopped using its app ID when it is hidden from the sources list. """
assert await _test_select_source (
hass ,
CONFIG_FIRETV_ADB_SERVER ,
" !com.app.test3 " ,
" com.app.test3 " ,
patchers . PATCH_STOP_APP ,
)
2019-12-15 10:31:59 +00:00
async def _test_setup_fail ( hass , config ) :
""" Test that the entity is not created when the ADB connection is not established. """
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( config )
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( False ) [
patch_key
] , patchers . patch_shell ( " " ) [
patch_key
] , patchers . PATCH_KEYGEN , patchers . PATCH_ANDROIDTV_OPEN , patchers . PATCH_SIGNER :
assert await async_setup_component ( hass , DOMAIN , config )
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is None
return True
async def test_setup_fail_androidtv ( hass ) :
""" Test that the Android TV entity is not created when the ADB connection is not established. """
assert await _test_setup_fail ( hass , CONFIG_ANDROIDTV_PYTHON_ADB )
async def test_setup_fail_firetv ( hass ) :
""" Test that the Fire TV entity is not created when the ADB connection is not established. """
assert await _test_setup_fail ( hass , CONFIG_FIRETV_PYTHON_ADB )
async def test_setup_two_devices ( hass ) :
""" Test that two devices can be set up. """
config = {
DOMAIN : [
CONFIG_ANDROIDTV_ADB_SERVER [ DOMAIN ] ,
CONFIG_FIRETV_ADB_SERVER [ DOMAIN ] . copy ( ) ,
]
}
config [ DOMAIN ] [ 1 ] [ CONF_HOST ] = " 127.0.0.2 "
patch_key = " server "
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , config )
for entity_id in [ " media_player.android_tv " , " media_player.fire_tv " ] :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
async def test_setup_same_device_twice ( hass ) :
""" Test that setup succeeds with a duplicated config entry. """
2020-01-06 22:10:13 +00:00
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
state = hass . states . get ( entity_id )
assert state is not None
assert hass . services . has_service ( ANDROIDTV_DOMAIN , SERVICE_ADB_COMMAND )
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
async def test_adb_command ( hass ) :
""" Test sending a command via the `androidtv.adb_command` service. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
command = " test command "
response = " test response "
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
with patch (
" androidtv.basetv.BaseTV.adb_shell " , return_value = response
) as patch_shell :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_ADB_COMMAND ,
{ ATTR_ENTITY_ID : entity_id , ATTR_COMMAND : command } ,
blocking = True ,
)
patch_shell . assert_called_with ( command )
state = hass . states . get ( entity_id )
assert state is not None
assert state . attributes [ " adb_response " ] == response
2020-01-07 12:30:34 +00:00
async def test_adb_command_unicode_decode_error ( hass ) :
""" Test sending a command via the `androidtv.adb_command` service that raises a UnicodeDecodeError exception. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
command = " test command "
response = b " test response "
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
with patch (
" androidtv.basetv.BaseTV.adb_shell " ,
side_effect = UnicodeDecodeError ( " utf-8 " , response , 0 , len ( response ) , " TEST " ) ,
) :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_ADB_COMMAND ,
{ ATTR_ENTITY_ID : entity_id , ATTR_COMMAND : command } ,
blocking = True ,
)
# patch_shell.assert_called_with(command)
state = hass . states . get ( entity_id )
assert state is not None
assert state . attributes [ " adb_response " ] is None
2020-01-06 22:10:13 +00:00
async def test_adb_command_key ( hass ) :
""" Test sending a key command via the `androidtv.adb_command` service. """
2019-12-15 10:31:59 +00:00
patch_key = " server "
2020-01-06 22:10:13 +00:00
entity_id = " media_player.android_tv "
command = " HOME "
response = None
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
2020-01-06 22:10:13 +00:00
with patch (
" androidtv.basetv.BaseTV.adb_shell " , return_value = response
) as patch_shell :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_ADB_COMMAND ,
{ ATTR_ENTITY_ID : entity_id , ATTR_COMMAND : command } ,
blocking = True ,
)
patch_shell . assert_called_with ( f " input keyevent { KEYS [ command ] } " )
state = hass . states . get ( entity_id )
assert state is not None
assert state . attributes [ " adb_response " ] is None
async def test_adb_command_get_properties ( hass ) :
""" Test sending the " GET_PROPERTIES " command via the `androidtv.adb_command` service. """
patch_key = " server "
entity_id = " media_player.android_tv "
command = " GET_PROPERTIES "
response = { " test key " : " test value " }
2019-12-15 10:31:59 +00:00
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
2020-01-06 22:10:13 +00:00
with patch (
" androidtv.androidtv.AndroidTV.get_properties_dict " , return_value = response
) as patch_get_props :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_ADB_COMMAND ,
{ ATTR_ENTITY_ID : entity_id , ATTR_COMMAND : command } ,
blocking = True ,
)
patch_get_props . assert_called ( )
state = hass . states . get ( entity_id )
assert state is not None
assert state . attributes [ " adb_response " ] == str ( response )
async def test_update_lock_not_acquired ( hass ) :
""" Test that the state does not get updated when a `LockNotAcquiredException` is raised. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
with patchers . patch_shell ( " " ) [ patch_key ] :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
with patch (
" androidtv.androidtv.AndroidTV.update " , side_effect = LockNotAcquiredException
) :
with patchers . patch_shell ( " 1 " ) [ patch_key ] :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_OFF
with patchers . patch_shell ( " 1 " ) [ patch_key ] :
await hass . helpers . entity_component . async_update_entity ( entity_id )
state = hass . states . get ( entity_id )
assert state is not None
assert state . state == STATE_IDLE
async def test_download ( hass ) :
""" Test the `androidtv.download` service. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
device_path = " device/path "
local_path = " local/path "
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
# Failed download because path is not whitelisted
with patch ( " androidtv.basetv.BaseTV.adb_pull " ) as patch_pull :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_DOWNLOAD ,
{
ATTR_ENTITY_ID : entity_id ,
ATTR_DEVICE_PATH : device_path ,
ATTR_LOCAL_PATH : local_path ,
} ,
blocking = True ,
)
patch_pull . assert_not_called ( )
# Successful download
with patch ( " androidtv.basetv.BaseTV.adb_pull " ) as patch_pull , patch . object (
hass . config , " is_allowed_path " , return_value = True
) :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_DOWNLOAD ,
{
ATTR_ENTITY_ID : entity_id ,
ATTR_DEVICE_PATH : device_path ,
ATTR_LOCAL_PATH : local_path ,
} ,
blocking = True ,
)
patch_pull . assert_called_with ( local_path , device_path )
async def test_upload ( hass ) :
""" Test the `androidtv.upload` service. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
device_path = " device/path "
local_path = " local/path "
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
# Failed upload because path is not whitelisted
with patch ( " androidtv.basetv.BaseTV.adb_push " ) as patch_push :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_UPLOAD ,
{
ATTR_ENTITY_ID : entity_id ,
ATTR_DEVICE_PATH : device_path ,
ATTR_LOCAL_PATH : local_path ,
} ,
blocking = True ,
)
patch_push . assert_not_called ( )
# Successful upload
with patch ( " androidtv.basetv.BaseTV.adb_push " ) as patch_push , patch . object (
hass . config , " is_allowed_path " , return_value = True
) :
await hass . services . async_call (
ANDROIDTV_DOMAIN ,
SERVICE_UPLOAD ,
{
ATTR_ENTITY_ID : entity_id ,
ATTR_DEVICE_PATH : device_path ,
ATTR_LOCAL_PATH : local_path ,
} ,
blocking = True ,
)
patch_push . assert_called_with ( local_path , device_path )
2020-01-26 09:39:19 +00:00
async def test_androidtv_volume_set ( hass ) :
""" Test setting the volume for an Android TV device. """
patch_key , entity_id = _setup ( CONFIG_ANDROIDTV_ADB_SERVER )
with patchers . PATCH_ADB_DEVICE_TCP , patchers . patch_connect ( True ) [
patch_key
] , patchers . patch_shell ( " " ) [ patch_key ] :
assert await async_setup_component ( hass , DOMAIN , CONFIG_ANDROIDTV_ADB_SERVER )
with patch (
" androidtv.basetv.BaseTV.set_volume_level " , return_value = 0.5
) as patch_set_volume_level :
await hass . services . async_call (
DOMAIN ,
SERVICE_VOLUME_SET ,
{ ATTR_ENTITY_ID : entity_id , " volume_level " : 0.5 } ,
blocking = True ,
)
patch_set_volume_level . assert_called_with ( 0.5 )