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

328 lines
16 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
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 == 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('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"])
# This is a direct call to a protected method, but the point of the test is to ensure functionality of this
# protected method, so we will allow this exception
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()