blob: 2d12adebbb60903f4a991763705d35daa0b2ebf1 [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tillerc2c79212015-02-16 12:00:01 -08002# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
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
Nicolas Nobleddef2462015-01-06 18:08:25 -080050
51import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080052import report_utils
ctiller3040cb72015-01-07 12:13:17 -080053import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080054
Craig Tiller2cc2b842015-02-27 11:38:31 -080055ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
56os.chdir(ROOT)
57
58
Craig Tiller06805272015-06-11 14:46:47 -070059_FORCE_ENVIRON_FOR_WRAPPERS = {}
60
61
Craig Tillerd50993d2015-08-05 08:04:36 -070062def platform_string():
63 if platform.system() == 'Windows':
64 return 'windows'
65 elif platform.system() == 'Darwin':
66 return 'mac'
67 elif platform.system() == 'Linux':
68 return 'linux'
69 else:
70 return 'posix'
71
72
Craig Tiller738c3342015-01-12 14:28:33 -080073# SimpleConfig: just compile with CONFIG=config, and run the binary to test
74class SimpleConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080075
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070076 def __init__(self, config, environ=None, timeout_multiplier=1):
murgatroid99132ce6a2015-03-04 17:29:14 -080077 if environ is None:
78 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080079 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080080 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080081 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080082 self.environ['CONFIG'] = config
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070083 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080084
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070085 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
86 shortname=None, environ={}):
Craig Tiller49f61322015-03-03 13:02:11 -080087 """Construct a jobset.JobSpec for a test under this config
88
89 Args:
90 cmdline: a list of strings specifying the command line the test
91 would like to run
92 hash_targets: either None (don't do caching of test results), or
93 a list of strings specifying files to include in a
94 binary hash to check if a test has changed
95 -- if used, all artifacts needed to run the test must
96 be listed
97 """
Craig Tiller4fc90032015-05-21 10:39:52 -070098 actual_environ = self.environ.copy()
99 for k, v in environ.iteritems():
100 actual_environ[k] = v
Craig Tiller49f61322015-03-03 13:02:11 -0800101 return jobset.JobSpec(cmdline=cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -0700102 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -0700103 environ=actual_environ,
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700104 timeout_seconds=self.timeout_multiplier * timeout_seconds,
Craig Tiller547db2b2015-01-30 14:08:39 -0800105 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700106 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700107 flake_retries=5 if args.allow_flakes else 0,
108 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800109
110
111# ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
112class ValgrindConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800113
murgatroid99132ce6a2015-03-04 17:29:14 -0800114 def __init__(self, config, tool, args=None):
115 if args is None:
116 args = []
Craig Tiller738c3342015-01-12 14:28:33 -0800117 self.build_config = config
Craig Tiller2aa4d642015-01-14 15:59:44 -0800118 self.tool = tool
Craig Tiller1a305b12015-02-18 13:37:06 -0800119 self.args = args
Craig Tillerc7449162015-01-16 14:42:10 -0800120 self.allow_hashing = False
Craig Tiller738c3342015-01-12 14:28:33 -0800121
Craig Tiller49f61322015-03-03 13:02:11 -0800122 def job_spec(self, cmdline, hash_targets):
Craig Tiller1a305b12015-02-18 13:37:06 -0800123 return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
Craig Tiller49f61322015-03-03 13:02:11 -0800124 self.args + cmdline,
Craig Tiller71ec6cb2015-06-03 00:51:11 -0700125 shortname='valgrind %s' % cmdline[0],
Craig Tillerd4509a12015-09-28 09:18:40 -0700126 hash_targets=None,
Craig Tiller95cc07b2015-09-28 13:41:30 -0700127 flake_retries=5 if args.allow_flakes else 0,
Craig Tiller35505de2015-10-08 13:31:33 -0700128 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800129
130
murgatroid99cf08daf2015-09-21 15:33:16 -0700131def get_c_tests(travis, test_lang) :
132 out = []
133 platforms_str = 'ci_platforms' if travis else 'platforms'
134 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700135 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700136 return [tgt
137 for tgt in js
138 if tgt['language'] == test_lang and
139 platform_string() in tgt[platforms_str] and
140 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700141
murgatroid99fafeeb32015-09-22 09:13:03 -0700142
Craig Tillerc7449162015-01-16 14:42:10 -0800143class CLanguage(object):
144
Craig Tillere9c959d2015-01-18 10:23:26 -0800145 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800146 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700147 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700148 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800149
Craig Tiller883064c2015-11-04 10:06:10 -0800150 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800151 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800152 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700153 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700154 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700155 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700156 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700157 binary = 'vsprojects/%s/%s.exe' % (
158 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700159 else:
160 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700161 if os.path.isfile(binary):
162 out.append(config.job_spec([binary], [binary]))
Craig Tiller883064c2015-11-04 10:06:10 -0800163 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700164 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700165 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800166
Craig Tiller883064c2015-11-04 10:06:10 -0800167 def make_targets(self, test_regex):
168 if platform_string() != 'windows' and test_regex != '.*':
169 # use the regex to minimize the number of things to build
170 return [target['name']
171 for target in get_c_tests(False, self.test_lang)
172 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700173 if platform_string() == 'windows':
174 # don't build tools on windows just yet
175 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700176 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800177
murgatroid99256d3df2015-09-21 16:58:02 -0700178 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700179 if self.platform == 'windows':
180 return [['tools\\run_tests\\pre_build_c.bat']]
181 else:
182 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700183
Craig Tillerc7449162015-01-16 14:42:10 -0800184 def build_steps(self):
185 return []
186
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200187 def post_tests_steps(self):
188 if self.platform == 'windows':
189 return []
190 else:
191 return [['tools/run_tests/post_tests_c.sh']]
192
murgatroid99a3e244f2015-09-22 11:25:53 -0700193 def makefile_name(self):
194 return 'Makefile'
195
murgatroid99132ce6a2015-03-04 17:29:14 -0800196 def supports_multi_config(self):
197 return True
198
199 def __str__(self):
200 return self.make_target
201
murgatroid992c8d5162015-01-26 10:41:21 -0800202class NodeLanguage(object):
203
Craig Tiller883064c2015-11-04 10:06:10 -0800204 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700205 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700206 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800207
murgatroid99256d3df2015-09-21 16:58:02 -0700208 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700209 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700210 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700211
Craig Tiller883064c2015-11-04 10:06:10 -0800212 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700213 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800214
215 def build_steps(self):
216 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800217
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200218 def post_tests_steps(self):
219 return []
220
murgatroid99a3e244f2015-09-22 11:25:53 -0700221 def makefile_name(self):
222 return 'Makefile'
223
murgatroid99132ce6a2015-03-04 17:29:14 -0800224 def supports_multi_config(self):
225 return False
226
227 def __str__(self):
228 return 'node'
229
Craig Tiller99775822015-01-30 13:07:16 -0800230
Craig Tillerc7449162015-01-16 14:42:10 -0800231class PhpLanguage(object):
232
Craig Tiller883064c2015-11-04 10:06:10 -0800233 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700234 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700235 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800236
murgatroid99256d3df2015-09-21 16:58:02 -0700237 def pre_build_steps(self):
238 return []
239
Craig Tiller883064c2015-11-04 10:06:10 -0800240 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700241 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800242
243 def build_steps(self):
244 return [['tools/run_tests/build_php.sh']]
245
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200246 def post_tests_steps(self):
247 return []
248
murgatroid99a3e244f2015-09-22 11:25:53 -0700249 def makefile_name(self):
250 return 'Makefile'
251
murgatroid99132ce6a2015-03-04 17:29:14 -0800252 def supports_multi_config(self):
253 return False
254
255 def __str__(self):
256 return 'php'
257
Craig Tillerc7449162015-01-16 14:42:10 -0800258
Nathaniel Manista840615e2015-01-22 20:31:47 +0000259class PythonLanguage(object):
260
Craig Tiller49f61322015-03-03 13:02:11 -0800261 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700262 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700263 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800264
Craig Tiller883064c2015-11-04 10:06:10 -0800265 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700266 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
267 environment['PYVER'] = '2.7'
268 return [config.job_spec(
269 ['tools/run_tests/run_python.sh'],
270 None,
271 environ=environment,
272 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700273 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700274 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000275
murgatroid99256d3df2015-09-21 16:58:02 -0700276 def pre_build_steps(self):
277 return []
278
Craig Tiller883064c2015-11-04 10:06:10 -0800279 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700280 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000281
282 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700283 commands = []
284 for python_version in self._build_python_versions:
285 try:
286 with open(os.devnull, 'w') as output:
287 subprocess.check_call(['which', 'python' + python_version],
288 stdout=output, stderr=output)
289 commands.append(['tools/run_tests/build_python.sh', python_version])
290 self._has_python_versions.append(python_version)
291 except:
292 jobset.message('WARNING', 'Missing Python ' + python_version,
293 do_newline=True)
294 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000295
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200296 def post_tests_steps(self):
297 return []
298
murgatroid99a3e244f2015-09-22 11:25:53 -0700299 def makefile_name(self):
300 return 'Makefile'
301
murgatroid99132ce6a2015-03-04 17:29:14 -0800302 def supports_multi_config(self):
303 return False
304
305 def __str__(self):
306 return 'python'
307
Craig Tillerd625d812015-04-08 15:52:35 -0700308
murgatroid996a4c4fa2015-02-27 12:08:57 -0800309class RubyLanguage(object):
310
Craig Tiller883064c2015-11-04 10:06:10 -0800311 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700312 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700313 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800314
murgatroid99256d3df2015-09-21 16:58:02 -0700315 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200316 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700317
Craig Tiller883064c2015-11-04 10:06:10 -0800318 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700319 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800320
321 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):
325 return []
326
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 Tattermusch9a7d30c2015-04-23 16:12:55 -0700342 assemblies = ['Grpc.Core.Tests',
343 'Grpc.Examples.Tests',
Jan Tattermusch9d67d8d2015-08-01 20:39:16 -0700344 'Grpc.HealthCheck.Tests',
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -0700345 'Grpc.IntegrationTesting']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700346 if self.platform == 'windows':
347 cmd = 'tools\\run_tests\\run_csharp.bat'
348 else:
349 cmd = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700350
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700351 if config.build_config == 'gcov':
352 # On Windows, we only collect C# code coverage.
353 # On Linux, we only collect coverage for native extension.
354 # For code coverage all tests need to run as one suite.
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700355 return [config.job_spec([cmd], None,
356 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
357 else:
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700358 return [config.job_spec([cmd, assembly],
359 None, shortname=assembly,
360 environ=_FORCE_ENVIRON_FOR_WRAPPERS)
361 for assembly in assemblies]
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800362
murgatroid99256d3df2015-09-21 16:58:02 -0700363 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700364 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700365 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700366 else:
367 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700368
Craig Tiller883064c2015-11-04 10:06:10 -0800369 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700370 # For Windows, this target doesn't really build anything,
371 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700372 if self.platform == 'windows':
373 return []
374 else:
375 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800376
377 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700378 if self.platform == 'windows':
379 return [['src\\csharp\\buildall.bat']]
380 else:
381 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000382
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200383 def post_tests_steps(self):
384 return []
385
murgatroid99a3e244f2015-09-22 11:25:53 -0700386 def makefile_name(self):
387 return 'Makefile'
388
murgatroid99132ce6a2015-03-04 17:29:14 -0800389 def supports_multi_config(self):
390 return False
391
392 def __str__(self):
393 return 'csharp'
394
Craig Tillerd625d812015-04-08 15:52:35 -0700395
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700396class ObjCLanguage(object):
397
Craig Tiller883064c2015-11-04 10:06:10 -0800398 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700399 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
400 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
401
murgatroid99256d3df2015-09-21 16:58:02 -0700402 def pre_build_steps(self):
403 return []
404
Craig Tiller883064c2015-11-04 10:06:10 -0800405 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700406 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700407
408 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700409 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700410
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200411 def post_tests_steps(self):
412 return []
413
murgatroid99a3e244f2015-09-22 11:25:53 -0700414 def makefile_name(self):
415 return 'Makefile'
416
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700417 def supports_multi_config(self):
418 return False
419
420 def __str__(self):
421 return 'objc'
422
423
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100424class Sanity(object):
425
Craig Tiller883064c2015-11-04 10:06:10 -0800426 def test_specs(self, config, args):
Jan Tattermusche3d66252015-10-26 11:33:45 -0700427 return [config.job_spec(['tools/run_tests/run_sanity.sh'], None),
428 config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100429
murgatroid99256d3df2015-09-21 16:58:02 -0700430 def pre_build_steps(self):
431 return []
432
Craig Tiller883064c2015-11-04 10:06:10 -0800433 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100434 return ['run_dep_checks']
435
436 def build_steps(self):
437 return []
438
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200439 def post_tests_steps(self):
440 return []
441
murgatroid99a3e244f2015-09-22 11:25:53 -0700442 def makefile_name(self):
443 return 'Makefile'
444
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100445 def supports_multi_config(self):
446 return False
447
448 def __str__(self):
449 return 'sanity'
450
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200451
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100452class Build(object):
453
Craig Tiller883064c2015-11-04 10:06:10 -0800454 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100455 return []
456
murgatroid99256d3df2015-09-21 16:58:02 -0700457 def pre_build_steps(self):
458 return []
459
Craig Tiller883064c2015-11-04 10:06:10 -0800460 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200461 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100462
463 def build_steps(self):
464 return []
465
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100466 def post_tests_steps(self):
467 return []
468
murgatroid99a3e244f2015-09-22 11:25:53 -0700469 def makefile_name(self):
470 return 'Makefile'
471
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100472 def supports_multi_config(self):
473 return True
474
475 def __str__(self):
476 return self.make_target
477
478
Craig Tiller738c3342015-01-12 14:28:33 -0800479# different configurations we can run under
480_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800481 'dbg': SimpleConfig('dbg'),
482 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700483 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700484 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700485 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800486 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700487 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800488 'ASAN_OPTIONS': 'detect_leaks=1:color=always',
Craig Tillerd4b13622015-05-29 09:10:10 -0700489 'LSAN_OPTIONS': 'report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700490 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800491 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800492 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800493 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800494 'helgrind': ValgrindConfig('dbg', 'helgrind')
495 }
Craig Tiller738c3342015-01-12 14:28:33 -0800496
497
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100498_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800499_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800500 'c++': CLanguage('cxx', 'c++'),
501 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800502 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000503 'php': PhpLanguage(),
504 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800505 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100506 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700507 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100508 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100509 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800510 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800511
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700512_WINDOWS_CONFIG = {
513 'dbg': 'Debug',
514 'opt': 'Release',
515 }
516
David Garcia Quintase90cd372015-05-31 18:15:26 -0700517
518def runs_per_test_type(arg_str):
519 """Auxilary function to parse the "runs_per_test" flag.
520
521 Returns:
522 A positive integer or 0, the latter indicating an infinite number of
523 runs.
524
525 Raises:
526 argparse.ArgumentTypeError: Upon invalid input.
527 """
528 if arg_str == 'inf':
529 return 0
530 try:
531 n = int(arg_str)
532 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700533 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700534 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700535 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700536 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700537
538# parse command line
539argp = argparse.ArgumentParser(description='Run grpc tests.')
540argp.add_argument('-c', '--config',
541 choices=['all'] + sorted(_CONFIGS.keys()),
542 nargs='+',
543 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700544argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
545 help='A positive integer or "inf". If "inf", all tests will run in an '
546 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800547argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller83762ac2015-05-22 14:04:06 -0700548argp.add_argument('-j', '--jobs', default=2 * multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800549argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800550argp.add_argument('-f', '--forever',
551 default=False,
552 action='store_const',
553 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100554argp.add_argument('-t', '--travis',
555 default=False,
556 action='store_const',
557 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800558argp.add_argument('--newline_on_success',
559 default=False,
560 action='store_const',
561 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800562argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700563 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800564 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700565 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700566argp.add_argument('-S', '--stop_on_failure',
567 default=False,
568 action='store_const',
569 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700570argp.add_argument('--use_docker',
571 default=False,
572 action='store_const',
573 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700574 help='Run all the tests under docker. That provides ' +
575 'additional isolation and prevents the need to install ' +
576 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700577argp.add_argument('--allow_flakes',
578 default=False,
579 action='store_const',
580 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700581 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Craig Tiller234b6e72015-05-23 10:12:40 -0700582argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200583argp.add_argument('-x', '--xml_report', default=None, type=str,
584 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800585args = argp.parse_args()
586
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700587if args.use_docker:
588 if not args.travis:
589 print 'Seen --use_docker flag, will run tests under docker.'
590 print
591 print 'IMPORTANT: The changes you are testing need to be locally committed'
592 print 'because only the committed changes in the current branch will be'
593 print 'copied to the docker environment.'
594 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700595
596 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700597 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700598
599 # TODO(jtattermusch): revisit if we need special handling for arch here
600 # set arch command prefix in case we are working with different arch.
601 arch_env = os.getenv('arch')
602 if arch_env:
603 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
604
605 env = os.environ.copy()
606 env['RUN_TESTS_COMMAND'] = run_tests_cmd
607 if args.xml_report:
608 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700609 if not args.travis:
610 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700611
612 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
613 shell=True,
614 env=env)
615 sys.exit(0)
616
Nicolas Nobleddef2462015-01-06 18:08:25 -0800617# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800618run_configs = set(_CONFIGS[cfg]
619 for cfg in itertools.chain.from_iterable(
620 _CONFIGS.iterkeys() if x == 'all' else [x]
621 for x in args.config))
622build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800623
Craig Tiller06805272015-06-11 14:46:47 -0700624if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700625 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700626
Adele Zhou6b9527c2015-11-20 15:56:35 -0800627if 'all' in args.language:
628 lang_list = _LANGUAGES.keys()
629else:
630 lang_list = args.language
631# We don't support code coverage on ObjC
632if 'gcov' in args.config and 'objc' in lang_list:
633 lang_list.remove('objc')
634
635languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800636
637if len(build_configs) > 1:
638 for language in languages:
639 if not language.supports_multi_config():
640 print language, 'does not support multiple build configurations'
641 sys.exit(1)
642
Craig Tiller5058c692015-04-08 09:42:04 -0700643if platform.system() == 'Windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700644 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700645 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700646 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700647 # empirically /m:2 gives the best performance/price and should prevent
648 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700649 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700650 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700651 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700652 return [
murgatroid99cf08daf2015-09-21 15:33:16 -0700653 jobset.JobSpec(['vsprojects\\build.bat',
654 'vsprojects\\%s.sln' % target,
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700655 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg]] +
656 extra_args,
Craig Tillerdfc3eee2015-09-01 16:32:16 -0700657 shell=True, timeout_seconds=90*60)
Craig Tiller6fd23842015-09-01 07:36:31 -0700658 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700659else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700660 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700661 if targets:
662 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
663 '-f', makefile,
664 '-j', '%d' % (multiprocessing.cpu_count() + 1),
665 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
666 args.slowdown,
667 'CONFIG=%s' % cfg] + targets,
668 timeout_seconds=30*60)]
669 else:
670 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700671make_targets = {}
672for l in languages:
673 makefile = l.makefile_name()
674 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800675 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700676
murgatroid99fddac962015-09-22 09:20:11 -0700677build_steps = list(set(
Nicolas "Pixel" Noble351da8d2015-10-08 02:55:08 +0200678 jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700679 for cfg in build_configs
680 for l in languages
681 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700682if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700683 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 -0700684 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700685build_steps.extend(set(
Jan Tattermusch21897192015-10-07 15:42:21 -0700686 jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, timeout_seconds=10*60)
murgatroid99132ce6a2015-03-04 17:29:14 -0800687 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800688 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700689 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800690
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200691post_tests_steps = list(set(
692 jobset.JobSpec(cmdline, environ={'CONFIG': cfg})
693 for cfg in build_configs
694 for l in languages
695 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800696runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800697forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800698
Nicolas Nobleddef2462015-01-06 18:08:25 -0800699
Craig Tiller71735182015-01-15 17:07:13 -0800700class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800701 """Cache for running tests."""
702
David Klempner25739582015-02-11 15:57:32 -0800703 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800704 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800705 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700706 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800707
708 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800709 if cmdline not in self._last_successful_run:
710 return True
711 if self._last_successful_run[cmdline] != bin_hash:
712 return True
David Klempner25739582015-02-11 15:57:32 -0800713 if not self._use_cache_results:
714 return True
Craig Tiller71735182015-01-15 17:07:13 -0800715 return False
716
717 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800718 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700719 if time.time() - self._last_save > 1:
720 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800721
722 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800723 return [{'cmdline': k, 'hash': v}
724 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800725
726 def parse(self, exdump):
727 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
728
729 def save(self):
730 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800731 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700732 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800733
Craig Tiller1cc11db2015-01-15 22:50:50 -0800734 def maybe_load(self):
735 if os.path.exists('.run_tests_cache'):
736 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800737 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800738
739
Craig Tillerf53d9c82015-08-04 14:19:43 -0700740def _start_port_server(port_server_port):
741 # check if a compatible port server is running
742 # if incompatible (version mismatch) ==> start a new one
743 # if not running ==> start a new one
744 # otherwise, leave it up
745 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700746 version = int(urllib2.urlopen(
747 'http://localhost:%d/version_number' % port_server_port,
748 timeout=1).read())
749 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700750 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700751 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700752 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700753 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700754 running = False
755 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700756 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800757 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
758 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700759 print 'my port server is version %d' % current_version
760 running = (version >= current_version)
761 if not running:
762 print 'port_server version mismatch: killing the old one'
763 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
764 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700765 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700766 fd, logfile = tempfile.mkstemp()
767 os.close(fd)
768 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800769 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
770 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700771 env = dict(os.environ)
772 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Craig Tillerd2c39712015-10-12 11:08:49 -0700773 if platform.system() == 'Windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800774 # Working directory of port server needs to be outside of Jenkins
775 # workspace to prevent file lock issues.
776 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700777 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700778 args,
779 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800780 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700781 creationflags = 0x00000008, # detached process
782 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700783 else:
784 port_server = subprocess.Popen(
785 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700786 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700787 preexec_fn=os.setsid,
788 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700789 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700790 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700791 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700792 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700793 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700794 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700795 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700796 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700797 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700798 # try one final time: maybe another build managed to start one
799 time.sleep(1)
800 try:
801 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
802 timeout=1).read()
803 print 'last ditch attempt to contact port server succeeded'
804 break
805 except:
806 traceback.print_exc();
807 port_log = open(logfile, 'r').read()
808 print port_log
809 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700810 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700811 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
812 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700813 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700814 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700815 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700816 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700817 traceback.print_exc();
818 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700819 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700820 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700821 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700822 traceback.print_exc();
823 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700824 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700825 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700826 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700827 port_server.kill()
828 raise
829
830
Adele Zhoud5fffa52015-10-23 15:51:42 -0700831def _calculate_num_runs_failures(list_of_results):
832 """Caculate number of runs and failures for a particular test.
833
834 Args:
835 list_of_results: (List) of JobResult object.
836 Returns:
837 A tuple of total number of runs and failures.
838 """
839 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
840 num_failures = 0
841 for jobresult in list_of_results:
842 if jobresult.retries > 0:
843 num_runs += jobresult.retries
844 if jobresult.num_failures > 0:
845 num_failures += jobresult.num_failures
846 return num_runs, num_failures
847
Adele Zhou6b9527c2015-11-20 15:56:35 -0800848
Craig Tillerf53d9c82015-08-04 14:19:43 -0700849def _build_and_run(
Craig Tiller883064c2015-11-04 10:06:10 -0800850 check_cancelled, newline_on_success, cache, xml_report=None):
ctiller3040cb72015-01-07 12:13:17 -0800851 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800852 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700853 num_failures, _ = jobset.run(
854 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800855 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700856 if num_failures:
Craig Tillerd86a3942015-01-14 12:48:54 -0800857 return 1
ctiller3040cb72015-01-07 12:13:17 -0800858
Craig Tiller234b6e72015-05-23 10:12:40 -0700859 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700860 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700861 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700862 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700863 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800864 resultset = None
Craig Tiller234b6e72015-05-23 10:12:40 -0700865 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700866 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700867 one_run = set(
868 spec
869 for config in run_configs
870 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800871 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700872 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700873 # When running on travis, we want out test runs to be as similar as possible
874 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800875 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700876 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
877 else:
878 # whereas otherwise, we want to shuffle things up to give all tests a
879 # chance to run.
880 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
881 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -0700882 if infinite_runs:
883 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 -0700884 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
885 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -0700886 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200887
Adele Zhoud5fffa52015-10-23 15:51:42 -0700888 number_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -0700889 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -0800890 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -0700891 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700892 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700893 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -0700894 if resultset:
895 for k, v in resultset.iteritems():
896 num_runs, num_failures = _calculate_num_runs_failures(v)
897 if num_failures == num_runs: # what about infinite_runs???
898 jobset.message('FAILED', k, do_newline=True)
899 elif num_failures > 0:
900 jobset.message(
901 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
902 do_newline=True)
903 else:
904 jobset.message('PASSED', k, do_newline=True)
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700905 if number_failures:
Craig Tiller234b6e72015-05-23 10:12:40 -0700906 return 2
907 finally:
908 for antagonist in antagonists:
909 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -0800910 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -0800911 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -0800912
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700913 number_failures, _ = jobset.run(
914 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800915 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700916 if number_failures:
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200917 return 3
918
Craig Tiller69cd2372015-06-11 09:38:09 -0700919 if cache: cache.save()
920
Craig Tillerd86a3942015-01-14 12:48:54 -0800921 return 0
ctiller3040cb72015-01-07 12:13:17 -0800922
923
David Klempner25739582015-02-11 15:57:32 -0800924test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -0800925test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -0800926
ctiller3040cb72015-01-07 12:13:17 -0800927if forever:
Nicolas Noble044db742015-01-14 16:57:24 -0800928 success = True
ctiller3040cb72015-01-07 12:13:17 -0800929 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -0800930 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -0800931 initial_time = dw.most_recent_change()
932 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -0800933 previous_success = success
Craig Tiller71735182015-01-15 17:07:13 -0800934 success = _build_and_run(check_cancelled=have_files_changed,
Nicolas Nobleb09078f2015-01-14 18:06:05 -0800935 newline_on_success=False,
Craig Tiller71735182015-01-15 17:07:13 -0800936 cache=test_cache) == 0
Nicolas Noble044db742015-01-14 16:57:24 -0800937 if not previous_success and success:
Nicolas Nobleb09078f2015-01-14 18:06:05 -0800938 jobset.message('SUCCESS',
939 'All tests are now passing properly',
940 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800941 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -0800942 while not have_files_changed():
943 time.sleep(1)
944else:
Craig Tiller71735182015-01-15 17:07:13 -0800945 result = _build_and_run(check_cancelled=lambda: False,
946 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200947 cache=test_cache,
948 xml_report=args.xml_report)
Nicolas Nobleb09078f2015-01-14 18:06:05 -0800949 if result == 0:
950 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
951 else:
952 jobset.message('FAILED', 'Some tests failed', do_newline=True)
953 sys.exit(result)