test_launcher_android.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Unit Tests for android launcher-wrappers: all are sanity code-path tests, since no interprocess actions should be taken
  6. """
  7. import unittest.mock as mock
  8. import pytest
  9. import ly_test_tools.launchers
  10. import ly_test_tools.launchers.platforms.android.launcher
  11. import ly_test_tools.launchers.exceptions
  12. import ly_test_tools.mobile.android
  13. pytestmark = pytest.mark.SUITE_smoke
  14. VALID_ANDROID_CONFIG = {
  15. 'android': {
  16. 'id': '000000000000000'
  17. }
  18. }
  19. PACKAGE_NAME = "dummy_project_path"
  20. class MockedWorkspace(object):
  21. def __init__(self):
  22. self.paths = mock.MagicMock()
  23. self.setup_assistant = mock.MagicMock()
  24. self.asset_processor = mock.MagicMock()
  25. self.shader_compiler = mock.MagicMock()
  26. self.settings = mock.MagicMock()
  27. self.paths.engine_root.return_value = 'engine_path'
  28. self.paths.build_directory.return_value = 'build_directory'
  29. self.paths.autoexec_file.return_value = 'autoexec.cfg'
  30. self.project = 'project_name'
  31. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.open', mock.mock_open())
  32. class TestLauncherModule:
  33. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.json.loads')
  34. def test_GetPackageName_MockJsonHasKey_ReturnsValue(self, mock_json):
  35. dummy_name = "some_name"
  36. mock_json.return_value = {'android_settings': {'package_name': dummy_name}}
  37. under_test = ly_test_tools.launchers.platforms.android.launcher.get_package_name(PACKAGE_NAME)
  38. assert under_test == "some_name"
  39. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.json.loads')
  40. def test_GetPackageName_MockJsonMissingKey_SetupError(self, mock_json):
  41. mock_json.return_value = {"one": "two"}
  42. with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
  43. ly_test_tools.launchers.platforms.android.launcher.get_package_name(PACKAGE_NAME)
  44. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  45. def test_GetPid_EarlyVersionAndExplodes_NoPid(self, mock_output):
  46. mock_output.side_effect = ['23', Exception]
  47. under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
  48. assert mock_output.call_count == 2
  49. assert not under_test
  50. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  51. def test_GetPid_LaterVersionAndExplodes_NoPid(self, mock_output):
  52. mock_output.side_effect = ['25', Exception]
  53. under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
  54. assert mock_output.call_count == 2
  55. assert not under_test
  56. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  57. def test_GetPid_PidHasSingleValue_ReturnPid(self, mock_output):
  58. mock_output.side_effect = ['25', '12']
  59. under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
  60. assert mock_output.call_count == 2
  61. assert under_test == '12'
  62. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  63. def test_GetPid_PidHasMultipleValues_ReturnPid(self, mock_output):
  64. mock_output.side_effect = ['25', 'value 12 found']
  65. under_test = ly_test_tools.launchers.platforms.android.launcher.get_pid("dummy", ["dummy"])
  66. assert mock_output.call_count == 2
  67. assert under_test == '12'
  68. def test_GenerateLoadLevelCommand_HasArg_ReturnsLoadLevelCommand(self):
  69. args_list = ['random_arg', '+LoadLevel', 'map_name', 'another_arg']
  70. under_test = ly_test_tools.launchers.platforms.android.launcher.generate_android_loadlevel_command(args_list)
  71. assert under_test == 'LoadLevel map_name'
  72. def test_GenerateLoadLevelCommand_NoArg_ReturnsEmptyString(self):
  73. args_list = ['random_arg', 'stuff', 'map_name', 'another_arg']
  74. under_test = ly_test_tools.launchers.platforms.android.launcher.generate_android_loadlevel_command(args_list)
  75. assert under_test == ''
  76. def test_GenerateLoadLevelCommand_InvalidArgFormat_ReturnsEmptyString(self):
  77. args_list = ['random_arg', 'stuff', 'map_name', 'another_arg', '+LoadLevel']
  78. under_test = ly_test_tools.launchers.platforms.android.launcher.generate_android_loadlevel_command(args_list)
  79. assert under_test == ''
  80. @mock.patch('os.listdir', mock.MagicMock())
  81. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_package_name',
  82. mock.MagicMock(return_value=PACKAGE_NAME))
  83. @mock.patch('ly_test_tools.environment.file_system.create_backup', mock.MagicMock)
  84. @mock.patch('ly_test_tools.environment.file_system.restore_backup', mock.MagicMock)
  85. @mock.patch('ly_test_tools.mobile.android.check_adb_connection_state', mock.MagicMock)
  86. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.open', mock.mock_open())
  87. class TestAndroidLauncher:
  88. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  89. def test_ReadDeviceConfigINI_ReturnDeviceID_DeviceIDSet(self, mock_config):
  90. mock_config.return_value = VALID_ANDROID_CONFIG
  91. mock_workspace = MockedWorkspace()
  92. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  93. assert launcher._device_id == VALID_ANDROID_CONFIG['android']['id']
  94. assert ['adb', '-s', launcher._device_id] == launcher._adb_prefix_command
  95. mock_config.assert_called_once()
  96. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  97. def test_ReadDeviceConfigINI_ReturnsInvalidConfig_SetupError(self, mock_config):
  98. mock_config.return_value = {'device': {'id': 12345}}
  99. mock_workspace = MockedWorkspace()
  100. with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
  101. ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  102. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  103. @mock.patch('ly_test_tools.mobile.android.forward_tcp')
  104. @mock.patch('ly_test_tools.mobile.android.reverse_tcp')
  105. @mock.patch('ly_test_tools.mobile.android.undo_tcp_port_changes')
  106. def test_EnableAndroidCaps_SetsAndroidCaps_CallsReverseTCPForwardTCP(self, mock_undo_tcp, mock_reverse_tcp,
  107. mock_forward_tcp, mock_config):
  108. mock_config.return_value = VALID_ANDROID_CONFIG
  109. mock_workspace = MockedWorkspace()
  110. under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  111. under_test._enable_android_capabilities()
  112. assert mock_reverse_tcp.call_count == 2
  113. mock_forward_tcp.assert_called_once()
  114. mock_undo_tcp.assert_called_with(VALID_ANDROID_CONFIG['android']['id'])
  115. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings')
  116. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings')
  117. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  118. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities')
  119. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._is_valid_android_environment')
  120. @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
  121. def test_Setup_ValidSetup_SetupCallsSucceed(self, mock_valid_env, mock_enable_caps, mock_config,
  122. mock_configure_settings, mock_backup):
  123. mock_config.return_value = VALID_ANDROID_CONFIG
  124. mock_workspace = MockedWorkspace()
  125. mock_project_log_path = 'c:/mock_project/log/'
  126. mock_workspace.paths.project_log.return_value = mock_project_log_path
  127. under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  128. under_test.setup()
  129. mock_enable_caps.assert_called_once()
  130. mock_valid_env.assert_called_once()
  131. mock_backup.assert_called_once()
  132. mock_configure_settings.assert_called_once()
  133. mock_workspace.shader_compiler.start.assert_called_once()
  134. @mock.patch('ly_test_tools.mobile.android.can_run_android')
  135. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings', mock.MagicMock())
  136. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings',
  137. mock.MagicMock())
  138. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  139. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
  140. mock.MagicMock())
  141. @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
  142. def test_Setup_InvalidSetupNoADB_RaisesNotImplementedErrorException(self, mock_config, mock_can_run_android):
  143. mock_config.return_value = VALID_ANDROID_CONFIG
  144. mock_workspace = MockedWorkspace()
  145. mock_can_run_android.return_value = False
  146. under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  147. with pytest.raises(NotImplementedError):
  148. under_test.setup()
  149. @mock.patch('ly_test_tools.mobile.android.get_devices')
  150. @mock.patch('ly_test_tools.mobile.android.can_run_android', mock.MagicMock(return_value=True))
  151. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.backup_settings', mock.MagicMock())
  152. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher.configure_settings',
  153. mock.MagicMock())
  154. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  155. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
  156. mock.MagicMock())
  157. @mock.patch('ly_test_tools.environment.waiter.wait_for', mock.MagicMock(return_value=True))
  158. def test_Setup_InvalidSetupNoDeviceConnected_RaisesSetupErrorException(self, mock_config, mock_get_devices):
  159. mock_config.return_value = VALID_ANDROID_CONFIG
  160. mock_workspace = MockedWorkspace()
  161. mock_get_devices.return_value = []
  162. under_test = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  163. with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
  164. under_test.setup()
  165. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  166. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher.restore_settings')
  167. @mock.patch('ly_test_tools.mobile.android.undo_tcp_port_changes', mock.MagicMock)
  168. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.AndroidLauncher._enable_android_capabilities',
  169. mock.MagicMock)
  170. def test_Teardown_ValidTeardown_TeardownSucceeds(self, mock_restore, mock_config):
  171. mock_config.return_value = VALID_ANDROID_CONFIG
  172. mock_workspace = MockedWorkspace()
  173. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  174. launcher.teardown()
  175. mock_restore.assert_called_once()
  176. mock_workspace.shader_compiler.stop.assert_called_once()
  177. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  178. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  179. @mock.patch('os.path.isfile', mock.MagicMock(return_value=False))
  180. def test_Launch_HappyPathNoAutoexec_CallsLaunchCmd(self, mock_call, mock_config):
  181. mock_config.return_value = VALID_ANDROID_CONFIG
  182. mock_workspace = MockedWorkspace()
  183. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  184. launcher.launch()
  185. mock_call.assert_called_once_with([
  186. 'adb', '-s', VALID_ANDROID_CONFIG['android']['id'], 'shell', 'monkey', '-p', PACKAGE_NAME,
  187. '-c', 'android.intent.category.LAUNCHER', '1'])
  188. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  189. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  190. @mock.patch('ly_test_tools.mobile.android.push_files_to_device', mock.MagicMock)
  191. @mock.patch('os.path.isfile', mock.MagicMock(return_value=True))
  192. def test_Launch_HappyPathHasAutoExec_PushesFilesToDevice(self, mock_push_files, mock_config):
  193. mock_config.return_value = VALID_ANDROID_CONFIG
  194. mock_workspace = MockedWorkspace()
  195. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  196. launcher.launch()
  197. mock_push_files.assert_called_once()
  198. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  199. @mock.patch('ly_test_tools.environment.process_utils.check_output')
  200. @mock.patch('os.path.isfile', mock.MagicMock(return_value=False))
  201. def test_Launch_MonkeyAbortedInLaunchResult_RaisesSetupError(self, mock_call, mock_config):
  202. mock_config.return_value = VALID_ANDROID_CONFIG
  203. mock_call.return_value = 'Monkey Aborted'
  204. mock_workspace = MockedWorkspace()
  205. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  206. with pytest.raises(ly_test_tools.launchers.exceptions.SetupError):
  207. launcher.launch()
  208. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_pid')
  209. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  210. def test_IsAlive_MockPidDNE_False(self, mock_config, mock_pid):
  211. mock_config.return_value = VALID_ANDROID_CONFIG
  212. mock_workspace = MockedWorkspace()
  213. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  214. mock_pid.return_value = ""
  215. assert not launcher.is_alive()
  216. @mock.patch('ly_test_tools.launchers.platforms.android.launcher.get_pid')
  217. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  218. def test_IsAlive_MockPidExists_True(self, mock_config, mock_pid):
  219. mock_config.return_value = VALID_ANDROID_CONFIG
  220. mock_workspace = MockedWorkspace()
  221. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  222. mock_pid.return_value = "1234"
  223. assert launcher.is_alive()
  224. @mock.patch('ly_test_tools.environment.process_utils.check_call')
  225. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  226. @mock.patch('ly_test_tools.mobile.android.check_adb_connection_state',
  227. mock.MagicMock(return_value=ly_test_tools.mobile.android.SINGLE_DEVICE))
  228. def test_Kill_HappyPath_KillCommandSuccess(self, mock_config, mock_call):
  229. mock_config.return_value = VALID_ANDROID_CONFIG
  230. mock_workspace = MockedWorkspace()
  231. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  232. # This is a direct call to a protected method, but the point of the test is to ensure functionality of this
  233. # protected method, so we will allow this exception
  234. launcher._kill()
  235. mock_call.assert_called_once_with(
  236. ['adb', '-s', VALID_ANDROID_CONFIG['android']['id'], 'shell', 'am', 'force-stop', PACKAGE_NAME])
  237. @mock.patch('ly_test_tools.launchers.platforms.base.Launcher._config_ini_to_dict')
  238. def test_BinaryPath_Called_RaisesNotImplementedError(self, mock_config):
  239. mock_config.return_value = VALID_ANDROID_CONFIG
  240. mock_workspace = MockedWorkspace()
  241. launcher = ly_test_tools.launchers.AndroidLauncher(mock_workspace, ["dummy"])
  242. with pytest.raises(NotImplementedError):
  243. launcher.binary_path()