blob: a428fb48537d5e31ac60efd4d7cf18c90abf2a43 [file] [log] [blame]
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +02001#!/usr/bin/env python2.7
2# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Run test matrix."""
32
33import argparse
Jan Tattermuscha1906d52016-09-19 18:37:17 +020034import multiprocessing
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020035import os
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020036import sys
Jan Tattermusch5c79a312016-12-20 11:02:50 +010037
38import python_utils.jobset as jobset
39import python_utils.report_utils as report_utils
40from python_utils.filter_pull_request_tests import filter_tests
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020041
42_ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
43os.chdir(_ROOT)
44
Jan Tattermusch060eb872016-09-20 16:06:13 +020045# Set the timeout high to allow enough time for sanitizers and pre-building
46# clang docker.
Ken Payson97e69202016-10-17 09:11:49 -070047_RUNTESTS_TIMEOUT = 4*60*60
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020048
Jan Tattermuscha1906d52016-09-19 18:37:17 +020049# Number of jobs assigned to each run_tests.py instance
Matt Kwongfef98962016-10-27 10:45:47 -070050_DEFAULT_INNER_JOBS = 2
Jan Tattermuscha1906d52016-09-19 18:37:17 +020051
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020052
Matt Kwongfef98962016-10-27 10:45:47 -070053def _docker_jobspec(name, runtests_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020054 """Run a single instance of run_tests.py in a docker container"""
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020055 test_job = jobset.JobSpec(
56 cmdline=['python', 'tools/run_tests/run_tests.py',
57 '--use_docker',
58 '-t',
Matt Kwongfef98962016-10-27 10:45:47 -070059 '-j', str(inner_jobs),
Jan Tattermuschcfcc0752016-10-09 17:02:34 +020060 '-x', 'report_%s.xml' % name,
61 '--report_suite_name', '%s' % name] + runtests_args,
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020062 shortname='run_tests_%s' % name,
63 timeout_seconds=_RUNTESTS_TIMEOUT)
64 return test_job
65
66
Matt Kwongfef98962016-10-27 10:45:47 -070067def _workspace_jobspec(name, runtests_args=[], workspace_name=None, inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020068 """Run a single instance of run_tests.py in a separate workspace"""
Jan Tattermuscha1906d52016-09-19 18:37:17 +020069 if not workspace_name:
70 workspace_name = 'workspace_%s' % name
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020071 env = {'WORKSPACE_NAME': workspace_name}
72 test_job = jobset.JobSpec(
Jan Tattermusch5c79a312016-12-20 11:02:50 +010073 cmdline=['tools/run_tests/helper_scripts/run_tests_in_workspace.sh',
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020074 '-t',
Matt Kwongfef98962016-10-27 10:45:47 -070075 '-j', str(inner_jobs),
Jan Tattermuschcfcc0752016-10-09 17:02:34 +020076 '-x', '../report_%s.xml' % name,
77 '--report_suite_name', '%s' % name] + runtests_args,
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020078 environ=env,
79 shortname='run_tests_%s' % name,
80 timeout_seconds=_RUNTESTS_TIMEOUT)
81 return test_job
82
83
84def _generate_jobs(languages, configs, platforms,
85 arch=None, compiler=None,
Matt Kwongfef98962016-10-27 10:45:47 -070086 labels=[], extra_args=[],
87 inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020088 result = []
89 for language in languages:
90 for platform in platforms:
91 for config in configs:
92 name = '%s_%s_%s' % (language, platform, config)
93 runtests_args = ['-l', language,
94 '-c', config]
95 if arch or compiler:
96 name += '_%s_%s' % (arch, compiler)
97 runtests_args += ['--arch', arch,
98 '--compiler', compiler]
99
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200100 runtests_args += extra_args
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200101 if platform == 'linux':
Matt Kwongfef98962016-10-27 10:45:47 -0700102 job = _docker_jobspec(name=name, runtests_args=runtests_args, inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200103 else:
Matt Kwongfef98962016-10-27 10:45:47 -0700104 job = _workspace_jobspec(name=name, runtests_args=runtests_args, inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200105
106 job.labels = [platform, config, language] + labels
107 result.append(job)
108 return result
109
110
Matt Kwongfef98962016-10-27 10:45:47 -0700111def _create_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200112 test_jobs = []
113 # supported on linux only
114 test_jobs += _generate_jobs(languages=['sanity', 'php7'],
115 configs=['dbg', 'opt'],
116 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200117 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700118 extra_args=extra_args,
119 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700120
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200121 # supported on all platforms.
122 test_jobs += _generate_jobs(languages=['c', 'csharp', 'node', 'python'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200123 configs=['dbg', 'opt'],
124 platforms=['linux', 'macos', 'windows'],
125 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700126 extra_args=extra_args,
127 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700128
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200129 # supported on linux and mac.
130 test_jobs += _generate_jobs(languages=['c++', 'ruby', 'php'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200131 configs=['dbg', 'opt'],
132 platforms=['linux', 'macos'],
133 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700134 extra_args=extra_args,
135 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700136
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200137 # supported on mac only.
138 test_jobs += _generate_jobs(languages=['objc'],
139 configs=['dbg', 'opt'],
140 platforms=['macos'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200141 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700142 extra_args=extra_args,
143 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700144
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200145 # sanitizers
146 test_jobs += _generate_jobs(languages=['c'],
147 configs=['msan', 'asan', 'tsan'],
148 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200149 labels=['sanitizers'],
Matt Kwongfef98962016-10-27 10:45:47 -0700150 extra_args=extra_args,
151 inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200152 test_jobs += _generate_jobs(languages=['c++'],
153 configs=['asan', 'tsan'],
154 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200155 labels=['sanitizers'],
Matt Kwongfef98962016-10-27 10:45:47 -0700156 extra_args=extra_args,
157 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700158
159 # libuv tests
160 test_jobs += _generate_jobs(languages=['c'],
161 configs=['dbg', 'opt'],
162 platforms=['linux'],
163 labels=['libuv'],
Matt Kwongfef98962016-10-27 10:45:47 -0700164 extra_args=extra_args + ['--iomgr_platform=uv'],
165 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700166
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200167 return test_jobs
168
murgatroid991687cab2016-10-11 11:42:01 -0700169
Matt Kwongfef98962016-10-27 10:45:47 -0700170def _create_portability_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200171 test_jobs = []
172 # portability C x86
173 test_jobs += _generate_jobs(languages=['c'],
174 configs=['dbg'],
175 platforms=['linux'],
176 arch='x86',
177 compiler='default',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200178 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700179 extra_args=extra_args,
180 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700181
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200182 # portability C and C++ on x64
183 for compiler in ['gcc4.4', 'gcc4.6', 'gcc5.3',
184 'clang3.5', 'clang3.6', 'clang3.7']:
Matt Kwonge3beac92016-11-01 12:53:04 -0700185 test_jobs += _generate_jobs(languages=['c'],
186 configs=['dbg'],
187 platforms=['linux'],
188 arch='x64',
189 compiler=compiler,
190 labels=['portability'],
191 extra_args=extra_args,
192 inner_jobs=inner_jobs)
Jan Tattermusch8613e472016-11-22 11:15:53 +0100193
Matt Kwonge3beac92016-11-01 12:53:04 -0700194 for compiler in ['gcc4.8', 'gcc5.3',
195 'clang3.5', 'clang3.6', 'clang3.7']:
196 test_jobs += _generate_jobs(languages=['c++'],
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200197 configs=['dbg'],
198 platforms=['linux'],
199 arch='x64',
200 compiler=compiler,
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200201 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700202 extra_args=extra_args,
203 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700204
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200205 # portability C on Windows
206 for arch in ['x86', 'x64']:
207 for compiler in ['vs2013', 'vs2015']:
208 test_jobs += _generate_jobs(languages=['c'],
209 configs=['dbg'],
210 platforms=['windows'],
211 arch=arch,
212 compiler=compiler,
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200213 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700214 extra_args=extra_args,
215 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700216
Jan Tattermuschdfb03bb2017-01-25 19:27:58 +0100217 # cmake build for C and C++
218 # TODO(jtattermusch): some of the tests are failing, so we force --build_only
219 # to make sure it's buildable at least.
220 test_jobs += _generate_jobs(languages=['c', 'c++'],
221 configs=['dbg'],
222 platforms=['linux', 'windows'],
223 arch='default',
224 compiler='cmake',
225 labels=['portability'],
226 extra_args=extra_args + ['--build_only'],
227 inner_jobs=inner_jobs)
228
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200229 test_jobs += _generate_jobs(languages=['python'],
230 configs=['dbg'],
231 platforms=['linux'],
232 arch='default',
233 compiler='python3.4',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200234 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700235 extra_args=extra_args,
236 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700237
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200238 test_jobs += _generate_jobs(languages=['csharp'],
239 configs=['dbg'],
240 platforms=['linux'],
241 arch='default',
242 compiler='coreclr',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200243 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700244 extra_args=extra_args,
245 inner_jobs=inner_jobs)
murgatroid99804c9e92016-12-05 12:19:57 -0800246
247 test_jobs += _generate_jobs(languages=['node'],
248 configs=['dbg'],
249 platforms=['linux'],
250 arch='default',
251 compiler='electron1.3',
252 labels=['portability'],
253 extra_args=extra_args,
254 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700255 return test_jobs
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200256
257
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200258def _allowed_labels():
259 """Returns a list of existing job labels."""
260 all_labels = set()
261 for job in _create_test_jobs() + _create_portability_test_jobs():
262 for label in job.labels:
263 all_labels.add(label)
264 return sorted(all_labels)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200265
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200266
Jan Tattermusch68e27bf2016-12-16 14:09:03 +0100267def _runs_per_test_type(arg_str):
Jan Tattermusch6a851292016-12-20 10:20:42 +0100268 """Auxiliary function to parse the "runs_per_test" flag."""
269 try:
270 n = int(arg_str)
271 if n <= 0: raise ValueError
272 return n
273 except:
274 msg = '\'{}\' is not a positive integer'.format(arg_str)
275 raise argparse.ArgumentTypeError(msg)
Jan Tattermusch68e27bf2016-12-16 14:09:03 +0100276
277
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700278if __name__ == "__main__":
279 argp = argparse.ArgumentParser(description='Run a matrix of run_tests.py tests.')
280 argp.add_argument('-j', '--jobs',
281 default=multiprocessing.cpu_count()/_DEFAULT_INNER_JOBS,
282 type=int,
283 help='Number of concurrent run_tests.py instances.')
284 argp.add_argument('-f', '--filter',
285 choices=_allowed_labels(),
286 nargs='+',
287 default=[],
288 help='Filter targets to run by label with AND semantics.')
Jan Tattermusch6a851292016-12-20 10:20:42 +0100289 argp.add_argument('--exclude',
290 choices=_allowed_labels(),
291 nargs='+',
292 default=[],
293 help='Exclude targets with any of given labels.')
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700294 argp.add_argument('--build_only',
295 default=False,
296 action='store_const',
297 const=True,
298 help='Pass --build_only flag to run_tests.py instances.')
299 argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
300 help='Pass --force_default_poller to run_tests.py instances.')
301 argp.add_argument('--dry_run',
302 default=False,
303 action='store_const',
304 const=True,
305 help='Only print what would be run.')
306 argp.add_argument('--filter_pr_tests',
307 default=False,
308 action='store_const',
309 const=True,
Jan Tattermusch68e27bf2016-12-16 14:09:03 +0100310 help='Filters out tests irrelevant to pull request changes.')
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700311 argp.add_argument('--base_branch',
312 default='origin/master',
313 type=str,
314 help='Branch that pull request is requesting to merge into')
315 argp.add_argument('--inner_jobs',
316 default=_DEFAULT_INNER_JOBS,
317 type=int,
318 help='Number of jobs in each run_tests.py instance')
Jan Tattermusch68e27bf2016-12-16 14:09:03 +0100319 argp.add_argument('-n', '--runs_per_test', default=1, type=_runs_per_test_type,
320 help='How many times to run each tests. >1 runs implies ' +
321 'omitting passing test from the output & reports.')
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700322 args = argp.parse_args()
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200323
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700324 extra_args = []
325 if args.build_only:
326 extra_args.append('--build_only')
327 if args.force_default_poller:
328 extra_args.append('--force_default_poller')
Jan Tattermusch68e27bf2016-12-16 14:09:03 +0100329 if args.runs_per_test > 1:
330 extra_args.append('-n')
331 extra_args.append('%s' % args.runs_per_test)
332 extra_args.append('--quiet_success')
Matt Kwongfef98962016-10-27 10:45:47 -0700333
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700334 all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
335 _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200336
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700337 jobs = []
338 for job in all_jobs:
339 if not args.filter or all(filter in job.labels for filter in args.filter):
Jan Tattermusch6a851292016-12-20 10:20:42 +0100340 if not any(exclude_label in job.labels for exclude_label in args.exclude):
341 jobs.append(job)
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200342
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700343 if not jobs:
344 jobset.message('FAILED', 'No test suites match given criteria.',
345 do_newline=True)
346 sys.exit(1)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200347
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700348 print('IMPORTANT: The changes you are testing need to be locally committed')
349 print('because only the committed changes in the current branch will be')
350 print('copied to the docker environment or into subworkspaces.')
murgatroid991687cab2016-10-11 11:42:01 -0700351
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700352 skipped_jobs = []
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200353
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700354 if args.filter_pr_tests:
355 print('Looking for irrelevant tests to skip...')
356 relevant_jobs = filter_tests(jobs, args.base_branch)
357 if len(relevant_jobs) == len(jobs):
358 print('No tests will be skipped.')
359 else:
360 print('These tests will be skipped:')
Matt Kwongaa6c94c2016-11-09 15:53:23 -0800361 skipped_jobs = list(set(jobs) - set(relevant_jobs))
362 # Sort by shortnames to make printing of skipped tests consistent
363 skipped_jobs.sort(key=lambda job: job.shortname)
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700364 for job in list(skipped_jobs):
365 print(' %s' % job.shortname)
366 jobs = relevant_jobs
367
368 print('Will run these tests:')
369 for job in jobs:
370 if args.dry_run:
371 print(' %s: "%s"' % (job.shortname, ' '.join(job.cmdline)))
372 else:
373 print(' %s' % job.shortname)
Matt Kwong5c691c62016-10-20 17:11:18 -0700374 print
375
Jan Tattermusch7b9c21a2016-09-22 14:44:27 +0200376 if args.dry_run:
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700377 print('--dry_run was used, exiting')
378 sys.exit(1)
379
380 jobset.message('START', 'Running test matrix.', do_newline=True)
381 num_failures, resultset = jobset.run(jobs,
382 newline_on_success=True,
383 travis=True,
384 maxjobs=args.jobs)
385 # Merge skipped tests into results to show skipped tests on report.xml
386 if skipped_jobs:
387 skipped_results = jobset.run(skipped_jobs,
388 skip_jobs=True)
389 resultset.update(skipped_results)
390 report_utils.render_junit_xml_report(resultset, 'report.xml',
391 suite_name='aggregate_tests')
392
393 if num_failures == 0:
394 jobset.message('SUCCESS', 'All run_tests.py instance finished successfully.',
395 do_newline=True)
Jan Tattermusch7b9c21a2016-09-22 14:44:27 +0200396 else:
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700397 jobset.message('FAILED', 'Some run_tests.py instance have failed.',
398 do_newline=True)
399 sys.exit(1)