Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /tools /autotest.py
blob: 067f5a151a7d11d69d139318e7a457dcc115fd23 [file] [log] [blame]
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]1#!/usr/bin/env python3
Avi Drissmandfd880852022-09-15 20:11:09[diff] [blame]2# Copyright 2020 The Chromium Authors
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]3# 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 Anjosad4625e2023-06-06 21:16:49[diff] [blame]7This script finds the appropriate test suites for the specified test files or
8directories, builds it, then runs it with the (optionally) specified filter,
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]9passing any extra args on to the test runner.
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]10
11Examples:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]12# 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:50[diff] [blame]18autotest.py -C out/Android UrlUtilitiesUnitTest --fast-local-dev -v
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]19
Edman Anjosad4625e2023-06-06 21:16:49[diff] [blame]20# Run all tests under base/strings.
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]21autotest.py -C out/foo --run-all base/strings
22
Edman Anjosad4625e2023-06-06 21:16:49[diff] [blame]23# 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:14[diff] [blame]26# 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 Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]29"""
30
31import argparse
Erik Staabdbdb3e5d2024-08-26 19:23:11[diff] [blame]32import json
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]33import locale
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]34import os
35import re
Andrew Grieve911128a2023-07-10 19:06:42[diff] [blame]36import shlex
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]37import subprocess
38import sys
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]39import shutil
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]40
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]41from enumimportEnum
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]42from pathlibimportPath
43
Erik Staabdbdb3e5d2024-08-26 19:23:11[diff] [blame]44# Don't write pyc files to the src tree, which show up in version control
45# in some environments.
46sys.dont_write_bytecode=True
47
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]48USE_PYTHON_3= f'This script will only run under python3.'
49
50SRC_DIR=Path(__file__).parent.parent.resolve()
Andrew Grieve911128a2023-07-10 19:06:42[diff] [blame]51sys.path.append(str(SRC_DIR/'build'))
52import gn_helpers
53
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]54sys.path.append(str(SRC_DIR/'build'/'android'))
55from pylibimport constants
56
57DEPOT_TOOLS_DIR= SRC_DIR/'third_party'/'depot_tools'
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]58DEBUG=False
59
Michael Thiessenf46171e2020-03-31 17:29:38[diff] [blame]60# Some test suites use suffixes that would also match non-test-suite targets.
61# Those test suites should be manually added here.
Andrew Grieve5370b0a92023-07-06 21:43:20[diff] [blame]62_TEST_TARGET_ALLOWLIST=[
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]63
Dan Harrington97d38e82025-05-21 01:05:30[diff] [blame]64# The tests below this line were output from the ripgrep command just below:
65'//ash:ash_pixeltests',
66'//build/rust/tests/test_serde_json_lenient:test_serde_json_lenient',
67'//chrome/browser/apps/app_service/app_install:app_install_fuzztests',
68'//chrome/browser/glic/e2e_test:glic_internal_e2e_interactive_ui_tests',
69'//chrome/browser/mac:install_sh_test',
70'//chrome/browser/metrics/perf:profile_provider_unittest',
71'//chrome/browser/privacy_sandbox/notice:fuzz_tests',
72'//chrome/browser/web_applications:web_application_fuzztests',
73'//chromecast/media/base:video_plane_controller_test',
74'//chromecast/metrics:cast_metrics_unittest',
Dan Harrington97d38e82025-05-21 01:05:30[diff] [blame]75'//chrome/enterprise_companion:enterprise_companion_integration_tests',
76'//chrome/enterprise_companion:enterprise_companion_tests',
77'//chrome/installer/gcapi:gcapi_test',
78'//chrome/installer/test:upgrade_test',
79'//chromeos/ash/components/kiosk/vision:kiosk_vision_unit_tests',
80'//chrome/test/android:chrome_public_apk_baseline_profile_generator',
81'//chrome/test:unit_tests',
82'//clank/javatests:chrome_apk_baseline_profile_generator',
83'//clank/javatests:chrome_smoke_test',
84'//clank/javatests:monochrome_bundle_smoke_test',
85'//clank/javatests:trichrome_chrome_google_bundle_smoke_test',
86'//components/chromeos_camera:jpeg_decode_accelerator_unittest',
87'//components/exo/wayland:wayland_client_compatibility_tests',
88'//components/exo/wayland:wayland_client_tests',
89'//components/facilitated_payments/core/validation:pix_code_validator_fuzzer',
90'//components/ip_protection:components_ip_protection_fuzztests',
91'//components/minidump_uploader:minidump_uploader_test',
92'//components/paint_preview/browser:paint_preview_browser_unit_tests',
93'//components/paint_preview/common:paint_preview_common_unit_tests',
94'//components/paint_preview/renderer:paint_preview_renderer_unit_tests',
95'//components/services/paint_preview_compositor:paint_preview_compositor_unit_tests',
96'//components/translate/core/language_detection:language_detection_util_fuzztest',
97'//components/webcrypto:webcrypto_testing_fuzzer',
98'//components/zucchini:zucchini_integration_test',
99'//content/test/fuzzer:devtools_protocol_encoding_json_fuzzer',
100'//fuchsia_web/runners:cast_runner_integration_tests',
101'//fuchsia_web/webengine:web_engine_integration_tests',
102'//google_apis/gcm:gcm_unit_tests',
103'//gpu:gl_tests',
104'//gpu:gpu_benchmark',
105'//gpu/vulkan/android:vk_tests',
106'//ios/web:ios_web_inttests',
107'//ios/web_view:ios_web_view_inttests',
108'//media/cdm:aes_decryptor_fuzztests',
109'//media/formats:ac3_util_fuzzer',
110'//media/gpu/chromeos:image_processor_test',
111'//media/gpu/v4l2:v4l2_unittest',
112'//media/gpu/vaapi/test/fake_libva_driver:fake_libva_driver_unittest',
113'//media/gpu/vaapi:vaapi_unittest',
114'//native_client/tests:large_tests',
115'//native_client/tests:medium_tests',
116'//native_client/tests:small_tests',
117'//sandbox/mac:sandbox_mac_fuzztests',
118'//sandbox/win:sbox_integration_tests',
119'//sandbox/win:sbox_validation_tests',
120'//testing/libfuzzer/fuzzers:libyuv_scale_fuzztest',
121'//testing/libfuzzer/fuzzers:paint_vector_icon_fuzztest',
122'//third_party/blink/renderer/controller:blink_perf_tests',
123'//third_party/blink/renderer/core:css_parser_fuzzer',
124'//third_party/blink/renderer/core:inspector_ghost_rules_fuzzer',
125'//third_party/blink/renderer/platform/loader:unencoded_digest_fuzzer',
126'//third_party/crc32c:crc32c_benchmark',
127'//third_party/crc32c:crc32c_tests',
128'//third_party/dawn/src/dawn/tests/benchmarks:dawn_benchmarks',
129'//third_party/highway:highway_tests',
130'//third_party/ipcz/src:ipcz_tests',
131'//third_party/libaom:av1_encoder_fuzz_test',
132'//third_party/libaom:test_libaom',
133'//third_party/libvpx:test_libvpx',
134'//third_party/libvpx:vp8_encoder_fuzz_test',
135'//third_party/libvpx:vp9_encoder_fuzz_test',
136'//third_party/libwebp:libwebp_advanced_api_fuzzer',
137'//third_party/libwebp:libwebp_animation_api_fuzzer',
138'//third_party/libwebp:libwebp_animencoder_fuzzer',
139'//third_party/libwebp:libwebp_enc_dec_api_fuzzer',
140'//third_party/libwebp:libwebp_huffman_fuzzer',
141'//third_party/libwebp:libwebp_mux_demux_api_fuzzer',
142'//third_party/libwebp:libwebp_simple_api_fuzzer',
143'//third_party/opus:test_opus_api',
144'//third_party/opus:test_opus_decode',
145'//third_party/opus:test_opus_encode',
146'//third_party/opus:test_opus_padding',
147'//third_party/pdfium:pdfium_embeddertests',
148'//third_party/pffft:pffft_unittest',
149'//third_party/rapidhash:rapidhash_fuzztests',
150'//ui/ozone:ozone_integration_tests',
151]
Dan Harrington254f5422025-05-21 16:05:19[diff] [blame]152r"""
Dan Harrington97d38e82025-05-21 01:05:30[diff] [blame]153 You can run this command to find test targets that do not match these regexes,
154 and use it to update _TEST_TARGET_ALLOWLIST.
155rg '^(instrumentation_test_runner|test)\("([^"]*)' -o -g'BUILD.gn' -r'$2' -N \
156 | rg -v '(_browsertests|_perftests|_wpr_tests|_unittests)$' \
157 | rg '^(.*)/BUILD.gn(.*)$' -r'\'//$1$2\',' \
158 | sort
159
160 And you can use a command like this to find source_set targets that do match
161 the test target regex (ideally this is minimal).
162rg '^source_set\("([^"]*)' -o -g'BUILD.gn' -r'$1' -N | \
163 rg '(_browsertests|_perftests|_wpr_tests|_unittests)$'
164"""
Dan Harrington31f4eb582024-01-24 16:43:47[diff] [blame]165_TEST_TARGET_REGEX= re.compile(
Dan Harrington97d38e82025-05-21 01:05:30[diff] [blame]166 r'(_browsertests|_perftests|_wpr_tests|_unittests)$')
Andrew Grievec2122d272021-02-10 16:22:29[diff] [blame]167
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]168_PREF_MAPPING_FILE_PATTERN= re.escape(
169 str(Path('components')/'policy'/'test'/'data'/'pref_mapping')+
170 r'/')+ r'.*\.json'
171
Edman Anjos2a7daff2025-03-14 14:38:27[diff] [blame]172TEST_FILE_NAME_REGEX= re.compile(
173 r'(.*Test\.java)'+
174 r'|(.*_[a-z]*test(?:_win|_mac|_linux|_chromeos|_android)?\.cc)'+ r'|('+
175 _PREF_MAPPING_FILE_PATTERN+ r')')
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]176
177# Some tests don't directly include gtest.h and instead include it via gmock.h
178# or a test_utils.h file, so make sure these cases are captured. Also include
179# files that use <...> for #includes instead of quotes.
Tushar Agarwal9cd8e4992022-05-20 15:03:11[diff] [blame]180GTEST_INCLUDE_REGEX= re.compile(
181 r'#include.*(gtest|gmock|_test_utils|browser_test)\.h("|>)')
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]182
183
184defExitWithMessage(*args):
185print(*args, file=sys.stderr)
186 sys.exit(1)
187
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]188
189classTestValidity(Enum):
190 NOT_A_TEST=0# Does not match test file regex.
191 MAYBE_A_TEST=1# Matches test file regex, but doesn't include gtest files.
192 VALID_TEST=2# Matches test file regex and includes gtest files.
193
194
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]195defFindRemoteCandidates(target):
196"""Find files using a remote code search utility, if installed."""
197ifnot shutil.which('cs'):
198return[]
199 results=RunCommand([
200'cs','-l',
201# Give the local path to the file, if the file exists.
202'--local',
203 f'file:{target}',
204# Restrict our search to Chromium
205'git:chrome-internal/codesearch/chrome/src@main']).splitlines()
206 exact= set()
207 close= set()
208for filenamein results:
209 file_validity=IsTestFile(filename)
210if file_validityisTestValidity.VALID_TEST:
211 exact.add(filename)
212elif file_validityisTestValidity.MAYBE_A_TEST:
213 close.add(filename)
214return list(exact), list(close)
215
216
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]217defIsTestFile(file_path):
218ifnot TEST_FILE_NAME_REGEX.match(file_path):
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]219returnTestValidity.NOT_A_TEST
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]220if file_path.endswith('.cc'):
221# Try a bit harder to remove non-test files for c++. Without this,
222# 'autotest.py base/' finds non-test files.
223try:
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]224with open(file_path,'r', encoding='utf-8')as f:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]225if GTEST_INCLUDE_REGEX.search(f.read())isnotNone:
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]226returnTestValidity.VALID_TEST
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]227exceptIOError:
228pass
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]229# It may still be a test file, even if it doesn't include a gtest file.
230returnTestValidity.MAYBE_A_TEST
231returnTestValidity.VALID_TEST
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]232
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]233
234classCommandError(Exception):
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]235"""Exception thrown when a subcommand fails."""
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]236
237def __init__(self, command, return_code, output=None):
238Exception.__init__(self)
239 self.command= command
240 self.return_code= return_code
241 self.output= output
242
243def __str__(self):
244 message=(f'\n***\nERROR: Error while running command {self.command}'
245 f'.\nExit status: {self.return_code}\n')
246if self.output:
247 message+= f'Output:\n{self.output}\n'
248 message+='***'
249return message
250
251
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]252defStreamCommandOrExit(cmd,**kwargs):
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]253try:
254 subprocess.check_call(cmd,**kwargs)
255except subprocess.CalledProcessErroras e:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]256 sys.exit(1)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]257
258
259defRunCommand(cmd,**kwargs):
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]260try:
261# Set an encoding to convert the binary output to a string.
262return subprocess.check_output(
263 cmd,**kwargs, encoding=locale.getpreferredencoding())
264except subprocess.CalledProcessErroras e:
265raiseCommandError(e.cmd, e.returncode, e.output)fromNone
266
267
Sam Maierde9930442025-06-20 15:10:15[diff] [blame]268defBuildTestTargets(out_dir, targets, dry_run, quiet):
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]269"""Builds the specified targets with ninja"""
Andrew Grieve911128a2023-07-10 19:06:42[diff] [blame]270 cmd= gn_helpers.CreateBuildCommand(out_dir)+ targets
271print('Building: '+ shlex.join(cmd))
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]272if(dry_run):
Michael Thiessena8a82f52020-11-30 18:05:32[diff] [blame]273returnTrue
Sam Maierde9930442025-06-20 15:10:15[diff] [blame]274 completed_process= subprocess.run(cmd,
275 capture_output=quiet,
276 encoding='utf-8')
277if completed_process.returncode!=0:
278if quiet:
279 before, _, after= completed_process.stdout.partition('stderr:')
280ifnot after:
281 before, _, after= completed_process.stdout.partition('stdout:')
282if after:
283print(after)
284else:
285print(before)
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]286returnFalse
287returnTrue
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]288
289
290defRecursiveMatchFilename(folder, filename):
291 current_dir= os.path.split(folder)[-1]
292if current_dir.startswith('out')or current_dir.startswith('.'):
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]293return[[],[]]
294 exact=[]
295 close=[]
Gary Tong21664c02025-03-06 16:17:17[diff] [blame]296try:
297with os.scandir(folder)as it:
298for entryin it:
299if(entry.is_symlink()):
300continue
301if(entry.is_file()and filenamein entry.pathand
302not os.path.basename(entry.path).startswith('.')):
303 file_validity=IsTestFile(entry.path)
304if file_validityisTestValidity.VALID_TEST:
305 exact.append(entry.path)
306elif file_validityisTestValidity.MAYBE_A_TEST:
307 close.append(entry.path)
308if entry.is_dir():
309# On Windows, junctions are like a symlink that python interprets as a
310# directory, leading to exceptions being thrown. We can just catch and
311# ignore these exceptions like we would ignore symlinks.
312try:
313 matches=RecursiveMatchFilename(entry.path, filename)
314 exact+= matches[0]
315 close+= matches[1]
316exceptFileNotFoundErroras e:
317if DEBUG:
318print(f'Failed to scan directory "{entry}" - junction?')
319pass
320exceptPermissionError:
321print(f'Permission error while scanning {folder}')
322
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]323return[exact, close]
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]324
325
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]326defFindTestFilesInDirectory(directory):
327 test_files=[]
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]328if DEBUG:
329print('Test files:')
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]330for root, _, filesin os.walk(directory):
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]331for fin files:
332 path= os.path.join(root, f)
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]333 file_validity=IsTestFile(path)
334if file_validityisTestValidity.VALID_TEST:
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]335if DEBUG:
336print(path)
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]337 test_files.append(path)
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]338elif DEBUGand file_validityisTestValidity.MAYBE_A_TEST:
339print(path+' matched but doesn\'t include gtest files, skipping.')
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]340return test_files
341
342
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]343defFindMatchingTestFiles(target, remote_search=False):
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]344# Return early if there's an exact file match.
345if os.path.isfile(target):
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]346# If the target is a C++ implementation file, try to guess the test file.
347if target.endswith('.cc')or target.endswith('.h'):
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]348 target_validity=IsTestFile(target)
349if target_validityisTestValidity.VALID_TEST:
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]350return[target]
351 alternate= f"{target.rsplit('.', 1)[0]}_unittest.cc"
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]352 alt_validity=TestValidity.NOT_A_TESTifnot os.path.isfile(
353 alternate)elseIsTestFile(alternate)
354if alt_validityisTestValidity.VALID_TEST:
355return[alternate]
356
357# If neither the target nor its alternative were valid, check if they just
358# didn't include the gtest files before deciding to exit.
359if target_validityisTestValidity.MAYBE_A_TEST:
360return[target]
361if alt_validityisTestValidity.MAYBE_A_TEST:
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]362return[alternate]
363ExitWithMessage(f"{target} doesn't look like a test file")
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]364return[target]
365# If this is a directory, return all the test files it contains.
366if os.path.isdir(target):
367 files=FindTestFilesInDirectory(target)
368ifnot files:
369ExitWithMessage('No tests found in directory')
370return files
371
Jesse McKenna83b6ac1b2020-05-07 18:25:38[diff] [blame]372if sys.platform.startswith('win32')and os.path.altsepin target:
373# Use backslash as the path separator on Windows to match os.scandir().
374if DEBUG:
375print('Replacing '+ os.path.altsep+' with '+ os.path.sep+' in: '
376+ target)
377 target= target.replace(os.path.altsep, os.path.sep)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]378if DEBUG:
379print('Finding files with full path containing: '+ target)
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]380
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]381if remote_search:
382 exact, close=FindRemoteCandidates(target)
383ifnot exactandnot close:
384print('Failed to find remote candidates; searching recursively')
385 exact, close=RecursiveMatchFilename(SRC_DIR, target)
386else:
387 exact, close=RecursiveMatchFilename(SRC_DIR, target)
388
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]389if DEBUG:
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]390if exact:
391print('Found exact matching file(s):')
392print('\n'.join(exact))
393if close:
394print('Found possible matching file(s):')
395print('\n'.join(close))
396
Andrew Grieve774439a2023-09-06 14:36:10[diff] [blame]397if len(exact)>=1:
398# Given "Foo", don't ask to disambiguate ModFoo.java vs Foo.java.
399 more_exact=[
400 pfor pin exactif os.path.basename(p)in(target, f'{target}.java')
401]
402if len(more_exact)==1:
403 test_files= more_exact
404else:
405 test_files= exact
406else:
407 test_files= close
408
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]409if len(test_files)>1:
Andrew Grieveecc9b872023-03-27 21:09:20[diff] [blame]410if len(test_files)<10:
411 test_files=[HaveUserPickFile(test_files)]
412else:
413# Arbitrarily capping at 10 results so we don't print the name of every
414# file in the repo if the target is poorly specified.
415 test_files= test_files[:10]
416ExitWithMessage(f'Target "{target}" is ambiguous. Matching files: '
417 f'{test_files}')
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]418ifnot test_files:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]419ExitWithMessage(f'Target "{target}" did not match any files.')
Mario Bianucci6b545002020-12-02 01:33:39[diff] [blame]420return test_files
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]421
422
Andrew Grieveecc9b872023-03-27 21:09:20[diff] [blame]423defHaveUserPickFile(paths):
424 paths= sorted(paths, key=lambda p:(len(p), p))
425 path_list='\n'.join(f'{i}. {t}'for i, tin enumerate(paths))
426
427whileTrue:
428 user_input= input(f'Please choose the path you mean.\n{path_list}\n')
429try:
430 value= int(user_input)
431return paths[value]
432except(ValueError,IndexError):
433print('Try again')
434
435
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]436defHaveUserPickTarget(paths, targets):
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]437# Cap to 10 targets for convenience [0-9].
438 targets= targets[:10]
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]439 target_list='\n'.join(f'{i}. {t}'for i, tin enumerate(targets))
440
441 user_input= input(f'Target "{paths}" is used by multiple test targets.\n'+
Svend Larsen1c38e2162024-12-20 15:35:08[diff] [blame]442 target_list+'\nPlease pick a target by its numeric index'
443'listed below: ')
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]444try:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]445 value= int(user_input)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]446return targets[value]
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]447except(ValueError,IndexError):
Svend Larsen1c38e2162024-12-20 15:35:08[diff] [blame]448print('Value entered was not a numeric index listed above. Trying again.')
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]449returnHaveUserPickTarget(paths, targets)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]450
451
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]452# A persistent cache to avoid running gn on repeated runs of autotest.
453classTargetCache:
454def __init__(self, out_dir):
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]455 self.out_dir= out_dir
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]456 self.path= os.path.join(out_dir,'autotest_cache')
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]457 self.gold_mtime= self.GetBuildNinjaMtime()
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]458 self.cache={}
459try:
460 mtime, cache= json.load(open(self.path,'r'))
461if mtime== self.gold_mtime:
462 self.cache= cache
463exceptException:
464pass
465
466defSave(self):
467with open(self.path,'w')as f:
468 json.dump([self.gold_mtime, self.cache], f)
469
470defFind(self, test_paths):
471 key=' '.join(test_paths)
472return self.cache.get(key,None)
473
474defStore(self, test_paths, test_targets):
475 key=' '.join(test_paths)
476 self.cache[key]= test_targets
477
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]478defGetBuildNinjaMtime(self):
479return os.path.getmtime(os.path.join(self.out_dir,'build.ninja'))
480
481defIsStillValid(self):
482return self.GetBuildNinjaMtime()== self.gold_mtime
483
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]484
Andrew Grieve5370b0a92023-07-06 21:43:20[diff] [blame]485def_TestTargetsFromGnRefs(targets):
486# First apply allowlists:
487 ret=[tfor tin targetsif'__'notin t]
488 ret=[
489 tfor tin ret
490if _TEST_TARGET_REGEX.search(t)or tin _TEST_TARGET_ALLOWLIST
491]
492if ret:
493return ret
494
495 _SUBTARGET_SUFFIXES=(
496'__java_binary',# robolectric_binary()
497'__test_runner_script',# test() targets
498'__test_apk',# instrumentation_test_apk() targets
499)
500 ret=[]
501for suffixin _SUBTARGET_SUFFIXES:
502 ret.extend(t[:-len(suffix)]for tin targetsif t.endswith(suffix))
503
504return ret
505
506
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]507defFindTestTargets(target_cache, out_dir, paths, run_all):
508# Normalize paths, so they can be cached.
509 paths=[os.path.realpath(p)for pin paths]
510 test_targets= target_cache.Find(paths)
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]511 used_cache=True
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]512ifnot test_targets:
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]513 used_cache=False
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]514
515# Use gn refs to recursively find all targets that depend on |path|, filter
516# internal gn targets, and match against well-known test suffixes, falling
517# back to a list of known test targets if that fails.
Henrique Nakashimaa227aa6d2025-05-01 19:26:34[diff] [blame]518 gn_path= os.path.join(DEPOT_TOOLS_DIR,'gn.py')
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]519
Henrique Nakashimaa227aa6d2025-05-01 19:26:34[diff] [blame]520 cmd=[sys.executable, gn_path,'refs', out_dir,'--all']+ paths
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]521 targets=RunCommand(cmd).splitlines()
Andrew Grieve5370b0a92023-07-06 21:43:20[diff] [blame]522 test_targets=_TestTargetsFromGnRefs(targets)
523
524# If not targets were identified as tests by looking at their names, ask GN
525# if any are executables.
526ifnot test_targetsand targets:
527 test_targets=RunCommand(cmd+['--type=executable']).splitlines()
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]528
529ifnot test_targets:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]530ExitWithMessage(
Andrew Grieve5370b0a92023-07-06 21:43:20[diff] [blame]531 f'"{paths}" did not match any test targets. Consider adding'
532 f' one of the following targets to _TEST_TARGET_ALLOWLIST within '
533 f'{__file__}: \n'+'\n'.join(targets))
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]534
Andrew Grievef6a1b1e2023-09-13 16:26:02[diff] [blame]535 test_targets.sort()
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]536 target_cache.Store(paths, test_targets)
537 target_cache.Save()
538
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]539if len(test_targets)>1:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]540if run_all:
541print(f'Warning, found {len(test_targets)} test targets.',
542 file=sys.stderr)
543if len(test_targets)>10:
544ExitWithMessage('Your query likely involves non-test sources.')
545print('Trying to run all of them!', file=sys.stderr)
546else:
547 test_targets=[HaveUserPickTarget(paths, test_targets)]
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]548
Andrew Grievef6a1b1e2023-09-13 16:26:02[diff] [blame]549# Remove the // prefix to turn GN label into ninja target.
550 test_targets=[t[2:]for tin test_targets]
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]551
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]552return(test_targets, used_cache)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]553
554
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]555defRunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter,
556 extra_args, dry_run, no_try_android_wrappers,
557 no_fast_local_dev):
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]558
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]559for targetin targets:
Andrew Grievef6a1b1e2023-09-13 16:26:02[diff] [blame]560 target_binary= target.split(':')[1]
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]561
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]562# Look for the Android wrapper script first.
Andrew Grievef6a1b1e2023-09-13 16:26:02[diff] [blame]563 path= os.path.join(out_dir,'bin', f'run_{target_binary}')
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]564if no_try_android_wrappersornot os.path.isfile(path):
565# If the wrapper is not found or disabled use the Desktop target
566# which is an executable.
Andrew Grievef6a1b1e2023-09-13 16:26:02[diff] [blame]567 path= os.path.join(out_dir, target_binary)
Andrew Grievecf0727622022-02-23 16:06:06[diff] [blame]568elifnot no_fast_local_dev:
569# Usually want this flag when developing locally.
570 extra_args= extra_args+['--fast-local-dev']
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]571
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]572 cmd=[path, f'--gtest_filter={gtest_filter}']
573if pref_mapping_filter:
574 cmd.append(f'--test_policy_to_pref_mappings_filter={pref_mapping_filter}')
575 cmd.extend(extra_args)
576
Andrew Grieve911128a2023-07-10 19:06:42[diff] [blame]577print('Running test: '+ shlex.join(cmd))
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]578ifnot dry_run:
579StreamCommandOrExit(cmd)
580
581
582defBuildCppTestFilter(filenames, line):
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]583 make_filter_command=[
Roman Sorokin34f5e2a2022-02-02 16:31:27[diff] [blame]584 sys.executable, SRC_DIR/'tools'/'make_gtest_filter.py'
Mario Bianucciebea79d2020-11-04 17:19:00[diff] [blame]585]
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]586if line:
587 make_filter_command+=['--line', str(line)]
588else:
589 make_filter_command+=['--class-only']
590 make_filter_command+= filenames
591returnRunCommand(make_filter_command).strip()
592
593
594defBuildJavaTestFilter(filenames):
Michael Thiessen7bbda482020-09-19 02:07:34[diff] [blame]595return':'.join('*.{}*'.format(os.path.splitext(os.path.basename(f))[0])
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]596for fin filenames)
597
598
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]599_PREF_MAPPING_GTEST_FILTER='*PolicyPrefsTest.PolicyToPrefsMapping*'
600
601_PREF_MAPPING_FILE_REGEX= re.compile(_PREF_MAPPING_FILE_PATTERN)
602
603SPECIAL_TEST_FILTERS=[(_PREF_MAPPING_FILE_REGEX, _PREF_MAPPING_GTEST_FILTER)]
604
605
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]606defBuildTestFilter(filenames, line):
607 java_files=[ffor fin filenamesif f.endswith('.java')]
608 cc_files=[ffor fin filenamesif f.endswith('.cc')]
609 filters=[]
610if java_files:
611 filters.append(BuildJavaTestFilter(java_files))
612if cc_files:
613 filters.append(BuildCppTestFilter(cc_files, line))
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]614for regex, gtest_filterin SPECIAL_TEST_FILTERS:
615if any(Truefor fin filenamesif regex.match(f)):
616 filters.append(gtest_filter)
617break
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]618return':'.join(filters)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]619
620
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]621defBuildPrefMappingTestFilter(filenames):
622 mapping_files=[ffor fin filenamesif _PREF_MAPPING_FILE_REGEX.match(f)]
623ifnot mapping_files:
624returnNone
625 names_without_extension=[Path(f).stemfor fin mapping_files]
626return':'.join(names_without_extension)
627
628
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]629def main():
630 parser= argparse.ArgumentParser(
631 description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
Andrew Grievea5193d3a2020-09-21 14:58:34[diff] [blame]632 parser.add_argument('--out-dir',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]633'--out_dir',
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]634'--output-directory',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]635'--output_directory',
Andrew Grievea5193d3a2020-09-21 14:58:34[diff] [blame]636'-C',
637 metavar='OUT_DIR',
638 help='output directory of the build')
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]639 parser.add_argument('--remote-search',
640'--remote_search',
641'-r',
642 action='store_true',
643 help='Search for tests using a remote service')
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]644 parser.add_argument(
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]645'--run-all',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]646'--run_all',
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]647 action='store_true',
648 help='Run all tests for the file or directory, instead of just one')
649 parser.add_argument('--line',
650 type=int,
651 help='run only the test on this line number. c++ only.')
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]652 parser.add_argument('--gtest-filter',
653'--gtest_filter',
Michael Thiessenfe328bc2022-11-30 02:37:52[diff] [blame]654'-f',
655 metavar='FILTER',
656 help='test filter')
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]657 parser.add_argument('--test-policy-to-pref-mappings-filter',
658'--test_policy_to_pref_mappings_filter',
659 metavar='FILTER',
660 help='policy pref mappings test filter')
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]661 parser.add_argument(
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]662'--dry-run',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]663'--dry_run',
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]664'-n',
665 action='store_true',
666 help='Print ninja and test run commands without executing them.')
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]667 parser.add_argument(
Sam Maierde9930442025-06-20 15:10:15[diff] [blame]668'--quiet',
669'-q',
670 action='store_true',
671 help='Do not print while building, only print if build fails.')
672 parser.add_argument(
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]673'--no-try-android-wrappers',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]674'--no_try_android_wrappers',
Olivier Li8ac87f412021-05-05 15:26:54[diff] [blame]675 action='store_true',
676 help='Do not try to use Android test wrappers to run tests.')
Andrew Grievecf0727622022-02-23 16:06:06[diff] [blame]677 parser.add_argument('--no-fast-local-dev',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]678'--no_fast_local_dev',
Andrew Grievecf0727622022-02-23 16:06:06[diff] [blame]679 action='store_true',
680 help='Do not add --fast-local-dev for Android tests.')
Edman Anjosad4625e2023-06-06 21:16:49[diff] [blame]681 parser.add_argument('files',
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]682 metavar='FILE_NAME',
Edman Anjos7d319a63e2024-01-29 10:58:31[diff] [blame]683 nargs='+',
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]684 help='test suite file (eg. FooTest.java)')
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]685
686 args, _extras= parser.parse_known_args()
687
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]688if args.out_dir:
689 constants.SetOutputDirectory(args.out_dir)
690 constants.CheckOutputDirectory()
691 out_dir: str= constants.GetOutDirectory()
692
693ifnot os.path.isdir(out_dir):
694 parser.error(f'OUT_DIR "{out_dir}" does not exist.')
695 target_cache=TargetCache(out_dir)
Edman Anjosad4625e2023-06-06 21:16:49[diff] [blame]696 filenames=[]
697for filein args.files:
Terrence Reillyeab6dc22025-06-03 02:45:25[diff] [blame]698 filenames.extend(FindMatchingTestFiles(file, args.remote_search))
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]699
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]700 targets, used_cache=FindTestTargets(target_cache, out_dir, filenames,
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]701 args.run_all)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]702
703 gtest_filter= args.gtest_filter
704ifnot gtest_filter:
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]705 gtest_filter=BuildTestFilter(filenames, args.line)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]706
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]707ifnot gtest_filter:
708ExitWithMessage('Failed to derive a gtest filter')
709
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]710 pref_mapping_filter= args.test_policy_to_pref_mappings_filter
711ifnot pref_mapping_filter:
712 pref_mapping_filter=BuildPrefMappingTestFilter(filenames)
713
Dan Harrington27d104d2020-09-08 18:30:14[diff] [blame]714assert targets
Sam Maierde9930442025-06-20 15:10:15[diff] [blame]715 build_ok=BuildTestTargets(out_dir, targets, args.dry_run, args.quiet)
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]716
717# If we used the target cache, it's possible we chose the wrong target because
718# a gn file was changed. The build step above will check for gn modifications
719# and update build.ninja. Use this opportunity the verify the cache is still
720# valid.
721if used_cacheandnot target_cache.IsStillValid():
Peter Wen1b84b4b2021-03-11 18:12:22[diff] [blame]722 target_cache=TargetCache(out_dir)
723 new_targets, _=FindTestTargets(target_cache, out_dir, filenames,
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]724 args.run_all)
725if targets!= new_targets:
726# Note that this can happen, for example, if you rename a test target.
727print('gn config was changed, trying to build again', file=sys.stderr)
728 targets= new_targets
Sam Maierde9930442025-06-20 15:10:15[diff] [blame]729 build_ok=BuildTestTargets(out_dir, targets, args.dry_run, args.quiet)
Dan Harrington8e95b892021-05-14 21:02:10[diff] [blame]730
731ifnot build_ok: sys.exit(1)
Dan Harringtonaa2c7ba2020-09-16 15:34:24[diff] [blame]732
Edman Anjos5617af5e2024-02-01 18:08:18[diff] [blame]733RunTestTargets(out_dir, targets, gtest_filter, pref_mapping_filter, _extras,
734 args.dry_run, args.no_try_android_wrappers,
735 args.no_fast_local_dev)
Michael Thiessen09c0e1d02020-03-23 18:44:50[diff] [blame]736
737
738if __name__=='__main__':
739 sys.exit(main())

[8]ページ先頭

©2009-2025 Movatter.jp