blob: d03e34766ee5a4ec4fa82c819aa73bfadcbabfbe [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
34import jobset
Jan Tattermuscha1906d52016-09-19 18:37:17 +020035import multiprocessing
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020036import os
37import report_utils
38import sys
Matt Kwonge9163f02016-10-05 11:42:55 -070039from filter_pull_request_tests import filter_tests
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020040
41_ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
42os.chdir(_ROOT)
43
Jan Tattermusch060eb872016-09-20 16:06:13 +020044# Set the timeout high to allow enough time for sanitizers and pre-building
45# clang docker.
Ken Payson97e69202016-10-17 09:11:49 -070046_RUNTESTS_TIMEOUT = 4*60*60
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020047
Jan Tattermuscha1906d52016-09-19 18:37:17 +020048# Number of jobs assigned to each run_tests.py instance
Matt Kwongfef98962016-10-27 10:45:47 -070049_DEFAULT_INNER_JOBS = 2
Jan Tattermuscha1906d52016-09-19 18:37:17 +020050
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020051
Matt Kwongfef98962016-10-27 10:45:47 -070052def _docker_jobspec(name, runtests_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020053 """Run a single instance of run_tests.py in a docker container"""
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020054 test_job = jobset.JobSpec(
55 cmdline=['python', 'tools/run_tests/run_tests.py',
56 '--use_docker',
57 '-t',
Matt Kwongfef98962016-10-27 10:45:47 -070058 '-j', str(inner_jobs),
Jan Tattermuschcfcc0752016-10-09 17:02:34 +020059 '-x', 'report_%s.xml' % name,
60 '--report_suite_name', '%s' % name] + runtests_args,
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020061 shortname='run_tests_%s' % name,
62 timeout_seconds=_RUNTESTS_TIMEOUT)
63 return test_job
64
65
Matt Kwongfef98962016-10-27 10:45:47 -070066def _workspace_jobspec(name, runtests_args=[], workspace_name=None, inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020067 """Run a single instance of run_tests.py in a separate workspace"""
Jan Tattermuscha1906d52016-09-19 18:37:17 +020068 if not workspace_name:
69 workspace_name = 'workspace_%s' % name
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020070 env = {'WORKSPACE_NAME': workspace_name}
71 test_job = jobset.JobSpec(
72 cmdline=['tools/run_tests/run_tests_in_workspace.sh',
73 '-t',
Matt Kwongfef98962016-10-27 10:45:47 -070074 '-j', str(inner_jobs),
Jan Tattermuschcfcc0752016-10-09 17:02:34 +020075 '-x', '../report_%s.xml' % name,
76 '--report_suite_name', '%s' % name] + runtests_args,
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020077 environ=env,
78 shortname='run_tests_%s' % name,
79 timeout_seconds=_RUNTESTS_TIMEOUT)
80 return test_job
81
82
83def _generate_jobs(languages, configs, platforms,
84 arch=None, compiler=None,
Matt Kwongfef98962016-10-27 10:45:47 -070085 labels=[], extra_args=[],
86 inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +020087 result = []
88 for language in languages:
89 for platform in platforms:
90 for config in configs:
91 name = '%s_%s_%s' % (language, platform, config)
92 runtests_args = ['-l', language,
93 '-c', config]
94 if arch or compiler:
95 name += '_%s_%s' % (arch, compiler)
96 runtests_args += ['--arch', arch,
97 '--compiler', compiler]
98
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +020099 runtests_args += extra_args
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200100 if platform == 'linux':
Matt Kwongfef98962016-10-27 10:45:47 -0700101 job = _docker_jobspec(name=name, runtests_args=runtests_args, inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200102 else:
Matt Kwongfef98962016-10-27 10:45:47 -0700103 job = _workspace_jobspec(name=name, runtests_args=runtests_args, inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200104
105 job.labels = [platform, config, language] + labels
106 result.append(job)
107 return result
108
109
Matt Kwongfef98962016-10-27 10:45:47 -0700110def _create_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200111 test_jobs = []
112 # supported on linux only
113 test_jobs += _generate_jobs(languages=['sanity', 'php7'],
114 configs=['dbg', 'opt'],
115 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200116 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700117 extra_args=extra_args,
118 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700119
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200120 # supported on all platforms.
121 test_jobs += _generate_jobs(languages=['c', 'csharp', 'node', 'python'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200122 configs=['dbg', 'opt'],
123 platforms=['linux', 'macos', 'windows'],
124 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700125 extra_args=extra_args,
126 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700127
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200128 # supported on linux and mac.
129 test_jobs += _generate_jobs(languages=['c++', 'ruby', 'php'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200130 configs=['dbg', 'opt'],
131 platforms=['linux', 'macos'],
132 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700133 extra_args=extra_args,
134 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700135
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200136 # supported on mac only.
137 test_jobs += _generate_jobs(languages=['objc'],
138 configs=['dbg', 'opt'],
139 platforms=['macos'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200140 labels=['basictests'],
Matt Kwongfef98962016-10-27 10:45:47 -0700141 extra_args=extra_args,
142 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700143
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200144 # sanitizers
145 test_jobs += _generate_jobs(languages=['c'],
146 configs=['msan', 'asan', 'tsan'],
147 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200148 labels=['sanitizers'],
Matt Kwongfef98962016-10-27 10:45:47 -0700149 extra_args=extra_args,
150 inner_jobs=inner_jobs)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200151 test_jobs += _generate_jobs(languages=['c++'],
152 configs=['asan', 'tsan'],
153 platforms=['linux'],
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200154 labels=['sanitizers'],
Matt Kwongfef98962016-10-27 10:45:47 -0700155 extra_args=extra_args,
156 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700157
158 # libuv tests
159 test_jobs += _generate_jobs(languages=['c'],
160 configs=['dbg', 'opt'],
161 platforms=['linux'],
162 labels=['libuv'],
Matt Kwongfef98962016-10-27 10:45:47 -0700163 extra_args=extra_args + ['--iomgr_platform=uv'],
164 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700165
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200166 return test_jobs
167
murgatroid991687cab2016-10-11 11:42:01 -0700168
Matt Kwongfef98962016-10-27 10:45:47 -0700169def _create_portability_test_jobs(extra_args=[], inner_jobs=_DEFAULT_INNER_JOBS):
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200170 test_jobs = []
171 # portability C x86
172 test_jobs += _generate_jobs(languages=['c'],
173 configs=['dbg'],
174 platforms=['linux'],
175 arch='x86',
176 compiler='default',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200177 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700178 extra_args=extra_args,
179 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700180
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200181 # portability C and C++ on x64
182 for compiler in ['gcc4.4', 'gcc4.6', 'gcc5.3',
183 'clang3.5', 'clang3.6', 'clang3.7']:
Matt Kwonge3beac92016-11-01 12:53:04 -0700184 test_jobs += _generate_jobs(languages=['c'],
185 configs=['dbg'],
186 platforms=['linux'],
187 arch='x64',
188 compiler=compiler,
189 labels=['portability'],
190 extra_args=extra_args,
191 inner_jobs=inner_jobs)
Jan Tattermusch8613e472016-11-22 11:15:53 +0100192
Matt Kwonge3beac92016-11-01 12:53:04 -0700193 for compiler in ['gcc4.8', 'gcc5.3',
194 'clang3.5', 'clang3.6', 'clang3.7']:
195 test_jobs += _generate_jobs(languages=['c++'],
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200196 configs=['dbg'],
197 platforms=['linux'],
198 arch='x64',
199 compiler=compiler,
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200200 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700201 extra_args=extra_args,
202 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700203
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200204 # portability C on Windows
205 for arch in ['x86', 'x64']:
206 for compiler in ['vs2013', 'vs2015']:
207 test_jobs += _generate_jobs(languages=['c'],
208 configs=['dbg'],
209 platforms=['windows'],
210 arch=arch,
211 compiler=compiler,
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200212 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700213 extra_args=extra_args,
214 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700215
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200216 test_jobs += _generate_jobs(languages=['python'],
217 configs=['dbg'],
218 platforms=['linux'],
219 arch='default',
220 compiler='python3.4',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200221 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700222 extra_args=extra_args,
223 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700224
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200225 test_jobs += _generate_jobs(languages=['csharp'],
226 configs=['dbg'],
227 platforms=['linux'],
228 arch='default',
229 compiler='coreclr',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200230 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700231 extra_args=extra_args,
232 inner_jobs=inner_jobs)
murgatroid99804c9e92016-12-05 12:19:57 -0800233
234 test_jobs += _generate_jobs(languages=['node'],
235 configs=['dbg'],
236 platforms=['linux'],
237 arch='default',
238 compiler='electron1.3',
239 labels=['portability'],
240 extra_args=extra_args,
241 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700242 return test_jobs
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200243
244
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200245def _allowed_labels():
246 """Returns a list of existing job labels."""
247 all_labels = set()
248 for job in _create_test_jobs() + _create_portability_test_jobs():
249 for label in job.labels:
250 all_labels.add(label)
251 return sorted(all_labels)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200252
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200253
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700254if __name__ == "__main__":
255 argp = argparse.ArgumentParser(description='Run a matrix of run_tests.py tests.')
256 argp.add_argument('-j', '--jobs',
257 default=multiprocessing.cpu_count()/_DEFAULT_INNER_JOBS,
258 type=int,
259 help='Number of concurrent run_tests.py instances.')
260 argp.add_argument('-f', '--filter',
261 choices=_allowed_labels(),
262 nargs='+',
263 default=[],
264 help='Filter targets to run by label with AND semantics.')
265 argp.add_argument('--build_only',
266 default=False,
267 action='store_const',
268 const=True,
269 help='Pass --build_only flag to run_tests.py instances.')
270 argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
271 help='Pass --force_default_poller to run_tests.py instances.')
272 argp.add_argument('--dry_run',
273 default=False,
274 action='store_const',
275 const=True,
276 help='Only print what would be run.')
277 argp.add_argument('--filter_pr_tests',
278 default=False,
279 action='store_const',
280 const=True,
281 help='Filters out tests irrelavant to pull request changes.')
282 argp.add_argument('--base_branch',
283 default='origin/master',
284 type=str,
285 help='Branch that pull request is requesting to merge into')
286 argp.add_argument('--inner_jobs',
287 default=_DEFAULT_INNER_JOBS,
288 type=int,
289 help='Number of jobs in each run_tests.py instance')
290 args = argp.parse_args()
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200291
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700292 extra_args = []
293 if args.build_only:
294 extra_args.append('--build_only')
295 if args.force_default_poller:
296 extra_args.append('--force_default_poller')
Matt Kwongfef98962016-10-27 10:45:47 -0700297
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700298 all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
299 _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200300
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700301 jobs = []
302 for job in all_jobs:
303 if not args.filter or all(filter in job.labels for filter in args.filter):
304 jobs.append(job)
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200305
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700306 if not jobs:
307 jobset.message('FAILED', 'No test suites match given criteria.',
308 do_newline=True)
309 sys.exit(1)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200310
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700311 print('IMPORTANT: The changes you are testing need to be locally committed')
312 print('because only the committed changes in the current branch will be')
313 print('copied to the docker environment or into subworkspaces.')
murgatroid991687cab2016-10-11 11:42:01 -0700314
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700315 skipped_jobs = []
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200316
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700317 if args.filter_pr_tests:
318 print('Looking for irrelevant tests to skip...')
319 relevant_jobs = filter_tests(jobs, args.base_branch)
320 if len(relevant_jobs) == len(jobs):
321 print('No tests will be skipped.')
322 else:
323 print('These tests will be skipped:')
Matt Kwongaa6c94c2016-11-09 15:53:23 -0800324 skipped_jobs = list(set(jobs) - set(relevant_jobs))
325 # Sort by shortnames to make printing of skipped tests consistent
326 skipped_jobs.sort(key=lambda job: job.shortname)
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700327 for job in list(skipped_jobs):
328 print(' %s' % job.shortname)
329 jobs = relevant_jobs
330
331 print('Will run these tests:')
332 for job in jobs:
333 if args.dry_run:
334 print(' %s: "%s"' % (job.shortname, ' '.join(job.cmdline)))
335 else:
336 print(' %s' % job.shortname)
Matt Kwong5c691c62016-10-20 17:11:18 -0700337 print
338
Jan Tattermusch7b9c21a2016-09-22 14:44:27 +0200339 if args.dry_run:
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700340 print('--dry_run was used, exiting')
341 sys.exit(1)
342
343 jobset.message('START', 'Running test matrix.', do_newline=True)
344 num_failures, resultset = jobset.run(jobs,
345 newline_on_success=True,
346 travis=True,
347 maxjobs=args.jobs)
348 # Merge skipped tests into results to show skipped tests on report.xml
349 if skipped_jobs:
350 skipped_results = jobset.run(skipped_jobs,
351 skip_jobs=True)
352 resultset.update(skipped_results)
353 report_utils.render_junit_xml_report(resultset, 'report.xml',
354 suite_name='aggregate_tests')
355
356 if num_failures == 0:
357 jobset.message('SUCCESS', 'All run_tests.py instance finished successfully.',
358 do_newline=True)
Jan Tattermusch7b9c21a2016-09-22 14:44:27 +0200359 else:
Matt Kwong7e9bd6c2016-10-24 17:30:25 -0700360 jobset.message('FAILED', 'Some run_tests.py instance have failed.',
361 do_newline=True)
362 sys.exit(1)