blob: 637aff8585b01ae83052e29415d9331f9a4fc61c [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,
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700100 timeout_seconds=self.timeout_multiplier * timeout_seconds,
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
murgatroid99256d3df2015-09-21 16:58:02 -0700181 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700182 if self.platform == 'windows':
183 return [['tools\\run_tests\\pre_build_c.bat']]
184 else:
185 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700186
Craig Tillerc7449162015-01-16 14:42:10 -0800187 def build_steps(self):
188 return []
189
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200190 def post_tests_steps(self):
191 if self.platform == 'windows':
192 return []
193 else:
194 return [['tools/run_tests/post_tests_c.sh']]
195
murgatroid99a3e244f2015-09-22 11:25:53 -0700196 def makefile_name(self):
197 return 'Makefile'
198
murgatroid99132ce6a2015-03-04 17:29:14 -0800199 def supports_multi_config(self):
200 return True
201
202 def __str__(self):
203 return self.make_target
204
Craig Tillercc0535d2015-12-08 15:14:47 -0800205
murgatroid992c8d5162015-01-26 10:41:21 -0800206class NodeLanguage(object):
207
Craig Tiller883064c2015-11-04 10:06:10 -0800208 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700209 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700210 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800211
murgatroid99256d3df2015-09-21 16:58:02 -0700212 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700213 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700214 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700215
Craig Tiller883064c2015-11-04 10:06:10 -0800216 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700217 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800218
219 def build_steps(self):
220 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800221
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200222 def post_tests_steps(self):
223 return []
224
murgatroid99a3e244f2015-09-22 11:25:53 -0700225 def makefile_name(self):
226 return 'Makefile'
227
murgatroid99132ce6a2015-03-04 17:29:14 -0800228 def supports_multi_config(self):
229 return False
230
231 def __str__(self):
232 return 'node'
233
Craig Tiller99775822015-01-30 13:07:16 -0800234
Craig Tillerc7449162015-01-16 14:42:10 -0800235class PhpLanguage(object):
236
Craig Tiller883064c2015-11-04 10:06:10 -0800237 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700238 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700239 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800240
murgatroid99256d3df2015-09-21 16:58:02 -0700241 def pre_build_steps(self):
242 return []
243
Craig Tiller883064c2015-11-04 10:06:10 -0800244 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700245 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800246
247 def build_steps(self):
248 return [['tools/run_tests/build_php.sh']]
249
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200250 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800251 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200252
murgatroid99a3e244f2015-09-22 11:25:53 -0700253 def makefile_name(self):
254 return 'Makefile'
255
murgatroid99132ce6a2015-03-04 17:29:14 -0800256 def supports_multi_config(self):
257 return False
258
259 def __str__(self):
260 return 'php'
261
Craig Tillerc7449162015-01-16 14:42:10 -0800262
Nathaniel Manista840615e2015-01-22 20:31:47 +0000263class PythonLanguage(object):
264
Craig Tiller49f61322015-03-03 13:02:11 -0800265 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700266 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700267 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800268
Craig Tiller883064c2015-11-04 10:06:10 -0800269 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700270 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
271 environment['PYVER'] = '2.7'
272 return [config.job_spec(
273 ['tools/run_tests/run_python.sh'],
274 None,
275 environ=environment,
276 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700277 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700278 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000279
murgatroid99256d3df2015-09-21 16:58:02 -0700280 def pre_build_steps(self):
281 return []
282
Craig Tiller883064c2015-11-04 10:06:10 -0800283 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700284 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000285
286 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700287 commands = []
288 for python_version in self._build_python_versions:
289 try:
290 with open(os.devnull, 'w') as output:
291 subprocess.check_call(['which', 'python' + python_version],
292 stdout=output, stderr=output)
293 commands.append(['tools/run_tests/build_python.sh', python_version])
294 self._has_python_versions.append(python_version)
295 except:
296 jobset.message('WARNING', 'Missing Python ' + python_version,
297 do_newline=True)
298 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000299
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200300 def post_tests_steps(self):
301 return []
302
murgatroid99a3e244f2015-09-22 11:25:53 -0700303 def makefile_name(self):
304 return 'Makefile'
305
murgatroid99132ce6a2015-03-04 17:29:14 -0800306 def supports_multi_config(self):
307 return False
308
309 def __str__(self):
310 return 'python'
311
Craig Tillerd625d812015-04-08 15:52:35 -0700312
murgatroid996a4c4fa2015-02-27 12:08:57 -0800313class RubyLanguage(object):
314
Craig Tiller883064c2015-11-04 10:06:10 -0800315 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700316 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700317 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800318
murgatroid99256d3df2015-09-21 16:58:02 -0700319 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200320 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700321
Craig Tiller883064c2015-11-04 10:06:10 -0800322 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700323 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800324
325 def build_steps(self):
326 return [['tools/run_tests/build_ruby.sh']]
327
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200328 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100329 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200330
murgatroid99a3e244f2015-09-22 11:25:53 -0700331 def makefile_name(self):
332 return 'Makefile'
333
murgatroid99132ce6a2015-03-04 17:29:14 -0800334 def supports_multi_config(self):
335 return False
336
337 def __str__(self):
338 return 'ruby'
339
Craig Tillerd625d812015-04-08 15:52:35 -0700340
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800341class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700342 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700343 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700344
Craig Tiller883064c2015-11-04 10:06:10 -0800345 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800346 with open('src/csharp/tests.json') as f:
347 tests_json = json.load(f)
348 assemblies = tests_json['assemblies']
349 tests = tests_json['tests']
350
351 msbuild_config = _WINDOWS_CONFIG[config.build_config]
352 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
353 for a in assemblies]
354
355 extra_args = ['-labels'] + assembly_files
356
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700357 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800358 script_name = 'tools\\run_tests\\run_csharp.bat'
359 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700360 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800361 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700362
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700363 if config.build_config == 'gcov':
364 # On Windows, we only collect C# code coverage.
365 # On Linux, we only collect coverage for native extension.
366 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800367 return [config.job_spec([script_name] + extra_args, None,
368 shortname='csharp.coverage',
369 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700370 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800371 specs = []
372 for test in tests:
373 cmdline = [script_name, '-run=%s' % test] + extra_args
374 if self.platform == 'windows':
375 # use different output directory for each test to prevent
376 # TestResult.xml clash between parallel test runs.
377 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
378 specs.append(config.job_spec(cmdline, None,
379 shortname='csharp.%s' % test,
380 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
381 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800382
murgatroid99256d3df2015-09-21 16:58:02 -0700383 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700384 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700385 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700386 else:
387 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700388
Craig Tiller883064c2015-11-04 10:06:10 -0800389 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700390 # For Windows, this target doesn't really build anything,
391 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700392 if self.platform == 'windows':
393 return []
394 else:
395 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800396
397 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700398 if self.platform == 'windows':
399 return [['src\\csharp\\buildall.bat']]
400 else:
401 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000402
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200403 def post_tests_steps(self):
404 return []
405
murgatroid99a3e244f2015-09-22 11:25:53 -0700406 def makefile_name(self):
407 return 'Makefile'
408
murgatroid99132ce6a2015-03-04 17:29:14 -0800409 def supports_multi_config(self):
410 return False
411
412 def __str__(self):
413 return 'csharp'
414
Craig Tillerd625d812015-04-08 15:52:35 -0700415
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700416class ObjCLanguage(object):
417
Craig Tiller883064c2015-11-04 10:06:10 -0800418 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700419 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
420 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
421
murgatroid99256d3df2015-09-21 16:58:02 -0700422 def pre_build_steps(self):
423 return []
424
Craig Tiller883064c2015-11-04 10:06:10 -0800425 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700426 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700427
428 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700429 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700430
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200431 def post_tests_steps(self):
432 return []
433
murgatroid99a3e244f2015-09-22 11:25:53 -0700434 def makefile_name(self):
435 return 'Makefile'
436
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700437 def supports_multi_config(self):
438 return False
439
440 def __str__(self):
441 return 'objc'
442
443
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100444class Sanity(object):
445
Craig Tiller883064c2015-11-04 10:06:10 -0800446 def test_specs(self, config, args):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800447 return [config.job_spec(['tools/run_tests/run_sanity.sh'], None, timeout_seconds=15*60),
Jan Tattermusche3d66252015-10-26 11:33:45 -0700448 config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100449
murgatroid99256d3df2015-09-21 16:58:02 -0700450 def pre_build_steps(self):
451 return []
452
Craig Tiller883064c2015-11-04 10:06:10 -0800453 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100454 return ['run_dep_checks']
455
456 def build_steps(self):
457 return []
458
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200459 def post_tests_steps(self):
460 return []
461
murgatroid99a3e244f2015-09-22 11:25:53 -0700462 def makefile_name(self):
463 return 'Makefile'
464
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100465 def supports_multi_config(self):
466 return False
467
468 def __str__(self):
469 return 'sanity'
470
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200471
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100472class Build(object):
473
Craig Tiller883064c2015-11-04 10:06:10 -0800474 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100475 return []
476
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" Noblec23827b2015-04-23 06:17:55 +0200481 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100482
483 def build_steps(self):
484 return []
485
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100486 def post_tests_steps(self):
487 return []
488
murgatroid99a3e244f2015-09-22 11:25:53 -0700489 def makefile_name(self):
490 return 'Makefile'
491
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100492 def supports_multi_config(self):
493 return True
494
495 def __str__(self):
496 return self.make_target
497
498
Craig Tiller738c3342015-01-12 14:28:33 -0800499# different configurations we can run under
500_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800501 'dbg': SimpleConfig('dbg'),
502 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700503 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700504 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700505 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800506 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700507 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800508 'ASAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:detect_leaks=1:color=always',
509 'LSAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700510 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800511 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800512 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800513 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800514 'helgrind': ValgrindConfig('dbg', 'helgrind')
515 }
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."""
541 if arch == 'default' or arch == 'windows_x86':
542 return '/p:Platform=Win32'
543 elif arch == 'windows_x64':
544 return '/p:Platform=x64'
545 else:
546 print 'Architecture %s not supported on current platform.' % arch
547 sys.exit(1)
548
549
550def _windows_build_bat(compiler):
551 """Returns name of build.bat for selected compiler."""
552 if compiler == 'default' or compiler == 'vs2013':
553 return 'vsprojects\\build_vs2013.bat'
554 elif compiler == 'vs2015':
555 return 'vsprojects\\build_vs2015.bat'
556 elif compiler == 'vs2010':
557 return 'vsprojects\\build_vs2010.bat'
558 else:
559 print 'Compiler %s not supported.' % compiler
560 sys.exit(1)
561
562
563def _windows_toolset_option(compiler):
564 """Returns msbuild PlatformToolset for selected compiler."""
565 if compiler == 'default' or compiler == 'vs2013':
566 return '/p:PlatformToolset=v120'
567 elif compiler == 'vs2015':
568 return '/p:PlatformToolset=v140'
569 elif compiler == 'vs2010':
570 return '/p:PlatformToolset=v100'
571 else:
572 print 'Compiler %s not supported.' % compiler
573 sys.exit(1)
574
575
David Garcia Quintase90cd372015-05-31 18:15:26 -0700576def runs_per_test_type(arg_str):
577 """Auxilary function to parse the "runs_per_test" flag.
578
579 Returns:
580 A positive integer or 0, the latter indicating an infinite number of
581 runs.
582
583 Raises:
584 argparse.ArgumentTypeError: Upon invalid input.
585 """
586 if arg_str == 'inf':
587 return 0
588 try:
589 n = int(arg_str)
590 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700591 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700592 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700593 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700594 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700595
596# parse command line
597argp = argparse.ArgumentParser(description='Run grpc tests.')
598argp.add_argument('-c', '--config',
599 choices=['all'] + sorted(_CONFIGS.keys()),
600 nargs='+',
601 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700602argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
603 help='A positive integer or "inf". If "inf", all tests will run in an '
604 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800605argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800606argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800607argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800608argp.add_argument('-f', '--forever',
609 default=False,
610 action='store_const',
611 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100612argp.add_argument('-t', '--travis',
613 default=False,
614 action='store_const',
615 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800616argp.add_argument('--newline_on_success',
617 default=False,
618 action='store_const',
619 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800620argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700621 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800622 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700623 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700624argp.add_argument('-S', '--stop_on_failure',
625 default=False,
626 action='store_const',
627 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700628argp.add_argument('--use_docker',
629 default=False,
630 action='store_const',
631 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700632 help='Run all the tests under docker. That provides ' +
633 'additional isolation and prevents the need to install ' +
634 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700635argp.add_argument('--allow_flakes',
636 default=False,
637 action='store_const',
638 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700639 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800640argp.add_argument('--arch',
641 choices=['default', 'windows_x86', 'windows_x64'],
642 default='default',
643 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
644argp.add_argument('--compiler',
645 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
646 default='default',
647 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
648argp.add_argument('--build_only',
649 default=False,
650 action='store_const',
651 const=True,
652 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800653argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
654 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800655argp.add_argument('--update_submodules', default=[], nargs='*',
656 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
657 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700658argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200659argp.add_argument('-x', '--xml_report', default=None, type=str,
660 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800661args = argp.parse_args()
662
Craig Tiller5f735a62016-01-20 09:31:15 -0800663jobset.measure_cpu_costs = args.measure_cpu_costs
664
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700665if args.use_docker:
666 if not args.travis:
667 print 'Seen --use_docker flag, will run tests under docker.'
668 print
669 print 'IMPORTANT: The changes you are testing need to be locally committed'
670 print 'because only the committed changes in the current branch will be'
671 print 'copied to the docker environment.'
672 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700673
674 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700675 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700676
677 # TODO(jtattermusch): revisit if we need special handling for arch here
678 # set arch command prefix in case we are working with different arch.
679 arch_env = os.getenv('arch')
680 if arch_env:
681 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
682
683 env = os.environ.copy()
684 env['RUN_TESTS_COMMAND'] = run_tests_cmd
685 if args.xml_report:
686 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700687 if not args.travis:
688 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700689
690 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
691 shell=True,
692 env=env)
693 sys.exit(0)
694
Craig Tiller1676f912016-01-05 10:49:44 -0800695# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800696need_to_regenerate_projects = False
697for spec in args.update_submodules:
698 spec = spec.split(':', 1)
699 if len(spec) == 1:
700 submodule = spec[0]
701 branch = 'master'
702 elif len(spec) == 2:
703 submodule = spec[0]
704 branch = spec[1]
705 cwd = 'third_party/%s' % submodule
706 def git(cmd, cwd=cwd):
707 print 'in %s: git %s' % (cwd, cmd)
708 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
709 git('fetch')
710 git('checkout %s' % branch)
711 git('pull origin %s' % branch)
712 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
713 need_to_regenerate_projects = True
714if need_to_regenerate_projects:
715 if jobset.platform_string() == 'linux':
716 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
717 else:
718 print 'WARNING: may need to regenerate projects, but since we are not on'
719 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800720
721
Nicolas Nobleddef2462015-01-06 18:08:25 -0800722# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800723run_configs = set(_CONFIGS[cfg]
724 for cfg in itertools.chain.from_iterable(
725 _CONFIGS.iterkeys() if x == 'all' else [x]
726 for x in args.config))
727build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800728
Craig Tiller06805272015-06-11 14:46:47 -0700729if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700730 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700731
Adele Zhou6b9527c2015-11-20 15:56:35 -0800732if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800733 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800734else:
735 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800736# We don't support code coverage on some languages
737if 'gcov' in args.config:
738 for bad in ['objc', 'sanity', 'build']:
739 if bad in lang_list:
740 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800741
742languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800743
744if len(build_configs) > 1:
745 for language in languages:
746 if not language.supports_multi_config():
747 print language, 'does not support multiple build configurations'
748 sys.exit(1)
749
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800750if platform_string() != 'windows':
751 if args.arch != 'default':
752 print 'Architecture %s not supported on current platform.' % args.arch
753 sys.exit(1)
754 if args.compiler != 'default':
755 print 'Compiler %s not supported on current platform.' % args.compiler
756 sys.exit(1)
757
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100758if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700759 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700760 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700761 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700762 # empirically /m:2 gives the best performance/price and should prevent
763 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700764 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700765 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700766 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700767 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800768 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700769 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800770 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
771 _windows_toolset_option(args.compiler),
772 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700773 extra_args,
Craig Tiller590105a2016-01-19 13:03:46 -0800774 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700775 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700776else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700777 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700778 if targets:
779 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
780 '-f', makefile,
781 '-j', '%d' % (multiprocessing.cpu_count() + 1),
Craig Tiller71a86042016-01-15 14:59:58 -0800782 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
783 'CONFIG=%s' % cfg] +
784 ([] if not args.travis else ['JENKINS_BUILD=1']) +
785 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800786 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700787 else:
788 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700789make_targets = {}
790for l in languages:
791 makefile = l.makefile_name()
792 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800793 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700794
Jan Tattermusche4a69182015-12-15 09:53:01 -0800795def build_step_environ(cfg):
796 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800797 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800798 if msbuild_cfg:
799 environ['MSBUILD_CONFIG'] = msbuild_cfg
800 return environ
801
murgatroid99fddac962015-09-22 09:20:11 -0700802build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800803 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700804 for cfg in build_configs
805 for l in languages
806 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700807if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700808 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 -0700809 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700810build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800811 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800812 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800813 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700814 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800815
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200816post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800817 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200818 for cfg in build_configs
819 for l in languages
820 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800821runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800822forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800823
Nicolas Nobleddef2462015-01-06 18:08:25 -0800824
Craig Tiller71735182015-01-15 17:07:13 -0800825class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800826 """Cache for running tests."""
827
David Klempner25739582015-02-11 15:57:32 -0800828 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800829 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800830 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700831 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800832
833 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800834 if cmdline not in self._last_successful_run:
835 return True
836 if self._last_successful_run[cmdline] != bin_hash:
837 return True
David Klempner25739582015-02-11 15:57:32 -0800838 if not self._use_cache_results:
839 return True
Craig Tiller71735182015-01-15 17:07:13 -0800840 return False
841
842 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800843 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700844 if time.time() - self._last_save > 1:
845 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800846
847 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800848 return [{'cmdline': k, 'hash': v}
849 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800850
851 def parse(self, exdump):
852 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
853
854 def save(self):
855 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800856 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700857 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800858
Craig Tiller1cc11db2015-01-15 22:50:50 -0800859 def maybe_load(self):
860 if os.path.exists('.run_tests_cache'):
861 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800862 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800863
864
Craig Tillerf53d9c82015-08-04 14:19:43 -0700865def _start_port_server(port_server_port):
866 # check if a compatible port server is running
867 # if incompatible (version mismatch) ==> start a new one
868 # if not running ==> start a new one
869 # otherwise, leave it up
870 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700871 version = int(urllib2.urlopen(
872 'http://localhost:%d/version_number' % port_server_port,
873 timeout=1).read())
874 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700875 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700876 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700877 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700878 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700879 running = False
880 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700881 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800882 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
883 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700884 print 'my port server is version %d' % current_version
885 running = (version >= current_version)
886 if not running:
887 print 'port_server version mismatch: killing the old one'
888 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
889 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700890 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700891 fd, logfile = tempfile.mkstemp()
892 os.close(fd)
893 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800894 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
895 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700896 env = dict(os.environ)
897 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100898 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800899 # Working directory of port server needs to be outside of Jenkins
900 # workspace to prevent file lock issues.
901 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700902 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700903 args,
904 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800905 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700906 creationflags = 0x00000008, # detached process
907 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700908 else:
909 port_server = subprocess.Popen(
910 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700911 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700912 preexec_fn=os.setsid,
913 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700914 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700915 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700916 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700917 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700918 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700919 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700920 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700921 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700922 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700923 # try one final time: maybe another build managed to start one
924 time.sleep(1)
925 try:
926 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
927 timeout=1).read()
928 print 'last ditch attempt to contact port server succeeded'
929 break
930 except:
931 traceback.print_exc();
932 port_log = open(logfile, 'r').read()
933 print port_log
934 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700935 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700936 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
937 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700938 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700939 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700940 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700941 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700942 traceback.print_exc();
943 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700944 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700945 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700946 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700947 traceback.print_exc();
948 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700949 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700950 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700951 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700952 port_server.kill()
953 raise
954
955
Adele Zhoud5fffa52015-10-23 15:51:42 -0700956def _calculate_num_runs_failures(list_of_results):
957 """Caculate number of runs and failures for a particular test.
958
959 Args:
960 list_of_results: (List) of JobResult object.
961 Returns:
962 A tuple of total number of runs and failures.
963 """
964 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
965 num_failures = 0
966 for jobresult in list_of_results:
967 if jobresult.retries > 0:
968 num_runs += jobresult.retries
969 if jobresult.num_failures > 0:
970 num_failures += jobresult.num_failures
971 return num_runs, num_failures
972
Adele Zhou6b9527c2015-11-20 15:56:35 -0800973
Craig Tillereb9de8b2016-01-08 08:57:41 -0800974# _build_and_run results
975class BuildAndRunError(object):
976
977 BUILD = object()
978 TEST = object()
979 POST_TEST = object()
980
981
982# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700983def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800984 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800985 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800986 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700987 num_failures, _ = jobset.run(
988 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800989 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700990 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800991 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -0800992
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800993 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800994 return []
ctiller3040cb72015-01-07 12:13:17 -0800995
Craig Tiller234b6e72015-05-23 10:12:40 -0700996 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700997 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700998 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700999 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -07001000 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -08001001 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -08001002 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -07001003 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -07001004 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001005 one_run = set(
1006 spec
1007 for config in run_configs
1008 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001009 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001010 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001011 # When running on travis, we want out test runs to be as similar as possible
1012 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001013 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001014 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1015 else:
1016 # whereas otherwise, we want to shuffle things up to give all tests a
1017 # chance to run.
1018 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1019 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001020 if infinite_runs:
1021 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 -07001022 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1023 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001024 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001025
Adele Zhou803af152015-11-30 15:16:16 -08001026 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001027 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001028 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001029 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001030 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001031 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001032 if resultset:
1033 for k, v in resultset.iteritems():
1034 num_runs, num_failures = _calculate_num_runs_failures(v)
1035 if num_failures == num_runs: # what about infinite_runs???
1036 jobset.message('FAILED', k, do_newline=True)
1037 elif num_failures > 0:
1038 jobset.message(
1039 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1040 do_newline=True)
1041 else:
1042 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001043 finally:
1044 for antagonist in antagonists:
1045 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001046 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001047 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001048
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001049 number_failures, _ = jobset.run(
1050 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001051 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001052
1053 out = []
1054 if number_failures:
1055 out.append(BuildAndRunError.POST_TEST)
1056 if num_test_failures:
1057 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001058
Craig Tiller69cd2372015-06-11 09:38:09 -07001059 if cache: cache.save()
1060
Craig Tillereb9de8b2016-01-08 08:57:41 -08001061 return out
ctiller3040cb72015-01-07 12:13:17 -08001062
1063
David Klempner25739582015-02-11 15:57:32 -08001064test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001065test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001066
ctiller3040cb72015-01-07 12:13:17 -08001067if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001068 success = True
ctiller3040cb72015-01-07 12:13:17 -08001069 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001070 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001071 initial_time = dw.most_recent_change()
1072 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001073 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001074 errors = _build_and_run(check_cancelled=have_files_changed,
1075 newline_on_success=False,
1076 cache=test_cache,
1077 build_only=args.build_only) == 0
1078 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001079 jobset.message('SUCCESS',
1080 'All tests are now passing properly',
1081 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001082 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001083 while not have_files_changed():
1084 time.sleep(1)
1085else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001086 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001087 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001088 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001089 xml_report=args.xml_report,
1090 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001091 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001092 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1093 else:
1094 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001095 exit_code = 0
1096 if BuildAndRunError.BUILD in errors:
1097 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001098 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001099 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001100 if BuildAndRunError.POST_TEST in errors:
1101 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001102 sys.exit(exit_code)