You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Tools/LyTestTools/tests/unit/test_launcher_android.py

325 lines
16 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project
SPDX-License-Identifier: Apache-2.0 OR MIT
Unit Tests for android launcher-wrappers: all are sanity code-path tests, since no interprocess actions should be taken
"""
import unittest.mock as mock
import pytest
import ly_test_tools.launchers
import ly_test_tools.launchers.platforms.android.launcher
import ly_test_tools.launchers.exceptions
import ly_test_tools.mobile.android
pytestmark = pytest.mark.SUITE_smoke
VALID_ANDROID_CONFIG = {
'android': {
'id': '000000000000000'
}
}
PACKAGE_NAME = "dummy_project_path"
class MockedWorkspace(object):
def __init__(self):
self.paths = mock.MagicMock()
self.setup_assistant = mock.MagicMock()
self.asset_processor = mock.MagicMock()
self.shader_compiler = mock.MagicMock()
self.settings = mock.MagicMock()
self.paths.engine_root.return_value = 'engine_path'
self.paths.build_directory.return_value = 'build_directory'
self.paths.autoexec_file.return_value = 'autoexec.cfg'
self.project = 'project_name'
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.open', mock.mock_open())
class TestLauncherModule:
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.json.loads')
def test_GetPackageName_MockJsonHasKey_ReturnsValue(self, mock_json):
dummy_name = "some_name"
mock_json.return_value = {'android_settings': {'package_name': dummy_name}}
under_test = ly_test_tools.launchers.platforms.android.launcher.get_package_name(PACKAGE_NAME)
assert under_test == "some_name"
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.json.loads')
def test_GetPackageName_MockJsonMissingKey_SetupError(self, mock_json):
mock_json.return_value = {"one": "two"}
with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
ly_test_tools.launchers.platforms.android.launcher.get_package_name(PACKAGE_NAME)
@mock.patch('ly_test_tools.environment.process_utils.check_output')
def test_GetPid_EarlyVersionAndExplodes_NoPid(self, mock_output):
mock_output.side_effect = ['23', Exception]
under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
assert mock_output.call_count == 2
assert not under_test
@mock.patch('ly_test_tools.environment.process_utils.check_output')
def test_GetPid_LaterVersionAndExplodes_NoPid(self, mock_output):
mock_output.side_effect = ['25', Exception]
under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
assert mock_output.call_count == 2
assert not under_test
@mock.patch('ly_test_tools.environment.process_utils.check_output')
def test_GetPid_PidHasSingleValue_ReturnPid(self, mock_output):
mock_output.side_effect = ['25', '12']
under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
assert mock_output.call_count == 2
assert under_test == '12'
@mock.patch('ly_test_tools.environment.process_utils.check_output')
def test_GetPid_PidHasMultipleValues_ReturnPid(self, mock_output):
mock_output.side_effect = ['25', 'value 12 found']
under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
assert mock_output.call_count == 2
assert under_test == '12'
def test_GenerateMapCommand_HasMapArg_ReturnsMapCommand(self):
args_list = ['random_arg', '+map', 'map_name', 'another_arg']
under_test = ly_test_tools.launchers.platforms.android.launcher.generate_android_map_command(args_list)
assert under_test == 'map map_name'
def test_GenerateMapCommand_NoMapArg_ReturnsEmptyString(self):
args_list = ['random_arg', 'stuff', 'map_name', 'another_arg']
under_test = ly_test_tools.launchers.platforms.android.launcher.generate_android_map_command(args_list)
assert under_test == ''
@mock.patch('os.listdir', mock.MagicMock())
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_package_name',
mock.MagicMock(return_value=PACKAGE_NAME))
@mock.patch('ly_test_tools.environment.file_system.create_backup', mock.MagicMock)
@mock.patch('ly_test_tools.environment.file_system.restore_backup', mock.MagicMock)
@mock.patch('ly_test_tools.mobile.android.check_adb_connection_state', mock.MagicMock)
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.open', mock.mock_open())
class TestAndroidLauncher:
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
def test_ReadDeviceConfigINI_ReturnDeviceID_DeviceIDSet(self, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
assert launcher._device_id == VALID_ANDROID_CONFIG['android']['id']
assert ['adb', '-s', launcher._device_id] == launcher._adb_prefix_command
mock_config.assert_called_once()
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
def test_ReadDeviceConfigINI_ReturnsInvalidConfig_SetupError(self, mock_config):
mock_config.return_value = {'device': {'id': 12345}}
mock_workspace = MockedWorkspace()
with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.mobile.android.forward_tcp')
@mock.patch('ly_test_tools.mobile.android.reverse_tcp')
@mock.patch('ly_test_tools.mobile.android.undo_tcp_port_changes')
def test_EnableAndroidCaps_SetsAndroidCaps_CallsReverseTCPForwardTCP(self, mock_undo_tcp, mock_reverse_tcp,
mock_forward_tcp, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
under_test._enable_android_capabilities()
assert mock_reverse_tcp.call_count == 2
mock_forward_tcp.assert_called_once()
mock_undo_tcp.assert_called_with(VALID_ANDROID_CONFIG['android']['id'])
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings')
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._is_valid_android_environment')
@mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
def test_Setup_ValidSetup_SetupCallsSucceed(self, mock_valid_env, mock_enable_caps, mock_config,
mock_configure_settings, mock_backup):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
mock_project_log_path = 'c:/mock_project/log/'
mock_workspace.paths.project_log.return_value = mock_project_log_path
under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
under_test.setup()
mock_enable_caps.assert_called_once()
mock_valid_env.assert_called_once()
mock_backup.assert_called_once()
mock_configure_settings.assert_called_once()
mock_workspace.shader_compiler.start.assert_called_once()
@mock.patch('ly_test_tools.mobile.android.can_run_android')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings', mock.MagicMock())
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings',
mock.MagicMock())
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
mock.MagicMock())
@mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
def test_Setup_InvalidSetupNoADB_RaisesNotImplementedErrorException(self, mock_config, mock_can_run_android):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
mock_can_run_android.return_value = False
under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
with pytest.raises(NotImplementedError):
under_test.setup()
@mock.patch('ly_test_tools.mobile.android.get_devices')
@mock.patch('ly_test_tools.mobile.android.can_run_android', mock.MagicMock(return_value=True))
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings', mock.MagicMock())
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings',
mock.MagicMock())
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
mock.MagicMock())
@mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
def test_Setup_InvalidSetupNoDeviceConnected_RaisesSetupErrorException(self, mock_config, mock_get_devices):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
mock_get_devices.return_value = []
under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
under_test.setup()
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher.restore_settings')
@mock.patch('ly_test_tools.mobile.android.undo_tcp_port_changes', mock.MagicMock)
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
mock.MagicMock)
def test_Teardown_ValidTeardown_TeardownSucceeds(self, mock_restore, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
launcher.teardown()
mock_restore.assert_called_once()
mock_workspace.shader_compiler.stop.assert_called_once()
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.o3de.settings.LySettings.modify_platform_setting', mock.MagicMock)
def test_ConfigureSettings_DefaultValues_SetsValues(self, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
launcher.configure_settings()
assert mock_workspace.settings.modify_platform_setting.call_count == 8
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.environment.process_utils.check_output')
@mock.patch('os.path.isfile', mock.MagicMock(return_value=False))
def test_Launch_HappyPathNoAutoexec_CallsLaunchCmd(self, mock_call, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
launcher.launch()
mock_call.assert_called_once_with([
'adb', '-s', VALID_ANDROID_CONFIG['android']['id'], 'shell', 'monkey', '-p', PACKAGE_NAME,
'-c', 'android.intent.category.LAUNCHER', '1'])
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.environment.process_utils.check_output')
@mock.patch('ly_test_tools.mobile.android.push_files_to_device', mock.MagicMock)
@mock.patch('os.path.isfile', mock.MagicMock(return_value=True))
def test_Launch_HappyPathHasAutoExec_PushesFilesToDevice(self, mock_push_files, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
launcher.launch()
mock_push_files.assert_called_once()
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.environment.process_utils.check_output')
@mock.patch('os.path.isfile', mock.MagicMock(return_value=False))
def test_Launch_MonkeyAbortedInLaunchResult_RaisesSetupError(self, mock_call, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_call.return_value = 'Monkey Aborted'
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
launcher.launch()
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_pid')
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
def test_IsAlive_MockPidDNE_False(self, mock_config, mock_pid):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
mock_pid.return_value = ""
assert not launcher.is_alive()
@mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_pid')
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
def test_IsAlive_MockPidExists_True(self, mock_config, mock_pid):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
mock_pid.return_value = "1234"
assert launcher.is_alive()
@mock.patch('ly_test_tools.environment.process_utils.check_call')
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
@mock.patch('ly_test_tools.mobile.android.check_adb_connection_state',
mock.MagicMock(return_value=ly_test_tools.mobile.android.SINGLE_DEVICE))
def test_Kill_HappyPath_KillCommandSuccess(self, mock_config, mock_call):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
launcher.kill()
mock_call.assert_called_once_with(
['adb', '-s', VALID_ANDROID_CONFIG['android']['id'], 'shell', 'am', 'force-stop', PACKAGE_NAME])
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
def test_BinaryPath_Called_RaisesNotImplementedError(self, mock_config):
mock_config.return_value = VALID_ANDROID_CONFIG
mock_workspace = MockedWorkspace()
launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
with pytest.raises(NotImplementedError):
launcher.binary_path()