15 """Run test matrix."""
17 from __future__
import print_function
20 import multiprocessing
28 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),
'../..'))
31 _DEFAULT_RUNTESTS_TIMEOUT = 1 * 60 * 60
34 _CPP_RUNTESTS_TIMEOUT = 4 * 60 * 60
37 _OBJC_RUNTESTS_TIMEOUT = 2 * 60 * 60
40 _DEFAULT_INNER_JOBS = 2
45 _MATRIX_REPORT_NAME =
'toplevel_run_tests_invocations'
49 """Reports with '+' in target name won't show correctly in ResultStore"""
50 return name.replace(
'+',
'p')
54 """Generates report file name with directory structure that leads to better presentation by internal CI"""
60 """Generate location for log file that will match the sponge_log.xml from the top-level matrix report."""
65 sponge_log_name =
'%s/%s/%s' % (
66 _MATRIX_REPORT_NAME, shortname_for_multi_target,
'sponge_log.log')
69 base_dir = os.getenv(
'GRPC_TEST_REPORT_BASE_DIR',
None)
71 sponge_log_name = os.path.join(base_dir, sponge_log_name)
72 return sponge_log_name
78 inner_jobs=_DEFAULT_INNER_JOBS,
79 timeout_seconds=None):
80 """Run a single instance of run_tests.py in a docker container"""
81 if not timeout_seconds:
82 timeout_seconds = _DEFAULT_RUNTESTS_TIMEOUT
83 shortname =
'run_tests_%s' % name
84 test_job = jobset.JobSpec(cmdline=[
85 'python3',
'tools/run_tests/run_tests.py',
'--use_docker',
'-t',
'-j',
86 str(inner_jobs),
'-x',
90 environ=runtests_envs,
92 timeout_seconds=timeout_seconds,
101 inner_jobs=_DEFAULT_INNER_JOBS,
102 timeout_seconds=None):
103 """Run a single instance of run_tests.py in a separate workspace"""
104 if not workspace_name:
105 workspace_name =
'workspace_%s' % name
106 if not timeout_seconds:
107 timeout_seconds = _DEFAULT_RUNTESTS_TIMEOUT
108 shortname =
'run_tests_%s' % name
109 env = {
'WORKSPACE_NAME': workspace_name}
110 env.update(runtests_envs)
112 report_dir_prefix =
'' if os.getenv(
'GRPC_TEST_REPORT_BASE_DIR',
114 test_job = jobset.JobSpec(cmdline=[
115 'bash',
'tools/run_tests/helper_scripts/run_tests_in_workspace.sh',
117 str(inner_jobs),
'-x',
124 timeout_seconds=timeout_seconds,
132 iomgr_platforms=['native'],
138 inner_jobs=_DEFAULT_INNER_JOBS,
139 timeout_seconds=None):
141 for language
in languages:
142 for platform
in platforms:
143 for iomgr_platform
in iomgr_platforms:
144 for config
in configs:
145 name =
'%s_%s_%s_%s' % (language, platform, config,
148 '-l', language,
'-c', config,
'--iomgr_platform',
152 name +=
'_%s_%s' % (arch, compiler)
154 '--arch', arch,
'--compiler', compiler
156 if '--build_only' in extra_args:
158 for extra_env
in extra_envs:
159 name +=
'_%s_%s' % (extra_env, extra_envs[extra_env])
161 runtests_args += extra_args
162 if platform ==
'linux':
164 runtests_args=runtests_args,
165 runtests_envs=extra_envs,
166 inner_jobs=inner_jobs,
167 timeout_seconds=timeout_seconds)
171 runtests_args=runtests_args,
172 runtests_envs=extra_envs,
173 inner_jobs=inner_jobs,
174 timeout_seconds=timeout_seconds)
176 job.labels = [platform, config, language, iomgr_platform
188 labels=[
'basictests'],
189 extra_args=extra_args +
190 [
'--report_multi_target'],
191 inner_jobs=inner_jobs)
196 configs=[
'dbg',
'opt'],
197 platforms=[
'linux',
'macos',
'windows'],
198 labels=[
'basictests',
'corelang'],
201 inner_jobs=inner_jobs,
202 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
206 configs=[
'dbg',
'opt'],
207 platforms=[
'linux',
'macos',
'windows'],
208 labels=[
'basictests',
'multilang'],
209 extra_args=extra_args +
210 [
'--report_multi_target'],
211 inner_jobs=inner_jobs)
215 configs=[
'dbg',
'opt'],
219 labels=[
'basictests_arm64'],
220 extra_args=extra_args +
221 [
'--report_multi_target'],
222 inner_jobs=inner_jobs)
226 platforms=[
'linux',
'macos',
'windows'],
227 iomgr_platforms=[
'native'],
228 labels=[
'basictests',
'multilang'],
229 extra_args=extra_args +
230 [
'--report_multi_target'],
231 inner_jobs=inner_jobs)
239 iomgr_platforms=[
'native'],
240 labels=[
'basictests_arm64'],
241 extra_args=extra_args +
242 [
'--report_multi_target'],
243 inner_jobs=inner_jobs)
248 configs=[
'dbg',
'opt'],
249 platforms=[
'linux',
'macos'],
250 labels=[
'basictests',
'corelang'],
253 inner_jobs=inner_jobs,
254 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
257 configs=[
'dbg',
'opt'],
258 platforms=[
'linux',
'macos'],
259 labels=[
'basictests',
'multilang'],
260 extra_args=extra_args +
261 [
'--report_multi_target'],
262 inner_jobs=inner_jobs)
266 configs=[
'dbg',
'opt'],
270 labels=[
'basictests_arm64'],
271 extra_args=extra_args +
272 [
'--report_multi_target'],
273 inner_jobs=inner_jobs)
279 labels=[
'basictests',
'multilang'],
280 extra_args=extra_args +
281 [
'--report_multi_target'],
282 inner_jobs=inner_jobs,
283 timeout_seconds=_OBJC_RUNTESTS_TIMEOUT)
289 inner_jobs=_DEFAULT_INNER_JOBS):
297 labels=[
'portability',
'corelang'],
298 extra_args=extra_args,
299 inner_jobs=inner_jobs)
303 'gcc6',
'gcc10.2_openssl102',
'gcc11',
'gcc_musl',
'clang6',
311 labels=[
'portability',
'corelang'],
312 extra_args=extra_args,
313 inner_jobs=inner_jobs,
314 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
319 platforms=[
'windows'],
322 labels=[
'portability',
'corelang'],
323 extra_args=extra_args,
324 inner_jobs=inner_jobs)
330 platforms=[
'windows'],
332 compiler=
'cmake_vs2017',
333 labels=[
'portability',
'corelang'],
334 extra_args=extra_args,
335 inner_jobs=inner_jobs)
341 platforms=[
'windows'],
344 labels=[
'portability',
'corelang'],
345 extra_args=extra_args + [
'--build_only'],
346 inner_jobs=inner_jobs,
347 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
353 platforms=[
'windows'],
355 compiler=
'cmake_ninja_vs2017',
356 labels=[
'portability',
'corelang'],
357 extra_args=extra_args + [
'--build_only'],
358 inner_jobs=inner_jobs,
359 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
363 configs=[
'noexcept'],
365 labels=[
'portability',
'corelang'],
366 extra_args=extra_args,
367 inner_jobs=inner_jobs,
368 timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
374 compiler=
'python_alpine',
375 labels=[
'portability',
'multilang'],
376 extra_args=extra_args +
377 [
'--report_multi_target'],
378 inner_jobs=inner_jobs)
384 """Returns a list of existing job labels."""
387 for label
in job.labels:
388 all_labels.add(label)
389 return sorted(all_labels)
393 """Auxiliary function to parse the "runs_per_test" flag."""
400 msg =
'\'{}\' is not a positive integer'.
format(arg_str)
401 raise argparse.ArgumentTypeError(msg)
404 if __name__ ==
"__main__":
405 argp = argparse.ArgumentParser(
406 description=
'Run a matrix of run_tests.py tests.')
407 argp.add_argument(
'-j',
409 default=multiprocessing.cpu_count() / _DEFAULT_INNER_JOBS,
411 help=
'Number of concurrent run_tests.py instances.')
412 argp.add_argument(
'-f',
417 help=
'Filter targets to run by label with AND semantics.')
418 argp.add_argument(
'--exclude',
422 help=
'Exclude targets with any of given labels.')
423 argp.add_argument(
'--build_only',
425 action=
'store_const',
427 help=
'Pass --build_only flag to run_tests.py instances.')
429 '--force_default_poller',
431 action=
'store_const',
433 help=
'Pass --force_default_poller to run_tests.py instances.')
434 argp.add_argument(
'--dry_run',
436 action=
'store_const',
438 help=
'Only print what would be run.')
442 action=
'store_const',
444 help=
'Filters out tests irrelevant to pull request changes.')
447 default=
'origin/master',
449 help=
'Branch that pull request is requesting to merge into')
450 argp.add_argument(
'--inner_jobs',
451 default=_DEFAULT_INNER_JOBS,
453 help=
'Number of jobs in each run_tests.py instance')
458 type=_runs_per_test_type,
459 help=
'How many times to run each tests. >1 runs implies ' +
460 'omitting passing test from the output & reports.')
461 argp.add_argument(
'--max_time',
464 help=
'Maximum amount of time to run tests for' +
465 '(other tests will be skipped)')
469 action=
'store_const',
472 '(Deprecated, has no effect) Put reports into subdirectories to improve presentation of '
473 'results by Kokoro.')
474 argp.add_argument(
'--bq_result_table',
478 help=
'Upload test results to a specified BQ table.')
479 argp.add_argument(
'--extra_args',
482 nargs=argparse.REMAINDER,
483 help=
'Extra test args passed to each sub-script.')
484 args = argp.parse_args()
488 extra_args.append(
'--build_only')
489 if args.force_default_poller:
490 extra_args.append(
'--force_default_poller')
491 if args.runs_per_test > 1:
492 extra_args.append(
'-n')
493 extra_args.append(
'%s' % args.runs_per_test)
494 extra_args.append(
'--quiet_success')
495 if args.max_time > 0:
496 extra_args.extend((
'--max_time',
'%d' % args.max_time))
497 if args.bq_result_table:
498 extra_args.append(
'--bq_result_table')
499 extra_args.append(
'%s' % args.bq_result_table)
500 extra_args.append(
'--measure_cpu_costs')
502 extra_args.extend(args.extra_args)
509 if not args.filter
or all(
510 filter
in job.labels
for filter
in args.filter):
511 if not any(exclude_label
in job.labels
512 for exclude_label
in args.exclude):
516 jobset.message(
'FAILED',
517 'No test suites match given criteria.',
521 print(
'IMPORTANT: The changes you are testing need to be locally committed')
522 print(
'because only the committed changes in the current branch will be')
523 print(
'copied to the docker environment or into subworkspaces.')
527 if args.filter_pr_tests:
528 print(
'Looking for irrelevant tests to skip...')
530 if len(relevant_jobs) ==
len(jobs):
531 print(
'No tests will be skipped.')
533 print(
'These tests will be skipped:')
534 skipped_jobs = list(
set(jobs) -
set(relevant_jobs))
536 skipped_jobs.sort(key=
lambda job: job.shortname)
537 for job
in list(skipped_jobs):
538 print(
' %s' % job.shortname)
541 print(
'Will run these tests:')
543 print(
' %s: "%s"' % (job.shortname,
' '.join(job.cmdline)))
547 print(
'--dry_run was used, exiting')
550 jobset.message(
'START',
'Running test matrix.', do_newline=
True)
551 num_failures, resultset = jobset.run(jobs,
552 newline_on_success=
True,
557 ignored_num_skipped_failures, skipped_results = jobset.run(
558 skipped_jobs, skip_jobs=
True)
559 resultset.update(skipped_results)
560 report_utils.render_junit_xml_report(resultset,
562 suite_name=_MATRIX_REPORT_NAME,
565 if num_failures == 0:
566 jobset.message(
'SUCCESS',
567 'All run_tests.py instances finished successfully.',
570 jobset.message(
'FAILED',
571 'Some run_tests.py instances have failed.',