blob: 553732c751d19cd35ad6c65f2cdaf9a2d5574b06 [file] [log] [blame]
Michael Thiessenf61b1bc2020-03-23 18:44:501#!/usr/bin/env python3
Avi Drissman7601e4b2022-09-15 20:11:092# Copyright 2020 The Chromium Authors
Michael Thiessenf61b1bc2020-03-23 18:44:503# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Builds and runs a test by filename.
6
Edman Anjosaf38a3e2023-06-06 21:16:497This script finds the appropriate test suites for the specified test files or
8directories, builds it, then runs it with the (optionally) specified filter,
Dan Harringtonfd507942020-09-08 18:30:149passing any extra args on to the test runner.
Michael Thiessenf61b1bc2020-03-23 18:44:5010
11Examples:
Dan Harringtonfd507942020-09-08 18:30:1412# Run the test target for bit_cast_unittest.cc. Use a custom test filter instead
13# of the automatically generated one.
14autotest.py -C out/Desktop bit_cast_unittest.cc --gtest_filter=BitCastTest*
15
16# Find and run UrlUtilitiesUnitTest.java's tests, pass remaining parameters to
17# the test binary.
Michael Thiessenf61b1bc2020-03-23 18:44:5018autotest.py -C out/Android UrlUtilitiesUnitTest --fast-local-dev -v
Dan Harringtonfd507942020-09-08 18:30:1419
Edman Anjosaf38a3e2023-06-06 21:16:4920# Run all tests under base/strings.
Dan Harringtonfd507942020-09-08 18:30:1421autotest.py -C out/foo --run-all base/strings
22
Edman Anjosaf38a3e2023-06-06 21:16:4923# Run tests in multiple files or directories.
24autotest.py -C out/foo base/strings base/pickle_unittest.cc
25
Dan Harringtonfd507942020-09-08 18:30:1426# Run only the test on line 11. Useful when running autotest.py from your text
27# editor.
28autotest.py -C out/foo --line 11 base/strings/strcat_unittest.cc
Michael Thiessenf61b1bc2020-03-23 18:44:5029"""
30
31import argparse
Erik Staab473a9d62024-08-26 19:23:1132import json
Michael Thiessenf61b1bc2020-03-23 18:44:5033import locale
Michael Thiessenf61b1bc2020-03-23 18:44:5034import os
35import re
Andrew Grieve5c59eaf2023-07-10 19:06:4236import shlex
Michael Thiessenf61b1bc2020-03-23 18:44:5037import subprocess
38import sys
39
Mario Bianuccie4cd3e22020-12-02 01:33:3940from enum import Enum
Michael Thiessenf61b1bc2020-03-23 18:44:5041from pathlib import Path
42
Erik Staab473a9d62024-08-26 19:23:1143# Don't write pyc files to the src tree, which show up in version control
44# in some environments.
45sys.dont_write_bytecode = True
46
Michael Thiessenf61b1bc2020-03-23 18:44:5047USE_PYTHON_3 = f'This script will only run under python3.'
48
49SRC_DIR = Path(__file__).parent.parent.resolve()
Andrew Grieve5c59eaf2023-07-10 19:06:4250sys.path.append(str(SRC_DIR / 'build'))
51import gn_helpers
52
Peter Wenf3df5de2021-03-11 18:12:2253sys.path.append(str(SRC_DIR / 'build' / 'android'))
54from pylib import constants
55
56DEPOT_TOOLS_DIR = SRC_DIR / 'third_party' / 'depot_tools'
Michael Thiessenf61b1bc2020-03-23 18:44:5057DEBUG = False
58
Michael Thiessen772fb802020-03-31 17:29:3859# Some test suites use suffixes that would also match non-test-suite targets.
60# Those test suites should be manually added here.
Andrew Grieve714cbd82023-07-06 21:43:2061_TEST_TARGET_ALLOWLIST = [
Edman Anjos1e4c9ca2023-02-28 17:25:1662 # Running ash_pixeltests requires the --no-try-android-wrappers flag.
63 '//ash:ash_pixeltests',
Michael Thiessenf61b1bc2020-03-23 18:44:5064 '//chrome/test:browser_tests',
William Liu25370992023-01-05 19:59:3065 '//chrome/test:interactive_ui_tests',
Michael Thiessen772fb802020-03-31 17:29:3866 '//chrome/test:unit_tests',
Michael Thiessenf61b1bc2020-03-23 18:44:5067]
68
Dan Harrington75aa8262024-01-24 16:43:4769_TEST_TARGET_REGEX = re.compile(
70 r'(_browsertests|_perftests|_wpr_tests|_unittests)$')
Andrew Grieve0973e6f2021-02-10 16:22:2971
Edman Anjosa2a55722024-02-01 18:08:1872_PREF_MAPPING_FILE_PATTERN = re.escape(
73 str(Path('components') / 'policy' / 'test' / 'data' / 'pref_mapping') +
74 r'/') + r'.*\.json'
75
Edman Anjos12859c52025-03-14 14:38:2776TEST_FILE_NAME_REGEX = re.compile(
77 r'(.*Test\.java)' +
78 r'|(.*_[a-z]*test(?:_win|_mac|_linux|_chromeos|_android)?\.cc)' + r'|(' +
79 _PREF_MAPPING_FILE_PATTERN + r')')
Mario Bianucci2b0725e2020-11-04 17:19:0080
81# Some tests don't directly include gtest.h and instead include it via gmock.h
82# or a test_utils.h file, so make sure these cases are captured. Also include
83# files that use <...> for #includes instead of quotes.
Tushar Agarwal165b3252022-05-20 15:03:1184GTEST_INCLUDE_REGEX = re.compile(
85 r'#include.*(gtest|gmock|_test_utils|browser_test)\.h("|>)')
Dan Harringtonfd507942020-09-08 18:30:1486
87
88def ExitWithMessage(*args):
89 print(*args, file=sys.stderr)
90 sys.exit(1)
91
Mario Bianuccie4cd3e22020-12-02 01:33:3992
93class TestValidity(Enum):
94 NOT_A_TEST = 0 # Does not match test file regex.
95 MAYBE_A_TEST = 1 # Matches test file regex, but doesn't include gtest files.
96 VALID_TEST = 2 # Matches test file regex and includes gtest files.
97
98
Dan Harringtonfd507942020-09-08 18:30:1499def IsTestFile(file_path):
100 if not TEST_FILE_NAME_REGEX.match(file_path):
Mario Bianuccie4cd3e22020-12-02 01:33:39101 return TestValidity.NOT_A_TEST
Dan Harringtonfd507942020-09-08 18:30:14102 if file_path.endswith('.cc'):
103 # Try a bit harder to remove non-test files for c++. Without this,
104 # 'autotest.py base/' finds non-test files.
105 try:
Mario Bianucci2b0725e2020-11-04 17:19:00106 with open(file_path, 'r', encoding='utf-8') as f:
Dan Harringtonfd507942020-09-08 18:30:14107 if GTEST_INCLUDE_REGEX.search(f.read()) is not None:
Mario Bianuccie4cd3e22020-12-02 01:33:39108 return TestValidity.VALID_TEST
Dan Harringtonfd507942020-09-08 18:30:14109 except IOError:
110 pass
Mario Bianuccie4cd3e22020-12-02 01:33:39111 # It may still be a test file, even if it doesn't include a gtest file.
112 return TestValidity.MAYBE_A_TEST
113 return TestValidity.VALID_TEST
Dan Harringtonfd507942020-09-08 18:30:14114
Michael Thiessenf61b1bc2020-03-23 18:44:50115
116class CommandError(Exception):
Dan Harringtonfd507942020-09-08 18:30:14117 """Exception thrown when a subcommand fails."""
Michael Thiessenf61b1bc2020-03-23 18:44:50118
119 def __init__(self, command, return_code, output=None):
120 Exception.__init__(self)
121 self.command = command
122 self.return_code = return_code
123 self.output = output
124
125 def __str__(self):
126 message = (f'\n***\nERROR: Error while running command {self.command}'
127 f'.\nExit status: {self.return_code}\n')
128 if self.output:
129 message += f'Output:\n{self.output}\n'
130 message += '***'
131 return message
132
133
Dan Harringtonfd507942020-09-08 18:30:14134def StreamCommandOrExit(cmd, **kwargs):
Michael Thiessenf61b1bc2020-03-23 18:44:50135 try:
136 subprocess.check_call(cmd, **kwargs)
137 except subprocess.CalledProcessError as e:
Dan Harringtonfd507942020-09-08 18:30:14138 sys.exit(1)
Michael Thiessenf61b1bc2020-03-23 18:44:50139
140
141def RunCommand(cmd, **kwargs):
Michael Thiessenf61b1bc2020-03-23 18:44:50142 try:
143 # Set an encoding to convert the binary output to a string.
144 return subprocess.check_output(
145 cmd, **kwargs, encoding=locale.getpreferredencoding())
146 except subprocess.CalledProcessError as e:
147 raise CommandError(e.cmd, e.returncode, e.output) from None
148
149
Andrew Grieve5c59eaf2023-07-10 19:06:42150def BuildTestTargets(out_dir, targets, dry_run):
Dan Harringtonfd507942020-09-08 18:30:14151 """Builds the specified targets with ninja"""
Andrew Grieve5c59eaf2023-07-10 19:06:42152 cmd = gn_helpers.CreateBuildCommand(out_dir) + targets
153 print('Building: ' + shlex.join(cmd))
Michael Thiessenf61b1bc2020-03-23 18:44:50154 if (dry_run):
Michael Thiessen3a9ba6e2020-11-30 18:05:32155 return True
Dan Harrington92392682020-09-16 15:34:24156 try:
157 subprocess.check_call(cmd)
158 except subprocess.CalledProcessError as e:
159 return False
160 return True
Michael Thiessenf61b1bc2020-03-23 18:44:50161
162
163def RecursiveMatchFilename(folder, filename):
164 current_dir = os.path.split(folder)[-1]
165 if current_dir.startswith('out') or current_dir.startswith('.'):
Mario Bianuccie4cd3e22020-12-02 01:33:39166 return [[], []]
167 exact = []
168 close = []
Gary Tong2333f942025-03-06 16:17:17169 try:
170 with os.scandir(folder) as it:
171 for entry in it:
172 if (entry.is_symlink()):
173 continue
174 if (entry.is_file() and filename in entry.path and
175 not os.path.basename(entry.path).startswith('.')):
176 file_validity = IsTestFile(entry.path)
177 if file_validity is TestValidity.VALID_TEST:
178 exact.append(entry.path)
179 elif file_validity is TestValidity.MAYBE_A_TEST:
180 close.append(entry.path)
181 if entry.is_dir():
182 # On Windows, junctions are like a symlink that python interprets as a
183 # directory, leading to exceptions being thrown. We can just catch and
184 # ignore these exceptions like we would ignore symlinks.
185 try:
186 matches = RecursiveMatchFilename(entry.path, filename)
187 exact += matches[0]
188 close += matches[1]
189 except FileNotFoundError as e:
190 if DEBUG:
191 print(f'Failed to scan directory "{entry}" - junction?')
192 pass
193 except PermissionError:
194 print(f'Permission error while scanning {folder}')
195
Mario Bianuccie4cd3e22020-12-02 01:33:39196 return [exact, close]
Michael Thiessenf61b1bc2020-03-23 18:44:50197
198
Dan Harringtonfd507942020-09-08 18:30:14199def FindTestFilesInDirectory(directory):
200 test_files = []
Mario Bianucci2b0725e2020-11-04 17:19:00201 if DEBUG:
202 print('Test files:')
Peter Wenf3df5de2021-03-11 18:12:22203 for root, _, files in os.walk(directory):
Dan Harringtonfd507942020-09-08 18:30:14204 for f in files:
205 path = os.path.join(root, f)
Mario Bianuccie4cd3e22020-12-02 01:33:39206 file_validity = IsTestFile(path)
207 if file_validity is TestValidity.VALID_TEST:
Mario Bianucci2b0725e2020-11-04 17:19:00208 if DEBUG:
209 print(path)
Dan Harringtonfd507942020-09-08 18:30:14210 test_files.append(path)
Mario Bianuccie4cd3e22020-12-02 01:33:39211 elif DEBUG and file_validity is TestValidity.MAYBE_A_TEST:
212 print(path + ' matched but doesn\'t include gtest files, skipping.')
Dan Harringtonfd507942020-09-08 18:30:14213 return test_files
214
215
216def FindMatchingTestFiles(target):
217 # Return early if there's an exact file match.
218 if os.path.isfile(target):
Dan Harrington92392682020-09-16 15:34:24219 # If the target is a C++ implementation file, try to guess the test file.
220 if target.endswith('.cc') or target.endswith('.h'):
Mario Bianuccie4cd3e22020-12-02 01:33:39221 target_validity = IsTestFile(target)
222 if target_validity is TestValidity.VALID_TEST:
Dan Harrington92392682020-09-16 15:34:24223 return [target]
224 alternate = f"{target.rsplit('.', 1)[0]}_unittest.cc"
Mario Bianuccie4cd3e22020-12-02 01:33:39225 alt_validity = TestValidity.NOT_A_TEST if not os.path.isfile(
226 alternate) else IsTestFile(alternate)
227 if alt_validity is TestValidity.VALID_TEST:
228 return [alternate]
229
230 # If neither the target nor its alternative were valid, check if they just
231 # didn't include the gtest files before deciding to exit.
232 if target_validity is TestValidity.MAYBE_A_TEST:
233 return [target]
234 if alt_validity is TestValidity.MAYBE_A_TEST:
Dan Harrington92392682020-09-16 15:34:24235 return [alternate]
236 ExitWithMessage(f"{target} doesn't look like a test file")
Dan Harringtonfd507942020-09-08 18:30:14237 return [target]
238 # If this is a directory, return all the test files it contains.
239 if os.path.isdir(target):
240 files = FindTestFilesInDirectory(target)
241 if not files:
242 ExitWithMessage('No tests found in directory')
243 return files
244
Jesse McKenna08e08482020-05-07 18:25:38245 if sys.platform.startswith('win32') and os.path.altsep in target:
246 # Use backslash as the path separator on Windows to match os.scandir().
247 if DEBUG:
248 print('Replacing ' + os.path.altsep + ' with ' + os.path.sep + ' in: '
249 + target)
250 target = target.replace(os.path.altsep, os.path.sep)
Michael Thiessenf61b1bc2020-03-23 18:44:50251 if DEBUG:
252 print('Finding files with full path containing: ' + target)
Mario Bianuccie4cd3e22020-12-02 01:33:39253
254 [exact, close] = RecursiveMatchFilename(SRC_DIR, target)
Michael Thiessenf61b1bc2020-03-23 18:44:50255 if DEBUG:
Mario Bianuccie4cd3e22020-12-02 01:33:39256 if exact:
257 print('Found exact matching file(s):')
258 print('\n'.join(exact))
259 if close:
260 print('Found possible matching file(s):')
261 print('\n'.join(close))
262
Andrew Grieve85b22a72023-09-06 14:36:10263 if len(exact) >= 1:
264 # Given "Foo", don't ask to disambiguate ModFoo.java vs Foo.java.
265 more_exact = [
266 p for p in exact if os.path.basename(p) in (target, f'{target}.java')
267 ]
268 if len(more_exact) == 1:
269 test_files = more_exact
270 else:
271 test_files = exact
272 else:
273 test_files = close
274
Mario Bianuccie4cd3e22020-12-02 01:33:39275 if len(test_files) > 1:
Andrew Grieve0fd579d2023-03-27 21:09:20276 if len(test_files) < 10:
277 test_files = [HaveUserPickFile(test_files)]
278 else:
279 # Arbitrarily capping at 10 results so we don't print the name of every
280 # file in the repo if the target is poorly specified.
281 test_files = test_files[:10]
282 ExitWithMessage(f'Target "{target}" is ambiguous. Matching files: '
283 f'{test_files}')
Mario Bianuccie4cd3e22020-12-02 01:33:39284 if not test_files:
Dan Harringtonfd507942020-09-08 18:30:14285 ExitWithMessage(f'Target "{target}" did not match any files.')
Mario Bianuccie4cd3e22020-12-02 01:33:39286 return test_files
Michael Thiessenf61b1bc2020-03-23 18:44:50287
288
Andrew Grieve0fd579d2023-03-27 21:09:20289def HaveUserPickFile(paths):
290 paths = sorted(paths, key=lambda p: (len(p), p))
291 path_list = '\n'.join(f'{i}. {t}' for i, t in enumerate(paths))
292
293 while True:
294 user_input = input(f'Please choose the path you mean.\n{path_list}\n')
295 try:
296 value = int(user_input)
297 return paths[value]
298 except (ValueError, IndexError):
299 print('Try again')
300
301
Dan Harringtonfd507942020-09-08 18:30:14302def HaveUserPickTarget(paths, targets):
Michael Thiessenf61b1bc2020-03-23 18:44:50303 # Cap to 10 targets for convenience [0-9].
304 targets = targets[:10]
Dan Harringtonfd507942020-09-08 18:30:14305 target_list = '\n'.join(f'{i}. {t}' for i, t in enumerate(targets))
306
307 user_input = input(f'Target "{paths}" is used by multiple test targets.\n' +
Svend Larsen275cd152024-12-20 15:35:08308 target_list + '\nPlease pick a target by its numeric index'
309 'listed below: ')
Michael Thiessenf61b1bc2020-03-23 18:44:50310 try:
Dan Harringtonfd507942020-09-08 18:30:14311 value = int(user_input)
Michael Thiessenf61b1bc2020-03-23 18:44:50312 return targets[value]
Dan Harringtonfd507942020-09-08 18:30:14313 except (ValueError, IndexError):
Svend Larsen275cd152024-12-20 15:35:08314 print('Value entered was not a numeric index listed above. Trying again.')
Dan Harringtonfd507942020-09-08 18:30:14315 return HaveUserPickTarget(paths, targets)
Michael Thiessenf61b1bc2020-03-23 18:44:50316
317
Dan Harringtonfd507942020-09-08 18:30:14318# A persistent cache to avoid running gn on repeated runs of autotest.
319class TargetCache:
320 def __init__(self, out_dir):
Dan Harrington92392682020-09-16 15:34:24321 self.out_dir = out_dir
Dan Harringtonfd507942020-09-08 18:30:14322 self.path = os.path.join(out_dir, 'autotest_cache')
Dan Harrington92392682020-09-16 15:34:24323 self.gold_mtime = self.GetBuildNinjaMtime()
Dan Harringtonfd507942020-09-08 18:30:14324 self.cache = {}
325 try:
326 mtime, cache = json.load(open(self.path, 'r'))
327 if mtime == self.gold_mtime:
328 self.cache = cache
329 except Exception:
330 pass
331
332 def Save(self):
333 with open(self.path, 'w') as f:
334 json.dump([self.gold_mtime, self.cache], f)
335
336 def Find(self, test_paths):
337 key = ' '.join(test_paths)
338 return self.cache.get(key, None)
339
340 def Store(self, test_paths, test_targets):
341 key = ' '.join(test_paths)
342 self.cache[key] = test_targets
343
Dan Harrington92392682020-09-16 15:34:24344 def GetBuildNinjaMtime(self):
345 return os.path.getmtime(os.path.join(self.out_dir, 'build.ninja'))
346
347 def IsStillValid(self):
348 return self.GetBuildNinjaMtime() == self.gold_mtime
349
Dan Harringtonfd507942020-09-08 18:30:14350
Andrew Grieve714cbd82023-07-06 21:43:20351def _TestTargetsFromGnRefs(targets):
352 # First apply allowlists:
353 ret = [t for t in targets if '__' not in t]
354 ret = [
355 t for t in ret
356 if _TEST_TARGET_REGEX.search(t) or t in _TEST_TARGET_ALLOWLIST
357 ]
358 if ret:
359 return ret
360
361 _SUBTARGET_SUFFIXES = (
362 '__java_binary', # robolectric_binary()
363 '__test_runner_script', # test() targets
364 '__test_apk', # instrumentation_test_apk() targets
365 )
366 ret = []
367 for suffix in _SUBTARGET_SUFFIXES:
368 ret.extend(t[:-len(suffix)] for t in targets if t.endswith(suffix))
369
370 return ret
371
372
Dan Harringtonfd507942020-09-08 18:30:14373def FindTestTargets(target_cache, out_dir, paths, run_all):
374 # Normalize paths, so they can be cached.
375 paths = [os.path.realpath(p) for p in paths]
376 test_targets = target_cache.Find(paths)
Dan Harrington92392682020-09-16 15:34:24377 used_cache = True
Dan Harringtonfd507942020-09-08 18:30:14378 if not test_targets:
Dan Harrington92392682020-09-16 15:34:24379 used_cache = False
Dan Harringtonfd507942020-09-08 18:30:14380
381 # Use gn refs to recursively find all targets that depend on |path|, filter
382 # internal gn targets, and match against well-known test suffixes, falling
383 # back to a list of known test targets if that fails.
Henrique Nakashima9378d402025-05-01 19:26:34384 gn_path = os.path.join(DEPOT_TOOLS_DIR, 'gn.py')
Dan Harringtonfd507942020-09-08 18:30:14385
Henrique Nakashima9378d402025-05-01 19:26:34386 cmd = [sys.executable, gn_path, 'refs', out_dir, '--all'] + paths
Dan Harringtonfd507942020-09-08 18:30:14387 targets = RunCommand(cmd).splitlines()
Andrew Grieve714cbd82023-07-06 21:43:20388 test_targets = _TestTargetsFromGnRefs(targets)
389
390 # If not targets were identified as tests by looking at their names, ask GN
391 # if any are executables.
392 if not test_targets and targets:
393 test_targets = RunCommand(cmd + ['--type=executable']).splitlines()
Michael Thiessenf61b1bc2020-03-23 18:44:50394
395 if not test_targets:
Dan Harringtonfd507942020-09-08 18:30:14396 ExitWithMessage(
Andrew Grieve714cbd82023-07-06 21:43:20397 f'"{paths}" did not match any test targets. Consider adding'
398 f' one of the following targets to _TEST_TARGET_ALLOWLIST within '
399 f'{__file__}: \n' + '\n'.join(targets))
Dan Harringtonfd507942020-09-08 18:30:14400
Andrew Grieve99f0a642023-09-13 16:26:02401 test_targets.sort()
Dan Harringtonfd507942020-09-08 18:30:14402 target_cache.Store(paths, test_targets)
403 target_cache.Save()
404
Michael Thiessenf61b1bc2020-03-23 18:44:50405 if len(test_targets) > 1:
Dan Harringtonfd507942020-09-08 18:30:14406 if run_all:
407 print(f'Warning, found {len(test_targets)} test targets.',
408 file=sys.stderr)
409 if len(test_targets) > 10:
410 ExitWithMessage('Your query likely involves non-test sources.')
411 print('Trying to run all of them!', file=sys.stderr)
412 else:
413 test_targets = [HaveUserPickTarget(paths, test_targets)]
Michael Thiessenf61b1bc2020-03-23 18:44:50414
Andrew Grieve99f0a642023-09-13 16:26:02415 # Remove the // prefix to turn GN label into ninja target.
416 test_targets = [t[2:] for t in test_targets]
Dan Harringtonfd507942020-09-08 18:30:14417
Dan Harrington92392682020-09-16 15:34:24418 return (test_targets, used_cache)
Michael Thiessenf61b1bc2020-03-23 18:44:50419
420
Edman Anjosa2a55722024-02-01 18:08:18421def RunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter,
422 extra_args, dry_run, no_try_android_wrappers,
423 no_fast_local_dev):
Olivier Li2a89d352021-05-05 15:26:54424
Dan Harringtonfd507942020-09-08 18:30:14425 for target in targets:
Andrew Grieve99f0a642023-09-13 16:26:02426 target_binary = target.split(':')[1]
Olivier Li2a89d352021-05-05 15:26:54427
Dan Harringtonfd507942020-09-08 18:30:14428 # Look for the Android wrapper script first.
Andrew Grieve99f0a642023-09-13 16:26:02429 path = os.path.join(out_dir, 'bin', f'run_{target_binary}')
Olivier Li2a89d352021-05-05 15:26:54430 if no_try_android_wrappers or not os.path.isfile(path):
431 # If the wrapper is not found or disabled use the Desktop target
432 # which is an executable.
Andrew Grieve99f0a642023-09-13 16:26:02433 path = os.path.join(out_dir, target_binary)
Andrew Grieve5d6db892022-02-23 16:06:06434 elif not no_fast_local_dev:
435 # Usually want this flag when developing locally.
436 extra_args = extra_args + ['--fast-local-dev']
Olivier Li2a89d352021-05-05 15:26:54437
Edman Anjosa2a55722024-02-01 18:08:18438 cmd = [path, f'--gtest_filter={gtest_filter}']
439 if pref_mapping_filter:
440 cmd.append(f'--test_policy_to_pref_mappings_filter={pref_mapping_filter}')
441 cmd.extend(extra_args)
442
Andrew Grieve5c59eaf2023-07-10 19:06:42443 print('Running test: ' + shlex.join(cmd))
Dan Harringtonfd507942020-09-08 18:30:14444 if not dry_run:
445 StreamCommandOrExit(cmd)
446
447
448def BuildCppTestFilter(filenames, line):
Mario Bianucci2b0725e2020-11-04 17:19:00449 make_filter_command = [
Roman Sorokin25b34762022-02-02 16:31:27450 sys.executable, SRC_DIR / 'tools' / 'make_gtest_filter.py'
Mario Bianucci2b0725e2020-11-04 17:19:00451 ]
Dan Harringtonfd507942020-09-08 18:30:14452 if line:
453 make_filter_command += ['--line', str(line)]
454 else:
455 make_filter_command += ['--class-only']
456 make_filter_command += filenames
457 return RunCommand(make_filter_command).strip()
458
459
460def BuildJavaTestFilter(filenames):
Michael Thiessen00fb08f2020-09-19 02:07:34461 return ':'.join('*.{}*'.format(os.path.splitext(os.path.basename(f))[0])
Dan Harringtonfd507942020-09-08 18:30:14462 for f in filenames)
463
464
Edman Anjosa2a55722024-02-01 18:08:18465_PREF_MAPPING_GTEST_FILTER = '*PolicyPrefsTest.PolicyToPrefsMapping*'
466
467_PREF_MAPPING_FILE_REGEX = re.compile(_PREF_MAPPING_FILE_PATTERN)
468
469SPECIAL_TEST_FILTERS = [(_PREF_MAPPING_FILE_REGEX, _PREF_MAPPING_GTEST_FILTER)]
470
471
Dan Harringtonfd507942020-09-08 18:30:14472def BuildTestFilter(filenames, line):
473 java_files = [f for f in filenames if f.endswith('.java')]
474 cc_files = [f for f in filenames if f.endswith('.cc')]
475 filters = []
476 if java_files:
477 filters.append(BuildJavaTestFilter(java_files))
478 if cc_files:
479 filters.append(BuildCppTestFilter(cc_files, line))
Edman Anjosa2a55722024-02-01 18:08:18480 for regex, gtest_filter in SPECIAL_TEST_FILTERS:
481 if any(True for f in filenames if regex.match(f)):
482 filters.append(gtest_filter)
483 break
Dan Harringtonfd507942020-09-08 18:30:14484 return ':'.join(filters)
Michael Thiessenf61b1bc2020-03-23 18:44:50485
486
Edman Anjosa2a55722024-02-01 18:08:18487def BuildPrefMappingTestFilter(filenames):
488 mapping_files = [f for f in filenames if _PREF_MAPPING_FILE_REGEX.match(f)]
489 if not mapping_files:
490 return None
491 names_without_extension = [Path(f).stem for f in mapping_files]
492 return ':'.join(names_without_extension)
493
494
Michael Thiessenf61b1bc2020-03-23 18:44:50495def main():
496 parser = argparse.ArgumentParser(
497 description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
Andrew Grieve9a2c08b2020-09-21 14:58:34498 parser.add_argument('--out-dir',
Edman Anjos03e2d972024-01-29 10:58:31499 '--out_dir',
Peter Wenf3df5de2021-03-11 18:12:22500 '--output-directory',
Edman Anjos03e2d972024-01-29 10:58:31501 '--output_directory',
Andrew Grieve9a2c08b2020-09-21 14:58:34502 '-C',
503 metavar='OUT_DIR',
504 help='output directory of the build')
Michael Thiessenf61b1bc2020-03-23 18:44:50505 parser.add_argument(
Dan Harringtonfd507942020-09-08 18:30:14506 '--run-all',
Edman Anjos03e2d972024-01-29 10:58:31507 '--run_all',
Dan Harringtonfd507942020-09-08 18:30:14508 action='store_true',
509 help='Run all tests for the file or directory, instead of just one')
510 parser.add_argument('--line',
511 type=int,
512 help='run only the test on this line number. c++ only.')
Edman Anjos03e2d972024-01-29 10:58:31513 parser.add_argument('--gtest-filter',
514 '--gtest_filter',
Michael Thiessen53ba6a82022-11-30 02:37:52515 '-f',
516 metavar='FILTER',
517 help='test filter')
Edman Anjosa2a55722024-02-01 18:08:18518 parser.add_argument('--test-policy-to-pref-mappings-filter',
519 '--test_policy_to_pref_mappings_filter',
520 metavar='FILTER',
521 help='policy pref mappings test filter')
Michael Thiessenf61b1bc2020-03-23 18:44:50522 parser.add_argument(
Dan Harringtonfd507942020-09-08 18:30:14523 '--dry-run',
Edman Anjos03e2d972024-01-29 10:58:31524 '--dry_run',
Michael Thiessenf61b1bc2020-03-23 18:44:50525 '-n',
526 action='store_true',
527 help='Print ninja and test run commands without executing them.')
Olivier Li2a89d352021-05-05 15:26:54528 parser.add_argument(
529 '--no-try-android-wrappers',
Edman Anjos03e2d972024-01-29 10:58:31530 '--no_try_android_wrappers',
Olivier Li2a89d352021-05-05 15:26:54531 action='store_true',
532 help='Do not try to use Android test wrappers to run tests.')
Andrew Grieve5d6db892022-02-23 16:06:06533 parser.add_argument('--no-fast-local-dev',
Edman Anjos03e2d972024-01-29 10:58:31534 '--no_fast_local_dev',
Andrew Grieve5d6db892022-02-23 16:06:06535 action='store_true',
536 help='Do not add --fast-local-dev for Android tests.')
Edman Anjosaf38a3e2023-06-06 21:16:49537 parser.add_argument('files',
Dan Harrington92392682020-09-16 15:34:24538 metavar='FILE_NAME',
Edman Anjos03e2d972024-01-29 10:58:31539 nargs='+',
Dan Harrington92392682020-09-16 15:34:24540 help='test suite file (eg. FooTest.java)')
Michael Thiessenf61b1bc2020-03-23 18:44:50541
542 args, _extras = parser.parse_known_args()
543
Peter Wenf3df5de2021-03-11 18:12:22544 if args.out_dir:
545 constants.SetOutputDirectory(args.out_dir)
546 constants.CheckOutputDirectory()
547 out_dir: str = constants.GetOutDirectory()
548
549 if not os.path.isdir(out_dir):
550 parser.error(f'OUT_DIR "{out_dir}" does not exist.')
551 target_cache = TargetCache(out_dir)
Edman Anjosaf38a3e2023-06-06 21:16:49552 filenames = []
553 for file in args.files:
554 filenames.extend(FindMatchingTestFiles(file))
Dan Harringtonfd507942020-09-08 18:30:14555
Peter Wenf3df5de2021-03-11 18:12:22556 targets, used_cache = FindTestTargets(target_cache, out_dir, filenames,
Dan Harrington92392682020-09-16 15:34:24557 args.run_all)
Michael Thiessenf61b1bc2020-03-23 18:44:50558
559 gtest_filter = args.gtest_filter
560 if not gtest_filter:
Dan Harringtonfd507942020-09-08 18:30:14561 gtest_filter = BuildTestFilter(filenames, args.line)
Michael Thiessenf61b1bc2020-03-23 18:44:50562
Dan Harringtonfd507942020-09-08 18:30:14563 if not gtest_filter:
564 ExitWithMessage('Failed to derive a gtest filter')
565
Edman Anjosa2a55722024-02-01 18:08:18566 pref_mapping_filter = args.test_policy_to_pref_mappings_filter
567 if not pref_mapping_filter:
568 pref_mapping_filter = BuildPrefMappingTestFilter(filenames)
569
Dan Harringtonfd507942020-09-08 18:30:14570 assert targets
Andrew Grieve5c59eaf2023-07-10 19:06:42571 build_ok = BuildTestTargets(out_dir, targets, args.dry_run)
Dan Harrington92392682020-09-16 15:34:24572
573 # If we used the target cache, it's possible we chose the wrong target because
574 # a gn file was changed. The build step above will check for gn modifications
575 # and update build.ninja. Use this opportunity the verify the cache is still
576 # valid.
577 if used_cache and not target_cache.IsStillValid():
Peter Wenf3df5de2021-03-11 18:12:22578 target_cache = TargetCache(out_dir)
579 new_targets, _ = FindTestTargets(target_cache, out_dir, filenames,
Dan Harrington92392682020-09-16 15:34:24580 args.run_all)
581 if targets != new_targets:
582 # Note that this can happen, for example, if you rename a test target.
583 print('gn config was changed, trying to build again', file=sys.stderr)
584 targets = new_targets
Andrew Grieve5c59eaf2023-07-10 19:06:42585 build_ok = BuildTestTargets(out_dir, targets, args.dry_run)
Dan Harringtonbd1fa352021-05-14 21:02:10586
587 if not build_ok: sys.exit(1)
Dan Harrington92392682020-09-16 15:34:24588
Edman Anjosa2a55722024-02-01 18:08:18589 RunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter, _extras,
590 args.dry_run, args.no_try_android_wrappers,
591 args.no_fast_local_dev)
Michael Thiessenf61b1bc2020-03-23 18:44:50592
593
594if __name__ == '__main__':
595 sys.exit(main())