blob: 61f345f26c76bd507e5b9e7227e5eab6909f76c8 [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tiller40839772016-01-05 12:34:49 -08002# Copyright 2015-2016, Google Inc.
Craig Tillerc2c79212015-02-16 12:00:01 -08003# 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
Nicolas Nobleddef2462015-01-06 18:08:25 -080031"""Run tests in parallel."""
32
33import argparse
34import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070035import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080036import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080037import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080038import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080039import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070040import platform
41import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080042import re
Craig Tiller82875232015-09-25 13:57:34 -070043import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070044import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080045import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070046import tempfile
47import traceback
ctiller3040cb72015-01-07 12:13:17 -080048import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070049import urllib2
Jan Tattermusch03c01062015-12-11 14:28:56 -080050import uuid
Nicolas Nobleddef2462015-01-06 18:08:25 -080051
52import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080053import report_utils
ctiller3040cb72015-01-07 12:13:17 -080054import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080055
Craig Tillerb361b4e2016-01-06 11:44:17 -080056
Craig Tiller2cc2b842015-02-27 11:38:31 -080057ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
58os.chdir(ROOT)
59
60
Craig Tiller06805272015-06-11 14:46:47 -070061_FORCE_ENVIRON_FOR_WRAPPERS = {}
62
63
Craig Tillerd50993d2015-08-05 08:04:36 -070064def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010065 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070066
67
Craig Tiller738c3342015-01-12 14:28:33 -080068# SimpleConfig: just compile with CONFIG=config, and run the binary to test
69class SimpleConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080070
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070071 def __init__(self, config, environ=None, timeout_multiplier=1):
murgatroid99132ce6a2015-03-04 17:29:14 -080072 if environ is None:
73 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080074 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080075 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080076 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080077 self.environ['CONFIG'] = config
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070078 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080079
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070080 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
Craig Tiller56c6b6a2016-01-20 08:27:37 -080081 shortname=None, environ={}, cpu_cost=1.0):
Craig Tiller49f61322015-03-03 13:02:11 -080082 """Construct a jobset.JobSpec for a test under this config
83
84 Args:
85 cmdline: a list of strings specifying the command line the test
86 would like to run
87 hash_targets: either None (don't do caching of test results), or
88 a list of strings specifying files to include in a
89 binary hash to check if a test has changed
90 -- if used, all artifacts needed to run the test must
91 be listed
92 """
Craig Tiller4fc90032015-05-21 10:39:52 -070093 actual_environ = self.environ.copy()
94 for k, v in environ.iteritems():
95 actual_environ[k] = v
Craig Tiller49f61322015-03-03 13:02:11 -080096 return jobset.JobSpec(cmdline=cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070097 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -070098 environ=actual_environ,
Craig Tiller56c6b6a2016-01-20 08:27:37 -080099 cpu_cost=cpu_cost,
Craig Tiller94d04a52016-01-20 10:58:23 -0800100 timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),
Craig Tiller547db2b2015-01-30 14:08:39 -0800101 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700102 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700103 flake_retries=5 if args.allow_flakes else 0,
104 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800105
106
107# ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
108class ValgrindConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800109
murgatroid99132ce6a2015-03-04 17:29:14 -0800110 def __init__(self, config, tool, args=None):
111 if args is None:
112 args = []
Craig Tiller738c3342015-01-12 14:28:33 -0800113 self.build_config = config
Craig Tiller2aa4d642015-01-14 15:59:44 -0800114 self.tool = tool
Craig Tiller1a305b12015-02-18 13:37:06 -0800115 self.args = args
Craig Tillerc7449162015-01-16 14:42:10 -0800116 self.allow_hashing = False
Craig Tiller738c3342015-01-12 14:28:33 -0800117
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800118 def job_spec(self, cmdline, hash_targets, cpu_cost=1.0):
Craig Tiller1a305b12015-02-18 13:37:06 -0800119 return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
Craig Tiller49f61322015-03-03 13:02:11 -0800120 self.args + cmdline,
Craig Tiller71ec6cb2015-06-03 00:51:11 -0700121 shortname='valgrind %s' % cmdline[0],
Craig Tillerd4509a12015-09-28 09:18:40 -0700122 hash_targets=None,
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800123 cpu_cost=cpu_cost,
Craig Tiller95cc07b2015-09-28 13:41:30 -0700124 flake_retries=5 if args.allow_flakes else 0,
Craig Tiller35505de2015-10-08 13:31:33 -0700125 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800126
127
murgatroid99cf08daf2015-09-21 15:33:16 -0700128def get_c_tests(travis, test_lang) :
129 out = []
130 platforms_str = 'ci_platforms' if travis else 'platforms'
131 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700132 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700133 return [tgt
134 for tgt in js
135 if tgt['language'] == test_lang and
136 platform_string() in tgt[platforms_str] and
137 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700138
murgatroid99fafeeb32015-09-22 09:13:03 -0700139
Craig Tillerc7449162015-01-16 14:42:10 -0800140class CLanguage(object):
141
Craig Tillere9c959d2015-01-18 10:23:26 -0800142 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800143 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700144 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700145 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800146
Craig Tiller883064c2015-11-04 10:06:10 -0800147 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800148 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800149 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700150 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700151 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700152 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700153 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700154 binary = 'vsprojects/%s/%s.exe' % (
155 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700156 else:
157 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700158 if os.path.isfile(binary):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800159 cmdline = [binary] + target['args']
160 out.append(config.job_spec(cmdline, [binary],
161 shortname=' '.join(cmdline),
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800162 cpu_cost=target['cpu_cost'],
Craig Tillercc0535d2015-12-08 15:14:47 -0800163 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
164 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800165 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
Craig Tiller883064c2015-11-04 10:06:10 -0800166 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700167 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700168 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800169
Craig Tiller883064c2015-11-04 10:06:10 -0800170 def make_targets(self, test_regex):
171 if platform_string() != 'windows' and test_regex != '.*':
172 # use the regex to minimize the number of things to build
173 return [target['name']
174 for target in get_c_tests(False, self.test_lang)
175 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700176 if platform_string() == 'windows':
177 # don't build tools on windows just yet
178 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700179 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800180
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800181 def make_options(self):
182 return []
183
murgatroid99256d3df2015-09-21 16:58:02 -0700184 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700185 if self.platform == 'windows':
186 return [['tools\\run_tests\\pre_build_c.bat']]
187 else:
188 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700189
Craig Tillerc7449162015-01-16 14:42:10 -0800190 def build_steps(self):
191 return []
192
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200193 def post_tests_steps(self):
194 if self.platform == 'windows':
195 return []
196 else:
197 return [['tools/run_tests/post_tests_c.sh']]
198
murgatroid99a3e244f2015-09-22 11:25:53 -0700199 def makefile_name(self):
200 return 'Makefile'
201
murgatroid99132ce6a2015-03-04 17:29:14 -0800202 def supports_multi_config(self):
203 return True
204
205 def __str__(self):
206 return self.make_target
207
Craig Tillercc0535d2015-12-08 15:14:47 -0800208
murgatroid992c8d5162015-01-26 10:41:21 -0800209class NodeLanguage(object):
210
Craig Tiller883064c2015-11-04 10:06:10 -0800211 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700212 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700213 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800214
murgatroid99256d3df2015-09-21 16:58:02 -0700215 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700216 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700217 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700218
Craig Tiller883064c2015-11-04 10:06:10 -0800219 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700220 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800221
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800222 def make_options(self):
223 return []
224
murgatroid992c8d5162015-01-26 10:41:21 -0800225 def build_steps(self):
226 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800227
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200228 def post_tests_steps(self):
229 return []
230
murgatroid99a3e244f2015-09-22 11:25:53 -0700231 def makefile_name(self):
232 return 'Makefile'
233
murgatroid99132ce6a2015-03-04 17:29:14 -0800234 def supports_multi_config(self):
235 return False
236
237 def __str__(self):
238 return 'node'
239
Craig Tiller99775822015-01-30 13:07:16 -0800240
Craig Tillerc7449162015-01-16 14:42:10 -0800241class PhpLanguage(object):
242
Craig Tiller883064c2015-11-04 10:06:10 -0800243 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700244 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700245 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800246
murgatroid99256d3df2015-09-21 16:58:02 -0700247 def pre_build_steps(self):
248 return []
249
Craig Tiller883064c2015-11-04 10:06:10 -0800250 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700251 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800252
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800253 def make_options(self):
254 return []
255
Craig Tillerc7449162015-01-16 14:42:10 -0800256 def build_steps(self):
257 return [['tools/run_tests/build_php.sh']]
258
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200259 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800260 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200261
murgatroid99a3e244f2015-09-22 11:25:53 -0700262 def makefile_name(self):
263 return 'Makefile'
264
murgatroid99132ce6a2015-03-04 17:29:14 -0800265 def supports_multi_config(self):
266 return False
267
268 def __str__(self):
269 return 'php'
270
Craig Tillerc7449162015-01-16 14:42:10 -0800271
Nathaniel Manista840615e2015-01-22 20:31:47 +0000272class PythonLanguage(object):
273
Craig Tiller49f61322015-03-03 13:02:11 -0800274 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700275 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700276 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800277
Craig Tiller883064c2015-11-04 10:06:10 -0800278 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700279 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
280 environment['PYVER'] = '2.7'
281 return [config.job_spec(
282 ['tools/run_tests/run_python.sh'],
283 None,
284 environ=environment,
285 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700286 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700287 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000288
murgatroid99256d3df2015-09-21 16:58:02 -0700289 def pre_build_steps(self):
290 return []
291
Craig Tiller883064c2015-11-04 10:06:10 -0800292 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700293 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000294
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800295 def make_options(self):
296 return []
297
Nathaniel Manista840615e2015-01-22 20:31:47 +0000298 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700299 commands = []
300 for python_version in self._build_python_versions:
301 try:
302 with open(os.devnull, 'w') as output:
303 subprocess.check_call(['which', 'python' + python_version],
304 stdout=output, stderr=output)
305 commands.append(['tools/run_tests/build_python.sh', python_version])
306 self._has_python_versions.append(python_version)
307 except:
308 jobset.message('WARNING', 'Missing Python ' + python_version,
309 do_newline=True)
310 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000311
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200312 def post_tests_steps(self):
313 return []
314
murgatroid99a3e244f2015-09-22 11:25:53 -0700315 def makefile_name(self):
316 return 'Makefile'
317
murgatroid99132ce6a2015-03-04 17:29:14 -0800318 def supports_multi_config(self):
319 return False
320
321 def __str__(self):
322 return 'python'
323
Craig Tillerd625d812015-04-08 15:52:35 -0700324
murgatroid996a4c4fa2015-02-27 12:08:57 -0800325class RubyLanguage(object):
326
Craig Tiller883064c2015-11-04 10:06:10 -0800327 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700328 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700329 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800330
murgatroid99256d3df2015-09-21 16:58:02 -0700331 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200332 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700333
Craig Tiller883064c2015-11-04 10:06:10 -0800334 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700335 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800336
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800337 def make_options(self):
338 return []
339
murgatroid996a4c4fa2015-02-27 12:08:57 -0800340 def build_steps(self):
341 return [['tools/run_tests/build_ruby.sh']]
342
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200343 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100344 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200345
murgatroid99a3e244f2015-09-22 11:25:53 -0700346 def makefile_name(self):
347 return 'Makefile'
348
murgatroid99132ce6a2015-03-04 17:29:14 -0800349 def supports_multi_config(self):
350 return False
351
352 def __str__(self):
353 return 'ruby'
354
Craig Tillerd625d812015-04-08 15:52:35 -0700355
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800356class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700357 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700358 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700359
Craig Tiller883064c2015-11-04 10:06:10 -0800360 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800361 with open('src/csharp/tests.json') as f:
362 tests_json = json.load(f)
363 assemblies = tests_json['assemblies']
364 tests = tests_json['tests']
365
366 msbuild_config = _WINDOWS_CONFIG[config.build_config]
367 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
368 for a in assemblies]
369
370 extra_args = ['-labels'] + assembly_files
371
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700372 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800373 script_name = 'tools\\run_tests\\run_csharp.bat'
374 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700375 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800376 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700377
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700378 if config.build_config == 'gcov':
379 # On Windows, we only collect C# code coverage.
380 # On Linux, we only collect coverage for native extension.
381 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800382 return [config.job_spec([script_name] + extra_args, None,
383 shortname='csharp.coverage',
384 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700385 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800386 specs = []
387 for test in tests:
388 cmdline = [script_name, '-run=%s' % test] + extra_args
389 if self.platform == 'windows':
390 # use different output directory for each test to prevent
391 # TestResult.xml clash between parallel test runs.
392 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
393 specs.append(config.job_spec(cmdline, None,
394 shortname='csharp.%s' % test,
395 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
396 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800397
murgatroid99256d3df2015-09-21 16:58:02 -0700398 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700399 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700400 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700401 else:
402 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700403
Craig Tiller883064c2015-11-04 10:06:10 -0800404 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700405 # For Windows, this target doesn't really build anything,
406 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700407 if self.platform == 'windows':
408 return []
409 else:
410 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800411
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800412 def make_options(self):
413 if self.platform == 'mac':
414 # On Mac, official distribution of mono is 32bit.
415 return ['CFLAGS=-arch i386', 'LDFLAGS=-arch i386']
416 else:
417 return []
418
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800419 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700420 if self.platform == 'windows':
421 return [['src\\csharp\\buildall.bat']]
422 else:
423 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000424
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200425 def post_tests_steps(self):
426 return []
427
murgatroid99a3e244f2015-09-22 11:25:53 -0700428 def makefile_name(self):
429 return 'Makefile'
430
murgatroid99132ce6a2015-03-04 17:29:14 -0800431 def supports_multi_config(self):
432 return False
433
434 def __str__(self):
435 return 'csharp'
436
Craig Tillerd625d812015-04-08 15:52:35 -0700437
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700438class ObjCLanguage(object):
439
Craig Tiller883064c2015-11-04 10:06:10 -0800440 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700441 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
442 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
443
murgatroid99256d3df2015-09-21 16:58:02 -0700444 def pre_build_steps(self):
445 return []
446
Craig Tiller883064c2015-11-04 10:06:10 -0800447 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700448 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700449
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800450 def make_options(self):
451 return []
452
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700453 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700454 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700455
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200456 def post_tests_steps(self):
457 return []
458
murgatroid99a3e244f2015-09-22 11:25:53 -0700459 def makefile_name(self):
460 return 'Makefile'
461
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700462 def supports_multi_config(self):
463 return False
464
465 def __str__(self):
466 return 'objc'
467
468
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100469class Sanity(object):
470
Craig Tiller883064c2015-11-04 10:06:10 -0800471 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800472 import yaml
473 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
Craig Tillera7a9e402016-01-20 11:05:34 -0800474 return [config.job_spec([cmd['script']], None, timeout_seconds=None, environ={'TEST': 'true'}, cpu_cost=cmd.get('cpu_cost', 1))
Craig Tiller94d04a52016-01-20 10:58:23 -0800475 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100476
murgatroid99256d3df2015-09-21 16:58:02 -0700477 def pre_build_steps(self):
478 return []
479
Craig Tiller883064c2015-11-04 10:06:10 -0800480 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100481 return ['run_dep_checks']
482
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800483 def make_options(self):
484 return []
485
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100486 def build_steps(self):
487 return []
488
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200489 def post_tests_steps(self):
490 return []
491
murgatroid99a3e244f2015-09-22 11:25:53 -0700492 def makefile_name(self):
493 return 'Makefile'
494
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100495 def supports_multi_config(self):
496 return False
497
498 def __str__(self):
499 return 'sanity'
500
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200501
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100502class Build(object):
503
Craig Tiller883064c2015-11-04 10:06:10 -0800504 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100505 return []
506
murgatroid99256d3df2015-09-21 16:58:02 -0700507 def pre_build_steps(self):
508 return []
509
Craig Tiller883064c2015-11-04 10:06:10 -0800510 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200511 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100512
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800513 def make_options(self):
514 return []
515
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100516 def build_steps(self):
517 return []
518
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100519 def post_tests_steps(self):
520 return []
521
murgatroid99a3e244f2015-09-22 11:25:53 -0700522 def makefile_name(self):
523 return 'Makefile'
524
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100525 def supports_multi_config(self):
526 return True
527
528 def __str__(self):
529 return self.make_target
530
531
Craig Tiller738c3342015-01-12 14:28:33 -0800532# different configurations we can run under
533_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800534 'dbg': SimpleConfig('dbg'),
535 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700536 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700537 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700538 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800539 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700540 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800541 'ASAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:detect_leaks=1:color=always',
542 'LSAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700543 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800544 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800545 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800546 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800547 'helgrind': ValgrindConfig('dbg', 'helgrind')
548 }
Craig Tiller738c3342015-01-12 14:28:33 -0800549
550
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100551_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800552_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800553 'c++': CLanguage('cxx', 'c++'),
554 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800555 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000556 'php': PhpLanguage(),
557 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800558 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100559 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700560 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100561 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100562 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800563 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800564
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700565_WINDOWS_CONFIG = {
566 'dbg': 'Debug',
567 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800568 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700569 }
570
David Garcia Quintase90cd372015-05-31 18:15:26 -0700571
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800572def _windows_arch_option(arch):
573 """Returns msbuild cmdline option for selected architecture."""
574 if arch == 'default' or arch == 'windows_x86':
575 return '/p:Platform=Win32'
576 elif arch == 'windows_x64':
577 return '/p:Platform=x64'
578 else:
579 print 'Architecture %s not supported on current platform.' % arch
580 sys.exit(1)
581
582
583def _windows_build_bat(compiler):
584 """Returns name of build.bat for selected compiler."""
585 if compiler == 'default' or compiler == 'vs2013':
586 return 'vsprojects\\build_vs2013.bat'
587 elif compiler == 'vs2015':
588 return 'vsprojects\\build_vs2015.bat'
589 elif compiler == 'vs2010':
590 return 'vsprojects\\build_vs2010.bat'
591 else:
592 print 'Compiler %s not supported.' % compiler
593 sys.exit(1)
594
595
596def _windows_toolset_option(compiler):
597 """Returns msbuild PlatformToolset for selected compiler."""
598 if compiler == 'default' or compiler == 'vs2013':
599 return '/p:PlatformToolset=v120'
600 elif compiler == 'vs2015':
601 return '/p:PlatformToolset=v140'
602 elif compiler == 'vs2010':
603 return '/p:PlatformToolset=v100'
604 else:
605 print 'Compiler %s not supported.' % compiler
606 sys.exit(1)
607
608
David Garcia Quintase90cd372015-05-31 18:15:26 -0700609def runs_per_test_type(arg_str):
610 """Auxilary function to parse the "runs_per_test" flag.
611
612 Returns:
613 A positive integer or 0, the latter indicating an infinite number of
614 runs.
615
616 Raises:
617 argparse.ArgumentTypeError: Upon invalid input.
618 """
619 if arg_str == 'inf':
620 return 0
621 try:
622 n = int(arg_str)
623 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700624 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700625 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700626 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700627 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700628
629# parse command line
630argp = argparse.ArgumentParser(description='Run grpc tests.')
631argp.add_argument('-c', '--config',
632 choices=['all'] + sorted(_CONFIGS.keys()),
633 nargs='+',
634 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700635argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
636 help='A positive integer or "inf". If "inf", all tests will run in an '
637 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800638argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800639argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800640argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800641argp.add_argument('-f', '--forever',
642 default=False,
643 action='store_const',
644 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100645argp.add_argument('-t', '--travis',
646 default=False,
647 action='store_const',
648 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800649argp.add_argument('--newline_on_success',
650 default=False,
651 action='store_const',
652 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800653argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700654 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800655 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700656 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700657argp.add_argument('-S', '--stop_on_failure',
658 default=False,
659 action='store_const',
660 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700661argp.add_argument('--use_docker',
662 default=False,
663 action='store_const',
664 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700665 help='Run all the tests under docker. That provides ' +
666 'additional isolation and prevents the need to install ' +
667 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700668argp.add_argument('--allow_flakes',
669 default=False,
670 action='store_const',
671 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700672 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800673argp.add_argument('--arch',
674 choices=['default', 'windows_x86', 'windows_x64'],
675 default='default',
676 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
677argp.add_argument('--compiler',
678 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
679 default='default',
680 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
681argp.add_argument('--build_only',
682 default=False,
683 action='store_const',
684 const=True,
685 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800686argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
687 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800688argp.add_argument('--update_submodules', default=[], nargs='*',
689 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
690 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700691argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200692argp.add_argument('-x', '--xml_report', default=None, type=str,
693 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800694args = argp.parse_args()
695
Craig Tiller5f735a62016-01-20 09:31:15 -0800696jobset.measure_cpu_costs = args.measure_cpu_costs
697
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700698if args.use_docker:
699 if not args.travis:
700 print 'Seen --use_docker flag, will run tests under docker.'
701 print
702 print 'IMPORTANT: The changes you are testing need to be locally committed'
703 print 'because only the committed changes in the current branch will be'
704 print 'copied to the docker environment.'
705 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700706
707 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700708 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700709
710 # TODO(jtattermusch): revisit if we need special handling for arch here
711 # set arch command prefix in case we are working with different arch.
712 arch_env = os.getenv('arch')
713 if arch_env:
714 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
715
716 env = os.environ.copy()
717 env['RUN_TESTS_COMMAND'] = run_tests_cmd
718 if args.xml_report:
719 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700720 if not args.travis:
721 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700722
723 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
724 shell=True,
725 env=env)
726 sys.exit(0)
727
Craig Tiller1676f912016-01-05 10:49:44 -0800728# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800729need_to_regenerate_projects = False
730for spec in args.update_submodules:
731 spec = spec.split(':', 1)
732 if len(spec) == 1:
733 submodule = spec[0]
734 branch = 'master'
735 elif len(spec) == 2:
736 submodule = spec[0]
737 branch = spec[1]
738 cwd = 'third_party/%s' % submodule
739 def git(cmd, cwd=cwd):
740 print 'in %s: git %s' % (cwd, cmd)
741 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
742 git('fetch')
743 git('checkout %s' % branch)
744 git('pull origin %s' % branch)
745 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
746 need_to_regenerate_projects = True
747if need_to_regenerate_projects:
748 if jobset.platform_string() == 'linux':
749 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
750 else:
751 print 'WARNING: may need to regenerate projects, but since we are not on'
752 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800753
754
Nicolas Nobleddef2462015-01-06 18:08:25 -0800755# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800756run_configs = set(_CONFIGS[cfg]
757 for cfg in itertools.chain.from_iterable(
758 _CONFIGS.iterkeys() if x == 'all' else [x]
759 for x in args.config))
760build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800761
Craig Tiller06805272015-06-11 14:46:47 -0700762if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700763 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700764
Adele Zhou6b9527c2015-11-20 15:56:35 -0800765if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800766 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800767else:
768 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800769# We don't support code coverage on some languages
770if 'gcov' in args.config:
771 for bad in ['objc', 'sanity', 'build']:
772 if bad in lang_list:
773 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800774
775languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800776
777if len(build_configs) > 1:
778 for language in languages:
779 if not language.supports_multi_config():
780 print language, 'does not support multiple build configurations'
781 sys.exit(1)
782
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800783language_make_options=[]
784if any(language.make_options() for language in languages):
785 if len(languages) != 1:
786 print 'languages with custom make options cannot be built simultaneously with other languages'
787 sys.exit(1)
788 else:
789 language_make_options = next(iter(languages)).make_options()
790
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800791if platform_string() != 'windows':
792 if args.arch != 'default':
793 print 'Architecture %s not supported on current platform.' % args.arch
794 sys.exit(1)
795 if args.compiler != 'default':
796 print 'Compiler %s not supported on current platform.' % args.compiler
797 sys.exit(1)
798
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100799if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700800 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700801 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700802 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700803 # empirically /m:2 gives the best performance/price and should prevent
804 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700805 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700806 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700807 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700808 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800809 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700810 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800811 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
812 _windows_toolset_option(args.compiler),
813 _windows_arch_option(args.arch)] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800814 extra_args +
815 language_make_options,
Craig Tiller590105a2016-01-19 13:03:46 -0800816 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700817 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700818else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700819 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700820 if targets:
821 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
822 '-f', makefile,
823 '-j', '%d' % (multiprocessing.cpu_count() + 1),
Craig Tiller71a86042016-01-15 14:59:58 -0800824 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
825 'CONFIG=%s' % cfg] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800826 language_make_options +
Craig Tiller71a86042016-01-15 14:59:58 -0800827 ([] if not args.travis else ['JENKINS_BUILD=1']) +
828 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800829 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700830 else:
831 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700832make_targets = {}
833for l in languages:
834 makefile = l.makefile_name()
835 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800836 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700837
Jan Tattermusche4a69182015-12-15 09:53:01 -0800838def build_step_environ(cfg):
839 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800840 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800841 if msbuild_cfg:
842 environ['MSBUILD_CONFIG'] = msbuild_cfg
843 return environ
844
murgatroid99fddac962015-09-22 09:20:11 -0700845build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800846 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700847 for cfg in build_configs
848 for l in languages
849 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700850if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700851 make_commands = itertools.chain.from_iterable(make_jobspec(cfg, list(targets), makefile) for cfg in build_configs for (makefile, targets) in make_targets.iteritems())
Craig Tiller6fd23842015-09-01 07:36:31 -0700852 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700853build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800854 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800855 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800856 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700857 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800858
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200859post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800860 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200861 for cfg in build_configs
862 for l in languages
863 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800864runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800865forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800866
Nicolas Nobleddef2462015-01-06 18:08:25 -0800867
Craig Tiller71735182015-01-15 17:07:13 -0800868class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800869 """Cache for running tests."""
870
David Klempner25739582015-02-11 15:57:32 -0800871 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800872 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800873 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700874 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800875
876 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800877 if cmdline not in self._last_successful_run:
878 return True
879 if self._last_successful_run[cmdline] != bin_hash:
880 return True
David Klempner25739582015-02-11 15:57:32 -0800881 if not self._use_cache_results:
882 return True
Craig Tiller71735182015-01-15 17:07:13 -0800883 return False
884
885 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800886 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700887 if time.time() - self._last_save > 1:
888 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800889
890 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800891 return [{'cmdline': k, 'hash': v}
892 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800893
894 def parse(self, exdump):
895 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
896
897 def save(self):
898 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800899 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700900 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800901
Craig Tiller1cc11db2015-01-15 22:50:50 -0800902 def maybe_load(self):
903 if os.path.exists('.run_tests_cache'):
904 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800905 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800906
907
Craig Tillerf53d9c82015-08-04 14:19:43 -0700908def _start_port_server(port_server_port):
909 # check if a compatible port server is running
910 # if incompatible (version mismatch) ==> start a new one
911 # if not running ==> start a new one
912 # otherwise, leave it up
913 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700914 version = int(urllib2.urlopen(
915 'http://localhost:%d/version_number' % port_server_port,
916 timeout=1).read())
917 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700918 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700919 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700920 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700921 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700922 running = False
923 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700924 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800925 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
926 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700927 print 'my port server is version %d' % current_version
928 running = (version >= current_version)
929 if not running:
930 print 'port_server version mismatch: killing the old one'
931 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
932 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700933 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700934 fd, logfile = tempfile.mkstemp()
935 os.close(fd)
936 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800937 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
938 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700939 env = dict(os.environ)
940 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100941 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800942 # Working directory of port server needs to be outside of Jenkins
943 # workspace to prevent file lock issues.
944 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700945 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700946 args,
947 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800948 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700949 creationflags = 0x00000008, # detached process
950 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700951 else:
952 port_server = subprocess.Popen(
953 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700954 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700955 preexec_fn=os.setsid,
956 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700957 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700958 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700959 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700960 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700961 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700962 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700963 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700964 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700965 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700966 # try one final time: maybe another build managed to start one
967 time.sleep(1)
968 try:
969 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
970 timeout=1).read()
971 print 'last ditch attempt to contact port server succeeded'
972 break
973 except:
974 traceback.print_exc();
975 port_log = open(logfile, 'r').read()
976 print port_log
977 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700978 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700979 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
980 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700981 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700982 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700983 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700984 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700985 traceback.print_exc();
986 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700987 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700988 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700989 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700990 traceback.print_exc();
991 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700992 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700993 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700994 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700995 port_server.kill()
996 raise
997
998
Adele Zhoud5fffa52015-10-23 15:51:42 -0700999def _calculate_num_runs_failures(list_of_results):
1000 """Caculate number of runs and failures for a particular test.
1001
1002 Args:
1003 list_of_results: (List) of JobResult object.
1004 Returns:
1005 A tuple of total number of runs and failures.
1006 """
1007 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
1008 num_failures = 0
1009 for jobresult in list_of_results:
1010 if jobresult.retries > 0:
1011 num_runs += jobresult.retries
1012 if jobresult.num_failures > 0:
1013 num_failures += jobresult.num_failures
1014 return num_runs, num_failures
1015
Adele Zhou6b9527c2015-11-20 15:56:35 -08001016
Craig Tillereb9de8b2016-01-08 08:57:41 -08001017# _build_and_run results
1018class BuildAndRunError(object):
1019
1020 BUILD = object()
1021 TEST = object()
1022 POST_TEST = object()
1023
1024
1025# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -07001026def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001027 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -08001028 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -08001029 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -07001030 num_failures, _ = jobset.run(
1031 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001032 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -07001033 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001034 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -08001035
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001036 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001037 return []
ctiller3040cb72015-01-07 12:13:17 -08001038
Craig Tiller234b6e72015-05-23 10:12:40 -07001039 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001040 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -07001041 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -07001042 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -07001043 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -08001044 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -08001045 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -07001046 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -07001047 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001048 one_run = set(
1049 spec
1050 for config in run_configs
1051 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001052 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001053 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001054 # When running on travis, we want out test runs to be as similar as possible
1055 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001056 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001057 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1058 else:
1059 # whereas otherwise, we want to shuffle things up to give all tests a
1060 # chance to run.
1061 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1062 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001063 if infinite_runs:
1064 assert len(massaged_one_run) > 0, 'Must have at least one test for a -n inf run'
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001065 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1066 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001067 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001068
Adele Zhou803af152015-11-30 15:16:16 -08001069 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001070 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001071 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001072 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001073 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001074 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001075 if resultset:
1076 for k, v in resultset.iteritems():
1077 num_runs, num_failures = _calculate_num_runs_failures(v)
1078 if num_failures == num_runs: # what about infinite_runs???
1079 jobset.message('FAILED', k, do_newline=True)
1080 elif num_failures > 0:
1081 jobset.message(
1082 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1083 do_newline=True)
1084 else:
1085 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001086 finally:
1087 for antagonist in antagonists:
1088 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001089 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001090 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001091
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001092 number_failures, _ = jobset.run(
1093 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001094 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001095
1096 out = []
1097 if number_failures:
1098 out.append(BuildAndRunError.POST_TEST)
1099 if num_test_failures:
1100 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001101
Craig Tiller69cd2372015-06-11 09:38:09 -07001102 if cache: cache.save()
1103
Craig Tillereb9de8b2016-01-08 08:57:41 -08001104 return out
ctiller3040cb72015-01-07 12:13:17 -08001105
1106
David Klempner25739582015-02-11 15:57:32 -08001107test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001108test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001109
ctiller3040cb72015-01-07 12:13:17 -08001110if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001111 success = True
ctiller3040cb72015-01-07 12:13:17 -08001112 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001113 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001114 initial_time = dw.most_recent_change()
1115 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001116 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001117 errors = _build_and_run(check_cancelled=have_files_changed,
1118 newline_on_success=False,
1119 cache=test_cache,
1120 build_only=args.build_only) == 0
1121 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001122 jobset.message('SUCCESS',
1123 'All tests are now passing properly',
1124 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001125 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001126 while not have_files_changed():
1127 time.sleep(1)
1128else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001129 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001130 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001131 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001132 xml_report=args.xml_report,
1133 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001134 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001135 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1136 else:
1137 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001138 exit_code = 0
1139 if BuildAndRunError.BUILD in errors:
1140 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001141 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001142 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001143 if BuildAndRunError.POST_TEST in errors:
1144 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001145 sys.exit(exit_code)