xref: /aosp_15_r20/external/angle/src/tests/capture_replay_tests.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#! /usr/bin/env vpython3
2*8975f5c5SAndroid Build Coastguard Worker#
3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2020 The ANGLE Project Authors. All rights reserved.
4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
6*8975f5c5SAndroid Build Coastguard Worker#
7*8975f5c5SAndroid Build Coastguard Worker"""
8*8975f5c5SAndroid Build Coastguard WorkerScript testing capture_replay with angle_end2end_tests
9*8975f5c5SAndroid Build Coastguard Worker"""
10*8975f5c5SAndroid Build Coastguard Worker
11*8975f5c5SAndroid Build Coastguard Worker# Automation script will:
12*8975f5c5SAndroid Build Coastguard Worker# 1. Build all tests in angle_end2end with frame capture enabled
13*8975f5c5SAndroid Build Coastguard Worker# 2. Run each test with frame capture
14*8975f5c5SAndroid Build Coastguard Worker# 3. Build CaptureReplayTest with cpp trace files
15*8975f5c5SAndroid Build Coastguard Worker# 4. Run CaptureReplayTest
16*8975f5c5SAndroid Build Coastguard Worker# 5. Output the number of test successes and failures. A test succeeds if no error occurs during
17*8975f5c5SAndroid Build Coastguard Worker# its capture and replay, and the GL states at the end of two runs match. Any unexpected failure
18*8975f5c5SAndroid Build Coastguard Worker# will return non-zero exit code
19*8975f5c5SAndroid Build Coastguard Worker
20*8975f5c5SAndroid Build Coastguard Worker# Run this script with Python to test capture replay on angle_end2end tests
21*8975f5c5SAndroid Build Coastguard Worker# python path/to/capture_replay_tests.py
22*8975f5c5SAndroid Build Coastguard Worker# Command line arguments: run with --help for a full list.
23*8975f5c5SAndroid Build Coastguard Worker
24*8975f5c5SAndroid Build Coastguard Workerimport argparse
25*8975f5c5SAndroid Build Coastguard Workerimport concurrent.futures
26*8975f5c5SAndroid Build Coastguard Workerimport contextlib
27*8975f5c5SAndroid Build Coastguard Workerimport difflib
28*8975f5c5SAndroid Build Coastguard Workerimport distutils.util
29*8975f5c5SAndroid Build Coastguard Workerimport getpass
30*8975f5c5SAndroid Build Coastguard Workerimport glob
31*8975f5c5SAndroid Build Coastguard Workerimport json
32*8975f5c5SAndroid Build Coastguard Workerimport logging
33*8975f5c5SAndroid Build Coastguard Workerimport os
34*8975f5c5SAndroid Build Coastguard Workerimport pathlib
35*8975f5c5SAndroid Build Coastguard Workerimport queue
36*8975f5c5SAndroid Build Coastguard Workerimport random
37*8975f5c5SAndroid Build Coastguard Workerimport re
38*8975f5c5SAndroid Build Coastguard Workerimport shutil
39*8975f5c5SAndroid Build Coastguard Workerimport subprocess
40*8975f5c5SAndroid Build Coastguard Workerimport sys
41*8975f5c5SAndroid Build Coastguard Workerimport tempfile
42*8975f5c5SAndroid Build Coastguard Workerimport threading
43*8975f5c5SAndroid Build Coastguard Workerimport time
44*8975f5c5SAndroid Build Coastguard Workerimport traceback
45*8975f5c5SAndroid Build Coastguard Worker
46*8975f5c5SAndroid Build Coastguard WorkerSCRIPT_DIR = str(pathlib.Path(__file__).resolve().parent)
47*8975f5c5SAndroid Build Coastguard WorkerPY_UTILS = str(pathlib.Path(SCRIPT_DIR) / 'py_utils')
48*8975f5c5SAndroid Build Coastguard Workerif PY_UTILS not in sys.path:
49*8975f5c5SAndroid Build Coastguard Worker    os.stat(PY_UTILS) and sys.path.insert(0, PY_UTILS)
50*8975f5c5SAndroid Build Coastguard Workerimport angle_test_util
51*8975f5c5SAndroid Build Coastguard Worker
52*8975f5c5SAndroid Build Coastguard WorkerPIPE_STDOUT = True
53*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_OUT_DIR = "out/CaptureReplayTest"  # relative to angle folder
54*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_FILTER = "*/ES2_Vulkan_SwiftShader"
55*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_TEST_SUITE = "angle_end2end_tests"
56*8975f5c5SAndroid Build Coastguard WorkerREPLAY_SAMPLE_FOLDER = "src/tests/capture_replay_tests"  # relative to angle folder
57*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_BATCH_COUNT = 1  # number of tests batched together for capture
58*8975f5c5SAndroid Build Coastguard WorkerCAPTURE_FRAME_END = 100
59*8975f5c5SAndroid Build Coastguard WorkerTRACE_FILE_SUFFIX = "_context"  # because we only deal with 1 context right now
60*8975f5c5SAndroid Build Coastguard WorkerRESULT_TAG = "*RESULT"
61*8975f5c5SAndroid Build Coastguard WorkerSTATUS_MESSAGE_PERIOD = 20  # in seconds
62*8975f5c5SAndroid Build Coastguard WorkerCAPTURE_SUBPROCESS_TIMEOUT = 600  # in seconds
63*8975f5c5SAndroid Build Coastguard WorkerREPLAY_SUBPROCESS_TIMEOUT = 60  # in seconds
64*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_RESULT_FILE = "results.txt"
65*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_LOG_LEVEL = "info"
66*8975f5c5SAndroid Build Coastguard WorkerDEFAULT_MAX_JOBS = 8
67*8975f5c5SAndroid Build Coastguard WorkerREPLAY_BINARY = "capture_replay_tests"
68*8975f5c5SAndroid Build Coastguard Workerif sys.platform == "win32":
69*8975f5c5SAndroid Build Coastguard Worker    REPLAY_BINARY += ".exe"
70*8975f5c5SAndroid Build Coastguard WorkerTRACE_FOLDER = "traces"
71*8975f5c5SAndroid Build Coastguard Worker
72*8975f5c5SAndroid Build Coastguard WorkerEXIT_SUCCESS = 0
73*8975f5c5SAndroid Build Coastguard WorkerEXIT_FAILURE = 1
74*8975f5c5SAndroid Build Coastguard WorkerREPLAY_INITIALIZATION_FAILURE = -1
75*8975f5c5SAndroid Build Coastguard WorkerREPLAY_SERIALIZATION_FAILURE = -2
76*8975f5c5SAndroid Build Coastguard Worker
77*8975f5c5SAndroid Build Coastguard Workerswitch_case_without_return_template = """\
78*8975f5c5SAndroid Build Coastguard Worker        case {case}:
79*8975f5c5SAndroid Build Coastguard Worker            {namespace}::{call}({params});
80*8975f5c5SAndroid Build Coastguard Worker            break;
81*8975f5c5SAndroid Build Coastguard Worker"""
82*8975f5c5SAndroid Build Coastguard Worker
83*8975f5c5SAndroid Build Coastguard Workerswitch_case_with_return_template = """\
84*8975f5c5SAndroid Build Coastguard Worker        case {case}:
85*8975f5c5SAndroid Build Coastguard Worker            return {namespace}::{call}({params});
86*8975f5c5SAndroid Build Coastguard Worker"""
87*8975f5c5SAndroid Build Coastguard Worker
88*8975f5c5SAndroid Build Coastguard Workerdefault_case_without_return_template = """\
89*8975f5c5SAndroid Build Coastguard Worker        default:
90*8975f5c5SAndroid Build Coastguard Worker            break;"""
91*8975f5c5SAndroid Build Coastguard Workerdefault_case_with_return_template = """\
92*8975f5c5SAndroid Build Coastguard Worker        default:
93*8975f5c5SAndroid Build Coastguard Worker            return {default_val};"""
94*8975f5c5SAndroid Build Coastguard Worker
95*8975f5c5SAndroid Build Coastguard Worker
96*8975f5c5SAndroid Build Coastguard Workerdef winext(name, ext):
97*8975f5c5SAndroid Build Coastguard Worker    return ("%s.%s" % (name, ext)) if sys.platform == "win32" else name
98*8975f5c5SAndroid Build Coastguard Worker
99*8975f5c5SAndroid Build Coastguard Worker
100*8975f5c5SAndroid Build Coastguard WorkerGN_PATH = os.path.join('third_party', 'depot_tools', winext('gn', 'bat'))
101*8975f5c5SAndroid Build Coastguard WorkerAUTONINJA_PATH = os.path.join('third_party', 'depot_tools', 'autoninja.py')
102*8975f5c5SAndroid Build Coastguard Worker
103*8975f5c5SAndroid Build Coastguard Worker
104*8975f5c5SAndroid Build Coastguard Workerdef GetGnArgsStr(args, extra_gn_args=[]):
105*8975f5c5SAndroid Build Coastguard Worker    gn_args = [('angle_with_capture_by_default', 'true'),
106*8975f5c5SAndroid Build Coastguard Worker               ('angle_enable_vulkan_api_dump_layer', 'false'),
107*8975f5c5SAndroid Build Coastguard Worker               ('angle_enable_wgpu', 'false')] + extra_gn_args
108*8975f5c5SAndroid Build Coastguard Worker    if args.use_reclient:
109*8975f5c5SAndroid Build Coastguard Worker        gn_args.append(('use_remoteexec', 'true'))
110*8975f5c5SAndroid Build Coastguard Worker    if not args.debug:
111*8975f5c5SAndroid Build Coastguard Worker        gn_args.append(('is_debug', 'false'))
112*8975f5c5SAndroid Build Coastguard Worker        gn_args.append(('symbol_level', '1'))
113*8975f5c5SAndroid Build Coastguard Worker        gn_args.append(('angle_assert_always_on', 'true'))
114*8975f5c5SAndroid Build Coastguard Worker    if args.asan:
115*8975f5c5SAndroid Build Coastguard Worker        gn_args.append(('is_asan', 'true'))
116*8975f5c5SAndroid Build Coastguard Worker    return ' '.join(['%s=%s' % (k, v) for (k, v) in gn_args])
117*8975f5c5SAndroid Build Coastguard Worker
118*8975f5c5SAndroid Build Coastguard Worker
119*8975f5c5SAndroid Build Coastguard Workerclass XvfbPool(object):
120*8975f5c5SAndroid Build Coastguard Worker
121*8975f5c5SAndroid Build Coastguard Worker    def __init__(self, worker_count):
122*8975f5c5SAndroid Build Coastguard Worker        self.queue = queue.Queue()
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker        self.processes = []
125*8975f5c5SAndroid Build Coastguard Worker        displays = set()
126*8975f5c5SAndroid Build Coastguard Worker        tmp = tempfile.TemporaryDirectory()
127*8975f5c5SAndroid Build Coastguard Worker
128*8975f5c5SAndroid Build Coastguard Worker        logging.info('Starting xvfb and openbox...')
129*8975f5c5SAndroid Build Coastguard Worker        # Based on the simplest case from testing/xvfb.py, with tweaks to minimize races.
130*8975f5c5SAndroid Build Coastguard Worker        try:
131*8975f5c5SAndroid Build Coastguard Worker            for worker in range(worker_count):
132*8975f5c5SAndroid Build Coastguard Worker                while True:
133*8975f5c5SAndroid Build Coastguard Worker                    # Pick a set of random displays from a custom range to hopefully avoid
134*8975f5c5SAndroid Build Coastguard Worker                    # collisions with anything else that might be using xvfb.
135*8975f5c5SAndroid Build Coastguard Worker                    # Another option would be -displayfd but that has its quirks too.
136*8975f5c5SAndroid Build Coastguard Worker                    display = random.randint(7700000, 7800000)
137*8975f5c5SAndroid Build Coastguard Worker                    if display in displays:
138*8975f5c5SAndroid Build Coastguard Worker                        continue
139*8975f5c5SAndroid Build Coastguard Worker
140*8975f5c5SAndroid Build Coastguard Worker                    x11_display_file = '/tmp/.X11-unix/X%d' % display
141*8975f5c5SAndroid Build Coastguard Worker
142*8975f5c5SAndroid Build Coastguard Worker                    if not os.path.exists(x11_display_file):
143*8975f5c5SAndroid Build Coastguard Worker                        break
144*8975f5c5SAndroid Build Coastguard Worker
145*8975f5c5SAndroid Build Coastguard Worker                displays.add(display)
146*8975f5c5SAndroid Build Coastguard Worker
147*8975f5c5SAndroid Build Coastguard Worker                x11_proc = subprocess.Popen([
148*8975f5c5SAndroid Build Coastguard Worker                    'Xvfb',
149*8975f5c5SAndroid Build Coastguard Worker                    ':%d' % display, '-screen', '0', '1280x1024x24', '-ac', '-nolisten', 'tcp',
150*8975f5c5SAndroid Build Coastguard Worker                    '-dpi', '96', '+extension', 'RANDR', '-maxclients', '512'
151*8975f5c5SAndroid Build Coastguard Worker                ],
152*8975f5c5SAndroid Build Coastguard Worker                                            stderr=subprocess.STDOUT)
153*8975f5c5SAndroid Build Coastguard Worker                self.processes.append(x11_proc)
154*8975f5c5SAndroid Build Coastguard Worker
155*8975f5c5SAndroid Build Coastguard Worker                start_time = time.time()
156*8975f5c5SAndroid Build Coastguard Worker                while not os.path.exists(x11_display_file):
157*8975f5c5SAndroid Build Coastguard Worker                    if time.time() - start_time >= 30:
158*8975f5c5SAndroid Build Coastguard Worker                        raise Exception('X11 failed to start')
159*8975f5c5SAndroid Build Coastguard Worker                    time.sleep(0.1)
160*8975f5c5SAndroid Build Coastguard Worker
161*8975f5c5SAndroid Build Coastguard Worker                env = os.environ.copy()
162*8975f5c5SAndroid Build Coastguard Worker                env['DISPLAY'] = ':%d' % display
163*8975f5c5SAndroid Build Coastguard Worker
164*8975f5c5SAndroid Build Coastguard Worker                # testing/xvfb.py uses signals instead, which is tricky with multiple displays.
165*8975f5c5SAndroid Build Coastguard Worker                openbox_ready_file = os.path.join(tmp.name, str(display))
166*8975f5c5SAndroid Build Coastguard Worker                openbox_proc = subprocess.Popen(
167*8975f5c5SAndroid Build Coastguard Worker                    ['openbox', '--sm-disable', '--startup',
168*8975f5c5SAndroid Build Coastguard Worker                     'touch %s' % openbox_ready_file],
169*8975f5c5SAndroid Build Coastguard Worker                    stderr=subprocess.STDOUT,
170*8975f5c5SAndroid Build Coastguard Worker                    env=env)
171*8975f5c5SAndroid Build Coastguard Worker                self.processes.append(openbox_proc)
172*8975f5c5SAndroid Build Coastguard Worker
173*8975f5c5SAndroid Build Coastguard Worker                start_time = time.time()
174*8975f5c5SAndroid Build Coastguard Worker                while not os.path.exists(openbox_ready_file):
175*8975f5c5SAndroid Build Coastguard Worker                    if time.time() - start_time >= 30:
176*8975f5c5SAndroid Build Coastguard Worker                        raise Exception('Openbox failed to start')
177*8975f5c5SAndroid Build Coastguard Worker                    time.sleep(0.1)
178*8975f5c5SAndroid Build Coastguard Worker
179*8975f5c5SAndroid Build Coastguard Worker                self.queue.put(display)
180*8975f5c5SAndroid Build Coastguard Worker
181*8975f5c5SAndroid Build Coastguard Worker            logging.info('Started a pool of %d xvfb displays: %s', worker_count,
182*8975f5c5SAndroid Build Coastguard Worker                         ' '.join(str(d) for d in sorted(displays)))
183*8975f5c5SAndroid Build Coastguard Worker        except Exception:
184*8975f5c5SAndroid Build Coastguard Worker            self.Teardown()
185*8975f5c5SAndroid Build Coastguard Worker            raise
186*8975f5c5SAndroid Build Coastguard Worker        finally:
187*8975f5c5SAndroid Build Coastguard Worker            tmp.cleanup()
188*8975f5c5SAndroid Build Coastguard Worker
189*8975f5c5SAndroid Build Coastguard Worker    def GrabDisplay(self):
190*8975f5c5SAndroid Build Coastguard Worker        return self.queue.get()
191*8975f5c5SAndroid Build Coastguard Worker
192*8975f5c5SAndroid Build Coastguard Worker    def ReleaseDisplay(self, display):
193*8975f5c5SAndroid Build Coastguard Worker        self.queue.put(display)
194*8975f5c5SAndroid Build Coastguard Worker
195*8975f5c5SAndroid Build Coastguard Worker    def Teardown(self):
196*8975f5c5SAndroid Build Coastguard Worker        logging.info('Stopping xvfb pool')
197*8975f5c5SAndroid Build Coastguard Worker        for p in reversed(self.processes):
198*8975f5c5SAndroid Build Coastguard Worker            p.kill()
199*8975f5c5SAndroid Build Coastguard Worker            p.wait()
200*8975f5c5SAndroid Build Coastguard Worker        self.processes = []
201*8975f5c5SAndroid Build Coastguard Worker
202*8975f5c5SAndroid Build Coastguard Worker
203*8975f5c5SAndroid Build Coastguard Worker@contextlib.contextmanager
204*8975f5c5SAndroid Build Coastguard Workerdef MaybeXvfbPool(xvfb, worker_count):
205*8975f5c5SAndroid Build Coastguard Worker    if xvfb:
206*8975f5c5SAndroid Build Coastguard Worker        try:
207*8975f5c5SAndroid Build Coastguard Worker            xvfb_pool = XvfbPool(worker_count)
208*8975f5c5SAndroid Build Coastguard Worker            yield xvfb_pool
209*8975f5c5SAndroid Build Coastguard Worker        finally:
210*8975f5c5SAndroid Build Coastguard Worker            xvfb_pool.Teardown()
211*8975f5c5SAndroid Build Coastguard Worker    else:
212*8975f5c5SAndroid Build Coastguard Worker        yield None
213*8975f5c5SAndroid Build Coastguard Worker
214*8975f5c5SAndroid Build Coastguard Worker
215*8975f5c5SAndroid Build Coastguard Worker@contextlib.contextmanager
216*8975f5c5SAndroid Build Coastguard Workerdef GetDisplayEnv(env, xvfb_pool):
217*8975f5c5SAndroid Build Coastguard Worker    if not xvfb_pool:
218*8975f5c5SAndroid Build Coastguard Worker        yield env
219*8975f5c5SAndroid Build Coastguard Worker        return
220*8975f5c5SAndroid Build Coastguard Worker
221*8975f5c5SAndroid Build Coastguard Worker    display = xvfb_pool.GrabDisplay()
222*8975f5c5SAndroid Build Coastguard Worker    display_var = ':%d' % display
223*8975f5c5SAndroid Build Coastguard Worker    try:
224*8975f5c5SAndroid Build Coastguard Worker        yield {**env, 'DISPLAY': display_var, 'XVFB_DISPLAY': display_var}
225*8975f5c5SAndroid Build Coastguard Worker    finally:
226*8975f5c5SAndroid Build Coastguard Worker        xvfb_pool.ReleaseDisplay(display)
227*8975f5c5SAndroid Build Coastguard Worker
228*8975f5c5SAndroid Build Coastguard Worker
229*8975f5c5SAndroid Build Coastguard Workerdef TestLabel(test_name):
230*8975f5c5SAndroid Build Coastguard Worker    return test_name.replace(".", "_").replace("/", "_")
231*8975f5c5SAndroid Build Coastguard Worker
232*8975f5c5SAndroid Build Coastguard Worker
233*8975f5c5SAndroid Build Coastguard Workerdef ParseTestNamesFromTestList(output, test_expectation, also_run_skipped_for_capture_tests):
234*8975f5c5SAndroid Build Coastguard Worker    output_lines = output.splitlines()
235*8975f5c5SAndroid Build Coastguard Worker    tests = []
236*8975f5c5SAndroid Build Coastguard Worker    seen_start_of_tests = False
237*8975f5c5SAndroid Build Coastguard Worker    disabled = 0
238*8975f5c5SAndroid Build Coastguard Worker    for line in output_lines:
239*8975f5c5SAndroid Build Coastguard Worker        l = line.strip()
240*8975f5c5SAndroid Build Coastguard Worker        if l == 'Tests list:':
241*8975f5c5SAndroid Build Coastguard Worker            seen_start_of_tests = True
242*8975f5c5SAndroid Build Coastguard Worker        elif l == 'End tests list.':
243*8975f5c5SAndroid Build Coastguard Worker            break
244*8975f5c5SAndroid Build Coastguard Worker        elif not seen_start_of_tests:
245*8975f5c5SAndroid Build Coastguard Worker            pass
246*8975f5c5SAndroid Build Coastguard Worker        elif not test_expectation.TestIsSkippedForCapture(l) or also_run_skipped_for_capture_tests:
247*8975f5c5SAndroid Build Coastguard Worker            tests.append(l)
248*8975f5c5SAndroid Build Coastguard Worker        else:
249*8975f5c5SAndroid Build Coastguard Worker            disabled += 1
250*8975f5c5SAndroid Build Coastguard Worker
251*8975f5c5SAndroid Build Coastguard Worker    logging.info('Found %s tests and %d disabled tests.' % (len(tests), disabled))
252*8975f5c5SAndroid Build Coastguard Worker    return tests
253*8975f5c5SAndroid Build Coastguard Worker
254*8975f5c5SAndroid Build Coastguard Worker
255*8975f5c5SAndroid Build Coastguard Workerclass GroupedResult():
256*8975f5c5SAndroid Build Coastguard Worker    Passed = "Pass"
257*8975f5c5SAndroid Build Coastguard Worker    Failed = "Fail"
258*8975f5c5SAndroid Build Coastguard Worker    TimedOut = "Timeout"
259*8975f5c5SAndroid Build Coastguard Worker    CompileFailed = "CompileFailed"
260*8975f5c5SAndroid Build Coastguard Worker    CaptureFailed = "CaptureFailed"
261*8975f5c5SAndroid Build Coastguard Worker    ReplayFailed = "ReplayFailed"
262*8975f5c5SAndroid Build Coastguard Worker    Skipped = "Skipped"
263*8975f5c5SAndroid Build Coastguard Worker    FailedToTrace = "FailedToTrace"
264*8975f5c5SAndroid Build Coastguard Worker
265*8975f5c5SAndroid Build Coastguard Worker    ResultTypes = [
266*8975f5c5SAndroid Build Coastguard Worker        Passed, Failed, TimedOut, CompileFailed, CaptureFailed, ReplayFailed, Skipped,
267*8975f5c5SAndroid Build Coastguard Worker        FailedToTrace
268*8975f5c5SAndroid Build Coastguard Worker    ]
269*8975f5c5SAndroid Build Coastguard Worker
270*8975f5c5SAndroid Build Coastguard Worker    def __init__(self, resultcode, message, output, tests):
271*8975f5c5SAndroid Build Coastguard Worker        self.resultcode = resultcode
272*8975f5c5SAndroid Build Coastguard Worker        self.message = message
273*8975f5c5SAndroid Build Coastguard Worker        self.output = output
274*8975f5c5SAndroid Build Coastguard Worker        self.tests = []
275*8975f5c5SAndroid Build Coastguard Worker        for test in tests:
276*8975f5c5SAndroid Build Coastguard Worker            self.tests.append(test)
277*8975f5c5SAndroid Build Coastguard Worker
278*8975f5c5SAndroid Build Coastguard Worker
279*8975f5c5SAndroid Build Coastguard Workerdef CaptureProducedRequiredFiles(all_trace_files, test_name):
280*8975f5c5SAndroid Build Coastguard Worker    label = TestLabel(test_name)
281*8975f5c5SAndroid Build Coastguard Worker
282*8975f5c5SAndroid Build Coastguard Worker    test_files = [f for f in all_trace_files if f.startswith(label)]
283*8975f5c5SAndroid Build Coastguard Worker
284*8975f5c5SAndroid Build Coastguard Worker    frame_files_count = 0
285*8975f5c5SAndroid Build Coastguard Worker    context_header_count = 0
286*8975f5c5SAndroid Build Coastguard Worker    context_source_count = 0
287*8975f5c5SAndroid Build Coastguard Worker    source_json_count = 0
288*8975f5c5SAndroid Build Coastguard Worker    context_id = 0
289*8975f5c5SAndroid Build Coastguard Worker    for f in test_files:
290*8975f5c5SAndroid Build Coastguard Worker        # TODO: Consolidate. http://anglebug.com/42266223
291*8975f5c5SAndroid Build Coastguard Worker        if "_001.cpp" in f or "_001.c" in f:
292*8975f5c5SAndroid Build Coastguard Worker            frame_files_count += 1
293*8975f5c5SAndroid Build Coastguard Worker        elif f.endswith(".json"):
294*8975f5c5SAndroid Build Coastguard Worker            source_json_count += 1
295*8975f5c5SAndroid Build Coastguard Worker        elif f.endswith(".h"):
296*8975f5c5SAndroid Build Coastguard Worker            context_header_count += 1
297*8975f5c5SAndroid Build Coastguard Worker            if TRACE_FILE_SUFFIX in f:
298*8975f5c5SAndroid Build Coastguard Worker                context = f.split(TRACE_FILE_SUFFIX)[1][:-2]
299*8975f5c5SAndroid Build Coastguard Worker                context_id = int(context)
300*8975f5c5SAndroid Build Coastguard Worker        # TODO: Consolidate. http://anglebug.com/42266223
301*8975f5c5SAndroid Build Coastguard Worker        elif f.endswith(".cpp") or f.endswith(".c"):
302*8975f5c5SAndroid Build Coastguard Worker            context_source_count += 1
303*8975f5c5SAndroid Build Coastguard Worker    got_all_files = (
304*8975f5c5SAndroid Build Coastguard Worker        frame_files_count >= 1 and context_header_count >= 1 and context_source_count >= 1 and
305*8975f5c5SAndroid Build Coastguard Worker        source_json_count == 1)
306*8975f5c5SAndroid Build Coastguard Worker    return got_all_files
307*8975f5c5SAndroid Build Coastguard Worker
308*8975f5c5SAndroid Build Coastguard Worker
309*8975f5c5SAndroid Build Coastguard Workerdef GetCaptureEnv(args, trace_folder_path):
310*8975f5c5SAndroid Build Coastguard Worker    env = {
311*8975f5c5SAndroid Build Coastguard Worker        'ANGLE_CAPTURE_SERIALIZE_STATE': '1',
312*8975f5c5SAndroid Build Coastguard Worker        'ANGLE_FEATURE_OVERRIDES_ENABLED': 'forceRobustResourceInit:forceInitShaderVariables',
313*8975f5c5SAndroid Build Coastguard Worker        'ANGLE_FEATURE_OVERRIDES_DISABLED': 'supportsHostImageCopy',
314*8975f5c5SAndroid Build Coastguard Worker        'ANGLE_CAPTURE_ENABLED': '1',
315*8975f5c5SAndroid Build Coastguard Worker        'ANGLE_CAPTURE_OUT_DIR': trace_folder_path,
316*8975f5c5SAndroid Build Coastguard Worker    }
317*8975f5c5SAndroid Build Coastguard Worker
318*8975f5c5SAndroid Build Coastguard Worker    if args.mec > 0:
319*8975f5c5SAndroid Build Coastguard Worker        env['ANGLE_CAPTURE_FRAME_START'] = '{}'.format(args.mec)
320*8975f5c5SAndroid Build Coastguard Worker        env['ANGLE_CAPTURE_FRAME_END'] = '{}'.format(args.mec + 1)
321*8975f5c5SAndroid Build Coastguard Worker    else:
322*8975f5c5SAndroid Build Coastguard Worker        env['ANGLE_CAPTURE_FRAME_END'] = '{}'.format(CAPTURE_FRAME_END)
323*8975f5c5SAndroid Build Coastguard Worker
324*8975f5c5SAndroid Build Coastguard Worker    if args.expose_nonconformant_features:
325*8975f5c5SAndroid Build Coastguard Worker        env['ANGLE_FEATURE_OVERRIDES_ENABLED'] += ':exposeNonConformantExtensionsAndVersions'
326*8975f5c5SAndroid Build Coastguard Worker
327*8975f5c5SAndroid Build Coastguard Worker    return env
328*8975f5c5SAndroid Build Coastguard Worker
329*8975f5c5SAndroid Build Coastguard Worker
330*8975f5c5SAndroid Build Coastguard Workerdef PrintContextDiff(replay_build_dir, test_name):
331*8975f5c5SAndroid Build Coastguard Worker    frame = 1
332*8975f5c5SAndroid Build Coastguard Worker    found = False
333*8975f5c5SAndroid Build Coastguard Worker    while True:
334*8975f5c5SAndroid Build Coastguard Worker        capture_file = "{}/{}_ContextCaptured{}.json".format(replay_build_dir, test_name, frame)
335*8975f5c5SAndroid Build Coastguard Worker        replay_file = "{}/{}_ContextReplayed{}.json".format(replay_build_dir, test_name, frame)
336*8975f5c5SAndroid Build Coastguard Worker        if os.path.exists(capture_file) and os.path.exists(replay_file):
337*8975f5c5SAndroid Build Coastguard Worker            found = True
338*8975f5c5SAndroid Build Coastguard Worker            captured_context = open(capture_file, "r").readlines()
339*8975f5c5SAndroid Build Coastguard Worker            replayed_context = open(replay_file, "r").readlines()
340*8975f5c5SAndroid Build Coastguard Worker            for line in difflib.unified_diff(
341*8975f5c5SAndroid Build Coastguard Worker                    captured_context, replayed_context, fromfile=capture_file, tofile=replay_file):
342*8975f5c5SAndroid Build Coastguard Worker                print(line, end="")
343*8975f5c5SAndroid Build Coastguard Worker        else:
344*8975f5c5SAndroid Build Coastguard Worker            if frame > CAPTURE_FRAME_END:
345*8975f5c5SAndroid Build Coastguard Worker                break
346*8975f5c5SAndroid Build Coastguard Worker        frame = frame + 1
347*8975f5c5SAndroid Build Coastguard Worker    if not found:
348*8975f5c5SAndroid Build Coastguard Worker        logging.error('Could not find serialization diff files for %s' % test_name)
349*8975f5c5SAndroid Build Coastguard Worker
350*8975f5c5SAndroid Build Coastguard Worker
351*8975f5c5SAndroid Build Coastguard Workerdef UnlinkContextStateJsonFilesIfPresent(replay_build_dir):
352*8975f5c5SAndroid Build Coastguard Worker    for f in glob.glob(os.path.join(replay_build_dir, '*_ContextCaptured*.json')):
353*8975f5c5SAndroid Build Coastguard Worker        os.unlink(f)
354*8975f5c5SAndroid Build Coastguard Worker    for f in glob.glob(os.path.join(replay_build_dir, '*_ContextReplayed*.json')):
355*8975f5c5SAndroid Build Coastguard Worker        os.unlink(f)
356*8975f5c5SAndroid Build Coastguard Worker
357*8975f5c5SAndroid Build Coastguard Worker
358*8975f5c5SAndroid Build Coastguard Workerclass TestExpectation():
359*8975f5c5SAndroid Build Coastguard Worker    # tests that must not be run as list
360*8975f5c5SAndroid Build Coastguard Worker    skipped_for_capture_tests = {}
361*8975f5c5SAndroid Build Coastguard Worker    skipped_for_capture_tests_re = {}
362*8975f5c5SAndroid Build Coastguard Worker
363*8975f5c5SAndroid Build Coastguard Worker    # test expectations for tests that do not pass
364*8975f5c5SAndroid Build Coastguard Worker    non_pass_results = {}
365*8975f5c5SAndroid Build Coastguard Worker
366*8975f5c5SAndroid Build Coastguard Worker    # COMPILE_FAIL
367*8975f5c5SAndroid Build Coastguard Worker    compile_fail_tests = {}
368*8975f5c5SAndroid Build Coastguard Worker    compile_fail_tests_re = {}
369*8975f5c5SAndroid Build Coastguard Worker
370*8975f5c5SAndroid Build Coastguard Worker    flaky_tests = []
371*8975f5c5SAndroid Build Coastguard Worker
372*8975f5c5SAndroid Build Coastguard Worker    non_pass_re = {}
373*8975f5c5SAndroid Build Coastguard Worker
374*8975f5c5SAndroid Build Coastguard Worker    result_map = {
375*8975f5c5SAndroid Build Coastguard Worker        "FAIL": GroupedResult.Failed,
376*8975f5c5SAndroid Build Coastguard Worker        "TIMEOUT": GroupedResult.TimedOut,
377*8975f5c5SAndroid Build Coastguard Worker        "COMPILE_FAIL": GroupedResult.CompileFailed,
378*8975f5c5SAndroid Build Coastguard Worker        "NOT_RUN": GroupedResult.Skipped,
379*8975f5c5SAndroid Build Coastguard Worker        "SKIP_FOR_CAPTURE": GroupedResult.Skipped,
380*8975f5c5SAndroid Build Coastguard Worker        "PASS": GroupedResult.Passed,
381*8975f5c5SAndroid Build Coastguard Worker    }
382*8975f5c5SAndroid Build Coastguard Worker
383*8975f5c5SAndroid Build Coastguard Worker    def __init__(self, args):
384*8975f5c5SAndroid Build Coastguard Worker        expected_results_filename = "capture_replay_expectations.txt"
385*8975f5c5SAndroid Build Coastguard Worker        expected_results_path = os.path.join(REPLAY_SAMPLE_FOLDER, expected_results_filename)
386*8975f5c5SAndroid Build Coastguard Worker        self._asan = args.asan
387*8975f5c5SAndroid Build Coastguard Worker        with open(expected_results_path, "rt") as f:
388*8975f5c5SAndroid Build Coastguard Worker            for line in f:
389*8975f5c5SAndroid Build Coastguard Worker                l = line.strip()
390*8975f5c5SAndroid Build Coastguard Worker                if l != "" and not l.startswith("#"):
391*8975f5c5SAndroid Build Coastguard Worker                    self.ReadOneExpectation(l, args.debug)
392*8975f5c5SAndroid Build Coastguard Worker
393*8975f5c5SAndroid Build Coastguard Worker    def _CheckTagsWithConfig(self, tags, config_tags):
394*8975f5c5SAndroid Build Coastguard Worker        for tag in tags:
395*8975f5c5SAndroid Build Coastguard Worker            if tag not in config_tags:
396*8975f5c5SAndroid Build Coastguard Worker                return False
397*8975f5c5SAndroid Build Coastguard Worker        return True
398*8975f5c5SAndroid Build Coastguard Worker
399*8975f5c5SAndroid Build Coastguard Worker    def ReadOneExpectation(self, line, is_debug):
400*8975f5c5SAndroid Build Coastguard Worker        (testpattern, result) = line.split('=')
401*8975f5c5SAndroid Build Coastguard Worker        (test_info_string, test_name_string) = testpattern.split(':')
402*8975f5c5SAndroid Build Coastguard Worker        test_name = test_name_string.strip()
403*8975f5c5SAndroid Build Coastguard Worker        test_info = test_info_string.strip().split()
404*8975f5c5SAndroid Build Coastguard Worker        result_stripped = result.strip()
405*8975f5c5SAndroid Build Coastguard Worker
406*8975f5c5SAndroid Build Coastguard Worker        tags = []
407*8975f5c5SAndroid Build Coastguard Worker        if len(test_info) > 1:
408*8975f5c5SAndroid Build Coastguard Worker            tags = test_info[1:]
409*8975f5c5SAndroid Build Coastguard Worker
410*8975f5c5SAndroid Build Coastguard Worker        config_tags = [GetPlatformForSkip()]
411*8975f5c5SAndroid Build Coastguard Worker        if self._asan:
412*8975f5c5SAndroid Build Coastguard Worker            config_tags += ['ASAN']
413*8975f5c5SAndroid Build Coastguard Worker        if is_debug:
414*8975f5c5SAndroid Build Coastguard Worker            config_tags += ['DEBUG']
415*8975f5c5SAndroid Build Coastguard Worker
416*8975f5c5SAndroid Build Coastguard Worker        if self._CheckTagsWithConfig(tags, config_tags):
417*8975f5c5SAndroid Build Coastguard Worker            test_name_regex = re.compile('^' + test_name.replace('*', '.*') + '$')
418*8975f5c5SAndroid Build Coastguard Worker            if result_stripped == 'COMPILE_FAIL':
419*8975f5c5SAndroid Build Coastguard Worker                self.compile_fail_tests[test_name] = self.result_map[result_stripped]
420*8975f5c5SAndroid Build Coastguard Worker                self.compile_fail_tests_re[test_name] = test_name_regex
421*8975f5c5SAndroid Build Coastguard Worker            if result_stripped == 'SKIP_FOR_CAPTURE' or result_stripped == 'TIMEOUT':
422*8975f5c5SAndroid Build Coastguard Worker                self.skipped_for_capture_tests[test_name] = self.result_map[result_stripped]
423*8975f5c5SAndroid Build Coastguard Worker                self.skipped_for_capture_tests_re[test_name] = test_name_regex
424*8975f5c5SAndroid Build Coastguard Worker            elif result_stripped == 'FLAKY':
425*8975f5c5SAndroid Build Coastguard Worker                self.flaky_tests.append(test_name_regex)
426*8975f5c5SAndroid Build Coastguard Worker            else:
427*8975f5c5SAndroid Build Coastguard Worker                self.non_pass_results[test_name] = self.result_map[result_stripped]
428*8975f5c5SAndroid Build Coastguard Worker                self.non_pass_re[test_name] = test_name_regex
429*8975f5c5SAndroid Build Coastguard Worker
430*8975f5c5SAndroid Build Coastguard Worker    def TestIsSkippedForCapture(self, test_name):
431*8975f5c5SAndroid Build Coastguard Worker        return any(p.match(test_name) for p in self.skipped_for_capture_tests_re.values())
432*8975f5c5SAndroid Build Coastguard Worker
433*8975f5c5SAndroid Build Coastguard Worker    def TestIsCompileFail(self, test_name):
434*8975f5c5SAndroid Build Coastguard Worker        return any(p.match(test_name) for p in self.compile_fail_tests_re.values())
435*8975f5c5SAndroid Build Coastguard Worker
436*8975f5c5SAndroid Build Coastguard Worker    def Filter(self, test_list, run_all_tests):
437*8975f5c5SAndroid Build Coastguard Worker        result = {}
438*8975f5c5SAndroid Build Coastguard Worker        for t in test_list:
439*8975f5c5SAndroid Build Coastguard Worker            for key in self.non_pass_results.keys():
440*8975f5c5SAndroid Build Coastguard Worker                if self.non_pass_re[key].match(t) is not None:
441*8975f5c5SAndroid Build Coastguard Worker                    result[t] = self.non_pass_results[key]
442*8975f5c5SAndroid Build Coastguard Worker            for key in self.compile_fail_tests.keys():
443*8975f5c5SAndroid Build Coastguard Worker                if self.compile_fail_tests_re[key].match(t) is not None:
444*8975f5c5SAndroid Build Coastguard Worker                    result[t] = self.compile_fail_tests[key]
445*8975f5c5SAndroid Build Coastguard Worker            if run_all_tests:
446*8975f5c5SAndroid Build Coastguard Worker                for [key, r] in self.skipped_for_capture_tests.items():
447*8975f5c5SAndroid Build Coastguard Worker                    if self.skipped_for_capture_tests_re[key].match(t) is not None:
448*8975f5c5SAndroid Build Coastguard Worker                        result[t] = r
449*8975f5c5SAndroid Build Coastguard Worker        return result
450*8975f5c5SAndroid Build Coastguard Worker
451*8975f5c5SAndroid Build Coastguard Worker    def IsFlaky(self, test_name):
452*8975f5c5SAndroid Build Coastguard Worker        for flaky in self.flaky_tests:
453*8975f5c5SAndroid Build Coastguard Worker            if flaky.match(test_name) is not None:
454*8975f5c5SAndroid Build Coastguard Worker                return True
455*8975f5c5SAndroid Build Coastguard Worker        return False
456*8975f5c5SAndroid Build Coastguard Worker
457*8975f5c5SAndroid Build Coastguard Worker
458*8975f5c5SAndroid Build Coastguard Workerdef GetPlatformForSkip():
459*8975f5c5SAndroid Build Coastguard Worker    # yapf: disable
460*8975f5c5SAndroid Build Coastguard Worker    # we want each pair on one line
461*8975f5c5SAndroid Build Coastguard Worker    platform_map = { 'win32' : 'WIN',
462*8975f5c5SAndroid Build Coastguard Worker                     'linux' : 'LINUX' }
463*8975f5c5SAndroid Build Coastguard Worker    # yapf: enable
464*8975f5c5SAndroid Build Coastguard Worker    return platform_map.get(sys.platform, 'UNKNOWN')
465*8975f5c5SAndroid Build Coastguard Worker
466*8975f5c5SAndroid Build Coastguard Worker
467*8975f5c5SAndroid Build Coastguard Workerdef RunInParallel(f, lst, max_workers, stop_event):
468*8975f5c5SAndroid Build Coastguard Worker    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
469*8975f5c5SAndroid Build Coastguard Worker        future_to_arg = {executor.submit(f, arg): arg for arg in lst}
470*8975f5c5SAndroid Build Coastguard Worker        try:
471*8975f5c5SAndroid Build Coastguard Worker            for future in concurrent.futures.as_completed(future_to_arg):
472*8975f5c5SAndroid Build Coastguard Worker                yield future, future_to_arg[future]
473*8975f5c5SAndroid Build Coastguard Worker        except KeyboardInterrupt:
474*8975f5c5SAndroid Build Coastguard Worker            stop_event.set()
475*8975f5c5SAndroid Build Coastguard Worker            raise
476*8975f5c5SAndroid Build Coastguard Worker
477*8975f5c5SAndroid Build Coastguard Worker
478*8975f5c5SAndroid Build Coastguard Workerdef RunProcess(cmd, env, xvfb_pool, stop_event, timeout):
479*8975f5c5SAndroid Build Coastguard Worker    stdout = [None]
480*8975f5c5SAndroid Build Coastguard Worker
481*8975f5c5SAndroid Build Coastguard Worker    def _Reader(process):
482*8975f5c5SAndroid Build Coastguard Worker        stdout[0] = process.stdout.read().decode()
483*8975f5c5SAndroid Build Coastguard Worker
484*8975f5c5SAndroid Build Coastguard Worker    with GetDisplayEnv(env, xvfb_pool) as run_env:
485*8975f5c5SAndroid Build Coastguard Worker        process = subprocess.Popen(
486*8975f5c5SAndroid Build Coastguard Worker            cmd, env=run_env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
487*8975f5c5SAndroid Build Coastguard Worker        t = threading.Thread(target=_Reader, args=(process,))
488*8975f5c5SAndroid Build Coastguard Worker        t.start()
489*8975f5c5SAndroid Build Coastguard Worker        time_start = time.time()
490*8975f5c5SAndroid Build Coastguard Worker
491*8975f5c5SAndroid Build Coastguard Worker        while True:
492*8975f5c5SAndroid Build Coastguard Worker            time.sleep(0.1)
493*8975f5c5SAndroid Build Coastguard Worker            if process.poll() is not None:
494*8975f5c5SAndroid Build Coastguard Worker                t.join()
495*8975f5c5SAndroid Build Coastguard Worker                return process.returncode, stdout[0]
496*8975f5c5SAndroid Build Coastguard Worker            if timeout is not None and time.time() - time_start > timeout:
497*8975f5c5SAndroid Build Coastguard Worker                process.kill()
498*8975f5c5SAndroid Build Coastguard Worker                t.join()
499*8975f5c5SAndroid Build Coastguard Worker                return subprocess.TimeoutExpired, stdout[0]
500*8975f5c5SAndroid Build Coastguard Worker            if stop_event.is_set():
501*8975f5c5SAndroid Build Coastguard Worker                process.kill()
502*8975f5c5SAndroid Build Coastguard Worker                t.join()
503*8975f5c5SAndroid Build Coastguard Worker                return None, stdout[0]
504*8975f5c5SAndroid Build Coastguard Worker
505*8975f5c5SAndroid Build Coastguard Worker
506*8975f5c5SAndroid Build Coastguard Workerdef ReturnCodeWithNote(rc):
507*8975f5c5SAndroid Build Coastguard Worker    s = 'rc=%s' % rc
508*8975f5c5SAndroid Build Coastguard Worker    if sys.platform.startswith('linux'):
509*8975f5c5SAndroid Build Coastguard Worker        if rc == -9:
510*8975f5c5SAndroid Build Coastguard Worker            # OOM killer sends SIGKILL to the process, return code is -signal
511*8975f5c5SAndroid Build Coastguard Worker            s += ' SIGKILL possibly due to OOM'
512*8975f5c5SAndroid Build Coastguard Worker    return s
513*8975f5c5SAndroid Build Coastguard Worker
514*8975f5c5SAndroid Build Coastguard Worker
515*8975f5c5SAndroid Build Coastguard Workerdef RunCaptureInParallel(args, trace_folder_path, test_names, worker_count, xvfb_pool):
516*8975f5c5SAndroid Build Coastguard Worker    n = args.batch_count
517*8975f5c5SAndroid Build Coastguard Worker    test_batches = [test_names[i:i + n] for i in range(0, len(test_names), n)]
518*8975f5c5SAndroid Build Coastguard Worker
519*8975f5c5SAndroid Build Coastguard Worker    extra_env = GetCaptureEnv(args, trace_folder_path)
520*8975f5c5SAndroid Build Coastguard Worker    env = {**os.environ.copy(), **extra_env}
521*8975f5c5SAndroid Build Coastguard Worker    test_exe_path = os.path.join(args.out_dir, 'Capture', args.test_suite)
522*8975f5c5SAndroid Build Coastguard Worker
523*8975f5c5SAndroid Build Coastguard Worker    stop_event = threading.Event()
524*8975f5c5SAndroid Build Coastguard Worker
525*8975f5c5SAndroid Build Coastguard Worker    def _RunCapture(tests):
526*8975f5c5SAndroid Build Coastguard Worker        filt = ':'.join(tests)
527*8975f5c5SAndroid Build Coastguard Worker
528*8975f5c5SAndroid Build Coastguard Worker        results_file = tempfile.mktemp()
529*8975f5c5SAndroid Build Coastguard Worker        cmd = [
530*8975f5c5SAndroid Build Coastguard Worker            test_exe_path,
531*8975f5c5SAndroid Build Coastguard Worker            '--gtest_filter=%s' % filt,
532*8975f5c5SAndroid Build Coastguard Worker            '--angle-per-test-capture-label',
533*8975f5c5SAndroid Build Coastguard Worker            '--results-file=' + results_file,
534*8975f5c5SAndroid Build Coastguard Worker        ]
535*8975f5c5SAndroid Build Coastguard Worker
536*8975f5c5SAndroid Build Coastguard Worker        # Add --use-config to avoid registering all test configurations
537*8975f5c5SAndroid Build Coastguard Worker        configs = set([t.split('/')[-1] for t in filt.split(':')])
538*8975f5c5SAndroid Build Coastguard Worker        if len(configs) == 1:
539*8975f5c5SAndroid Build Coastguard Worker            config, = configs
540*8975f5c5SAndroid Build Coastguard Worker            if '*' not in config:
541*8975f5c5SAndroid Build Coastguard Worker                cmd.append('--use-config=%s' % config)
542*8975f5c5SAndroid Build Coastguard Worker
543*8975f5c5SAndroid Build Coastguard Worker        test_results = None
544*8975f5c5SAndroid Build Coastguard Worker        try:
545*8975f5c5SAndroid Build Coastguard Worker            rc, stdout = RunProcess(cmd, env, xvfb_pool, stop_event, CAPTURE_SUBPROCESS_TIMEOUT)
546*8975f5c5SAndroid Build Coastguard Worker            if rc == 0:
547*8975f5c5SAndroid Build Coastguard Worker                with open(results_file) as f:
548*8975f5c5SAndroid Build Coastguard Worker                    test_results = json.load(f)
549*8975f5c5SAndroid Build Coastguard Worker        finally:
550*8975f5c5SAndroid Build Coastguard Worker            try:
551*8975f5c5SAndroid Build Coastguard Worker                os.unlink(results_file)
552*8975f5c5SAndroid Build Coastguard Worker            except Exception:
553*8975f5c5SAndroid Build Coastguard Worker                pass
554*8975f5c5SAndroid Build Coastguard Worker
555*8975f5c5SAndroid Build Coastguard Worker        return rc, test_results, stdout
556*8975f5c5SAndroid Build Coastguard Worker
557*8975f5c5SAndroid Build Coastguard Worker    skipped_by_suite = set()
558*8975f5c5SAndroid Build Coastguard Worker    capture_failed = False
559*8975f5c5SAndroid Build Coastguard Worker    for (future, tests) in RunInParallel(_RunCapture, test_batches, worker_count, stop_event):
560*8975f5c5SAndroid Build Coastguard Worker        rc, test_results, stdout = future.result()
561*8975f5c5SAndroid Build Coastguard Worker
562*8975f5c5SAndroid Build Coastguard Worker        if rc == subprocess.TimeoutExpired:
563*8975f5c5SAndroid Build Coastguard Worker            logging.error('Capture failed - timed out after %ss\nTests: %s\nPartial stdout:\n%s',
564*8975f5c5SAndroid Build Coastguard Worker                          CAPTURE_SUBPROCESS_TIMEOUT, ':'.join(tests), stdout)
565*8975f5c5SAndroid Build Coastguard Worker            capture_failed = True
566*8975f5c5SAndroid Build Coastguard Worker            continue
567*8975f5c5SAndroid Build Coastguard Worker
568*8975f5c5SAndroid Build Coastguard Worker        if rc != 0:
569*8975f5c5SAndroid Build Coastguard Worker            logging.error('Capture failed (%s)\nTests: %s\nStdout:\n%s\n', rc,
570*8975f5c5SAndroid Build Coastguard Worker                          ReturnCodeWithNote(rc), ':'.join(tests), stdout)
571*8975f5c5SAndroid Build Coastguard Worker            capture_failed = True
572*8975f5c5SAndroid Build Coastguard Worker            continue
573*8975f5c5SAndroid Build Coastguard Worker
574*8975f5c5SAndroid Build Coastguard Worker        if args.show_capture_stdout:
575*8975f5c5SAndroid Build Coastguard Worker            logging.info('Capture test stdout:\n%s\n', stdout)
576*8975f5c5SAndroid Build Coastguard Worker
577*8975f5c5SAndroid Build Coastguard Worker        for test_name, res in test_results['tests'].items():
578*8975f5c5SAndroid Build Coastguard Worker            if res['actual'] == 'SKIP':
579*8975f5c5SAndroid Build Coastguard Worker                skipped_by_suite.add(test_name)
580*8975f5c5SAndroid Build Coastguard Worker
581*8975f5c5SAndroid Build Coastguard Worker    return not capture_failed, skipped_by_suite
582*8975f5c5SAndroid Build Coastguard Worker
583*8975f5c5SAndroid Build Coastguard Worker
584*8975f5c5SAndroid Build Coastguard Workerdef RunReplayTestsInParallel(args, replay_build_dir, replay_tests, expected_results,
585*8975f5c5SAndroid Build Coastguard Worker                             labels_to_tests, worker_count, xvfb_pool):
586*8975f5c5SAndroid Build Coastguard Worker    extra_env = {}
587*8975f5c5SAndroid Build Coastguard Worker    if args.expose_nonconformant_features:
588*8975f5c5SAndroid Build Coastguard Worker        extra_env['ANGLE_FEATURE_OVERRIDES_ENABLED'] = 'exposeNonConformantExtensionsAndVersions'
589*8975f5c5SAndroid Build Coastguard Worker    env = {**os.environ.copy(), **extra_env}
590*8975f5c5SAndroid Build Coastguard Worker
591*8975f5c5SAndroid Build Coastguard Worker    stop_event = threading.Event()
592*8975f5c5SAndroid Build Coastguard Worker
593*8975f5c5SAndroid Build Coastguard Worker    def _RunReplay(test):
594*8975f5c5SAndroid Build Coastguard Worker        replay_exe_path = os.path.join(replay_build_dir, REPLAY_BINARY)
595*8975f5c5SAndroid Build Coastguard Worker        cmd = [replay_exe_path, TestLabel(test)]
596*8975f5c5SAndroid Build Coastguard Worker        return RunProcess(cmd, env, xvfb_pool, stop_event, REPLAY_SUBPROCESS_TIMEOUT)
597*8975f5c5SAndroid Build Coastguard Worker
598*8975f5c5SAndroid Build Coastguard Worker    replay_failed = False
599*8975f5c5SAndroid Build Coastguard Worker    for (future, test) in RunInParallel(_RunReplay, replay_tests, worker_count, stop_event):
600*8975f5c5SAndroid Build Coastguard Worker        expected_to_pass = expected_results[test] == GroupedResult.Passed
601*8975f5c5SAndroid Build Coastguard Worker        rc, stdout = future.result()
602*8975f5c5SAndroid Build Coastguard Worker        if rc == subprocess.TimeoutExpired:
603*8975f5c5SAndroid Build Coastguard Worker            if expected_to_pass:
604*8975f5c5SAndroid Build Coastguard Worker                logging.error('Replay failed - timed out after %ss\nTest: %s\nPartial stdout:\n%s',
605*8975f5c5SAndroid Build Coastguard Worker                              REPLAY_SUBPROCESS_TIMEOUT, test, stdout)
606*8975f5c5SAndroid Build Coastguard Worker                replay_failed = True
607*8975f5c5SAndroid Build Coastguard Worker            else:
608*8975f5c5SAndroid Build Coastguard Worker                logging.info('Ignoring replay timeout due to expectation: %s [expected %s]', test,
609*8975f5c5SAndroid Build Coastguard Worker                             expected_results[test])
610*8975f5c5SAndroid Build Coastguard Worker            continue
611*8975f5c5SAndroid Build Coastguard Worker
612*8975f5c5SAndroid Build Coastguard Worker        if rc != 0:
613*8975f5c5SAndroid Build Coastguard Worker            if expected_to_pass:
614*8975f5c5SAndroid Build Coastguard Worker                logging.error('Replay failed (%s)\nTest: %s\nStdout:\n%s\n',
615*8975f5c5SAndroid Build Coastguard Worker                              ReturnCodeWithNote(rc), test, stdout)
616*8975f5c5SAndroid Build Coastguard Worker                replay_failed = True
617*8975f5c5SAndroid Build Coastguard Worker            else:
618*8975f5c5SAndroid Build Coastguard Worker                logging.info('Ignoring replay failure due to expectation: %s [expected %s]', test,
619*8975f5c5SAndroid Build Coastguard Worker                             expected_results[test])
620*8975f5c5SAndroid Build Coastguard Worker            continue
621*8975f5c5SAndroid Build Coastguard Worker
622*8975f5c5SAndroid Build Coastguard Worker        if args.show_replay_stdout:
623*8975f5c5SAndroid Build Coastguard Worker            logging.info('Replay test stdout:\n%s\n', stdout)
624*8975f5c5SAndroid Build Coastguard Worker
625*8975f5c5SAndroid Build Coastguard Worker        output_lines = stdout.splitlines()
626*8975f5c5SAndroid Build Coastguard Worker        for output_line in output_lines:
627*8975f5c5SAndroid Build Coastguard Worker            words = output_line.split(" ")
628*8975f5c5SAndroid Build Coastguard Worker            if len(words) == 3 and words[0] == RESULT_TAG:
629*8975f5c5SAndroid Build Coastguard Worker                test_name = labels_to_tests[words[1]]
630*8975f5c5SAndroid Build Coastguard Worker                result = int(words[2])
631*8975f5c5SAndroid Build Coastguard Worker
632*8975f5c5SAndroid Build Coastguard Worker                if result == 0:
633*8975f5c5SAndroid Build Coastguard Worker                    pass
634*8975f5c5SAndroid Build Coastguard Worker                elif result == REPLAY_INITIALIZATION_FAILURE:
635*8975f5c5SAndroid Build Coastguard Worker                    if expected_to_pass:
636*8975f5c5SAndroid Build Coastguard Worker                        replay_failed = True
637*8975f5c5SAndroid Build Coastguard Worker                        logging.error('Replay failed. Initialization failure: %s' % test_name)
638*8975f5c5SAndroid Build Coastguard Worker                    else:
639*8975f5c5SAndroid Build Coastguard Worker                        logging.info(
640*8975f5c5SAndroid Build Coastguard Worker                            'Ignoring replay failure due to expectation: %s [expected %s]', test,
641*8975f5c5SAndroid Build Coastguard Worker                            expected_results[test])
642*8975f5c5SAndroid Build Coastguard Worker                elif result == REPLAY_SERIALIZATION_FAILURE:
643*8975f5c5SAndroid Build Coastguard Worker                    if expected_to_pass:
644*8975f5c5SAndroid Build Coastguard Worker                        replay_failed = True
645*8975f5c5SAndroid Build Coastguard Worker                        logging.error('Replay failed. Context comparison failed: %s' % test_name)
646*8975f5c5SAndroid Build Coastguard Worker                        PrintContextDiff(replay_build_dir, words[1])
647*8975f5c5SAndroid Build Coastguard Worker                    else:
648*8975f5c5SAndroid Build Coastguard Worker                        logging.info(
649*8975f5c5SAndroid Build Coastguard Worker                            'Ignoring replay context diff due to expectation: %s [expected %s]',
650*8975f5c5SAndroid Build Coastguard Worker                            test, expected_results[test])
651*8975f5c5SAndroid Build Coastguard Worker                else:
652*8975f5c5SAndroid Build Coastguard Worker                    replay_failed = True
653*8975f5c5SAndroid Build Coastguard Worker                    logging.error('Replay failed. Unknown result code: %s -> %d' %
654*8975f5c5SAndroid Build Coastguard Worker                                  (test_name, result))
655*8975f5c5SAndroid Build Coastguard Worker
656*8975f5c5SAndroid Build Coastguard Worker    return not replay_failed
657*8975f5c5SAndroid Build Coastguard Worker
658*8975f5c5SAndroid Build Coastguard Worker
659*8975f5c5SAndroid Build Coastguard Workerdef CleanupAfterReplay(replay_build_dir, test_labels):
660*8975f5c5SAndroid Build Coastguard Worker    # Remove files that have test labels in the file name, .e.g:
661*8975f5c5SAndroid Build Coastguard Worker    # ClearTest_ClearIsClamped_ES2_Vulkan_SwiftShader.dll.pdb
662*8975f5c5SAndroid Build Coastguard Worker    for build_file in os.listdir(replay_build_dir):
663*8975f5c5SAndroid Build Coastguard Worker        if any(label in build_file for label in test_labels):
664*8975f5c5SAndroid Build Coastguard Worker            os.unlink(os.path.join(replay_build_dir, build_file))
665*8975f5c5SAndroid Build Coastguard Worker
666*8975f5c5SAndroid Build Coastguard Worker
667*8975f5c5SAndroid Build Coastguard Workerdef main(args):
668*8975f5c5SAndroid Build Coastguard Worker    angle_test_util.SetupLogging(args.log.upper())
669*8975f5c5SAndroid Build Coastguard Worker
670*8975f5c5SAndroid Build Coastguard Worker    # Set cwd to ANGLE root
671*8975f5c5SAndroid Build Coastguard Worker    os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
672*8975f5c5SAndroid Build Coastguard Worker
673*8975f5c5SAndroid Build Coastguard Worker    if getpass.getuser() == 'chrome-bot':
674*8975f5c5SAndroid Build Coastguard Worker        # bots need different re-client auth settings than developers b/319246651
675*8975f5c5SAndroid Build Coastguard Worker        os.environ["RBE_use_gce_credentials"] = "true"
676*8975f5c5SAndroid Build Coastguard Worker        os.environ["RBE_use_application_default_credentials"] = "false"
677*8975f5c5SAndroid Build Coastguard Worker        os.environ["RBE_automatic_auth"] = "false"
678*8975f5c5SAndroid Build Coastguard Worker        os.environ["RBE_experimental_credentials_helper"] = ""
679*8975f5c5SAndroid Build Coastguard Worker        os.environ["RBE_experimental_credentials_helper_args"] = ""
680*8975f5c5SAndroid Build Coastguard Worker
681*8975f5c5SAndroid Build Coastguard Worker    trace_dir = "%s%d" % (TRACE_FOLDER, 0)
682*8975f5c5SAndroid Build Coastguard Worker    trace_folder_path = os.path.join(REPLAY_SAMPLE_FOLDER, trace_dir)
683*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(trace_folder_path):
684*8975f5c5SAndroid Build Coastguard Worker        shutil.rmtree(trace_folder_path)
685*8975f5c5SAndroid Build Coastguard Worker    os.makedirs(trace_folder_path)
686*8975f5c5SAndroid Build Coastguard Worker
687*8975f5c5SAndroid Build Coastguard Worker    capture_build_dir = os.path.join(args.out_dir, 'Capture')
688*8975f5c5SAndroid Build Coastguard Worker    replay_build_dir = os.path.join(args.out_dir, 'Replay%d' % 0)
689*8975f5c5SAndroid Build Coastguard Worker
690*8975f5c5SAndroid Build Coastguard Worker    logging.info('Building capture tests')
691*8975f5c5SAndroid Build Coastguard Worker
692*8975f5c5SAndroid Build Coastguard Worker    subprocess.check_call([GN_PATH, 'gen', '--args=%s' % GetGnArgsStr(args), capture_build_dir])
693*8975f5c5SAndroid Build Coastguard Worker    subprocess.check_call(
694*8975f5c5SAndroid Build Coastguard Worker        [sys.executable, AUTONINJA_PATH, '-C', capture_build_dir, args.test_suite])
695*8975f5c5SAndroid Build Coastguard Worker
696*8975f5c5SAndroid Build Coastguard Worker    with MaybeXvfbPool(args.xvfb, 1) as xvfb_pool:
697*8975f5c5SAndroid Build Coastguard Worker        logging.info('Getting test list')
698*8975f5c5SAndroid Build Coastguard Worker        test_path = os.path.join(capture_build_dir, args.test_suite)
699*8975f5c5SAndroid Build Coastguard Worker        with GetDisplayEnv(os.environ, xvfb_pool) as env:
700*8975f5c5SAndroid Build Coastguard Worker            test_list = subprocess.check_output(
701*8975f5c5SAndroid Build Coastguard Worker                [test_path, "--list-tests",
702*8975f5c5SAndroid Build Coastguard Worker                 "--gtest_filter=%s" % args.filter], env=env, text=True)
703*8975f5c5SAndroid Build Coastguard Worker
704*8975f5c5SAndroid Build Coastguard Worker    test_expectation = TestExpectation(args)
705*8975f5c5SAndroid Build Coastguard Worker    test_names = ParseTestNamesFromTestList(test_list, test_expectation,
706*8975f5c5SAndroid Build Coastguard Worker                                            args.also_run_skipped_for_capture_tests)
707*8975f5c5SAndroid Build Coastguard Worker    test_expectation_for_list = test_expectation.Filter(test_names,
708*8975f5c5SAndroid Build Coastguard Worker                                                        args.also_run_skipped_for_capture_tests)
709*8975f5c5SAndroid Build Coastguard Worker
710*8975f5c5SAndroid Build Coastguard Worker    test_names = [
711*8975f5c5SAndroid Build Coastguard Worker        t for t in test_names if (not test_expectation.TestIsCompileFail(t) and
712*8975f5c5SAndroid Build Coastguard Worker                                  not test_expectation.TestIsSkippedForCapture(t))
713*8975f5c5SAndroid Build Coastguard Worker    ]
714*8975f5c5SAndroid Build Coastguard Worker
715*8975f5c5SAndroid Build Coastguard Worker    if not test_names:
716*8975f5c5SAndroid Build Coastguard Worker        logging.error('No capture tests to run. Is everything skipped?')
717*8975f5c5SAndroid Build Coastguard Worker        return EXIT_FAILURE
718*8975f5c5SAndroid Build Coastguard Worker
719*8975f5c5SAndroid Build Coastguard Worker    worker_count = min(args.max_jobs, os.cpu_count(), 1 + len(test_names) // 10)
720*8975f5c5SAndroid Build Coastguard Worker
721*8975f5c5SAndroid Build Coastguard Worker    logging.info('Running %d capture tests, worker_count=%d batch_count=%d', len(test_names),
722*8975f5c5SAndroid Build Coastguard Worker                 worker_count, args.batch_count)
723*8975f5c5SAndroid Build Coastguard Worker
724*8975f5c5SAndroid Build Coastguard Worker    with MaybeXvfbPool(args.xvfb, worker_count) as xvfb_pool:
725*8975f5c5SAndroid Build Coastguard Worker        success, skipped_by_suite = RunCaptureInParallel(args, trace_folder_path, test_names,
726*8975f5c5SAndroid Build Coastguard Worker                                                         worker_count, xvfb_pool)
727*8975f5c5SAndroid Build Coastguard Worker        if not success:
728*8975f5c5SAndroid Build Coastguard Worker            logging.error('Capture tests failed, see "Capture failed" errors above')
729*8975f5c5SAndroid Build Coastguard Worker            return EXIT_FAILURE
730*8975f5c5SAndroid Build Coastguard Worker
731*8975f5c5SAndroid Build Coastguard Worker        logging.info('RunCaptureInParallel finished')
732*8975f5c5SAndroid Build Coastguard Worker
733*8975f5c5SAndroid Build Coastguard Worker        labels_to_tests = {TestLabel(t): t for t in test_names}
734*8975f5c5SAndroid Build Coastguard Worker
735*8975f5c5SAndroid Build Coastguard Worker        all_trace_files = [f.name for f in os.scandir(trace_folder_path) if f.is_file()]
736*8975f5c5SAndroid Build Coastguard Worker
737*8975f5c5SAndroid Build Coastguard Worker        replay_tests = []
738*8975f5c5SAndroid Build Coastguard Worker        failed = False
739*8975f5c5SAndroid Build Coastguard Worker        for test_name in test_names:
740*8975f5c5SAndroid Build Coastguard Worker            if test_name not in skipped_by_suite:
741*8975f5c5SAndroid Build Coastguard Worker                if CaptureProducedRequiredFiles(all_trace_files, test_name):
742*8975f5c5SAndroid Build Coastguard Worker                    replay_tests.append(test_name)
743*8975f5c5SAndroid Build Coastguard Worker                else:
744*8975f5c5SAndroid Build Coastguard Worker                    logging.error('Capture failed: test missing replay files: %s', test_name)
745*8975f5c5SAndroid Build Coastguard Worker                    failed = True
746*8975f5c5SAndroid Build Coastguard Worker
747*8975f5c5SAndroid Build Coastguard Worker        if failed:
748*8975f5c5SAndroid Build Coastguard Worker            logging.error('Capture tests failed, see "Capture failed" errors above')
749*8975f5c5SAndroid Build Coastguard Worker            return EXIT_FAILURE
750*8975f5c5SAndroid Build Coastguard Worker
751*8975f5c5SAndroid Build Coastguard Worker        logging.info('CaptureProducedRequiredFiles finished')
752*8975f5c5SAndroid Build Coastguard Worker
753*8975f5c5SAndroid Build Coastguard Worker        composite_file_id = 1
754*8975f5c5SAndroid Build Coastguard Worker        names_path = os.path.join(trace_folder_path, 'test_names_%d.json' % composite_file_id)
755*8975f5c5SAndroid Build Coastguard Worker        with open(names_path, 'w') as f:
756*8975f5c5SAndroid Build Coastguard Worker            f.write(json.dumps({'traces': [TestLabel(t) for t in replay_tests]}))
757*8975f5c5SAndroid Build Coastguard Worker
758*8975f5c5SAndroid Build Coastguard Worker        replay_build_dir = os.path.join(args.out_dir, 'Replay%d' % 0)
759*8975f5c5SAndroid Build Coastguard Worker        UnlinkContextStateJsonFilesIfPresent(replay_build_dir)
760*8975f5c5SAndroid Build Coastguard Worker
761*8975f5c5SAndroid Build Coastguard Worker        logging.info('Building replay tests')
762*8975f5c5SAndroid Build Coastguard Worker
763*8975f5c5SAndroid Build Coastguard Worker        extra_gn_args = [('angle_build_capture_replay_tests', 'true'),
764*8975f5c5SAndroid Build Coastguard Worker                         ('angle_capture_replay_test_trace_dir', '"%s"' % trace_dir),
765*8975f5c5SAndroid Build Coastguard Worker                         ('angle_capture_replay_composite_file_id', str(composite_file_id))]
766*8975f5c5SAndroid Build Coastguard Worker        subprocess.check_call(
767*8975f5c5SAndroid Build Coastguard Worker            [GN_PATH, 'gen',
768*8975f5c5SAndroid Build Coastguard Worker             '--args=%s' % GetGnArgsStr(args, extra_gn_args), replay_build_dir])
769*8975f5c5SAndroid Build Coastguard Worker        subprocess.check_call(
770*8975f5c5SAndroid Build Coastguard Worker            [sys.executable, AUTONINJA_PATH, '-C', replay_build_dir, REPLAY_BINARY])
771*8975f5c5SAndroid Build Coastguard Worker
772*8975f5c5SAndroid Build Coastguard Worker        if not replay_tests:
773*8975f5c5SAndroid Build Coastguard Worker            logging.error('No replay tests to run. Is everything skipped?')
774*8975f5c5SAndroid Build Coastguard Worker            return EXIT_FAILURE
775*8975f5c5SAndroid Build Coastguard Worker        logging.info('Running %d replay tests', len(replay_tests))
776*8975f5c5SAndroid Build Coastguard Worker
777*8975f5c5SAndroid Build Coastguard Worker        expected_results = {}
778*8975f5c5SAndroid Build Coastguard Worker        for test in replay_tests:
779*8975f5c5SAndroid Build Coastguard Worker            expected_result = test_expectation_for_list.get(test, GroupedResult.Passed)
780*8975f5c5SAndroid Build Coastguard Worker            if test_expectation.IsFlaky(test):
781*8975f5c5SAndroid Build Coastguard Worker                expected_result = 'Flaky'
782*8975f5c5SAndroid Build Coastguard Worker            expected_results[test] = expected_result
783*8975f5c5SAndroid Build Coastguard Worker
784*8975f5c5SAndroid Build Coastguard Worker        if not RunReplayTestsInParallel(args, replay_build_dir, replay_tests, expected_results,
785*8975f5c5SAndroid Build Coastguard Worker                                        labels_to_tests, worker_count, xvfb_pool):
786*8975f5c5SAndroid Build Coastguard Worker            logging.error('Replay tests failed, see "Replay failed" errors above')
787*8975f5c5SAndroid Build Coastguard Worker            return EXIT_FAILURE
788*8975f5c5SAndroid Build Coastguard Worker
789*8975f5c5SAndroid Build Coastguard Worker        logging.info('Replay tests finished successfully')
790*8975f5c5SAndroid Build Coastguard Worker
791*8975f5c5SAndroid Build Coastguard Worker    if not args.keep_temp_files:
792*8975f5c5SAndroid Build Coastguard Worker        CleanupAfterReplay(replay_build_dir, list(labels_to_tests.keys()))
793*8975f5c5SAndroid Build Coastguard Worker        shutil.rmtree(trace_folder_path)
794*8975f5c5SAndroid Build Coastguard Worker
795*8975f5c5SAndroid Build Coastguard Worker    return EXIT_SUCCESS
796*8975f5c5SAndroid Build Coastguard Worker
797*8975f5c5SAndroid Build Coastguard Worker
798*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
799*8975f5c5SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
800*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
801*8975f5c5SAndroid Build Coastguard Worker        '--out-dir',
802*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_OUT_DIR,
803*8975f5c5SAndroid Build Coastguard Worker        help='Where to build ANGLE for capture and replay. Relative to the ANGLE folder. Default is "%s".'
804*8975f5c5SAndroid Build Coastguard Worker        % DEFAULT_OUT_DIR)
805*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
806*8975f5c5SAndroid Build Coastguard Worker        '-f',
807*8975f5c5SAndroid Build Coastguard Worker        '--filter',
808*8975f5c5SAndroid Build Coastguard Worker        '--gtest_filter',
809*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_FILTER,
810*8975f5c5SAndroid Build Coastguard Worker        help='Same as GoogleTest\'s filter argument. Default is "%s".' % DEFAULT_FILTER)
811*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
812*8975f5c5SAndroid Build Coastguard Worker        '--test-suite',
813*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_TEST_SUITE,
814*8975f5c5SAndroid Build Coastguard Worker        help='Test suite binary to execute. Default is "%s".' % DEFAULT_TEST_SUITE)
815*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
816*8975f5c5SAndroid Build Coastguard Worker        '--batch-count',
817*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_BATCH_COUNT,
818*8975f5c5SAndroid Build Coastguard Worker        type=int,
819*8975f5c5SAndroid Build Coastguard Worker        help='Number of tests in a (capture) batch. Default is %d.' % DEFAULT_BATCH_COUNT)
820*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
821*8975f5c5SAndroid Build Coastguard Worker        '--keep-temp-files',
822*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
823*8975f5c5SAndroid Build Coastguard Worker        help='Whether to keep the temp files and folders. Off by default')
824*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
825*8975f5c5SAndroid Build Coastguard Worker        '--use-reclient',
826*8975f5c5SAndroid Build Coastguard Worker        default=False,
827*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
828*8975f5c5SAndroid Build Coastguard Worker        help='Set use_remoteexec=true in args.gn.')
829*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
830*8975f5c5SAndroid Build Coastguard Worker        '--result-file',
831*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_RESULT_FILE,
832*8975f5c5SAndroid Build Coastguard Worker        help='Name of the result file in the capture_replay_tests folder. Default is "%s".' %
833*8975f5c5SAndroid Build Coastguard Worker        DEFAULT_RESULT_FILE)
834*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument('-v', '--verbose', action='store_true', help='Shows full test output.')
835*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
836*8975f5c5SAndroid Build Coastguard Worker        '-l',
837*8975f5c5SAndroid Build Coastguard Worker        '--log',
838*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_LOG_LEVEL,
839*8975f5c5SAndroid Build Coastguard Worker        help='Controls the logging level. Default is "%s".' % DEFAULT_LOG_LEVEL)
840*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
841*8975f5c5SAndroid Build Coastguard Worker        '-j',
842*8975f5c5SAndroid Build Coastguard Worker        '--max-jobs',
843*8975f5c5SAndroid Build Coastguard Worker        default=DEFAULT_MAX_JOBS,
844*8975f5c5SAndroid Build Coastguard Worker        type=int,
845*8975f5c5SAndroid Build Coastguard Worker        help='Maximum number of test processes. Default is %d.' % DEFAULT_MAX_JOBS)
846*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
847*8975f5c5SAndroid Build Coastguard Worker        '-M',
848*8975f5c5SAndroid Build Coastguard Worker        '--mec',
849*8975f5c5SAndroid Build Coastguard Worker        default=0,
850*8975f5c5SAndroid Build Coastguard Worker        type=int,
851*8975f5c5SAndroid Build Coastguard Worker        help='Enable mid execution capture starting at specified frame, (default: 0 = normal capture)'
852*8975f5c5SAndroid Build Coastguard Worker    )
853*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
854*8975f5c5SAndroid Build Coastguard Worker        '-a',
855*8975f5c5SAndroid Build Coastguard Worker        '--also-run-skipped-for-capture-tests',
856*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
857*8975f5c5SAndroid Build Coastguard Worker        help='Also run tests that are disabled in the expectations by SKIP_FOR_CAPTURE')
858*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument('--xvfb', action='store_true', help='Run with xvfb.')
859*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument('--asan', action='store_true', help='Build with ASAN.')
860*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
861*8975f5c5SAndroid Build Coastguard Worker        '-E',
862*8975f5c5SAndroid Build Coastguard Worker        '--expose-nonconformant-features',
863*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
864*8975f5c5SAndroid Build Coastguard Worker        help='Expose non-conformant features to advertise GLES 3.2')
865*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
866*8975f5c5SAndroid Build Coastguard Worker        '--show-capture-stdout', action='store_true', help='Print test stdout during capture.')
867*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument(
868*8975f5c5SAndroid Build Coastguard Worker        '--show-replay-stdout', action='store_true', help='Print test stdout during replay.')
869*8975f5c5SAndroid Build Coastguard Worker    parser.add_argument('--debug', action='store_true', help='Debug builds (default is Release).')
870*8975f5c5SAndroid Build Coastguard Worker    args = parser.parse_args()
871*8975f5c5SAndroid Build Coastguard Worker    if args.debug and (args.out_dir == DEFAULT_OUT_DIR):
872*8975f5c5SAndroid Build Coastguard Worker        args.out_dir = args.out_dir + "Debug"
873*8975f5c5SAndroid Build Coastguard Worker
874*8975f5c5SAndroid Build Coastguard Worker    if sys.platform == "win32":
875*8975f5c5SAndroid Build Coastguard Worker        args.test_suite += ".exe"
876*8975f5c5SAndroid Build Coastguard Worker
877*8975f5c5SAndroid Build Coastguard Worker    sys.exit(main(args))
878