blob: a3908642375d3c82d99625b27039ca5c198e4da0 [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
Craig Tiller9279ac22016-01-20 17:05:23 -080034import ast
Nicolas Nobleddef2462015-01-06 18:08:25 -080035import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070036import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080037import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080038import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080039import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080040import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070041import platform
42import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080043import re
Craig Tiller82875232015-09-25 13:57:34 -070044import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070045import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080046import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070047import tempfile
48import traceback
ctiller3040cb72015-01-07 12:13:17 -080049import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070050import urllib2
Jan Tattermusch03c01062015-12-11 14:28:56 -080051import uuid
Nicolas Nobleddef2462015-01-06 18:08:25 -080052
53import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080054import report_utils
ctiller3040cb72015-01-07 12:13:17 -080055import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080056
Craig Tillerb361b4e2016-01-06 11:44:17 -080057
Craig Tiller2cc2b842015-02-27 11:38:31 -080058ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
59os.chdir(ROOT)
60
61
Craig Tiller06805272015-06-11 14:46:47 -070062_FORCE_ENVIRON_FOR_WRAPPERS = {}
63
64
Craig Tillerd50993d2015-08-05 08:04:36 -070065def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010066 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070067
68
Craig Tiller738c3342015-01-12 14:28:33 -080069# SimpleConfig: just compile with CONFIG=config, and run the binary to test
Craig Tillera0f85172016-01-20 15:56:06 -080070class Config(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080071
Craig Tillera0f85172016-01-20 15:56:06 -080072 def __init__(self, config, environ=None, timeout_multiplier=1, tool_prefix=[]):
murgatroid99132ce6a2015-03-04 17:29:14 -080073 if environ is None:
74 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080075 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080076 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080077 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080078 self.environ['CONFIG'] = config
Craig Tillera0f85172016-01-20 15:56:06 -080079 self.tool_prefix = tool_prefix
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070080 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080081
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070082 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
Craig Tiller56c6b6a2016-01-20 08:27:37 -080083 shortname=None, environ={}, cpu_cost=1.0):
Craig Tiller49f61322015-03-03 13:02:11 -080084 """Construct a jobset.JobSpec for a test under this config
85
86 Args:
87 cmdline: a list of strings specifying the command line the test
88 would like to run
89 hash_targets: either None (don't do caching of test results), or
90 a list of strings specifying files to include in a
91 binary hash to check if a test has changed
92 -- if used, all artifacts needed to run the test must
93 be listed
94 """
Craig Tiller4fc90032015-05-21 10:39:52 -070095 actual_environ = self.environ.copy()
96 for k, v in environ.iteritems():
97 actual_environ[k] = v
Craig Tillera0f85172016-01-20 15:56:06 -080098 return jobset.JobSpec(cmdline=self.tool_prefix + cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070099 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -0700100 environ=actual_environ,
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800101 cpu_cost=cpu_cost,
Craig Tiller94d04a52016-01-20 10:58:23 -0800102 timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),
Craig Tiller547db2b2015-01-30 14:08:39 -0800103 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700104 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700105 flake_retries=5 if args.allow_flakes else 0,
106 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800107
108
murgatroid99cf08daf2015-09-21 15:33:16 -0700109def get_c_tests(travis, test_lang) :
110 out = []
111 platforms_str = 'ci_platforms' if travis else 'platforms'
112 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700113 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700114 return [tgt
115 for tgt in js
116 if tgt['language'] == test_lang and
117 platform_string() in tgt[platforms_str] and
118 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700119
murgatroid99fafeeb32015-09-22 09:13:03 -0700120
Craig Tillerc7449162015-01-16 14:42:10 -0800121class CLanguage(object):
122
Craig Tillere9c959d2015-01-18 10:23:26 -0800123 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800124 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700125 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700126 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800127
Craig Tiller883064c2015-11-04 10:06:10 -0800128 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800129 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800130 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700131 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700132 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700133 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700134 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700135 binary = 'vsprojects/%s/%s.exe' % (
136 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700137 else:
138 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700139 if os.path.isfile(binary):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800140 cmdline = [binary] + target['args']
141 out.append(config.job_spec(cmdline, [binary],
142 shortname=' '.join(cmdline),
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800143 cpu_cost=target['cpu_cost'],
Craig Tillercc0535d2015-12-08 15:14:47 -0800144 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
145 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800146 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
Craig Tiller883064c2015-11-04 10:06:10 -0800147 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700148 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700149 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800150
Craig Tiller883064c2015-11-04 10:06:10 -0800151 def make_targets(self, test_regex):
152 if platform_string() != 'windows' and test_regex != '.*':
153 # use the regex to minimize the number of things to build
154 return [target['name']
155 for target in get_c_tests(False, self.test_lang)
156 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700157 if platform_string() == 'windows':
158 # don't build tools on windows just yet
159 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700160 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800161
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800162 def make_options(self):
163 return []
164
murgatroid99256d3df2015-09-21 16:58:02 -0700165 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700166 if self.platform == 'windows':
167 return [['tools\\run_tests\\pre_build_c.bat']]
168 else:
169 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700170
Craig Tillerc7449162015-01-16 14:42:10 -0800171 def build_steps(self):
172 return []
173
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200174 def post_tests_steps(self):
175 if self.platform == 'windows':
176 return []
177 else:
178 return [['tools/run_tests/post_tests_c.sh']]
179
murgatroid99a3e244f2015-09-22 11:25:53 -0700180 def makefile_name(self):
181 return 'Makefile'
182
murgatroid99132ce6a2015-03-04 17:29:14 -0800183 def supports_multi_config(self):
184 return True
185
186 def __str__(self):
187 return self.make_target
188
Craig Tillercc0535d2015-12-08 15:14:47 -0800189
murgatroid992c8d5162015-01-26 10:41:21 -0800190class NodeLanguage(object):
191
Craig Tiller883064c2015-11-04 10:06:10 -0800192 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700193 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700194 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800195
murgatroid99256d3df2015-09-21 16:58:02 -0700196 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700197 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700198 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700199
Craig Tiller883064c2015-11-04 10:06:10 -0800200 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700201 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800202
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800203 def make_options(self):
204 return []
205
murgatroid992c8d5162015-01-26 10:41:21 -0800206 def build_steps(self):
207 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800208
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200209 def post_tests_steps(self):
210 return []
211
murgatroid99a3e244f2015-09-22 11:25:53 -0700212 def makefile_name(self):
213 return 'Makefile'
214
murgatroid99132ce6a2015-03-04 17:29:14 -0800215 def supports_multi_config(self):
216 return False
217
218 def __str__(self):
219 return 'node'
220
Craig Tiller99775822015-01-30 13:07:16 -0800221
Craig Tillerc7449162015-01-16 14:42:10 -0800222class PhpLanguage(object):
223
Craig Tiller883064c2015-11-04 10:06:10 -0800224 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700225 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700226 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800227
murgatroid99256d3df2015-09-21 16:58:02 -0700228 def pre_build_steps(self):
229 return []
230
Craig Tiller883064c2015-11-04 10:06:10 -0800231 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700232 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800233
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800234 def make_options(self):
235 return []
236
Craig Tillerc7449162015-01-16 14:42:10 -0800237 def build_steps(self):
238 return [['tools/run_tests/build_php.sh']]
239
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200240 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800241 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200242
murgatroid99a3e244f2015-09-22 11:25:53 -0700243 def makefile_name(self):
244 return 'Makefile'
245
murgatroid99132ce6a2015-03-04 17:29:14 -0800246 def supports_multi_config(self):
247 return False
248
249 def __str__(self):
250 return 'php'
251
Craig Tillerc7449162015-01-16 14:42:10 -0800252
Nathaniel Manista840615e2015-01-22 20:31:47 +0000253class PythonLanguage(object):
254
Craig Tiller49f61322015-03-03 13:02:11 -0800255 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700256 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700257 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800258
Craig Tiller883064c2015-11-04 10:06:10 -0800259 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700260 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
261 environment['PYVER'] = '2.7'
262 return [config.job_spec(
263 ['tools/run_tests/run_python.sh'],
264 None,
265 environ=environment,
266 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700267 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700268 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000269
murgatroid99256d3df2015-09-21 16:58:02 -0700270 def pre_build_steps(self):
271 return []
272
Craig Tiller883064c2015-11-04 10:06:10 -0800273 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700274 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000275
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800276 def make_options(self):
277 return []
278
Nathaniel Manista840615e2015-01-22 20:31:47 +0000279 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700280 commands = []
281 for python_version in self._build_python_versions:
282 try:
283 with open(os.devnull, 'w') as output:
284 subprocess.check_call(['which', 'python' + python_version],
285 stdout=output, stderr=output)
286 commands.append(['tools/run_tests/build_python.sh', python_version])
287 self._has_python_versions.append(python_version)
288 except:
289 jobset.message('WARNING', 'Missing Python ' + python_version,
290 do_newline=True)
291 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000292
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200293 def post_tests_steps(self):
294 return []
295
murgatroid99a3e244f2015-09-22 11:25:53 -0700296 def makefile_name(self):
297 return 'Makefile'
298
murgatroid99132ce6a2015-03-04 17:29:14 -0800299 def supports_multi_config(self):
300 return False
301
302 def __str__(self):
303 return 'python'
304
Craig Tillerd625d812015-04-08 15:52:35 -0700305
murgatroid996a4c4fa2015-02-27 12:08:57 -0800306class RubyLanguage(object):
307
Craig Tiller883064c2015-11-04 10:06:10 -0800308 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700309 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700310 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800311
murgatroid99256d3df2015-09-21 16:58:02 -0700312 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200313 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700314
Craig Tiller883064c2015-11-04 10:06:10 -0800315 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700316 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800317
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800318 def make_options(self):
319 return []
320
murgatroid996a4c4fa2015-02-27 12:08:57 -0800321 def build_steps(self):
322 return [['tools/run_tests/build_ruby.sh']]
323
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200324 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100325 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200326
murgatroid99a3e244f2015-09-22 11:25:53 -0700327 def makefile_name(self):
328 return 'Makefile'
329
murgatroid99132ce6a2015-03-04 17:29:14 -0800330 def supports_multi_config(self):
331 return False
332
333 def __str__(self):
334 return 'ruby'
335
Craig Tillerd625d812015-04-08 15:52:35 -0700336
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800337class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700338 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700339 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700340
Craig Tiller883064c2015-11-04 10:06:10 -0800341 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800342 with open('src/csharp/tests.json') as f:
343 tests_json = json.load(f)
344 assemblies = tests_json['assemblies']
345 tests = tests_json['tests']
346
347 msbuild_config = _WINDOWS_CONFIG[config.build_config]
348 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
349 for a in assemblies]
350
351 extra_args = ['-labels'] + assembly_files
352
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700353 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800354 script_name = 'tools\\run_tests\\run_csharp.bat'
355 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700356 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800357 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700358
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700359 if config.build_config == 'gcov':
360 # On Windows, we only collect C# code coverage.
361 # On Linux, we only collect coverage for native extension.
362 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800363 return [config.job_spec([script_name] + extra_args, None,
364 shortname='csharp.coverage',
365 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700366 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800367 specs = []
368 for test in tests:
369 cmdline = [script_name, '-run=%s' % test] + extra_args
370 if self.platform == 'windows':
371 # use different output directory for each test to prevent
372 # TestResult.xml clash between parallel test runs.
373 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
374 specs.append(config.job_spec(cmdline, None,
375 shortname='csharp.%s' % test,
376 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
377 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800378
murgatroid99256d3df2015-09-21 16:58:02 -0700379 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700380 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700381 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700382 else:
383 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700384
Craig Tiller883064c2015-11-04 10:06:10 -0800385 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700386 # For Windows, this target doesn't really build anything,
387 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700388 if self.platform == 'windows':
389 return []
390 else:
391 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800392
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800393 def make_options(self):
394 if self.platform == 'mac':
395 # On Mac, official distribution of mono is 32bit.
396 return ['CFLAGS=-arch i386', 'LDFLAGS=-arch i386']
397 else:
398 return []
399
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800400 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700401 if self.platform == 'windows':
402 return [['src\\csharp\\buildall.bat']]
403 else:
404 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000405
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200406 def post_tests_steps(self):
407 return []
408
murgatroid99a3e244f2015-09-22 11:25:53 -0700409 def makefile_name(self):
410 return 'Makefile'
411
murgatroid99132ce6a2015-03-04 17:29:14 -0800412 def supports_multi_config(self):
413 return False
414
415 def __str__(self):
416 return 'csharp'
417
Craig Tillerd625d812015-04-08 15:52:35 -0700418
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700419class ObjCLanguage(object):
420
Craig Tiller883064c2015-11-04 10:06:10 -0800421 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700422 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
423 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
424
murgatroid99256d3df2015-09-21 16:58:02 -0700425 def pre_build_steps(self):
426 return []
427
Craig Tiller883064c2015-11-04 10:06:10 -0800428 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700429 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700430
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800431 def make_options(self):
432 return []
433
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700434 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700435 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700436
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200437 def post_tests_steps(self):
438 return []
439
murgatroid99a3e244f2015-09-22 11:25:53 -0700440 def makefile_name(self):
441 return 'Makefile'
442
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700443 def supports_multi_config(self):
444 return False
445
446 def __str__(self):
447 return 'objc'
448
449
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100450class Sanity(object):
451
Craig Tiller883064c2015-11-04 10:06:10 -0800452 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800453 import yaml
454 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
Craig Tiller528fd1e2016-01-21 15:27:37 -0800455 return [config.job_spec(cmd['script'].split(), None, timeout_seconds=None, environ={'TEST': 'true'}, cpu_cost=cmd.get('cpu_cost', 1))
Craig Tiller94d04a52016-01-20 10:58:23 -0800456 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100457
murgatroid99256d3df2015-09-21 16:58:02 -0700458 def pre_build_steps(self):
459 return []
460
Craig Tiller883064c2015-11-04 10:06:10 -0800461 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100462 return ['run_dep_checks']
463
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800464 def make_options(self):
465 return []
466
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100467 def build_steps(self):
468 return []
469
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200470 def post_tests_steps(self):
471 return []
472
murgatroid99a3e244f2015-09-22 11:25:53 -0700473 def makefile_name(self):
474 return 'Makefile'
475
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100476 def supports_multi_config(self):
477 return False
478
479 def __str__(self):
480 return 'sanity'
481
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200482
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100483class Build(object):
484
Craig Tiller883064c2015-11-04 10:06:10 -0800485 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100486 return []
487
murgatroid99256d3df2015-09-21 16:58:02 -0700488 def pre_build_steps(self):
489 return []
490
Craig Tiller883064c2015-11-04 10:06:10 -0800491 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200492 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100493
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800494 def make_options(self):
495 return []
496
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100497 def build_steps(self):
498 return []
499
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100500 def post_tests_steps(self):
501 return []
502
murgatroid99a3e244f2015-09-22 11:25:53 -0700503 def makefile_name(self):
504 return 'Makefile'
505
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100506 def supports_multi_config(self):
507 return True
508
509 def __str__(self):
510 return self.make_target
511
512
Craig Tiller738c3342015-01-12 14:28:33 -0800513# different configurations we can run under
Craig Tillera0f85172016-01-20 15:56:06 -0800514with open('tools/run_tests/configs.json') as f:
Craig Tiller1dce9062016-01-20 17:01:56 -0800515 _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))
Craig Tiller738c3342015-01-12 14:28:33 -0800516
517
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100518_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800519_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800520 'c++': CLanguage('cxx', 'c++'),
521 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800522 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000523 'php': PhpLanguage(),
524 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800525 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100526 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700527 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100528 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100529 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800530 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800531
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700532_WINDOWS_CONFIG = {
533 'dbg': 'Debug',
534 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800535 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700536 }
537
David Garcia Quintase90cd372015-05-31 18:15:26 -0700538
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800539def _windows_arch_option(arch):
540 """Returns msbuild cmdline option for selected architecture."""
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800541 if arch == 'default' or arch == 'x86':
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800542 return '/p:Platform=Win32'
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800543 elif arch == 'x64':
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800544 return '/p:Platform=x64'
545 else:
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800546 print 'Architecture %s not supported.' % arch
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800547 sys.exit(1)
Jan Tattermuschf08018a2016-01-26 08:22:09 -0800548
549
550def _check_arch_option(arch):
551 """Checks that architecture option is valid."""
552 if platform_string() == 'windows':
553 _windows_arch_option(arch)
554 elif platform_string() == 'linux':
555 # On linux, we need to be running under docker with the right architecture.
556 if arch == 'default':
557 return
558 elif runtime_arch == '64bit' and arch == 'x64':
559 return
560 elif runtime_arch == '32bit' and arch == 'x86':
561 return
562 else:
563 print 'Architecture %s does not match current runtime architecture.' % arch
564 sys.exit(1)
565 else:
566 if args.arch != 'default':
567 print 'Architecture %s not supported on current platform.' % args.arch
568 sys.exit(1)
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800569
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800570
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800571def _windows_build_bat(compiler):
572 """Returns name of build.bat for selected compiler."""
573 if compiler == 'default' or compiler == 'vs2013':
574 return 'vsprojects\\build_vs2013.bat'
575 elif compiler == 'vs2015':
576 return 'vsprojects\\build_vs2015.bat'
577 elif compiler == 'vs2010':
578 return 'vsprojects\\build_vs2010.bat'
579 else:
580 print 'Compiler %s not supported.' % compiler
581 sys.exit(1)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800582
583
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800584def _windows_toolset_option(compiler):
585 """Returns msbuild PlatformToolset for selected compiler."""
586 if compiler == 'default' or compiler == 'vs2013':
587 return '/p:PlatformToolset=v120'
588 elif compiler == 'vs2015':
589 return '/p:PlatformToolset=v140'
590 elif compiler == 'vs2010':
591 return '/p:PlatformToolset=v100'
592 else:
593 print 'Compiler %s not supported.' % compiler
594 sys.exit(1)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800595
596
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800597def _get_dockerfile_dir(arch):
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800598 """Returns dockerfile to use"""
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800599 if arch == 'default' or arch == 'x64':
Jan Tattermusch5371d732016-01-25 21:40:04 -0800600 return 'tools/dockerfile/grpc_jenkins_slave_x64'
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800601 elif arch == 'x86':
Jan Tattermusch5371d732016-01-25 21:40:04 -0800602 return 'tools/dockerfile/grpc_jenkins_slave_x86'
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800603 else:
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800604 print 'Architecture %s not supported with current settings.' % arch
605 sys.exit(1)
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800606
David Garcia Quintase90cd372015-05-31 18:15:26 -0700607def runs_per_test_type(arg_str):
608 """Auxilary function to parse the "runs_per_test" flag.
609
610 Returns:
611 A positive integer or 0, the latter indicating an infinite number of
612 runs.
613
614 Raises:
615 argparse.ArgumentTypeError: Upon invalid input.
616 """
617 if arg_str == 'inf':
618 return 0
619 try:
620 n = int(arg_str)
621 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700622 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700623 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700624 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700625 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700626
627# parse command line
628argp = argparse.ArgumentParser(description='Run grpc tests.')
629argp.add_argument('-c', '--config',
630 choices=['all'] + sorted(_CONFIGS.keys()),
631 nargs='+',
632 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700633argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
634 help='A positive integer or "inf". If "inf", all tests will run in an '
635 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800636argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800637argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800638argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800639argp.add_argument('-f', '--forever',
640 default=False,
641 action='store_const',
642 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100643argp.add_argument('-t', '--travis',
644 default=False,
645 action='store_const',
646 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800647argp.add_argument('--newline_on_success',
648 default=False,
649 action='store_const',
650 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800651argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700652 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800653 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700654 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700655argp.add_argument('-S', '--stop_on_failure',
656 default=False,
657 action='store_const',
658 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700659argp.add_argument('--use_docker',
660 default=False,
661 action='store_const',
662 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700663 help='Run all the tests under docker. That provides ' +
664 'additional isolation and prevents the need to install ' +
665 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700666argp.add_argument('--allow_flakes',
667 default=False,
668 action='store_const',
669 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700670 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800671argp.add_argument('--arch',
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800672 choices=['default', 'x86', 'x64'],
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800673 default='default',
674 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
675argp.add_argument('--compiler',
676 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
677 default='default',
678 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
679argp.add_argument('--build_only',
680 default=False,
681 action='store_const',
682 const=True,
683 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800684argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
685 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800686argp.add_argument('--update_submodules', default=[], nargs='*',
687 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
688 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700689argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200690argp.add_argument('-x', '--xml_report', default=None, type=str,
691 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800692args = argp.parse_args()
693
Craig Tiller5f735a62016-01-20 09:31:15 -0800694jobset.measure_cpu_costs = args.measure_cpu_costs
695
Craig Tiller1676f912016-01-05 10:49:44 -0800696# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800697need_to_regenerate_projects = False
698for spec in args.update_submodules:
699 spec = spec.split(':', 1)
700 if len(spec) == 1:
701 submodule = spec[0]
702 branch = 'master'
703 elif len(spec) == 2:
704 submodule = spec[0]
705 branch = spec[1]
706 cwd = 'third_party/%s' % submodule
707 def git(cmd, cwd=cwd):
708 print 'in %s: git %s' % (cwd, cmd)
709 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
710 git('fetch')
711 git('checkout %s' % branch)
712 git('pull origin %s' % branch)
713 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
714 need_to_regenerate_projects = True
715if need_to_regenerate_projects:
716 if jobset.platform_string() == 'linux':
717 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
718 else:
719 print 'WARNING: may need to regenerate projects, but since we are not on'
720 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800721
722
Nicolas Nobleddef2462015-01-06 18:08:25 -0800723# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800724run_configs = set(_CONFIGS[cfg]
725 for cfg in itertools.chain.from_iterable(
726 _CONFIGS.iterkeys() if x == 'all' else [x]
727 for x in args.config))
728build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800729
Craig Tiller06805272015-06-11 14:46:47 -0700730if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700731 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700732
Adele Zhou6b9527c2015-11-20 15:56:35 -0800733if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800734 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800735else:
736 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800737# We don't support code coverage on some languages
738if 'gcov' in args.config:
739 for bad in ['objc', 'sanity', 'build']:
740 if bad in lang_list:
741 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800742
743languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800744
745if len(build_configs) > 1:
746 for language in languages:
747 if not language.supports_multi_config():
748 print language, 'does not support multiple build configurations'
749 sys.exit(1)
750
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800751language_make_options=[]
752if any(language.make_options() for language in languages):
753 if len(languages) != 1:
754 print 'languages with custom make options cannot be built simultaneously with other languages'
755 sys.exit(1)
756 else:
757 language_make_options = next(iter(languages)).make_options()
758
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800759if len(languages) != 1 or len(build_configs) != 1:
760 print 'Multi-language and multi-config testing is not supported.'
761 sys.exit(1)
762
763if args.use_docker:
764 if not args.travis:
765 print 'Seen --use_docker flag, will run tests under docker.'
766 print
767 print 'IMPORTANT: The changes you are testing need to be locally committed'
768 print 'because only the committed changes in the current branch will be'
769 print 'copied to the docker environment.'
770 time.sleep(5)
771
772 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
773 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
774
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800775 env = os.environ.copy()
776 env['RUN_TESTS_COMMAND'] = run_tests_cmd
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800777 env['DOCKERFILE_DIR'] = _get_dockerfile_dir(args.arch)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800778 env['DOCKER_RUN_SCRIPT'] = 'tools/jenkins/docker_run_tests.sh'
779 if args.xml_report:
780 env['XML_REPORT'] = args.xml_report
781 if not args.travis:
782 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
783
784 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
785 shell=True,
786 env=env)
787 sys.exit(0)
Jan Tattermuschf08018a2016-01-26 08:22:09 -0800788
789if platform_string() != 'windows' and args.compiler != 'default':
790 print 'Compiler %s not supported on current platform.' % args.compiler
791 sys.exit(1)
792
793_check_arch_option(args.arch)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800794
Jan Tattermuschfba65302016-01-25 18:21:14 -0800795def make_jobspec(cfg, targets, makefile='Makefile'):
796 if platform_string() == 'windows':
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700797 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700798 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700799 # empirically /m:2 gives the best performance/price and should prevent
800 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700801 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700802 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700803 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700804 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800805 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700806 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800807 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
808 _windows_toolset_option(args.compiler),
809 _windows_arch_option(args.arch)] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800810 extra_args +
811 language_make_options,
Craig Tiller590105a2016-01-19 13:03:46 -0800812 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700813 for target in targets]
Jan Tattermuschfba65302016-01-25 18:21:14 -0800814 else:
murgatroid998ae409f2015-10-26 16:39:00 -0700815 if targets:
816 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
817 '-f', makefile,
Craig Tillerdd6f7ed2016-01-21 12:54:42 -0800818 '-j', '%d' % args.jobs,
Craig Tiller71a86042016-01-15 14:59:58 -0800819 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
820 'CONFIG=%s' % cfg] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800821 language_make_options +
Craig Tiller71a86042016-01-15 14:59:58 -0800822 ([] if not args.travis else ['JENKINS_BUILD=1']) +
823 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800824 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700825 else:
826 return []
Jan Tattermuschfba65302016-01-25 18:21:14 -0800827
murgatroid99a3e244f2015-09-22 11:25:53 -0700828make_targets = {}
829for l in languages:
830 makefile = l.makefile_name()
831 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800832 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700833
Jan Tattermusche4a69182015-12-15 09:53:01 -0800834def build_step_environ(cfg):
835 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800836 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800837 if msbuild_cfg:
838 environ['MSBUILD_CONFIG'] = msbuild_cfg
839 return environ
840
murgatroid99fddac962015-09-22 09:20:11 -0700841build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800842 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700843 for cfg in build_configs
844 for l in languages
845 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700846if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700847 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 -0700848 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700849build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800850 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800851 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800852 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700853 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800854
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200855post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800856 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200857 for cfg in build_configs
858 for l in languages
859 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800860runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800861forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800862
Nicolas Nobleddef2462015-01-06 18:08:25 -0800863
Craig Tiller71735182015-01-15 17:07:13 -0800864class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800865 """Cache for running tests."""
866
David Klempner25739582015-02-11 15:57:32 -0800867 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800868 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800869 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700870 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800871
872 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800873 if cmdline not in self._last_successful_run:
874 return True
875 if self._last_successful_run[cmdline] != bin_hash:
876 return True
David Klempner25739582015-02-11 15:57:32 -0800877 if not self._use_cache_results:
878 return True
Craig Tiller71735182015-01-15 17:07:13 -0800879 return False
880
881 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800882 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700883 if time.time() - self._last_save > 1:
884 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800885
886 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800887 return [{'cmdline': k, 'hash': v}
888 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800889
890 def parse(self, exdump):
891 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
892
893 def save(self):
894 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800895 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700896 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800897
Craig Tiller1cc11db2015-01-15 22:50:50 -0800898 def maybe_load(self):
899 if os.path.exists('.run_tests_cache'):
900 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800901 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800902
903
Craig Tillerf53d9c82015-08-04 14:19:43 -0700904def _start_port_server(port_server_port):
905 # check if a compatible port server is running
906 # if incompatible (version mismatch) ==> start a new one
907 # if not running ==> start a new one
908 # otherwise, leave it up
909 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700910 version = int(urllib2.urlopen(
911 'http://localhost:%d/version_number' % port_server_port,
912 timeout=1).read())
913 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700914 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700915 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700916 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700917 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700918 running = False
919 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700920 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800921 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
922 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700923 print 'my port server is version %d' % current_version
924 running = (version >= current_version)
925 if not running:
926 print 'port_server version mismatch: killing the old one'
927 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
928 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700929 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700930 fd, logfile = tempfile.mkstemp()
931 os.close(fd)
932 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800933 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
934 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700935 env = dict(os.environ)
936 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100937 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800938 # Working directory of port server needs to be outside of Jenkins
939 # workspace to prevent file lock issues.
940 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700941 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700942 args,
943 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800944 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700945 creationflags = 0x00000008, # detached process
946 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700947 else:
948 port_server = subprocess.Popen(
949 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700950 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700951 preexec_fn=os.setsid,
952 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700953 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700954 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700955 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700956 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700957 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700958 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700959 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700960 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700961 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700962 # try one final time: maybe another build managed to start one
963 time.sleep(1)
964 try:
965 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
966 timeout=1).read()
967 print 'last ditch attempt to contact port server succeeded'
968 break
969 except:
970 traceback.print_exc();
971 port_log = open(logfile, 'r').read()
972 print port_log
973 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700974 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700975 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
976 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700977 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700978 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700979 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700980 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700981 traceback.print_exc();
982 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700983 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700984 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700985 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700986 traceback.print_exc();
987 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700988 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700989 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700990 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700991 port_server.kill()
992 raise
993
994
Adele Zhoud5fffa52015-10-23 15:51:42 -0700995def _calculate_num_runs_failures(list_of_results):
996 """Caculate number of runs and failures for a particular test.
997
998 Args:
999 list_of_results: (List) of JobResult object.
1000 Returns:
1001 A tuple of total number of runs and failures.
1002 """
1003 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
1004 num_failures = 0
1005 for jobresult in list_of_results:
1006 if jobresult.retries > 0:
1007 num_runs += jobresult.retries
1008 if jobresult.num_failures > 0:
1009 num_failures += jobresult.num_failures
1010 return num_runs, num_failures
1011
Adele Zhou6b9527c2015-11-20 15:56:35 -08001012
Craig Tillereb9de8b2016-01-08 08:57:41 -08001013# _build_and_run results
1014class BuildAndRunError(object):
1015
1016 BUILD = object()
1017 TEST = object()
1018 POST_TEST = object()
1019
1020
1021# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -07001022def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001023 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -08001024 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -08001025 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -07001026 num_failures, _ = jobset.run(
1027 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001028 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -07001029 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001030 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -08001031
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001032 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001033 return []
ctiller3040cb72015-01-07 12:13:17 -08001034
Craig Tiller234b6e72015-05-23 10:12:40 -07001035 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001036 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -07001037 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -07001038 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -07001039 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -08001040 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -08001041 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -07001042 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -07001043 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001044 one_run = set(
1045 spec
1046 for config in run_configs
1047 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001048 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001049 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001050 # When running on travis, we want out test runs to be as similar as possible
1051 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001052 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001053 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1054 else:
1055 # whereas otherwise, we want to shuffle things up to give all tests a
1056 # chance to run.
1057 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1058 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001059 if infinite_runs:
1060 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 -07001061 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1062 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001063 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001064
Adele Zhou803af152015-11-30 15:16:16 -08001065 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001066 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001067 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001068 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001069 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001070 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001071 if resultset:
1072 for k, v in resultset.iteritems():
1073 num_runs, num_failures = _calculate_num_runs_failures(v)
1074 if num_failures == num_runs: # what about infinite_runs???
1075 jobset.message('FAILED', k, do_newline=True)
1076 elif num_failures > 0:
1077 jobset.message(
1078 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1079 do_newline=True)
1080 else:
1081 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001082 finally:
1083 for antagonist in antagonists:
1084 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001085 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001086 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001087
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001088 number_failures, _ = jobset.run(
1089 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001090 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001091
1092 out = []
1093 if number_failures:
1094 out.append(BuildAndRunError.POST_TEST)
1095 if num_test_failures:
1096 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001097
Craig Tiller69cd2372015-06-11 09:38:09 -07001098 if cache: cache.save()
1099
Craig Tillereb9de8b2016-01-08 08:57:41 -08001100 return out
ctiller3040cb72015-01-07 12:13:17 -08001101
1102
David Klempner25739582015-02-11 15:57:32 -08001103test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001104test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001105
ctiller3040cb72015-01-07 12:13:17 -08001106if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001107 success = True
ctiller3040cb72015-01-07 12:13:17 -08001108 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001109 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001110 initial_time = dw.most_recent_change()
1111 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001112 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001113 errors = _build_and_run(check_cancelled=have_files_changed,
1114 newline_on_success=False,
1115 cache=test_cache,
1116 build_only=args.build_only) == 0
1117 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001118 jobset.message('SUCCESS',
1119 'All tests are now passing properly',
1120 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001121 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001122 while not have_files_changed():
1123 time.sleep(1)
1124else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001125 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001126 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001127 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001128 xml_report=args.xml_report,
1129 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001130 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001131 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1132 else:
1133 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001134 exit_code = 0
1135 if BuildAndRunError.BUILD in errors:
1136 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001137 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001138 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001139 if BuildAndRunError.POST_TEST in errors:
1140 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001141 sys.exit(exit_code)
Craig Tillera0f85172016-01-20 15:56:06 -08001142