• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 The ANGLE Project Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import contextlib
6import glob
7import json
8import logging
9import os
10import posixpath
11import random
12import subprocess
13import tarfile
14import tempfile
15import time
16
17import angle_path_util
18
19
20def _ApkPath(suite_name):
21    return os.path.join('%s_apk' % suite_name, '%s-debug.apk' % suite_name)
22
23
24def ApkFileExists(suite_name):
25    return os.path.exists(_ApkPath(suite_name))
26
27
28def _Run(cmd):
29    logging.debug('Executing command: %s', cmd)
30    startupinfo = None
31    if hasattr(subprocess, 'STARTUPINFO'):
32        # Prevent console window popping up on Windows
33        startupinfo = subprocess.STARTUPINFO()
34        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
35        startupinfo.wShowWindow = subprocess.SW_HIDE
36    output = subprocess.check_output(cmd, startupinfo=startupinfo)
37    return output
38
39
40class Adb(object):
41
42    def __init__(self, adb_path=None):
43        if not adb_path:
44            adb_path = os.path.join(angle_path_util.ANGLE_ROOT_DIR,
45                                    'third_party/android_sdk/public/platform-tools/adb')
46        self._adb_path = adb_path
47
48    def Run(self, args):
49        return _Run([self._adb_path] + args)
50
51    def Shell(self, cmd):
52        return _Run([self._adb_path, 'shell', cmd])
53
54
55def _GetAdbRoot(adb):
56    adb.Run(['root'])
57
58    for _ in range(20):
59        time.sleep(0.5)
60        try:
61            id_out = adb.Shell(adb_path, 'id').decode('ascii')
62            if 'uid=0(root)' in id_out:
63                return
64        except Exception:
65            continue
66    raise Exception("adb root failed")
67
68
69def _ReadDeviceFile(adb, device_path):
70    out_wc = adb.Shell('cat %s | wc -c' % device_path)
71    expected_size = int(out_wc.decode('ascii').strip())
72    out = adb.Run(['exec-out', 'cat %s' % device_path])
73    assert len(out) == expected_size, 'exec-out mismatch'
74    return out
75
76
77def _RemoveDeviceFile(adb, device_path):
78    adb.Shell('rm -f ' + device_path + ' || true')  # ignore errors
79
80
81def _AddRestrictedTracesJson(adb):
82    adb.Shell('mkdir -p /sdcard/chromium_tests_root/')
83
84    def add(tar, fn):
85        assert (fn.startswith('../../'))
86        tar.add(fn, arcname=fn.replace('../../', ''))
87
88    with _TempLocalFile() as tempfile_path:
89        with tarfile.open(tempfile_path, 'w', format=tarfile.GNU_FORMAT) as tar:
90            for f in glob.glob('../../src/tests/restricted_traces/*/*.json', recursive=True):
91                add(tar, f)
92            add(tar, '../../src/tests/restricted_traces/restricted_traces.json')
93        adb.Run(['push', tempfile_path, '/sdcard/chromium_tests_root/t.tar'])
94
95    adb.Shell('r=/sdcard/chromium_tests_root; tar -xf $r/t.tar -C $r/ && rm $r/t.tar')
96
97
98def PrepareTestSuite(adb, suite_name):
99    apk_path = _ApkPath(suite_name)
100    logging.info('Installing apk path=%s size=%s' % (apk_path, os.path.getsize(apk_path)))
101
102    adb.Run(['install', '-r', '-d', apk_path])
103
104    permissions = [
105        'android.permission.CAMERA', 'android.permission.CHANGE_CONFIGURATION',
106        'android.permission.READ_EXTERNAL_STORAGE', 'android.permission.RECORD_AUDIO',
107        'android.permission.WRITE_EXTERNAL_STORAGE'
108    ]
109    adb.Shell('p=com.android.angle.test;'
110              'for q in %s;do pm grant "$p" "$q";done;' % ' '.join(permissions))
111
112    if suite_name == 'angle_perftests':
113        _AddRestrictedTracesJson(adb)
114
115
116def PrepareRestrictedTraces(adb, traces):
117    start = time.time()
118    total_size = 0
119    for trace in traces:
120        path_from_root = 'src/tests/restricted_traces/' + trace + '/' + trace + '.angledata.gz'
121        local_path = '../../' + path_from_root
122        total_size += os.path.getsize(local_path)
123        adb.Run(['push', local_path, '/sdcard/chromium_tests_root/' + path_from_root])
124
125    logging.info('Pushed %d trace files (%.1fMB) in %.1fs', len(traces), total_size / 1e6,
126                 time.time() - start)
127
128
129def _RandomHex():
130    return hex(random.randint(0, 2**64))[2:]
131
132
133@contextlib.contextmanager
134def _TempDeviceDir(adb):
135    path = '/sdcard/Download/temp_dir-%s' % _RandomHex()
136    adb.Shell('mkdir -p ' + path)
137    try:
138        yield path
139    finally:
140        adb.Shell('rm -rf ' + path)
141
142
143@contextlib.contextmanager
144def _TempDeviceFile(adb):
145    path = '/sdcard/Download/temp_file-%s' % _RandomHex()
146    try:
147        yield path
148    finally:
149        adb.Shell('rm -f ' + path)
150
151
152@contextlib.contextmanager
153def _TempLocalFile():
154    fd, path = tempfile.mkstemp()
155    os.close(fd)
156    try:
157        yield path
158    finally:
159        os.remove(path)
160
161
162def _RunInstrumentation(adb, flags):
163    with _TempDeviceFile(adb) as temp_device_file:
164        cmd = ' '.join([
165            'p=com.android.angle.test;',
166            'ntr=org.chromium.native_test.NativeTestInstrumentationTestRunner;',
167            'am instrument -w',
168            '-e $ntr.NativeTestActivity "$p".AngleUnitTestActivity',
169            '-e $ntr.ShardNanoTimeout 2400000000000',
170            '-e org.chromium.native_test.NativeTest.CommandLineFlags "%s"' % ' '.join(flags),
171            '-e $ntr.StdoutFile ' + temp_device_file,
172            '"$p"/org.chromium.build.gtest_apk.NativeTestInstrumentationTestRunner',
173        ])
174
175        adb.Shell(cmd)
176        return _ReadDeviceFile(adb, temp_device_file)
177
178
179def AngleSystemInfo(adb, args):
180    PrepareTestSuite(adb, 'angle_system_info_test')
181
182    with _TempDeviceDir(adb) as temp_dir:
183        _RunInstrumentation(adb, args + ['--render-test-output-dir=' + temp_dir])
184        output_file = posixpath.join(temp_dir, 'angle_system_info.json')
185        return json.loads(_ReadDeviceFile(adb, output_file))
186
187
188def ListTests(adb):
189    out_lines = _RunInstrumentation(adb, ["--list-tests"]).decode('ascii').split('\n')
190
191    start = out_lines.index('Tests list:')
192    end = out_lines.index('End tests list.')
193    return out_lines[start + 1:end]
194
195
196def _PullDir(adb, device_dir, local_dir):
197    files = adb.Shell('ls -1 %s' % device_dir).decode('ascii').split('\n')
198    for f in files:
199        f = f.strip()
200        if f:
201            adb.Run(['pull', posixpath.join(device_dir, f), posixpath.join(local_dir, f)])
202
203
204def RunTests(adb, test_suite, args, stdoutfile, output_dir):
205    with _TempDeviceDir(adb) as temp_dir:
206        output = _RunInstrumentation(adb, args + ['--render-test-output-dir=' + temp_dir])
207        with open(stdoutfile, 'wb') as f:
208            f.write(output)
209            logging.info(output.decode())
210        _PullDir(adb, temp_dir, output_dir)
211