xref: /aosp_15_r20/external/angle/src/tests/angle_android_test_runner.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 #! /usr/bin/env vpython3
2 #
3 # Copyright 2023 The ANGLE Project Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6 #
7 # angle_android_test_runner.py:
8 #   Runs (a subset of) ANGLE tests on Android using android_helper
9 #   instead of chromium test_runner scripts. This script is integrated into
10 #   gn builds via the android_test_runner_script var, which creates wrappers
11 #   (out/<config>/angle_trace_tests) calling into this script
12 #   with additional args like --output-directory=out/<config> etc
13 
14 import argparse
15 import json
16 import logging
17 import os
18 import pathlib
19 import subprocess
20 import sys
21 
22 PY_UTILS = str(pathlib.Path(__file__).resolve().parent / 'py_utils')
23 if PY_UTILS not in sys.path:
24     os.stat(PY_UTILS) and sys.path.insert(0, PY_UTILS)
25 import android_helper
26 import angle_path_util
27 import angle_test_util
28 
29 
30 def AddCommonParserArgs(parser):
31     parser.add_argument(
32         '--suite',
33         help='Test suite to run.',
34         required=True,
35         choices=[
36             'angle_end2end_tests',
37             'angle_perftests',
38             'angle_trace_tests',
39             'angle_unittests',
40             # dEQP - grep \"angle_deqp infra/specs/gn_isolate_map.pyl
41             'angle_deqp_egl_tests',
42             'angle_deqp_gles2_tests',
43             'angle_deqp_gles31_rotate180_tests',
44             'angle_deqp_gles31_rotate270_tests',
45             'angle_deqp_gles31_rotate90_tests',
46             'angle_deqp_gles31_tests',
47             'angle_deqp_gles3_rotate180_tests',
48             'angle_deqp_gles3_rotate270_tests',
49             'angle_deqp_gles3_rotate90_tests',
50             'angle_deqp_gles3_tests',
51             'angle_deqp_khr_gles2_tests',
52             'angle_deqp_khr_gles31_tests',
53             'angle_deqp_khr_gles32_tests',
54             'angle_deqp_khr_gles3_tests',
55             'angle_deqp_khr_noctx_gles2_tests',
56             'angle_deqp_khr_noctx_gles32_tests',
57             'angle_deqp_khr_single_gles32_tests',
58         ])
59     parser.add_argument('-l', '--log', help='Logging level.', default='info')
60     parser.add_argument('--list-tests', help='List tests.', action='store_true')
61     parser.add_argument(
62         '-f',
63         '--filter',
64         '--isolated-script-test-filter',
65         '--gtest_filter',
66         type=str,
67         help='Test filter.')
68     parser.add_argument(
69         '--prepare-only',
70         help='Only prepare traces, but do not actually run them.',
71         action='store_true')
72 
73 
74 def RunWithAngleTestRunner(args, extra_args):
75     angle_test_util.SetupLogging(args.log.upper())
76 
77     android_helper.Initialize(args.suite)
78     assert android_helper.IsAndroid()
79 
80     rc, output, _ = android_helper.RunTests(
81         args.suite, ['--list-tests', '--verbose'] + extra_args, log_output=False)
82     if rc != 0:
83         logging.fatal('Could not find test list from test output:\n%s' % output)
84         return rc
85 
86     tests = angle_test_util.GetTestsFromOutput(output)
87     if not tests:
88         logging.fatal('Could not find test list from test output:\n%s' % output)
89         return 1
90 
91     if args.filter:
92         tests = angle_test_util.FilterTests(tests, args.filter)
93 
94     if args.list_tests:
95         print('\n'.join(['Tests list:'] + tests + ['End tests list.']))
96         return 0
97 
98     if args.suite == 'angle_trace_tests':
99         traces = set(android_helper.GetTraceFromTestName(test) for test in tests)
100         android_helper.PrepareRestrictedTraces(traces)
101 
102         if args.prepare_only:
103             print('Prepared traces: %s' % traces)
104             return 0
105 
106     flags = ['--gtest_filter=' + args.filter] if args.filter else []
107     return android_helper.RunTests(args.suite, flags + extra_args)[0]
108 
109 
110 def RunWithChromiumTestRunner():
111     # Import only here to avoid breaking trace bundles and other catapult imports
112     angle_path_util.AddDepsDirToPath('build/android')
113     import test_runner
114 
115     # Workaround from test_runner's __main__
116     exit_code = test_runner.main()
117     if exit_code == test_runner.constants.INFRA_EXIT_CODE:
118         sys.stdout.flush()
119         sys.stderr.flush()
120         os._exit(exit_code)
121     return exit_code
122 
123 
124 def CheckDeviceScreenState():
125     screen_state = subprocess.check_output(
126         [android_helper.FindAdb(), 'shell',
127          'dumpsys deviceidle | grep mScreen || true']).decode().split()
128     expected_screen_state = ['mScreenOn=true', 'mScreenLocked=false']
129     if not set(expected_screen_state).issubset(screen_state):
130         raise Exception('Unexpected device screen state: %s; expected: %s' %
131                         (screen_state, expected_screen_state))
132 
133 
134 def main():
135     parser = argparse.ArgumentParser(add_help=False)
136     parser.add_argument('-h', '--help', action='store_true')
137     parser.add_argument('test_type', choices=['gtest'])
138     parser.add_argument('--output-directory', required=True)
139     parser.add_argument('--wrapper-script-args')
140     parser.add_argument('--runtime-deps-path')
141     AddCommonParserArgs(parser)
142 
143     parser.add_argument(
144         '--angle-test-runner',
145         help='Use ANGLE test runner instead of Chromium/catapult',
146         action='store_true')
147 
148     args, extra_args = parser.parse_known_args()
149 
150     if not args.help:
151         CheckDeviceScreenState()
152 
153     if args.angle_test_runner or args.suite == 'angle_trace_tests':
154         if args.help:
155             parser.print_help()
156         else:
157             os.chdir(args.output_directory)
158             return RunWithAngleTestRunner(args, extra_args)
159     else:
160         # Chromium runner parses args by itself, including --help
161         return RunWithChromiumTestRunner()
162 
163 
164 if __name__ == "__main__":
165     sys.exit(main())
166