blob: 9840a4b35d9977907f22ecdab89cf79440408b88 [file] [log] [blame]
Fumitoshi Ukaide058c1c2025-09-08 13:51:001#!/usr/bin/env vpython3
Avi Drissmandfd880852022-09-15 20:11:092# Copyright 2020 The Chromium Authors
Michael Thiessen09c0e1d02020-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
Dan Harringtond2841ffd2025-10-23 15:50:567This script finds the appropriate test suites for the specified test files,
8directories, or test names, builds it, then runs it with the (optionally) specified
9filter, passing any extra args on to the test runner.
Michael Thiessen09c0e1d02020-03-23 18:44:5010
11Examples:
Dan Harrington27d104d2020-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 Thiessen09c0e1d02020-03-23 18:44:5018autotest.py -C out/Android UrlUtilitiesUnitTest --fast-local-dev -v
Dan Harrington27d104d2020-09-08 18:30:1419
Edman Anjosad4625e2023-06-06 21:16:4920# Run all tests under base/strings.
Dan Harrington27d104d2020-09-08 18:30:1421autotest.py -C out/foo --run-all base/strings
22
Edman Anjosad4625e2023-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 Harrington27d104d2020-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
Dan Harringtond2841ffd2025-10-23 15:50:5629
30# Search for and run tests with the given names.
31autotest.py -C out/foo StringUtilTest.IsStringUTF8 SpanTest.AsStringView
Michael Thiessen09c0e1d02020-03-23 18:44:5032"""
33
34import argparse
Erik Staabdbdb3e5d2024-08-26 19:23:1135import json
Michael Thiessen09c0e1d02020-03-23 18:44:5036import locale
Michael Thiessen09c0e1d02020-03-23 18:44:5037import os
Michael Thiessen09c0e1d02020-03-23 18:44:5038import re
Andrew Grieve911128a2023-07-10 19:06:4239import shlex
Michael Thiessen09c0e1d02020-03-23 18:44:5040import subprocess
41import sys
Terrence Reillyeab6dc22025-06-03 02:45:2542import shutil
Michael Thiessen09c0e1d02020-03-23 18:44:5043
Mario Bianucci6b545002020-12-02 01:33:3944from enum import Enum
Michael Thiessen09c0e1d02020-03-23 18:44:5045from pathlib import Path
46
Erik Staabdbdb3e5d2024-08-26 19:23:1147# Don't write pyc files to the src tree, which show up in version control
48# in some environments.
49sys.dont_write_bytecode = True
50
Michael Thiessen09c0e1d02020-03-23 18:44:5051USE_PYTHON_3 = f'This script will only run under python3.'
52
53SRC_DIR = Path(__file__).parent.parent.resolve()
Andrew Grieve911128a2023-07-10 19:06:4254sys.path.append(str(SRC_DIR / 'build'))
55import gn_helpers
56
Peter Wen1b84b4b2021-03-11 18:12:2257sys.path.append(str(SRC_DIR / 'build' / 'android'))
58from pylib import constants
59
60DEPOT_TOOLS_DIR = SRC_DIR / 'third_party' / 'depot_tools'
Michael Thiessen09c0e1d02020-03-23 18:44:5061DEBUG = False
62
Michael Thiessenf46171e2020-03-31 17:29:3863# Some test suites use suffixes that would also match non-test-suite targets.
64# Those test suites should be manually added here.
Andrew Grieve5370b0a92023-07-06 21:43:2065_TEST_TARGET_ALLOWLIST = [
Michael Thiessen09c0e1d02020-03-23 18:44:5066
Dan Harrington97d38e82025-05-21 01:05:3067 # The tests below this line were output from the ripgrep command just below:
68 '//ash:ash_pixeltests',
69 '//build/rust/tests/test_serde_json_lenient:test_serde_json_lenient',
70 '//chrome/browser/apps/app_service/app_install:app_install_fuzztests',
71 '//chrome/browser/glic/e2e_test:glic_internal_e2e_interactive_ui_tests',
72 '//chrome/browser/mac:install_sh_test',
73 '//chrome/browser/metrics/perf:profile_provider_unittest',
74 '//chrome/browser/privacy_sandbox/notice:fuzz_tests',
75 '//chrome/browser/web_applications:web_application_fuzztests',
76 '//chromecast/media/base:video_plane_controller_test',
77 '//chromecast/metrics:cast_metrics_unittest',
Dan Harrington97d38e82025-05-21 01:05:3078 '//chrome/enterprise_companion:enterprise_companion_integration_tests',
79 '//chrome/enterprise_companion:enterprise_companion_tests',
80 '//chrome/installer/gcapi:gcapi_test',
81 '//chrome/installer/test:upgrade_test',
82 '//chromeos/ash/components/kiosk/vision:kiosk_vision_unit_tests',
83 '//chrome/test/android:chrome_public_apk_baseline_profile_generator',
84 '//chrome/test:unit_tests',
85 '//clank/javatests:chrome_apk_baseline_profile_generator',
86 '//clank/javatests:chrome_smoke_test',
Dan Harrington97d38e82025-05-21 01:05:3087 '//clank/javatests:trichrome_chrome_google_bundle_smoke_test',
88 '//components/chromeos_camera:jpeg_decode_accelerator_unittest',
89 '//components/exo/wayland:wayland_client_compatibility_tests',
90 '//components/exo/wayland:wayland_client_tests',
91 '//components/facilitated_payments/core/validation:pix_code_validator_fuzzer',
Dan Harrington97d38e82025-05-21 01:05:3092 '//components/minidump_uploader:minidump_uploader_test',
93 '//components/paint_preview/browser:paint_preview_browser_unit_tests',
94 '//components/paint_preview/common:paint_preview_common_unit_tests',
95 '//components/paint_preview/renderer:paint_preview_renderer_unit_tests',
96 '//components/services/paint_preview_compositor:paint_preview_compositor_unit_tests',
97 '//components/translate/core/language_detection:language_detection_util_fuzztest',
98 '//components/webcrypto:webcrypto_testing_fuzzer',
99 '//components/zucchini:zucchini_integration_test',
100 '//content/test/fuzzer:devtools_protocol_encoding_json_fuzzer',
101 '//fuchsia_web/runners:cast_runner_integration_tests',
102 '//fuchsia_web/webengine:web_engine_integration_tests',
103 '//google_apis/gcm:gcm_unit_tests',
104 '//gpu:gl_tests',
105 '//gpu:gpu_benchmark',
106 '//gpu/vulkan/android:vk_tests',
107 '//ios/web:ios_web_inttests',
Sylvain Defresnefc0c4e82025-09-30 12:14:17108 '//ios/web_view/test:ios_web_view_inttests',
Dan Harrington97d38e82025-05-21 01:05:30109 '//media/cdm:aes_decryptor_fuzztests',
110 '//media/formats:ac3_util_fuzzer',
111 '//media/gpu/chromeos:image_processor_test',
112 '//media/gpu/v4l2:v4l2_unittest',
113 '//media/gpu/vaapi/test/fake_libva_driver:fake_libva_driver_unittest',
114 '//media/gpu/vaapi:vaapi_unittest',
115 '//native_client/tests:large_tests',
116 '//native_client/tests:medium_tests',
117 '//native_client/tests:small_tests',
118 '//sandbox/mac:sandbox_mac_fuzztests',
119 '//sandbox/win:sbox_integration_tests',
120 '//sandbox/win:sbox_validation_tests',
121 '//testing/libfuzzer/fuzzers:libyuv_scale_fuzztest',
122 '//testing/libfuzzer/fuzzers:paint_vector_icon_fuzztest',
123 '//third_party/blink/renderer/controller:blink_perf_tests',
124 '//third_party/blink/renderer/core:css_parser_fuzzer',
125 '//third_party/blink/renderer/core:inspector_ghost_rules_fuzzer',
126 '//third_party/blink/renderer/platform/loader:unencoded_digest_fuzzer',
127 '//third_party/crc32c:crc32c_benchmark',
128 '//third_party/crc32c:crc32c_tests',
129 '//third_party/dawn/src/dawn/tests/benchmarks:dawn_benchmarks',
Jay Zhou0bb653c2025-09-02 18:07:43130 '//third_party/federated_compute:federated_compute_tests',
Dan Harrington97d38e82025-05-21 01:05:30131 '//third_party/highway:highway_tests',
132 '//third_party/ipcz/src:ipcz_tests',
133 '//third_party/libaom:av1_encoder_fuzz_test',
134 '//third_party/libaom:test_libaom',
135 '//third_party/libvpx:test_libvpx',
136 '//third_party/libvpx:vp8_encoder_fuzz_test',
137 '//third_party/libvpx:vp9_encoder_fuzz_test',
138 '//third_party/libwebp:libwebp_advanced_api_fuzzer',
139 '//third_party/libwebp:libwebp_animation_api_fuzzer',
140 '//third_party/libwebp:libwebp_animencoder_fuzzer',
141 '//third_party/libwebp:libwebp_enc_dec_api_fuzzer',
142 '//third_party/libwebp:libwebp_huffman_fuzzer',
143 '//third_party/libwebp:libwebp_mux_demux_api_fuzzer',
144 '//third_party/libwebp:libwebp_simple_api_fuzzer',
145 '//third_party/opus:test_opus_api',
146 '//third_party/opus:test_opus_decode',
147 '//third_party/opus:test_opus_encode',
148 '//third_party/opus:test_opus_padding',
149 '//third_party/pdfium:pdfium_embeddertests',
150 '//third_party/pffft:pffft_unittest',
151 '//third_party/rapidhash:rapidhash_fuzztests',
152 '//ui/ozone:ozone_integration_tests',
153]
Dan Harrington254f5422025-05-21 16:05:19154r"""
Dan Harrington97d38e82025-05-21 01:05:30155 You can run this command to find test targets that do not match these regexes,
156 and use it to update _TEST_TARGET_ALLOWLIST.
157rg '^(instrumentation_test_runner|test)\("([^"]*)' -o -g'BUILD.gn' -r'$2' -N \
158 | rg -v '(_browsertests|_perftests|_wpr_tests|_unittests)$' \
159 | rg '^(.*)/BUILD.gn(.*)$' -r'\'//$1$2\',' \
160 | sort
161
162 And you can use a command like this to find source_set targets that do match
163 the test target regex (ideally this is minimal).
164rg '^source_set\("([^"]*)' -o -g'BUILD.gn' -r'$1' -N | \
165 rg '(_browsertests|_perftests|_wpr_tests|_unittests)$'
166"""
Dan Harrington31f4eb582024-01-24 16:43:47167_TEST_TARGET_REGEX = re.compile(
Dan Harrington97d38e82025-05-21 01:05:30168 r'(_browsertests|_perftests|_wpr_tests|_unittests)$')
Andrew Grievec2122d272021-02-10 16:22:29169
Edman Anjos5617af5e2024-02-01 18:08:18170_PREF_MAPPING_FILE_PATTERN = re.escape(
171 str(Path('components') / 'policy' / 'test' / 'data' / 'pref_mapping') +
172 r'/') + r'.*\.json'
173
Edman Anjos2a7daff2025-03-14 14:38:27174TEST_FILE_NAME_REGEX = re.compile(
175 r'(.*Test\.java)' +
Jonathan Leea2f47542025-08-01 16:55:44176 r'|(.*_[a-z]*test(?:_win|_mac|_linux|_chromeos|_android)?\.(cc|mm))' +
177 r'|(' + _PREF_MAPPING_FILE_PATTERN + r')')
Mario Bianucciebea79d2020-11-04 17:19:00178
179# Some tests don't directly include gtest.h and instead include it via gmock.h
180# or a test_utils.h file, so make sure these cases are captured. Also include
181# files that use <...> for #includes instead of quotes.
Tushar Agarwal9cd8e4992022-05-20 15:03:11182GTEST_INCLUDE_REGEX = re.compile(
183 r'#include.*(gtest|gmock|_test_utils|browser_test)\.h("|>)')
Dan Harrington27d104d2020-09-08 18:30:14184
185
186def ExitWithMessage(*args):
187 print(*args, file=sys.stderr)
188 sys.exit(1)
189
Mario Bianucci6b545002020-12-02 01:33:39190
191class TestValidity(Enum):
192 NOT_A_TEST = 0 # Does not match test file regex.
193 MAYBE_A_TEST = 1 # Matches test file regex, but doesn't include gtest files.
194 VALID_TEST = 2 # Matches test file regex and includes gtest files.
195
196
Dan Harringtond2841ffd2025-10-23 15:50:56197def CodeSearchFiles(query_args):
198 lines = RunCommand([
199 'cs',
200 '-l',
201 # Give the local path to the file, if the file exists.
202 '--local',
203 # Restrict our search to Chromium
204 'git:chrome-internal/codesearch/chrome/src@main',
205 ] + query_args).splitlines()
206 return [l.strip() for l in lines if l.strip()]
207
208
Terrence Reillyeab6dc22025-06-03 02:45:25209def FindRemoteCandidates(target):
210 """Find files using a remote code search utility, if installed."""
211 if not shutil.which('cs'):
212 return []
Dan Harringtond2841ffd2025-10-23 15:50:56213 results = CodeSearchFiles([f'file:{target}'])
Terrence Reillyeab6dc22025-06-03 02:45:25214 exact = set()
215 close = set()
216 for filename in results:
217 file_validity = IsTestFile(filename)
218 if file_validity is TestValidity.VALID_TEST:
219 exact.add(filename)
220 elif file_validity is TestValidity.MAYBE_A_TEST:
221 close.add(filename)
222 return list(exact), list(close)
223
224
Dan Harrington27d104d2020-09-08 18:30:14225def IsTestFile(file_path):
226 if not TEST_FILE_NAME_REGEX.match(file_path):
Mario Bianucci6b545002020-12-02 01:33:39227 return TestValidity.NOT_A_TEST
Jonathan Leea2f47542025-08-01 16:55:44228 if file_path.endswith('.cc') or file_path.endswith('.mm'):
Dan Harrington27d104d2020-09-08 18:30:14229 # Try a bit harder to remove non-test files for c++. Without this,
230 # 'autotest.py base/' finds non-test files.
231 try:
Mario Bianucciebea79d2020-11-04 17:19:00232 with open(file_path, 'r', encoding='utf-8') as f:
Dan Harrington27d104d2020-09-08 18:30:14233 if GTEST_INCLUDE_REGEX.search(f.read()) is not None:
Mario Bianucci6b545002020-12-02 01:33:39234 return TestValidity.VALID_TEST
Dan Harrington27d104d2020-09-08 18:30:14235 except IOError:
236 pass
Mario Bianucci6b545002020-12-02 01:33:39237 # It may still be a test file, even if it doesn't include a gtest file.
238 return TestValidity.MAYBE_A_TEST
239 return TestValidity.VALID_TEST
Dan Harrington27d104d2020-09-08 18:30:14240
Michael Thiessen09c0e1d02020-03-23 18:44:50241
242class CommandError(Exception):
Dan Harrington27d104d2020-09-08 18:30:14243 """Exception thrown when a subcommand fails."""
Michael Thiessen09c0e1d02020-03-23 18:44:50244
245 def __init__(self, command, return_code, output=None):
246 Exception.__init__(self)
247 self.command = command
248 self.return_code = return_code
249 self.output = output
250
251 def __str__(self):
252 message = (f'\n***\nERROR: Error while running command {self.command}'
253 f'.\nExit status: {self.return_code}\n')
254 if self.output:
255 message += f'Output:\n{self.output}\n'
256 message += '***'
257 return message
258
259
Dan Harrington27d104d2020-09-08 18:30:14260def StreamCommandOrExit(cmd, **kwargs):
Michael Thiessen09c0e1d02020-03-23 18:44:50261 try:
262 subprocess.check_call(cmd, **kwargs)
263 except subprocess.CalledProcessError as e:
Dan Harrington27d104d2020-09-08 18:30:14264 sys.exit(1)
Michael Thiessen09c0e1d02020-03-23 18:44:50265
266
267def RunCommand(cmd, **kwargs):
Michael Thiessen09c0e1d02020-03-23 18:44:50268 try:
269 # Set an encoding to convert the binary output to a string.
Dan Harringtond2841ffd2025-10-23 15:50:56270 return subprocess.check_output(cmd,
271 **kwargs,
272 encoding=locale.getpreferredencoding())
Michael Thiessen09c0e1d02020-03-23 18:44:50273 except subprocess.CalledProcessError as e:
274 raise CommandError(e.cmd, e.returncode, e.output) from None
275
276
Sam Maierde9930442025-06-20 15:10:15277def BuildTestTargets(out_dir, targets, dry_run, quiet):
Dan Harrington27d104d2020-09-08 18:30:14278 """Builds the specified targets with ninja"""
Andrew Grieve911128a2023-07-10 19:06:42279 cmd = gn_helpers.CreateBuildCommand(out_dir) + targets
280 print('Building: ' + shlex.join(cmd))
Michael Thiessen09c0e1d02020-03-23 18:44:50281 if (dry_run):
Michael Thiessena8a82f52020-11-30 18:05:32282 return True
Sam Maierde9930442025-06-20 15:10:15283 completed_process = subprocess.run(cmd,
284 capture_output=quiet,
285 encoding='utf-8')
286 if completed_process.returncode != 0:
287 if quiet:
288 before, _, after = completed_process.stdout.partition('stderr:')
289 if not after:
290 before, _, after = completed_process.stdout.partition('stdout:')
291 if after:
292 print(after)
293 else:
294 print(before)
Dan Harringtonaa2c7ba2020-09-16 15:34:24295 return False
296 return True
Michael Thiessen09c0e1d02020-03-23 18:44:50297
298
299def RecursiveMatchFilename(folder, filename):
300 current_dir = os.path.split(folder)[-1]
301 if current_dir.startswith('out') or current_dir.startswith('.'):
Mario Bianucci6b545002020-12-02 01:33:39302 return [[], []]
303 exact = []
304 close = []
Gary Tong21664c02025-03-06 16:17:17305 try:
306 with os.scandir(folder) as it:
307 for entry in it:
308 if (entry.is_symlink()):
309 continue
Dan Harringtond2841ffd2025-10-23 15:50:56310 if (entry.is_file() and filename in entry.path
311 and not os.path.basename(entry.path).startswith('.')):
Gary Tong21664c02025-03-06 16:17:17312 file_validity = IsTestFile(entry.path)
313 if file_validity is TestValidity.VALID_TEST:
314 exact.append(entry.path)
315 elif file_validity is TestValidity.MAYBE_A_TEST:
316 close.append(entry.path)
317 if entry.is_dir():
318 # On Windows, junctions are like a symlink that python interprets as a
319 # directory, leading to exceptions being thrown. We can just catch and
320 # ignore these exceptions like we would ignore symlinks.
321 try:
322 matches = RecursiveMatchFilename(entry.path, filename)
323 exact += matches[0]
324 close += matches[1]
325 except FileNotFoundError as e:
326 if DEBUG:
327 print(f'Failed to scan directory "{entry}" - junction?')
328 pass
329 except PermissionError:
330 print(f'Permission error while scanning {folder}')
331
Mario Bianucci6b545002020-12-02 01:33:39332 return [exact, close]
Michael Thiessen09c0e1d02020-03-23 18:44:50333
334
Dan Harrington27d104d2020-09-08 18:30:14335def FindTestFilesInDirectory(directory):
336 test_files = []
Mario Bianucciebea79d2020-11-04 17:19:00337 if DEBUG:
338 print('Test files:')
Peter Wen1b84b4b2021-03-11 18:12:22339 for root, _, files in os.walk(directory):
Dan Harrington27d104d2020-09-08 18:30:14340 for f in files:
341 path = os.path.join(root, f)
Mario Bianucci6b545002020-12-02 01:33:39342 file_validity = IsTestFile(path)
343 if file_validity is TestValidity.VALID_TEST:
Mario Bianucciebea79d2020-11-04 17:19:00344 if DEBUG:
345 print(path)
Dan Harrington27d104d2020-09-08 18:30:14346 test_files.append(path)
Mario Bianucci6b545002020-12-02 01:33:39347 elif DEBUG and file_validity is TestValidity.MAYBE_A_TEST:
348 print(path + ' matched but doesn\'t include gtest files, skipping.')
Dan Harrington27d104d2020-09-08 18:30:14349 return test_files
350
351
Dan Harringtond2841ffd2025-10-23 15:50:56352def SearchForTestsByName(terms, quiet, remote_search):
353
354 def GetPatternForTerm(term):
355 ANY = '.' if not remote_search else r'[\s\S]'
356 slash_parts = term.split('/')
357 # These are the formats, for now, just ignore the prefix and suffix here.
358 # Prefix/Test.Name/Suffix -> \bTest\b.*\bName\b
359 # Test.Name/Suffix -> \bTest\b.*\bName\b
360 # Test.Name -> \bTest\b.*\bName\b
361 if len(slash_parts) <= 2:
362 dot_parts = slash_parts[0].split('.')
363 else:
364 dot_parts = slash_parts[1].split('.')
365 return f'{ANY}*'.join(r'\b' + re.escape(p) + r'\b' for p in dot_parts)
366
367 def GetFilterForTerm(term):
368 # If the user supplied a '/', assume they've included the full test name.
369 if '/' in term:
370 return term
371 # If there's no '.', assume this is a test prefix or suffix.
372 if '.' not in term:
373 return '*' + term + '*'
374 # Otherwise run any parameterized tests with this prefix.
375 return f'{term}:{term}/*'
376
377 pattern = '|'.join(f'({GetPatternForTerm(t)})' for t in terms)
378
379 # find files containing the tests.
380 if not remote_search:
381 # Use ripgrep.
382 files = [
383 f for f in RunCommand([
384 'rg', '-l', '--multiline', '--multiline-dotall', '-t', 'cpp', '-t',
Andrew Grievec63e89b2025-10-27 20:29:50385 'java', '-t', 'objcpp', pattern, SRC_DIR
Dan Harringtond2841ffd2025-10-23 15:50:56386 ]).splitlines()
387 ]
388 else:
389 # Use code search.
390 files = CodeSearchFiles(['pcre:true', pattern])
391 files = [f for f in files if IsTestFile(f) != TestValidity.NOT_A_TEST]
392 gtest_filter = ':'.join(GetFilterForTerm(t) for t in terms)
393
394 if files and not quiet:
395 print('Found tests in files:')
396 print('\n'.join([f' {f}' for f in files]))
397 return files, gtest_filter
398
399
400def IsProbablyFile(name):
401 '''Returns whether the name is likely a test file name, path, or directory path.'''
402 return TEST_FILE_NAME_REGEX.match(name) or os.path.exists(name)
403
404
Andrew Grieved253b3672025-10-28 15:48:45405def FindMatchingTestFiles(target, remote_search=False, path_index=None):
Dan Harrington27d104d2020-09-08 18:30:14406 # Return early if there's an exact file match.
407 if os.path.isfile(target):
Jonathan Leea2f47542025-08-01 16:55:44408 if test_file := _FindTestForFile(target):
409 return [test_file]
410 ExitWithMessage(f"{target} doesn't look like a test file")
Dan Harrington27d104d2020-09-08 18:30:14411 # If this is a directory, return all the test files it contains.
412 if os.path.isdir(target):
413 files = FindTestFilesInDirectory(target)
414 if not files:
415 ExitWithMessage('No tests found in directory')
416 return files
417
Jesse McKenna83b6ac1b2020-05-07 18:25:38418 if sys.platform.startswith('win32') and os.path.altsep in target:
419 # Use backslash as the path separator on Windows to match os.scandir().
420 if DEBUG:
Dan Harringtond2841ffd2025-10-23 15:50:56421 print('Replacing ' + os.path.altsep + ' with ' + os.path.sep + ' in: ' +
422 target)
Jesse McKenna83b6ac1b2020-05-07 18:25:38423 target = target.replace(os.path.altsep, os.path.sep)
Michael Thiessen09c0e1d02020-03-23 18:44:50424 if DEBUG:
425 print('Finding files with full path containing: ' + target)
Mario Bianucci6b545002020-12-02 01:33:39426
Terrence Reillyeab6dc22025-06-03 02:45:25427 if remote_search:
428 exact, close = FindRemoteCandidates(target)
429 if not exact and not close:
430 print('Failed to find remote candidates; searching recursively')
431 exact, close = RecursiveMatchFilename(SRC_DIR, target)
432 else:
433 exact, close = RecursiveMatchFilename(SRC_DIR, target)
434
Michael Thiessen09c0e1d02020-03-23 18:44:50435 if DEBUG:
Mario Bianucci6b545002020-12-02 01:33:39436 if exact:
437 print('Found exact matching file(s):')
438 print('\n'.join(exact))
439 if close:
440 print('Found possible matching file(s):')
441 print('\n'.join(close))
442
Andrew Grieve774439a2023-09-06 14:36:10443 if len(exact) >= 1:
444 # Given "Foo", don't ask to disambiguate ModFoo.java vs Foo.java.
445 more_exact = [
446 p for p in exact if os.path.basename(p) in (target, f'{target}.java')
447 ]
448 if len(more_exact) == 1:
449 test_files = more_exact
450 else:
451 test_files = exact
452 else:
453 test_files = close
454
Mario Bianucci6b545002020-12-02 01:33:39455 if len(test_files) > 1:
Andrew Grieved253b3672025-10-28 15:48:45456 if path_index is not None and 0 <= path_index < len(test_files):
457 test_files = [test_files[path_index]]
Andrew Grieveecc9b872023-03-27 21:09:20458 else:
Andrew Grieved253b3672025-10-28 15:48:45459 test_files = [HaveUserPickFile(test_files)]
Mario Bianucci6b545002020-12-02 01:33:39460 if not test_files:
Dan Harrington27d104d2020-09-08 18:30:14461 ExitWithMessage(f'Target "{target}" did not match any files.')
Mario Bianucci6b545002020-12-02 01:33:39462 return test_files
Michael Thiessen09c0e1d02020-03-23 18:44:50463
464
Jonathan Leea2f47542025-08-01 16:55:44465def _FindTestForFile(target: os.PathLike) -> str | None:
466 root, ext = os.path.splitext(target)
467 # If the target is a C++ implementation file, try to guess the test file.
468 # Candidates should be ordered most to least promising.
469 test_candidates = [target]
470 if ext == '.h':
471 # `*_unittest.{cc,mm}` are both possible.
472 test_candidates.append(f'{root}_unittest.cc')
473 test_candidates.append(f'{root}_unittest.mm')
474 elif ext == '.cc' or ext == '.mm':
475 test_candidates.append(f'{root}_unittest{ext}')
476 else:
477 return target
478
479 maybe_valid = []
480 for candidate in test_candidates:
481 if not os.path.isfile(candidate):
482 continue
483 validity = IsTestFile(candidate)
484 if validity is TestValidity.VALID_TEST:
485 return candidate
486 elif validity is TestValidity.MAYBE_A_TEST:
487 maybe_valid.append(candidate)
488 return maybe_valid[0] if maybe_valid else None
489
490
Andrew Grieved253b3672025-10-28 15:48:45491def _ChooseByIndex(msg, options):
492 while True:
493 user_input = input(msg)
494 try:
495 return options[int(user_input)]
496 except (ValueError, IndexError):
497 msg = 'Invalid index. Try again: '
498
499
Andrew Grieveecc9b872023-03-27 21:09:20500def HaveUserPickFile(paths):
Andrew Grieved253b3672025-10-28 15:48:45501 paths = sorted(paths, key=lambda p: (len(p), p))[:20]
Andrew Grieveecc9b872023-03-27 21:09:20502 path_list = '\n'.join(f'{i}. {t}' for i, t in enumerate(paths))
503
Andrew Grieved253b3672025-10-28 15:48:45504 msg = f"""\
505Found multiple paths with that name.
Christian Biesinger7aa50442025-11-26 18:48:01506Hint: Avoid this in subsequent runs using --path-index=$INDEX, or --run-all.
Andrew Grieved253b3672025-10-28 15:48:45507
508{path_list}
509
510Pick the path that you want by its index: """
511 return _ChooseByIndex(msg, paths)
Andrew Grieveecc9b872023-03-27 21:09:20512
513
Dan Harrington27d104d2020-09-08 18:30:14514def HaveUserPickTarget(paths, targets):
Andrew Grieved253b3672025-10-28 15:48:45515 targets = targets[:20]
Dan Harrington27d104d2020-09-08 18:30:14516 target_list = '\n'.join(f'{i}. {t}' for i, t in enumerate(targets))
517
Andrew Grieved253b3672025-10-28 15:48:45518 msg = f"""\
519Path(s) belong to multiple test targets.
Christian Biesinger7aa50442025-11-26 18:48:01520Hint: Avoid this in subsequent runs using --target-index=$INDEX, or --run-all.
Andrew Grieved253b3672025-10-28 15:48:45521
522{target_list}
523
524Pick a target by its index: """
525 return _ChooseByIndex(msg, targets)
Michael Thiessen09c0e1d02020-03-23 18:44:50526
527
Dan Harrington27d104d2020-09-08 18:30:14528# A persistent cache to avoid running gn on repeated runs of autotest.
529class TargetCache:
Dan Harringtond2841ffd2025-10-23 15:50:56530
Dan Harrington27d104d2020-09-08 18:30:14531 def __init__(self, out_dir):
Dan Harringtonaa2c7ba2020-09-16 15:34:24532 self.out_dir = out_dir
Dan Harrington27d104d2020-09-08 18:30:14533 self.path = os.path.join(out_dir, 'autotest_cache')
Dan Harringtonaa2c7ba2020-09-16 15:34:24534 self.gold_mtime = self.GetBuildNinjaMtime()
Dan Harrington27d104d2020-09-08 18:30:14535 self.cache = {}
536 try:
537 mtime, cache = json.load(open(self.path, 'r'))
538 if mtime == self.gold_mtime:
539 self.cache = cache
540 except Exception:
541 pass
542
543 def Save(self):
544 with open(self.path, 'w') as f:
545 json.dump([self.gold_mtime, self.cache], f)
546
547 def Find(self, test_paths):
548 key = ' '.join(test_paths)
549 return self.cache.get(key, None)
550
551 def Store(self, test_paths, test_targets):
552 key = ' '.join(test_paths)
553 self.cache[key] = test_targets
554
Dan Harringtonaa2c7ba2020-09-16 15:34:24555 def GetBuildNinjaMtime(self):
556 return os.path.getmtime(os.path.join(self.out_dir, 'build.ninja'))
557
558 def IsStillValid(self):
559 return self.GetBuildNinjaMtime() == self.gold_mtime
560
Dan Harrington27d104d2020-09-08 18:30:14561
Andrew Grieve5370b0a92023-07-06 21:43:20562def _TestTargetsFromGnRefs(targets):
Jack Thiesene5c20162025-10-15 16:44:13563 # Prevent repeated targets.
564 all_test_targets = set()
565
566 # Find "standard" targets (e.g., GTests).
567 standard_targets = [t for t in targets if '__' not in t]
568 standard_targets = [
569 t for t in standard_targets
Andrew Grieve5370b0a92023-07-06 21:43:20570 if _TEST_TARGET_REGEX.search(t) or t in _TEST_TARGET_ALLOWLIST
571 ]
Jack Thiesene5c20162025-10-15 16:44:13572 all_test_targets.update(standard_targets)
Andrew Grieve5370b0a92023-07-06 21:43:20573
Jack Thiesene5c20162025-10-15 16:44:13574 # Find targets using internal GN suffixes (e.g., Java APKs).
Andrew Grieve5370b0a92023-07-06 21:43:20575 _SUBTARGET_SUFFIXES = (
576 '__java_binary', # robolectric_binary()
577 '__test_runner_script', # test() targets
578 '__test_apk', # instrumentation_test_apk() targets
579 )
Andrew Grieve5370b0a92023-07-06 21:43:20580 for suffix in _SUBTARGET_SUFFIXES:
Jack Thiesene5c20162025-10-15 16:44:13581 all_test_targets.update(t[:-len(suffix)] for t in targets
582 if t.endswith(suffix))
Andrew Grieve5370b0a92023-07-06 21:43:20583
Jack Thiesene5c20162025-10-15 16:44:13584 return sorted(list(all_test_targets))
Andrew Grieve5370b0a92023-07-06 21:43:20585
586
Andrew Grieve16b72bd2025-09-10 14:16:30587def _ParseRefsOutput(output):
588 targets = output.splitlines()
589 # Filter out any warnings messages. E.g. those about unused GN args.
590 # https://crbug.com/444024516
591 targets = [t for t in targets if t.startswith('//')]
592 return targets
593
594
Andrew Grieved253b3672025-10-28 15:48:45595def FindTestTargets(target_cache, out_dir, paths, args):
596 run_all = args.run_all or args.run_changed
597 target_index = args.target_index
598
Dan Harrington27d104d2020-09-08 18:30:14599 # Normalize paths, so they can be cached.
600 paths = [os.path.realpath(p) for p in paths]
601 test_targets = target_cache.Find(paths)
Dan Harringtonaa2c7ba2020-09-16 15:34:24602 used_cache = True
Dan Harrington27d104d2020-09-08 18:30:14603 if not test_targets:
Dan Harringtonaa2c7ba2020-09-16 15:34:24604 used_cache = False
Dan Harrington27d104d2020-09-08 18:30:14605
606 # Use gn refs to recursively find all targets that depend on |path|, filter
607 # internal gn targets, and match against well-known test suffixes, falling
608 # back to a list of known test targets if that fails.
Henrique Nakashimaa227aa6d2025-05-01 19:26:34609 gn_path = os.path.join(DEPOT_TOOLS_DIR, 'gn.py')
Dan Harrington27d104d2020-09-08 18:30:14610
Dan Harringtonc23a5df2025-11-10 20:47:53611 cmd = [
612 sys.executable,
613 gn_path,
614 'refs',
615 out_dir,
616 '--all',
617 '--relation=source',
618 '--relation=input',
619 ] + paths
Andrew Grieve16b72bd2025-09-10 14:16:30620 targets = _ParseRefsOutput(RunCommand(cmd))
Andrew Grieve5370b0a92023-07-06 21:43:20621 test_targets = _TestTargetsFromGnRefs(targets)
622
Andrew Grieve16b72bd2025-09-10 14:16:30623 # If no targets were identified as tests by looking at their names, ask GN
Andrew Grieve5370b0a92023-07-06 21:43:20624 # if any are executables.
625 if not test_targets and targets:
Andrew Grieve16b72bd2025-09-10 14:16:30626 test_targets = _ParseRefsOutput(RunCommand(cmd + ['--type=executable']))
Michael Thiessen09c0e1d02020-03-23 18:44:50627
628 if not test_targets:
Dan Harrington27d104d2020-09-08 18:30:14629 ExitWithMessage(
Andrew Grieve5370b0a92023-07-06 21:43:20630 f'"{paths}" did not match any test targets. Consider adding'
631 f' one of the following targets to _TEST_TARGET_ALLOWLIST within '
632 f'{__file__}: \n' + '\n'.join(targets))
Dan Harrington27d104d2020-09-08 18:30:14633
Andrew Grievef6a1b1e2023-09-13 16:26:02634 test_targets.sort()
Dan Harrington27d104d2020-09-08 18:30:14635 target_cache.Store(paths, test_targets)
636 target_cache.Save()
637
Michael Thiessen09c0e1d02020-03-23 18:44:50638 if len(test_targets) > 1:
Dan Harrington27d104d2020-09-08 18:30:14639 if run_all:
640 print(f'Warning, found {len(test_targets)} test targets.',
641 file=sys.stderr)
642 if len(test_targets) > 10:
643 ExitWithMessage('Your query likely involves non-test sources.')
644 print('Trying to run all of them!', file=sys.stderr)
Andrew Grieved253b3672025-10-28 15:48:45645 elif target_index is not None and 0 <= target_index < len(test_targets):
646 test_targets = [test_targets[target_index]]
Dan Harrington27d104d2020-09-08 18:30:14647 else:
648 test_targets = [HaveUserPickTarget(paths, test_targets)]
Michael Thiessen09c0e1d02020-03-23 18:44:50649
Andrew Grievef6a1b1e2023-09-13 16:26:02650 # Remove the // prefix to turn GN label into ninja target.
651 test_targets = [t[2:] for t in test_targets]
Dan Harrington27d104d2020-09-08 18:30:14652
Dan Harringtonaa2c7ba2020-09-16 15:34:24653 return (test_targets, used_cache)
Michael Thiessen09c0e1d02020-03-23 18:44:50654
655
Edman Anjos5617af5e2024-02-01 18:08:18656def RunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter,
657 extra_args, dry_run, no_try_android_wrappers,
658 no_fast_local_dev):
Olivier Li8ac87f412021-05-05 15:26:54659
Dan Harrington27d104d2020-09-08 18:30:14660 for target in targets:
Andrew Grievef6a1b1e2023-09-13 16:26:02661 target_binary = target.split(':')[1]
Olivier Li8ac87f412021-05-05 15:26:54662
Dan Harrington27d104d2020-09-08 18:30:14663 # Look for the Android wrapper script first.
Andrew Grievef6a1b1e2023-09-13 16:26:02664 path = os.path.join(out_dir, 'bin', f'run_{target_binary}')
Olivier Li8ac87f412021-05-05 15:26:54665 if no_try_android_wrappers or not os.path.isfile(path):
666 # If the wrapper is not found or disabled use the Desktop target
667 # which is an executable.
Andrew Grievef6a1b1e2023-09-13 16:26:02668 path = os.path.join(out_dir, target_binary)
Andrew Grievecf0727622022-02-23 16:06:06669 elif not no_fast_local_dev:
670 # Usually want this flag when developing locally.
671 extra_args = extra_args + ['--fast-local-dev']
Olivier Li8ac87f412021-05-05 15:26:54672
Edman Anjos5617af5e2024-02-01 18:08:18673 cmd = [path, f'--gtest_filter={gtest_filter}']
674 if pref_mapping_filter:
675 cmd.append(f'--test_policy_to_pref_mappings_filter={pref_mapping_filter}')
676 cmd.extend(extra_args)
677
Andrew Grieve911128a2023-07-10 19:06:42678 print('Running test: ' + shlex.join(cmd))
Dan Harrington27d104d2020-09-08 18:30:14679 if not dry_run:
680 StreamCommandOrExit(cmd)
681
682
683def BuildCppTestFilter(filenames, line):
Mario Bianucciebea79d2020-11-04 17:19:00684 make_filter_command = [
Roman Sorokin34f5e2a2022-02-02 16:31:27685 sys.executable, SRC_DIR / 'tools' / 'make_gtest_filter.py'
Mario Bianucciebea79d2020-11-04 17:19:00686 ]
Dan Harrington27d104d2020-09-08 18:30:14687 if line:
688 make_filter_command += ['--line', str(line)]
689 else:
690 make_filter_command += ['--class-only']
691 make_filter_command += filenames
692 return RunCommand(make_filter_command).strip()
693
694
695def BuildJavaTestFilter(filenames):
Michael Thiessen7bbda482020-09-19 02:07:34696 return ':'.join('*.{}*'.format(os.path.splitext(os.path.basename(f))[0])
Dan Harrington27d104d2020-09-08 18:30:14697 for f in filenames)
698
699
Edman Anjos5617af5e2024-02-01 18:08:18700_PREF_MAPPING_GTEST_FILTER = '*PolicyPrefsTest.PolicyToPrefsMapping*'
701
702_PREF_MAPPING_FILE_REGEX = re.compile(_PREF_MAPPING_FILE_PATTERN)
703
704SPECIAL_TEST_FILTERS = [(_PREF_MAPPING_FILE_REGEX, _PREF_MAPPING_GTEST_FILTER)]
705
706
Dan Harrington27d104d2020-09-08 18:30:14707def BuildTestFilter(filenames, line):
708 java_files = [f for f in filenames if f.endswith('.java')]
Jonathan Leea2f47542025-08-01 16:55:44709 # TODO(crbug.com/434009870): Support EarlGrey tests, which don't use
710 # Googletest's macros or pascal case naming convention.
711 cc_files = [
712 f for f in filenames if f.endswith('.cc') or f.endswith('_unittest.mm')
713 ]
Dan Harrington27d104d2020-09-08 18:30:14714 filters = []
715 if java_files:
716 filters.append(BuildJavaTestFilter(java_files))
717 if cc_files:
718 filters.append(BuildCppTestFilter(cc_files, line))
Edman Anjos5617af5e2024-02-01 18:08:18719 for regex, gtest_filter in SPECIAL_TEST_FILTERS:
720 if any(True for f in filenames if regex.match(f)):
721 filters.append(gtest_filter)
722 break
Dan Harrington27d104d2020-09-08 18:30:14723 return ':'.join(filters)
Michael Thiessen09c0e1d02020-03-23 18:44:50724
725
Edman Anjos5617af5e2024-02-01 18:08:18726def BuildPrefMappingTestFilter(filenames):
727 mapping_files = [f for f in filenames if _PREF_MAPPING_FILE_REGEX.match(f)]
728 if not mapping_files:
729 return None
730 names_without_extension = [Path(f).stem for f in mapping_files]
731 return ':'.join(names_without_extension)
732
733
Jack Thiesen750cd0d2025-09-29 15:26:13734def GetChangedTestFiles():
735 # Find both committed and uncommitted changes.
736 merge_base_command = ['git', 'merge-base', 'origin/main', 'HEAD']
737 merge_base = RunCommand(merge_base_command).strip()
738 git_command = [
739 'git', 'diff', '--name-only', '--diff-filter=ACMRT', merge_base
740 ]
741 changed_files = RunCommand(git_command).splitlines()
742
743 test_files = []
744 for f in changed_files:
745 if IsTestFile(f) is TestValidity.VALID_TEST:
746 test_files.append(f)
747 return test_files
748
749
Michael Thiessen09c0e1d02020-03-23 18:44:50750def main():
751 parser = argparse.ArgumentParser(
752 description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
Andrew Grievea5193d3a2020-09-21 14:58:34753 parser.add_argument('--out-dir',
Edman Anjos7d319a63e2024-01-29 10:58:31754 '--out_dir',
Peter Wen1b84b4b2021-03-11 18:12:22755 '--output-directory',
Edman Anjos7d319a63e2024-01-29 10:58:31756 '--output_directory',
Andrew Grievea5193d3a2020-09-21 14:58:34757 '-C',
758 metavar='OUT_DIR',
759 help='output directory of the build')
Terrence Reillyeab6dc22025-06-03 02:45:25760 parser.add_argument('--remote-search',
761 '--remote_search',
762 '-r',
763 action='store_true',
764 help='Search for tests using a remote service')
Dan Harringtond2841ffd2025-10-23 15:50:56765 parser.add_argument('--name',
766 action='append',
767 help='Search for the test by name, and apply test filter')
Michael Thiessen09c0e1d02020-03-23 18:44:50768 parser.add_argument(
Dan Harrington27d104d2020-09-08 18:30:14769 '--run-all',
Edman Anjos7d319a63e2024-01-29 10:58:31770 '--run_all',
Dan Harrington27d104d2020-09-08 18:30:14771 action='store_true',
772 help='Run all tests for the file or directory, instead of just one')
Jack Thiesen750cd0d2025-09-29 15:26:13773 parser.add_argument(
Andrew Grieved253b3672025-10-28 15:48:45774 '--target-index',
775 '--target_index',
776 type=int,
777 help='When the target is ambiguous, choose the one with this index.')
778 parser.add_argument(
779 '--path-index',
780 '--path_index',
781 type=int,
782 help='When the test path is ambiguous, choose the one with this index.')
783 parser.add_argument(
Jack Thiesen750cd0d2025-09-29 15:26:13784 '--run-changed',
785 '--run_changed',
786 action='store_true',
787 help='Run tests files modified since this branch diverged from main.')
Dan Harrington27d104d2020-09-08 18:30:14788 parser.add_argument('--line',
789 type=int,
790 help='run only the test on this line number. c++ only.')
Edman Anjos7d319a63e2024-01-29 10:58:31791 parser.add_argument('--gtest-filter',
792 '--gtest_filter',
Michael Thiessenfe328bc2022-11-30 02:37:52793 '-f',
794 metavar='FILTER',
795 help='test filter')
Edman Anjos5617af5e2024-02-01 18:08:18796 parser.add_argument('--test-policy-to-pref-mappings-filter',
797 '--test_policy_to_pref_mappings_filter',
798 metavar='FILTER',
799 help='policy pref mappings test filter')
Michael Thiessen09c0e1d02020-03-23 18:44:50800 parser.add_argument(
Dan Harrington27d104d2020-09-08 18:30:14801 '--dry-run',
Edman Anjos7d319a63e2024-01-29 10:58:31802 '--dry_run',
Michael Thiessen09c0e1d02020-03-23 18:44:50803 '-n',
804 action='store_true',
805 help='Print ninja and test run commands without executing them.')
Olivier Li8ac87f412021-05-05 15:26:54806 parser.add_argument(
Sam Maierde9930442025-06-20 15:10:15807 '--quiet',
808 '-q',
809 action='store_true',
810 help='Do not print while building, only print if build fails.')
811 parser.add_argument(
Olivier Li8ac87f412021-05-05 15:26:54812 '--no-try-android-wrappers',
Edman Anjos7d319a63e2024-01-29 10:58:31813 '--no_try_android_wrappers',
Olivier Li8ac87f412021-05-05 15:26:54814 action='store_true',
815 help='Do not try to use Android test wrappers to run tests.')
Andrew Grievecf0727622022-02-23 16:06:06816 parser.add_argument('--no-fast-local-dev',
Edman Anjos7d319a63e2024-01-29 10:58:31817 '--no_fast_local_dev',
Andrew Grievecf0727622022-02-23 16:06:06818 action='store_true',
819 help='Do not add --fast-local-dev for Android tests.')
Edman Anjosad4625e2023-06-06 21:16:49820 parser.add_argument('files',
Dan Harringtonaa2c7ba2020-09-16 15:34:24821 metavar='FILE_NAME',
Jack Thiesen750cd0d2025-09-29 15:26:13822 nargs='*',
Dan Harringtond2841ffd2025-10-23 15:50:56823 help='test suite file (eg. FooTest.java) or test name')
Michael Thiessen09c0e1d02020-03-23 18:44:50824
825 args, _extras = parser.parse_known_args()
826
Peter Wen1b84b4b2021-03-11 18:12:22827 if args.out_dir:
828 constants.SetOutputDirectory(args.out_dir)
829 constants.CheckOutputDirectory()
830 out_dir: str = constants.GetOutDirectory()
831
832 if not os.path.isdir(out_dir):
833 parser.error(f'OUT_DIR "{out_dir}" does not exist.')
834 target_cache = TargetCache(out_dir)
Jack Thiesen750cd0d2025-09-29 15:26:13835
Dan Harringtond2841ffd2025-10-23 15:50:56836 if not args.run_changed and not args.files and not args.name:
Jack Thiesen750cd0d2025-09-29 15:26:13837 parser.error('Specify a file to test or use --run-changed')
838
Dan Harrington51c1e5d2025-10-31 19:41:30839 # Cog is almost unusable with local search, so turn on remote_search.
840 use_remote_search = args.remote_search
841 if not use_remote_search and SRC_DIR.parts[:3] == ('/', 'google', 'cog'):
842 if DEBUG:
843 print('Detected cog, turning on remote-search.')
844 use_remote_search = True
845
Dan Harringtond2841ffd2025-10-23 15:50:56846 gtest_filter = args.gtest_filter
847
848 # Don't try to search if rg is not installed, and use the old behavior.
Dan Harrington51c1e5d2025-10-31 19:41:30849 if not use_remote_search and not shutil.which('rg'):
Dan Harringtond2841ffd2025-10-23 15:50:56850 if not args.quiet:
851 print(
852 'rg command not found. Install ripgrep to enable running tests by name.'
853 )
854 files_to_test = args.files
855 test_names = []
856 else:
857 test_names = [f for f in args.files if not IsProbablyFile(f)]
858 files_to_test = [f for f in args.files if IsProbablyFile(f)]
859
860 if args.name:
861 test_names.extend(args.name)
862 if test_names:
863 files, filter = SearchForTestsByName(test_names, args.quiet,
Dan Harrington51c1e5d2025-10-31 19:41:30864 use_remote_search)
Dan Harringtond2841ffd2025-10-23 15:50:56865 if not gtest_filter:
866 gtest_filter = filter
867 files_to_test.extend(files)
868
Jack Thiesen750cd0d2025-09-29 15:26:13869 if args.run_changed:
870 files_to_test.extend(GetChangedTestFiles())
871 # Remove duplicates.
872 files_to_test = list(set(files_to_test))
873
Edman Anjosad4625e2023-06-06 21:16:49874 filenames = []
Jack Thiesen750cd0d2025-09-29 15:26:13875 for file in files_to_test:
Dan Harrington51c1e5d2025-10-31 19:41:30876 filenames.extend(
877 FindMatchingTestFiles(file, use_remote_search, args.path_index))
Dan Harrington27d104d2020-09-08 18:30:14878
Jack Thiesen750cd0d2025-09-29 15:26:13879 if not filenames:
880 ExitWithMessage('No associated test files found.')
881
Andrew Grieved253b3672025-10-28 15:48:45882 targets, used_cache = FindTestTargets(target_cache, out_dir, filenames, args)
Michael Thiessen09c0e1d02020-03-23 18:44:50883
Michael Thiessen09c0e1d02020-03-23 18:44:50884 if not gtest_filter:
Dan Harrington27d104d2020-09-08 18:30:14885 gtest_filter = BuildTestFilter(filenames, args.line)
Michael Thiessen09c0e1d02020-03-23 18:44:50886
Dan Harrington27d104d2020-09-08 18:30:14887 if not gtest_filter:
888 ExitWithMessage('Failed to derive a gtest filter')
889
Edman Anjos5617af5e2024-02-01 18:08:18890 pref_mapping_filter = args.test_policy_to_pref_mappings_filter
891 if not pref_mapping_filter:
892 pref_mapping_filter = BuildPrefMappingTestFilter(filenames)
893
Dan Harrington27d104d2020-09-08 18:30:14894 assert targets
Sam Maierde9930442025-06-20 15:10:15895 build_ok = BuildTestTargets(out_dir, targets, args.dry_run, args.quiet)
Dan Harringtonaa2c7ba2020-09-16 15:34:24896
897 # If we used the target cache, it's possible we chose the wrong target because
898 # a gn file was changed. The build step above will check for gn modifications
899 # and update build.ninja. Use this opportunity the verify the cache is still
900 # valid.
901 if used_cache and not target_cache.IsStillValid():
Peter Wen1b84b4b2021-03-11 18:12:22902 target_cache = TargetCache(out_dir)
Andrew Grieved253b3672025-10-28 15:48:45903 new_targets, _ = FindTestTargets(target_cache, out_dir, filenames, args)
Dan Harringtonaa2c7ba2020-09-16 15:34:24904 if targets != new_targets:
905 # Note that this can happen, for example, if you rename a test target.
906 print('gn config was changed, trying to build again', file=sys.stderr)
907 targets = new_targets
Sam Maierde9930442025-06-20 15:10:15908 build_ok = BuildTestTargets(out_dir, targets, args.dry_run, args.quiet)
Dan Harrington8e95b892021-05-14 21:02:10909
910 if not build_ok: sys.exit(1)
Dan Harringtonaa2c7ba2020-09-16 15:34:24911
Edman Anjos5617af5e2024-02-01 18:08:18912 RunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter, _extras,
913 args.dry_run, args.no_try_android_wrappers,
914 args.no_fast_local_dev)
Michael Thiessen09c0e1d02020-03-23 18:44:50915
916
917if __name__ == '__main__':
918 sys.exit(main())