diff options
Diffstat (limited to 'src/tests/py_utils/android_helper.py')
-rw-r--r-- | src/tests/py_utils/android_helper.py | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/tests/py_utils/android_helper.py b/src/tests/py_utils/android_helper.py new file mode 100644 index 0000000000..e3648c3a14 --- /dev/null +++ b/src/tests/py_utils/android_helper.py @@ -0,0 +1,210 @@ +# Copyright 2022 The ANGLE Project Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import contextlib +import glob +import json +import logging +import os +import posixpath +import random +import subprocess +import tarfile +import tempfile +import time + +import angle_path_util + + +def _ApkPath(suite_name): + return os.path.join('%s_apk' % suite_name, '%s-debug.apk' % suite_name) + + +def ApkFileExists(suite_name): + return os.path.exists(_ApkPath(suite_name)) + + +def _Run(cmd): + logging.debug('Executing command: %s', cmd) + startupinfo = None + if hasattr(subprocess, 'STARTUPINFO'): + # Prevent console window popping up on Windows + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + output = subprocess.check_output(cmd, startupinfo=startupinfo) + return output + + +class Adb(object): + + def __init__(self, adb_path=None): + if not adb_path: + adb_path = os.path.join(angle_path_util.ANGLE_ROOT_DIR, + 'third_party/android_sdk/public/platform-tools/adb') + self._adb_path = adb_path + + def Run(self, args): + return _Run([self._adb_path] + args) + + def Shell(self, cmd): + return _Run([self._adb_path, 'shell', cmd]) + + +def _GetAdbRoot(adb): + adb.Run(['root']) + + for _ in range(20): + time.sleep(0.5) + try: + id_out = adb.Shell(adb_path, 'id').decode('ascii') + if 'uid=0(root)' in id_out: + return + except Exception: + continue + raise Exception("adb root failed") + + +def _ReadDeviceFile(adb, device_path): + out_wc = adb.Shell('cat %s | wc -c' % device_path) + expected_size = int(out_wc.decode('ascii').strip()) + out = adb.Run(['exec-out', 'cat %s' % device_path]) + assert len(out) == expected_size, 'exec-out mismatch' + return out + + +def _RemoveDeviceFile(adb, device_path): + adb.Shell('rm -f ' + device_path + ' || true') # ignore errors + + +def _AddRestrictedTracesJson(adb): + adb.Shell('mkdir -p /sdcard/chromium_tests_root/') + + def add(tar, fn): + assert (fn.startswith('../../')) + tar.add(fn, arcname=fn.replace('../../', '')) + + with _TempLocalFile() as tempfile_path: + with tarfile.open(tempfile_path, 'w', format=tarfile.GNU_FORMAT) as tar: + for f in glob.glob('../../src/tests/restricted_traces/*/*.json', recursive=True): + add(tar, f) + add(tar, '../../src/tests/restricted_traces/restricted_traces.json') + adb.Run(['push', tempfile_path, '/sdcard/chromium_tests_root/t.tar']) + + adb.Shell('r=/sdcard/chromium_tests_root; tar -xf $r/t.tar -C $r/ && rm $r/t.tar') + + +def PrepareTestSuite(adb, suite_name): + apk_path = _ApkPath(suite_name) + logging.info('Installing apk path=%s size=%s' % (apk_path, os.path.getsize(apk_path))) + + adb.Run(['install', '-r', '-d', apk_path]) + + permissions = [ + 'android.permission.CAMERA', 'android.permission.CHANGE_CONFIGURATION', + 'android.permission.READ_EXTERNAL_STORAGE', 'android.permission.RECORD_AUDIO', + 'android.permission.WRITE_EXTERNAL_STORAGE' + ] + adb.Shell('p=com.android.angle.test;' + 'for q in %s;do pm grant "$p" "$q";done;' % ' '.join(permissions)) + + if suite_name == 'angle_perftests': + _AddRestrictedTracesJson(adb) + + +def PrepareRestrictedTraces(adb, traces): + start = time.time() + total_size = 0 + for trace in traces: + path_from_root = 'src/tests/restricted_traces/' + trace + '/' + trace + '.angledata.gz' + local_path = '../../' + path_from_root + total_size += os.path.getsize(local_path) + adb.Run(['push', local_path, '/sdcard/chromium_tests_root/' + path_from_root]) + + logging.info('Pushed %d trace files (%.1fMB) in %.1fs', len(traces), total_size / 1e6, + time.time() - start) + + +def _RandomHex(): + return hex(random.randint(0, 2**64))[2:] + + +@contextlib.contextmanager +def _TempDeviceDir(adb): + path = '/sdcard/Download/temp_dir-%s' % _RandomHex() + adb.Shell('mkdir -p ' + path) + try: + yield path + finally: + adb.Shell('rm -rf ' + path) + + +@contextlib.contextmanager +def _TempDeviceFile(adb): + path = '/sdcard/Download/temp_file-%s' % _RandomHex() + try: + yield path + finally: + adb.Shell('rm -f ' + path) + + +@contextlib.contextmanager +def _TempLocalFile(): + fd, path = tempfile.mkstemp() + os.close(fd) + try: + yield path + finally: + os.remove(path) + + +def _RunInstrumentation(adb, flags): + with _TempDeviceFile(adb) as temp_device_file: + cmd = ' '.join([ + 'p=com.android.angle.test;', + 'ntr=org.chromium.native_test.NativeTestInstrumentationTestRunner;', + 'am instrument -w', + '-e $ntr.NativeTestActivity "$p".AngleUnitTestActivity', + '-e $ntr.ShardNanoTimeout 2400000000000', + '-e org.chromium.native_test.NativeTest.CommandLineFlags "%s"' % ' '.join(flags), + '-e $ntr.StdoutFile ' + temp_device_file, + '"$p"/org.chromium.build.gtest_apk.NativeTestInstrumentationTestRunner', + ]) + + adb.Shell(cmd) + return _ReadDeviceFile(adb, temp_device_file) + + +def AngleSystemInfo(adb, args): + PrepareTestSuite(adb, 'angle_system_info_test') + + with _TempDeviceDir(adb) as temp_dir: + _RunInstrumentation(adb, args + ['--render-test-output-dir=' + temp_dir]) + output_file = posixpath.join(temp_dir, 'angle_system_info.json') + return json.loads(_ReadDeviceFile(adb, output_file)) + + +def ListTests(adb): + out_lines = _RunInstrumentation(adb, ["--list-tests"]).decode('ascii').split('\n') + + start = out_lines.index('Tests list:') + end = out_lines.index('End tests list.') + return out_lines[start + 1:end] + + +def _PullDir(adb, device_dir, local_dir): + files = adb.Shell('ls -1 %s' % device_dir).decode('ascii').split('\n') + for f in files: + f = f.strip() + if f: + adb.Run(['pull', posixpath.join(device_dir, f), posixpath.join(local_dir, f)]) + + +def RunTests(adb, test_suite, args, stdoutfile, output_dir): + with _TempDeviceDir(adb) as temp_dir: + output = _RunInstrumentation(adb, args + ['--render-test-output-dir=' + temp_dir]) + with open(stdoutfile, 'wb') as f: + f.write(output) + logging.info(output.decode()) + _PullDir(adb, temp_dir, output_dir) |