unit_test_android_deployment.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  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. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. import datetime
  9. import os
  10. import pathlib
  11. import platform
  12. import pytest
  13. import time
  14. from unittest.mock import patch, Mock
  15. from cmake.Tools.Platform.Android import android_deployment
  16. TEST_GAME_NAME = "Foo"
  17. TEST_DEV_ROOT = pathlib.Path("Foo")
  18. TEST_ASSET_MODE = 'LOOSE'
  19. TEST_ASSET_TYPE = 'android'
  20. TEST_ANDROID_SDK_PATH = pathlib.Path('c:\\AndroidSDK')
  21. TEST_BUILD_DIR = 'android_gradle_test'
  22. TEST_DEVICE_ID = '9A201FFAZ000ER'
  23. def match_arg_list(input_args, expected_args):
  24. if len(input_args) != len(expected_args):
  25. return False
  26. for index in range(len(input_args)):
  27. if input_args[index] != expected_args[index]:
  28. return False
  29. return True
  30. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.read_android_settings', return_value={})
  31. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.resolve_adb_tool', return_value=pathlib.Path("Foo"))
  32. def test_Initialize(mock_resolve_adb_tool, mock_read_android_settings):
  33. attrs = {'glob.return_value': ["foo.bar"]}
  34. mock_local_asset_path = Mock(**attrs)
  35. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  36. build_dir=TEST_BUILD_DIR,
  37. configuration='profile',
  38. game_name=TEST_GAME_NAME,
  39. asset_mode=TEST_ASSET_MODE,
  40. asset_type=TEST_ASSET_TYPE,
  41. embedded_assets=True,
  42. android_device_filter=None,
  43. clean_deploy=False,
  44. android_sdk_path=TEST_ANDROID_SDK_PATH,
  45. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  46. mock_resolve_adb_tool.assert_called_once_with(TEST_ANDROID_SDK_PATH)
  47. mock_read_android_settings.assert_called_once_with(TEST_DEV_ROOT, TEST_GAME_NAME)
  48. assert inst
  49. def test_read_android_settings(tmpdir):
  50. game_name = "Foo"
  51. package_name = "o3de.org.Foo"
  52. tmpdir.ensure(f'dev_root/{game_name}/Platform/Android/android_project.json')
  53. game_project_json_file = tmpdir.join(f'dev_root/{game_name}/Platform/Android/android_project.json')
  54. game_project_json_file.write(f'{{"android_settings": {{"package_name": "{package_name}" }} }}')
  55. result = android_deployment.AndroidDeployment.read_android_settings(pathlib.Path(tmpdir.join('dev_root').realpath()), game_name)
  56. assert result['package_name'] == package_name
  57. def test_resolve_adb_tool(tmpdir):
  58. sdk_path = 'android_sdk'
  59. adb_target = 'adb.exe' if platform.system() == 'Windows' else 'adb'
  60. tmpdir.ensure(f'{sdk_path}/platform-tools/{adb_target}')
  61. dummy_adb_file = tmpdir.join(f'{sdk_path}/platform-tools/{adb_target}')
  62. dummy_adb_file.write('adb')
  63. result = android_deployment.AndroidDeployment.resolve_adb_tool(pathlib.Path(tmpdir.join(sdk_path).realpath()))
  64. adb_name = os.path.basename(result).lower()
  65. assert adb_name == adb_target
  66. @patch('subprocess.check_output', return_value=b'PASS')
  67. def test_adb_call(mock_check_output):
  68. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  69. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  70. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  71. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  72. build_dir=TEST_BUILD_DIR,
  73. configuration='profile',
  74. game_name=TEST_GAME_NAME,
  75. asset_mode=TEST_ASSET_MODE,
  76. asset_type=TEST_ASSET_TYPE,
  77. embedded_assets=True,
  78. android_device_filter=None,
  79. clean_deploy=False,
  80. android_sdk_path=TEST_ANDROID_SDK_PATH,
  81. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  82. result = inst.adb_call(arg_list=['foo'], device_id='123')
  83. mock_check_output.assert_called_once()
  84. assert result == 'PASS'
  85. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_call', return_value='PASS')
  86. def test_adb_shell(mock_adb_call):
  87. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  88. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  89. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  90. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  91. build_dir=TEST_BUILD_DIR,
  92. configuration='profile',
  93. game_name=TEST_GAME_NAME,
  94. asset_mode=TEST_ASSET_MODE,
  95. asset_type=TEST_ASSET_TYPE,
  96. embedded_assets=True,
  97. android_device_filter=None,
  98. clean_deploy=False,
  99. android_sdk_path=TEST_ANDROID_SDK_PATH,
  100. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  101. result = inst.adb_shell(command='foo me', device_id='123')
  102. expected_args = ['shell', 'foo me']
  103. mock_adb_call.assert_called_once_with(expected_args, device_id='123')
  104. assert result == 'PASS'
  105. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value='output')
  106. def test_adb_ls_success(mock_adb_shell):
  107. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  108. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  109. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  110. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  111. build_dir=TEST_BUILD_DIR,
  112. configuration='profile',
  113. game_name=TEST_GAME_NAME,
  114. asset_mode=TEST_ASSET_MODE,
  115. asset_type=TEST_ASSET_TYPE,
  116. embedded_assets=True,
  117. android_device_filter=None,
  118. clean_deploy=False,
  119. android_sdk_path=TEST_ANDROID_SDK_PATH,
  120. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  121. result, output = inst.adb_ls(path='/foo/bar', device_id='123')
  122. mock_adb_shell.assert_called_once_with(command='ls /foo/bar', device_id='123')
  123. assert result
  124. assert output == 'output'
  125. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value='')
  126. def test_adb_ls_error_no_output(mock_adb_shell):
  127. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  128. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  129. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  130. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  131. build_dir=TEST_BUILD_DIR,
  132. configuration='profile',
  133. game_name=TEST_GAME_NAME,
  134. asset_mode=TEST_ASSET_MODE,
  135. asset_type=TEST_ASSET_TYPE,
  136. embedded_assets=True,
  137. android_device_filter=None,
  138. clean_deploy=False,
  139. android_sdk_path=TEST_ANDROID_SDK_PATH,
  140. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  141. result, output = inst.adb_ls(path='/foo/bar', device_id='123')
  142. mock_adb_shell.assert_called_once_with(command='ls /foo/bar', device_id='123')
  143. assert not result
  144. assert output is None
  145. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value='No such file or directory')
  146. def test_adb_ls_error_no_such_file(mock_adb_shell):
  147. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  148. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  149. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  150. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  151. build_dir=TEST_BUILD_DIR,
  152. configuration='profile',
  153. game_name=TEST_GAME_NAME,
  154. asset_mode=TEST_ASSET_MODE,
  155. asset_type=TEST_ASSET_TYPE,
  156. embedded_assets=True,
  157. android_device_filter=None,
  158. clean_deploy=False,
  159. android_sdk_path=TEST_ANDROID_SDK_PATH,
  160. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH)
  161. result, output = inst.adb_ls(path='/foo/bar', device_id='123')
  162. mock_adb_shell.assert_called_once_with(command='ls /foo/bar', device_id='123')
  163. assert not result
  164. assert output == 'No such file or directory'
  165. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value='Permission denied')
  166. def test_adb_ls_error_permission_denied(mock_adb_shell):
  167. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  168. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  169. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  170. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  171. build_dir=TEST_BUILD_DIR,
  172. configuration='profile',
  173. game_name=TEST_GAME_NAME,
  174. asset_mode=TEST_ASSET_MODE,
  175. asset_type=TEST_ASSET_TYPE,
  176. embedded_assets=True,
  177. android_device_filter=None,
  178. clean_deploy=False,
  179. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  180. android_sdk_path=TEST_ANDROID_SDK_PATH)
  181. result, output = inst.adb_ls(path='/foo/bar', device_id='123')
  182. mock_adb_shell.assert_called_once_with(command='ls /foo/bar', device_id='123')
  183. assert not result
  184. assert output == 'Permission denied'
  185. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_call',
  186. return_value=f'List of devices attached{os.linesep}9A201FFAZ000ER device{os.linesep}1A201FFAZ000ER device{os.linesep}9A201FFAZ456ER unauthorized')
  187. def test_get_target_android_devices(mock_adb_call):
  188. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  189. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  190. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  191. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  192. build_dir=TEST_BUILD_DIR,
  193. configuration='profile',
  194. game_name=TEST_GAME_NAME,
  195. asset_mode=TEST_ASSET_MODE,
  196. asset_type=TEST_ASSET_TYPE,
  197. embedded_assets=True,
  198. android_device_filter=f'{TEST_DEVICE_ID},AAAAASDSFGG',
  199. clean_deploy=False,
  200. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  201. android_sdk_path=TEST_ANDROID_SDK_PATH)
  202. result = inst.get_target_android_devices()
  203. mock_adb_call.assert_called_once_with("devices")
  204. assert len(result) == 1
  205. assert result[0] == TEST_DEVICE_ID
  206. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_ls', return_value=(True,"file"))
  207. def test_check_known_android_paths_success(mock_adb_ls):
  208. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  209. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  210. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  211. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  212. build_dir=TEST_BUILD_DIR,
  213. configuration='profile',
  214. game_name=TEST_GAME_NAME,
  215. asset_mode=TEST_ASSET_MODE,
  216. asset_type=TEST_ASSET_TYPE,
  217. embedded_assets=True,
  218. android_device_filter=None,
  219. clean_deploy=False,
  220. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  221. android_sdk_path=TEST_ANDROID_SDK_PATH)
  222. result = inst.check_known_android_paths(device_id='123')
  223. mock_adb_ls.assert_called_once()
  224. assert result == android_deployment.KNOWN_ANDROID_EXTERNAL_STORAGE_PATHS[0][:-1]
  225. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_ls', return_value=(False,None))
  226. def test_check_known_android_paths_fail(mock_adb_ls):
  227. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  228. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  229. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  230. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  231. build_dir=TEST_BUILD_DIR,
  232. configuration='profile',
  233. game_name=TEST_GAME_NAME,
  234. asset_mode=TEST_ASSET_MODE,
  235. asset_type=TEST_ASSET_TYPE,
  236. embedded_assets=True,
  237. android_device_filter=None,
  238. clean_deploy=False,
  239. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  240. android_sdk_path=TEST_ANDROID_SDK_PATH)
  241. result = inst.check_known_android_paths(device_id='123')
  242. assert not result
  243. assert mock_adb_ls.call_count == len(android_deployment.KNOWN_ANDROID_EXTERNAL_STORAGE_PATHS)
  244. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value=None)
  245. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.check_known_android_paths', return_value="PATH")
  246. def test_detect_device_storage_path_no_external_storage_env(mock_check_known_android_paths, mock_adb_shell):
  247. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  248. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")),\
  249. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  250. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  251. build_dir=TEST_BUILD_DIR,
  252. configuration='profile',
  253. game_name=TEST_GAME_NAME,
  254. asset_mode=TEST_ASSET_MODE,
  255. asset_type=TEST_ASSET_TYPE,
  256. embedded_assets=True,
  257. android_device_filter=None,
  258. clean_deploy=False,
  259. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  260. android_sdk_path=TEST_ANDROID_SDK_PATH)
  261. result = inst.detect_device_storage_path(device_id=TEST_DEVICE_ID)
  262. assert result == "PATH"
  263. mock_adb_shell.assert_called_once()
  264. mock_check_known_android_paths.assert_called_once_with(TEST_DEVICE_ID)
  265. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value="NotSet")
  266. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.check_known_android_paths', return_value="PATH")
  267. def test_detect_device_storage_path_invalid_external_storage_env(mock_check_known_android_paths, mock_adb_shell):
  268. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  269. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")),\
  270. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  271. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  272. build_dir=TEST_BUILD_DIR,
  273. configuration='profile',
  274. game_name=TEST_GAME_NAME,
  275. asset_mode=TEST_ASSET_MODE,
  276. asset_type=TEST_ASSET_TYPE,
  277. embedded_assets=True,
  278. android_device_filter=None,
  279. clean_deploy=False,
  280. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  281. android_sdk_path=TEST_ANDROID_SDK_PATH)
  282. result = inst.detect_device_storage_path(device_id=TEST_DEVICE_ID)
  283. assert result == "PATH"
  284. mock_adb_shell.assert_called_once()
  285. mock_check_known_android_paths.assert_called_once_with(TEST_DEVICE_ID)
  286. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value="EXTERNAL_STORAGE=/foo/bar")
  287. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_ls', return_value=(True, "foo.bar"))
  288. def test_detect_device_storage_path_valid_external_storage_env(mock_adb_ls, mock_adb_shell):
  289. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  290. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")),\
  291. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  292. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  293. build_dir=TEST_BUILD_DIR,
  294. configuration='profile',
  295. game_name=TEST_GAME_NAME,
  296. asset_mode=TEST_ASSET_MODE,
  297. asset_type=TEST_ASSET_TYPE,
  298. embedded_assets=True,
  299. android_device_filter=None,
  300. clean_deploy=False,
  301. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  302. android_sdk_path=TEST_ANDROID_SDK_PATH)
  303. result = inst.detect_device_storage_path(device_id=TEST_DEVICE_ID)
  304. assert result == "/foo/bar"
  305. mock_adb_shell.assert_called_once()
  306. mock_adb_ls.assert_called_once_with(path='/foo/bar', device_id=TEST_DEVICE_ID)
  307. def test_detect_device_storage_path_real_path():
  308. def _mock_adb_shell(command, device_id):
  309. if command == "set | grep EXTERNAL_STORAGE":
  310. return "EXTERNAL_STORAGE=/foo/bar"
  311. elif command == f'realpath /foo/bar':
  312. return "/foo_reals"
  313. else:
  314. raise AssertionError
  315. def _mock_adb_ls(path, device_id, args=None):
  316. if path == "/foo/bar":
  317. return False, None
  318. elif path == '/foo_reals':
  319. return True, "foo.bar"
  320. else:
  321. raise AssertionError
  322. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  323. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  324. patch.object(android_deployment.AndroidDeployment, 'adb_shell', wraps=_mock_adb_shell), \
  325. patch.object(android_deployment.AndroidDeployment, 'adb_ls', wraps=_mock_adb_ls), \
  326. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  327. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  328. build_dir=TEST_BUILD_DIR,
  329. configuration='profile',
  330. game_name=TEST_GAME_NAME,
  331. asset_mode=TEST_ASSET_MODE,
  332. asset_type=TEST_ASSET_TYPE,
  333. embedded_assets=True,
  334. android_device_filter=None,
  335. clean_deploy=False,
  336. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  337. android_sdk_path=TEST_ANDROID_SDK_PATH)
  338. result = inst.detect_device_storage_path(device_id=TEST_DEVICE_ID)
  339. assert result == "/foo_reals"
  340. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.check_known_android_paths', return_value="PATH")
  341. def test_detect_device_storage_path_real_path_fail(mock_check_known_android_paths):
  342. def _mock_adb_shell(command, device_id):
  343. if command == "set | grep EXTERNAL_STORAGE":
  344. return "EXTERNAL_STORAGE=/foo/bar"
  345. elif command == f'realpath /foo/bar':
  346. return "/foo_reals"
  347. else:
  348. raise AssertionError
  349. def _mock_adb_ls(path, device_id, args=None):
  350. if path == "/foo/bar":
  351. return False, None
  352. elif path == '/foo_reals':
  353. return False, None
  354. else:
  355. raise AssertionError
  356. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  357. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  358. patch.object(android_deployment.AndroidDeployment, 'adb_shell', wraps=_mock_adb_shell), \
  359. patch.object(android_deployment.AndroidDeployment, 'adb_ls', wraps=_mock_adb_ls), \
  360. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  361. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  362. build_dir=TEST_BUILD_DIR,
  363. configuration='profile',
  364. game_name=TEST_GAME_NAME,
  365. asset_mode=TEST_ASSET_MODE,
  366. asset_type=TEST_ASSET_TYPE,
  367. embedded_assets=True,
  368. android_device_filter=None,
  369. clean_deploy=False,
  370. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  371. android_sdk_path=TEST_ANDROID_SDK_PATH)
  372. result = inst.detect_device_storage_path(device_id=TEST_DEVICE_ID)
  373. assert result == "PATH"
  374. mock_check_known_android_paths.assert_called_once_with(TEST_DEVICE_ID)
  375. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value="2020-04-30 09:20:00.0000")
  376. def test_get_device_file_timestamp_success(mock_adb_shell):
  377. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  378. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  379. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  380. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  381. build_dir=TEST_BUILD_DIR,
  382. configuration='profile',
  383. game_name=TEST_GAME_NAME,
  384. asset_mode=TEST_ASSET_MODE,
  385. asset_type=TEST_ASSET_TYPE,
  386. embedded_assets=True,
  387. android_device_filter=None,
  388. clean_deploy=False,
  389. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  390. android_sdk_path=TEST_ANDROID_SDK_PATH)
  391. remote_path = "/foo/bar/timestamp.txt"
  392. result = inst.get_device_file_timestamp(remote_file_path=remote_path,
  393. device_id=TEST_DEVICE_ID)
  394. assert result == time.mktime(time.strptime("2020-04-30 09:20:00.0000", '%Y-%m-%d %H:%M:%S.%f'))
  395. mock_adb_shell.assert_called_once_with(command=f'cat {remote_path}',
  396. device_id=TEST_DEVICE_ID)
  397. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value=None)
  398. def test_get_device_file_timestamp_no_file(mock_adb_shell):
  399. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  400. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  401. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  402. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  403. build_dir=TEST_BUILD_DIR,
  404. configuration='profile',
  405. game_name=TEST_GAME_NAME,
  406. asset_mode=TEST_ASSET_MODE,
  407. asset_type=TEST_ASSET_TYPE,
  408. embedded_assets=True,
  409. android_device_filter=None,
  410. clean_deploy=False,
  411. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  412. android_sdk_path=TEST_ANDROID_SDK_PATH)
  413. remote_path = "/foo/bar/timestamp.txt"
  414. result = inst.get_device_file_timestamp(remote_file_path=remote_path,
  415. device_id=TEST_DEVICE_ID)
  416. assert not result
  417. mock_adb_shell.assert_called_once_with(command=f'cat {remote_path}',
  418. device_id=TEST_DEVICE_ID)
  419. @patch('cmake.Tools.Platform.Android.android_deployment.AndroidDeployment.adb_shell', return_value="ZZZZXXXX")
  420. def test_get_device_file_timestamp_bad_timestamp_file(mock_adb_shell):
  421. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  422. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  423. patch.object(pathlib.Path, 'glob', return_value=["foo.bar"]):
  424. inst = android_deployment.AndroidDeployment(dev_root=TEST_DEV_ROOT,
  425. build_dir=TEST_BUILD_DIR,
  426. configuration='profile',
  427. game_name=TEST_GAME_NAME,
  428. asset_mode=TEST_ASSET_MODE,
  429. asset_type=TEST_ASSET_TYPE,
  430. embedded_assets=True,
  431. android_device_filter=None,
  432. clean_deploy=False,
  433. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  434. android_sdk_path=TEST_ANDROID_SDK_PATH)
  435. remote_path = "/foo/bar/timestamp.txt"
  436. result = inst.get_device_file_timestamp(remote_file_path=remote_path,
  437. device_id=TEST_DEVICE_ID)
  438. assert not result
  439. mock_adb_shell.assert_called_once_with(command=f'cat {remote_path}',
  440. device_id=TEST_DEVICE_ID)
  441. def test_update_device_file_timestamp(tmpdir):
  442. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets/deploy.timestamp").ensure()
  443. mock_dev_root = tmpdir.join(TEST_DEV_ROOT).realpath()
  444. with patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={}), \
  445. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  446. patch.object(android_deployment.AndroidDeployment, 'adb_call', return_value="") as mock_adb_call:
  447. inst = android_deployment.AndroidDeployment(dev_root=mock_dev_root,
  448. build_dir=TEST_BUILD_DIR,
  449. configuration='profile',
  450. game_name=TEST_GAME_NAME,
  451. asset_mode=TEST_ASSET_MODE,
  452. asset_type=TEST_ASSET_TYPE,
  453. embedded_assets=False,
  454. android_device_filter=None,
  455. clean_deploy=False,
  456. deployment_type=android_deployment.AndroidDeployment.DEPLOY_ASSETS_ONLY,
  457. android_sdk_path=TEST_ANDROID_SDK_PATH)
  458. remote_path = "/foo/bar/timestamp.txt"
  459. inst.update_device_file_timestamp(relative_assets_path=remote_path,
  460. device_id=TEST_DEVICE_ID)
  461. mock_adb_call.assert_called()
  462. @pytest.mark.parametrize(
  463. "test_config, test_package_name, test_device_storage_path", [
  464. pytest.param('profile', 'org.o3de.foo', '/data/fool_storage'),
  465. pytest.param('debug', 'org.o3de.foo', '/data/fool_storage'),
  466. pytest.param('profile', 'org.o3de.bar', '/data/fool_storage'),
  467. pytest.param('debug', 'org.o3de.bar', '/data/fool_storage'),
  468. pytest.param('profile', 'org.o3de.foo', '/data/fool_storage2'),
  469. pytest.param('debug', 'org.o3de.foo', '/data/fool_storage2'),
  470. pytest.param('profile', 'org.o3de.bar', '/data/fool_storage2'),
  471. pytest.param('debug', 'org.o3de.bar', '/data/fool_storage2')
  472. ]
  473. )
  474. def test_execute_success(tmpdir, test_config, test_package_name, test_device_storage_path):
  475. mock_dev_root = tmpdir.join(TEST_DEV_ROOT).realpath()
  476. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").ensure()
  477. expected_apk_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").realpath())
  478. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets/dummy.txt").ensure()
  479. expected_asset_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets").realpath())
  480. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/main/assets/Registry/dummy.txt").ensure()
  481. expected_registry_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/main/assets/Registry").realpath())
  482. expected_storage_registry_path = f'{test_device_storage_path}/Android/data/{test_package_name}/files/Registry'
  483. def _mock_adb_call(arg_list, device_id=None):
  484. if arg_list == "start-server":
  485. return "SUCCESS"
  486. if arg_list == "kill-server":
  487. return "SUCCESS"
  488. elif isinstance(arg_list, list):
  489. if match_arg_list(arg_list, ['install', '-r', expected_apk_path]):
  490. return "SUCCESS"
  491. elif match_arg_list(arg_list, ['install', '-t', '-r', expected_apk_path]):
  492. return "SUCCESS"
  493. elif match_arg_list(arg_list, ['push', f'{expected_asset_path}/.', f'{test_device_storage_path}/Android/data/{test_package_name}/files']):
  494. return "SUCCESS"
  495. elif match_arg_list(arg_list, ['push', expected_registry_path, expected_storage_registry_path]):
  496. return "SUCCESS"
  497. elif match_arg_list(arg_list, ['shell', 'cmd', 'package', 'list', 'packages', test_package_name]):
  498. return "SUCCESS"
  499. elif match_arg_list(arg_list, ['shell', f'mkdir {test_device_storage_path}/Android/data/{test_package_name}']):
  500. return "SUCCESS"
  501. elif match_arg_list(arg_list, ['shell', f'mkdir {test_device_storage_path}/Android/data/{test_package_name}/files']):
  502. return "SUCCESS"
  503. elif match_arg_list(arg_list, ['shell', f'mkdir {test_device_storage_path}/Android/data/{test_package_name}/files']):
  504. return "SUCCESS"
  505. raise AssertionError
  506. with patch.object(android_deployment.AndroidDeployment, 'get_target_android_devices', return_value=[TEST_DEVICE_ID]), \
  507. patch.object(android_deployment.AndroidDeployment, 'detect_device_storage_path', return_value=test_device_storage_path), \
  508. patch.object(android_deployment.AndroidDeployment, 'get_device_file_timestamp', return_value=None), \
  509. patch.object(android_deployment.AndroidDeployment, 'update_device_file_timestamp') as mock_update_device_file_timestamp, \
  510. patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={'package_name': test_package_name}), \
  511. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  512. patch.object(android_deployment.AndroidDeployment, 'adb_call', wraps=_mock_adb_call) as mock_adb_call, \
  513. patch.object(pathlib.Path, 'glob', return_value=[pathlib.Path("foo.bar")]):
  514. inst = android_deployment.AndroidDeployment(dev_root=mock_dev_root,
  515. build_dir=TEST_BUILD_DIR,
  516. configuration=test_config,
  517. game_name=TEST_GAME_NAME,
  518. asset_mode=TEST_ASSET_MODE,
  519. asset_type=TEST_ASSET_TYPE,
  520. embedded_assets=False,
  521. android_device_filter=None,
  522. clean_deploy=False,
  523. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  524. android_sdk_path=TEST_ANDROID_SDK_PATH)
  525. inst.execute()
  526. mock_update_device_file_timestamp.assert_called_once()
  527. assert mock_adb_call.call_count == 6
  528. @pytest.mark.parametrize(
  529. "test_game_name, test_config, test_package_name, test_device_storage_path, test_asset_type", [
  530. pytest.param('game1','profile', 'org.o3de.foo', '/data/fool_storage', 'android'),
  531. pytest.param('game1','debug', 'org.o3de.foo', '/data/fool_storage', 'android'),
  532. pytest.param('game2','profile', 'org.o3de.bar', '/data/fool_storage', 'android'),
  533. pytest.param('game2','debug', 'org.o3de.bar', '/data/fool_storage', 'android'),
  534. pytest.param('game3','profile', 'org.o3de.foo', '/data/fool_storage2', 'pc'),
  535. pytest.param('game3','debug', 'org.o3de.foo', '/data/fool_storage2', 'pc'),
  536. pytest.param('game4','profile', 'org.o3de.bar', '/data/fool_storage2', 'pc'),
  537. pytest.param('game4','debug', 'org.o3de.bar', '/data/fool_storage2', 'pc')
  538. ]
  539. )
  540. def test_execute_clean_deploy_success(tmpdir, test_game_name, test_config, test_package_name, test_device_storage_path, test_asset_type):
  541. mock_dev_root = tmpdir.join(TEST_DEV_ROOT).realpath()
  542. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").ensure()
  543. expected_apk_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").realpath())
  544. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets/Registry/dummy.txt").ensure()
  545. expected_asset_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets").realpath())
  546. expected_registry_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/main/assets/Registry").realpath())
  547. expected_storage_path = f'{test_device_storage_path}/Android/data/{test_package_name}/files'
  548. expected_storage_registry_path = f'{test_device_storage_path}/Android/data/{test_package_name}/files/Registry'
  549. def _mock_adb_call(arg_list, device_id=None):
  550. if arg_list == "start-server":
  551. return "SUCCESS"
  552. if arg_list == "kill-server":
  553. return "SUCCESS"
  554. elif isinstance(arg_list,list):
  555. if match_arg_list(arg_list, ['install', '-r', expected_apk_path]):
  556. return "SUCCESS"
  557. elif match_arg_list(arg_list, ['install', '-t', '-r', expected_apk_path]):
  558. return "SUCCESS"
  559. elif match_arg_list(arg_list, ['push', expected_asset_path, '-r', expected_apk_path, expected_storage_path]):
  560. return "SUCCESS"
  561. elif match_arg_list(arg_list, ['shell', 'cmd', 'package', 'list', 'packages', test_package_name]):
  562. return test_package_name
  563. elif match_arg_list(arg_list, ['uninstall', test_package_name]):
  564. return "SUCCESS"
  565. elif match_arg_list(arg_list, ['push', f'{expected_asset_path}/.', expected_storage_path]):
  566. return "SUCCESS"
  567. elif match_arg_list(arg_list, ['push', expected_registry_path, expected_storage_registry_path]):
  568. return "SUCCESS"
  569. elif match_arg_list(arg_list, ['push', expected_registry_path, expected_storage_registry_path]):
  570. return "SUCCESS"
  571. raise AssertionError
  572. def _mock_adb_shell(command, device_id):
  573. assert command.startswith('rm -rf') or command.startswith('mkdir')
  574. def _mock_adb_ls(path, device_id, args=None):
  575. if path == "/foo/bar":
  576. return False, None
  577. elif path == '/foo_reals':
  578. return False, None
  579. else:
  580. raise AssertionError
  581. with patch.object(android_deployment.AndroidDeployment, 'get_target_android_devices', return_value=[TEST_DEVICE_ID]) as mock_get_target_android_devices, \
  582. patch.object(android_deployment.AndroidDeployment, 'detect_device_storage_path', return_value=test_device_storage_path) as mock_detect_device_storage_path, \
  583. patch.object(android_deployment.AndroidDeployment, 'get_device_file_timestamp', return_value=None) as mock_get_device_file_timestamp, \
  584. patch.object(android_deployment.AndroidDeployment, 'update_device_file_timestamp') as mock_update_device_file_timestamp, \
  585. patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={'package_name': test_package_name}), \
  586. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  587. patch.object(android_deployment.AndroidDeployment, 'adb_call', wraps=_mock_adb_call) as mock_adb_call, \
  588. patch.object(android_deployment.AndroidDeployment, 'adb_shell', wraps=_mock_adb_shell) as mock_adb_shell, \
  589. patch.object(android_deployment.AndroidDeployment, 'adb_ls', wraps=_mock_adb_ls), \
  590. patch.object(pathlib.Path, 'glob', return_value=[pathlib.Path("foo.bar")]):
  591. inst = android_deployment.AndroidDeployment(dev_root=mock_dev_root,
  592. build_dir=TEST_BUILD_DIR,
  593. configuration=test_config,
  594. game_name=test_game_name,
  595. asset_mode=TEST_ASSET_MODE,
  596. asset_type=test_asset_type,
  597. embedded_assets=False,
  598. android_device_filter=None,
  599. clean_deploy=True,
  600. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  601. android_sdk_path=TEST_ANDROID_SDK_PATH)
  602. inst.execute()
  603. assert mock_adb_call.call_count == 6
  604. mock_adb_shell.assert_called()
  605. mock_update_device_file_timestamp.assert_called_once()
  606. mock_get_device_file_timestamp.assert_called_once()
  607. mock_detect_device_storage_path.assert_called_once()
  608. mock_get_target_android_devices.assert_called_once()
  609. @pytest.mark.parametrize(
  610. "test_config, test_package_name, test_device_storage_path", [
  611. pytest.param('profile', 'org.o3de.foo', '/data/fool_storage'),
  612. pytest.param('debug', 'org.o3de.foo', '/data/fool_storage'),
  613. pytest.param('profile', 'org.o3de.bar', '/data/fool_storage'),
  614. pytest.param('debug', 'org.o3de.bar', '/data/fool_storage'),
  615. pytest.param('profile', 'org.o3de.foo', '/data/fool_storage2'),
  616. pytest.param('debug', 'org.o3de.foo', '/data/fool_storage2'),
  617. pytest.param('profile', 'org.o3de.bar', '/data/fool_storage2'),
  618. pytest.param('debug', 'org.o3de.bar', '/data/fool_storage2')
  619. ]
  620. )
  621. def test_execute_incremental_deploy_success(tmpdir, test_config, test_package_name, test_device_storage_path):
  622. mock_dev_root = tmpdir.join(TEST_DEV_ROOT).realpath()
  623. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").ensure()
  624. expected_apk_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/build/outputs/apk/{test_config}/app-{test_config}.apk").realpath())
  625. tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/assets/Registry/dummy.txt").ensure()
  626. expected_registry_path = str(tmpdir.join(f"{TEST_DEV_ROOT}/{TEST_BUILD_DIR}/app/src/main/assets/Registry").realpath())
  627. expected_storage_registry_path = f'{test_device_storage_path}/Android/data/{test_package_name}/files/Registry'
  628. def _mock_adb_call(arg_list, device_id=None):
  629. if arg_list == "start-server":
  630. return "SUCCESS"
  631. if arg_list == "kill-server":
  632. return "SUCCESS"
  633. elif isinstance(arg_list,list):
  634. if match_arg_list(arg_list, ['install', '-r', expected_apk_path]):
  635. return "SUCCESS"
  636. elif match_arg_list(arg_list, ['install', '-t', '-r', expected_apk_path]):
  637. return "SUCCESS"
  638. elif match_arg_list(arg_list, ['push', expected_registry_path, expected_storage_registry_path]):
  639. return "SUCCESS"
  640. elif match_arg_list(arg_list, ['shell', 'cmd', 'package', 'list', 'packages', test_package_name]):
  641. return "SUCCESS"
  642. elif match_arg_list(arg_list, ['shell', f'mkdir {test_device_storage_path}/Android/data/{test_package_name}']):
  643. return "SUCCESS"
  644. elif match_arg_list(arg_list, ['shell', f'mkdir {test_device_storage_path}/Android/data/{test_package_name}/files']):
  645. return "SUCCESS"
  646. elif len(arg_list) == 3 and arg_list[0] == 'push' and arg_list[1] == 'foo.bar':
  647. return "SUCCESS"
  648. raise AssertionError
  649. def _mock_should_copy_file(check_path, check_time):
  650. return check_path == 'foo.bar'
  651. with patch.object(android_deployment.AndroidDeployment, 'get_target_android_devices', return_value=[TEST_DEVICE_ID]) as mock_get_target_android_devices, \
  652. patch.object(android_deployment.AndroidDeployment, 'detect_device_storage_path', return_value=test_device_storage_path) as mock_detect_device_storage_path, \
  653. patch.object(android_deployment.AndroidDeployment, 'get_device_file_timestamp', return_value=datetime.datetime.now()) as mock_get_device_file_timestamp, \
  654. patch.object(android_deployment.AndroidDeployment, 'update_device_file_timestamp') as mock_update_device_file_timestamp, \
  655. patch.object(android_deployment.AndroidDeployment, 'read_android_settings', return_value={'package_name': test_package_name}), \
  656. patch.object(android_deployment.AndroidDeployment, 'resolve_adb_tool', return_value=pathlib.Path("Foo")), \
  657. patch.object(android_deployment.AndroidDeployment, 'adb_call', wraps=_mock_adb_call) as mock_adb_call, \
  658. patch.object(android_deployment.AndroidDeployment, 'should_copy_file', wraps=_mock_should_copy_file), \
  659. patch.object(pathlib.Path, 'glob', return_value=[pathlib.Path("foo.bar"), pathlib.Path("no.bar")]):
  660. inst = android_deployment.AndroidDeployment(dev_root=mock_dev_root,
  661. build_dir=TEST_BUILD_DIR,
  662. configuration=test_config,
  663. game_name=TEST_GAME_NAME,
  664. asset_mode=TEST_ASSET_MODE,
  665. asset_type=TEST_ASSET_TYPE,
  666. embedded_assets=False,
  667. android_device_filter=None,
  668. clean_deploy=False,
  669. deployment_type=android_deployment.AndroidDeployment.DEPLOY_BOTH,
  670. android_sdk_path=TEST_ANDROID_SDK_PATH)
  671. inst.execute()
  672. assert mock_adb_call.call_count == 5
  673. mock_update_device_file_timestamp.assert_called_once()
  674. mock_get_device_file_timestamp.assert_called_once()
  675. mock_detect_device_storage_path.assert_called_once()
  676. mock_get_target_android_devices.assert_called_once()