blob: 2656f1ac5dc0122497cfc0693402fe44ded9e19d [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']:
184 test_jobs += _generate_jobs(languages=['c', 'c++'],
185 configs=['dbg'],
186 platforms=['linux'],
187 arch='x64',
188 compiler=compiler,
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200189 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700190 extra_args=extra_args,
191 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700192
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200193 # portability C on Windows
194 for arch in ['x86', 'x64']:
195 for compiler in ['vs2013', 'vs2015']:
196 test_jobs += _generate_jobs(languages=['c'],
197 configs=['dbg'],
198 platforms=['windows'],
199 arch=arch,
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 test_jobs += _generate_jobs(languages=['python'],
206 configs=['dbg'],
207 platforms=['linux'],
208 arch='default',
209 compiler='python3.4',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200210 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700211 extra_args=extra_args,
212 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700213
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200214 test_jobs += _generate_jobs(languages=['csharp'],
215 configs=['dbg'],
216 platforms=['linux'],
217 arch='default',
218 compiler='coreclr',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200219 labels=['portability'],
Matt Kwongfef98962016-10-27 10:45:47 -0700220 extra_args=extra_args,
221 inner_jobs=inner_jobs)
murgatroid991687cab2016-10-11 11:42:01 -0700222 return test_jobs
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200223
224
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200225def _allowed_labels():
226 """Returns a list of existing job labels."""
227 all_labels = set()
228 for job in _create_test_jobs() + _create_portability_test_jobs():
229 for label in job.labels:
230 all_labels.add(label)
231 return sorted(all_labels)
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200232
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200233
234argp = argparse.ArgumentParser(description='Run a matrix of run_tests.py tests.')
Jan Tattermuscha1906d52016-09-19 18:37:17 +0200235argp.add_argument('-j', '--jobs',
Matt Kwongfef98962016-10-27 10:45:47 -0700236 default=multiprocessing.cpu_count()/_DEFAULT_INNER_JOBS,
Jan Tattermuscha1906d52016-09-19 18:37:17 +0200237 type=int,
238 help='Number of concurrent run_tests.py instances.')
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200239argp.add_argument('-f', '--filter',
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200240 choices=_allowed_labels(),
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200241 nargs='+',
242 default=[],
243 help='Filter targets to run by label with AND semantics.')
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200244argp.add_argument('--build_only',
245 default=False,
246 action='store_const',
247 const=True,
248 help='Pass --build_only flag to run_tests.py instances.')
249argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,
250 help='Pass --force_default_poller to run_tests.py instances.')
Jan Tattermuschc1e44902016-09-22 14:31:10 +0200251argp.add_argument('--dry_run',
252 default=False,
253 action='store_const',
254 const=True,
255 help='Only print what would be run.')
Matt Kwonge9163f02016-10-05 11:42:55 -0700256argp.add_argument('--filter_pr_tests',
257 default=False,
258 action='store_const',
259 const=True,
260 help='Filters out tests irrelavant to pull request changes.')
Matt Kwongf3f28722016-10-07 15:25:56 -0700261argp.add_argument('--base_branch',
262 default='origin/master',
263 type=str,
264 help='Branch that pull request is requesting to merge into')
Matt Kwongfef98962016-10-27 10:45:47 -0700265argp.add_argument('--inner_jobs',
266 default=_DEFAULT_INNER_JOBS,
267 type=int,
268 help='Number of jobs in each run_tests.py instance')
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200269args = argp.parse_args()
270
Matt Kwongfef98962016-10-27 10:45:47 -0700271
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200272extra_args = []
273if args.build_only:
274 extra_args.append('--build_only')
275if args.force_default_poller:
276 extra_args.append('--force_default_poller')
277
Matt Kwongfef98962016-10-27 10:45:47 -0700278all_jobs = _create_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs) + \
279 _create_portability_test_jobs(extra_args=extra_args, inner_jobs=args.inner_jobs)
Jan Tattermusch6d7c6ef2016-09-22 13:40:48 +0200280
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200281jobs = []
282for job in all_jobs:
283 if not args.filter or all(filter in job.labels for filter in args.filter):
284 jobs.append(job)
285
286if not jobs:
287 jobset.message('FAILED', 'No test suites match given criteria.',
288 do_newline=True)
289 sys.exit(1)
murgatroid991687cab2016-10-11 11:42:01 -0700290
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200291print('IMPORTANT: The changes you are testing need to be locally committed')
292print('because only the committed changes in the current branch will be')
293print('copied to the docker environment or into subworkspaces.')
294
murgatroid991687cab2016-10-11 11:42:01 -0700295print
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200296print 'Will run these tests:'
297for job in jobs:
Jan Tattermusch7b9c21a2016-09-22 14:44:27 +0200298 if args.dry_run:
299 print ' %s: "%s"' % (job.shortname, ' '.join(job.cmdline))
300 else:
301 print ' %s' % job.shortname
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200302print
303
Matt Kwonge9163f02016-10-05 11:42:55 -0700304if args.filter_pr_tests:
305 print 'IMPORTANT: Test filtering is not active; this is only for testing.'
Matt Kwongf3f28722016-10-07 15:25:56 -0700306 relevant_jobs = filter_tests(jobs, args.base_branch)
Matt Kwongf01122c2016-10-12 18:24:53 -0700307 # todo(mattkwong): add skipped tests to report.xml
Matt Kwonge9163f02016-10-05 11:42:55 -0700308 print
309 if len(relevant_jobs) == len(jobs):
Matt Kwongf3f28722016-10-07 15:25:56 -0700310 print '(TESTING) No tests will be skipped.'
Matt Kwonge9163f02016-10-05 11:42:55 -0700311 else:
Matt Kwongf3f28722016-10-07 15:25:56 -0700312 print '(TESTING) These tests will be skipped:'
Matt Kwonge9163f02016-10-05 11:42:55 -0700313 for job in list(set(jobs) - set(relevant_jobs)):
314 print ' %s' % job.shortname
315 print
316
Jan Tattermuschc1e44902016-09-22 14:31:10 +0200317if args.dry_run:
318 print '--dry_run was used, exiting'
319 sys.exit(1)
320
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200321jobset.message('START', 'Running test matrix.', do_newline=True)
322num_failures, resultset = jobset.run(jobs,
323 newline_on_success=True,
324 travis=True,
Jan Tattermuscha1906d52016-09-19 18:37:17 +0200325 maxjobs=args.jobs)
Jan Tattermuschcfcc0752016-10-09 17:02:34 +0200326report_utils.render_junit_xml_report(resultset, 'report.xml',
327 suite_name='aggregate_tests')
Jan Tattermusch9c79e8d2016-09-19 14:33:18 +0200328
329if num_failures == 0:
330 jobset.message('SUCCESS', 'All run_tests.py instance finished successfully.',
331 do_newline=True)
332else:
333 jobset.message('FAILED', 'Some run_tests.py instance have failed.',
334 do_newline=True)
335 sys.exit(1)