blob: 26a3fff8831fb65e2fa5361cb116aed42249fdd6 [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,
81 shortname=None, environ={}):
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 Tiller94d04a52016-01-20 10:58:23 -080099 timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),
Craig Tiller547db2b2015-01-30 14:08:39 -0800100 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700101 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700102 flake_retries=5 if args.allow_flakes else 0,
103 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800104
105
106# ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
107class ValgrindConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800108
murgatroid99132ce6a2015-03-04 17:29:14 -0800109 def __init__(self, config, tool, args=None):
110 if args is None:
111 args = []
Craig Tiller738c3342015-01-12 14:28:33 -0800112 self.build_config = config
Craig Tiller2aa4d642015-01-14 15:59:44 -0800113 self.tool = tool
Craig Tiller1a305b12015-02-18 13:37:06 -0800114 self.args = args
Craig Tillerc7449162015-01-16 14:42:10 -0800115 self.allow_hashing = False
Craig Tiller738c3342015-01-12 14:28:33 -0800116
Craig Tiller49f61322015-03-03 13:02:11 -0800117 def job_spec(self, cmdline, hash_targets):
Craig Tiller1a305b12015-02-18 13:37:06 -0800118 return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
Craig Tiller49f61322015-03-03 13:02:11 -0800119 self.args + cmdline,
Craig Tiller71ec6cb2015-06-03 00:51:11 -0700120 shortname='valgrind %s' % cmdline[0],
Craig Tillerd4509a12015-09-28 09:18:40 -0700121 hash_targets=None,
Craig Tiller95cc07b2015-09-28 13:41:30 -0700122 flake_retries=5 if args.allow_flakes else 0,
Craig Tiller35505de2015-10-08 13:31:33 -0700123 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800124
125
murgatroid99cf08daf2015-09-21 15:33:16 -0700126def get_c_tests(travis, test_lang) :
127 out = []
128 platforms_str = 'ci_platforms' if travis else 'platforms'
129 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700130 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700131 return [tgt
132 for tgt in js
133 if tgt['language'] == test_lang and
134 platform_string() in tgt[platforms_str] and
135 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700136
murgatroid99fafeeb32015-09-22 09:13:03 -0700137
Craig Tillerc7449162015-01-16 14:42:10 -0800138class CLanguage(object):
139
Craig Tillere9c959d2015-01-18 10:23:26 -0800140 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800141 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700142 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700143 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800144
Craig Tiller883064c2015-11-04 10:06:10 -0800145 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800146 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800147 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700148 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700149 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700150 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700151 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700152 binary = 'vsprojects/%s/%s.exe' % (
153 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700154 else:
155 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700156 if os.path.isfile(binary):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800157 cmdline = [binary] + target['args']
158 out.append(config.job_spec(cmdline, [binary],
159 shortname=' '.join(cmdline),
Craig Tillercc0535d2015-12-08 15:14:47 -0800160 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
161 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800162 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
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
Craig Tillercc0535d2015-12-08 15:14:47 -0800202
murgatroid992c8d5162015-01-26 10:41:21 -0800203class NodeLanguage(object):
204
Craig Tiller883064c2015-11-04 10:06:10 -0800205 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700206 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700207 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800208
murgatroid99256d3df2015-09-21 16:58:02 -0700209 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700210 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700211 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700212
Craig Tiller883064c2015-11-04 10:06:10 -0800213 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700214 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800215
216 def build_steps(self):
217 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800218
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200219 def post_tests_steps(self):
220 return []
221
murgatroid99a3e244f2015-09-22 11:25:53 -0700222 def makefile_name(self):
223 return 'Makefile'
224
murgatroid99132ce6a2015-03-04 17:29:14 -0800225 def supports_multi_config(self):
226 return False
227
228 def __str__(self):
229 return 'node'
230
Craig Tiller99775822015-01-30 13:07:16 -0800231
Craig Tillerc7449162015-01-16 14:42:10 -0800232class PhpLanguage(object):
233
Craig Tiller883064c2015-11-04 10:06:10 -0800234 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700235 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700236 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800237
murgatroid99256d3df2015-09-21 16:58:02 -0700238 def pre_build_steps(self):
239 return []
240
Craig Tiller883064c2015-11-04 10:06:10 -0800241 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700242 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800243
244 def build_steps(self):
245 return [['tools/run_tests/build_php.sh']]
246
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200247 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800248 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200249
murgatroid99a3e244f2015-09-22 11:25:53 -0700250 def makefile_name(self):
251 return 'Makefile'
252
murgatroid99132ce6a2015-03-04 17:29:14 -0800253 def supports_multi_config(self):
254 return False
255
256 def __str__(self):
257 return 'php'
258
Craig Tillerc7449162015-01-16 14:42:10 -0800259
Nathaniel Manista840615e2015-01-22 20:31:47 +0000260class PythonLanguage(object):
261
Craig Tiller49f61322015-03-03 13:02:11 -0800262 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700263 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700264 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800265
Craig Tiller883064c2015-11-04 10:06:10 -0800266 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700267 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
268 environment['PYVER'] = '2.7'
269 return [config.job_spec(
270 ['tools/run_tests/run_python.sh'],
271 None,
272 environ=environment,
273 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700274 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700275 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000276
murgatroid99256d3df2015-09-21 16:58:02 -0700277 def pre_build_steps(self):
278 return []
279
Craig Tiller883064c2015-11-04 10:06:10 -0800280 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700281 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000282
283 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700284 commands = []
285 for python_version in self._build_python_versions:
286 try:
287 with open(os.devnull, 'w') as output:
288 subprocess.check_call(['which', 'python' + python_version],
289 stdout=output, stderr=output)
290 commands.append(['tools/run_tests/build_python.sh', python_version])
291 self._has_python_versions.append(python_version)
292 except:
293 jobset.message('WARNING', 'Missing Python ' + python_version,
294 do_newline=True)
295 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000296
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200297 def post_tests_steps(self):
298 return []
299
murgatroid99a3e244f2015-09-22 11:25:53 -0700300 def makefile_name(self):
301 return 'Makefile'
302
murgatroid99132ce6a2015-03-04 17:29:14 -0800303 def supports_multi_config(self):
304 return False
305
306 def __str__(self):
307 return 'python'
308
Craig Tillerd625d812015-04-08 15:52:35 -0700309
murgatroid996a4c4fa2015-02-27 12:08:57 -0800310class RubyLanguage(object):
311
Craig Tiller883064c2015-11-04 10:06:10 -0800312 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700313 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700314 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800315
murgatroid99256d3df2015-09-21 16:58:02 -0700316 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200317 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700318
Craig Tiller883064c2015-11-04 10:06:10 -0800319 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700320 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800321
322 def build_steps(self):
323 return [['tools/run_tests/build_ruby.sh']]
324
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200325 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100326 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200327
murgatroid99a3e244f2015-09-22 11:25:53 -0700328 def makefile_name(self):
329 return 'Makefile'
330
murgatroid99132ce6a2015-03-04 17:29:14 -0800331 def supports_multi_config(self):
332 return False
333
334 def __str__(self):
335 return 'ruby'
336
Craig Tillerd625d812015-04-08 15:52:35 -0700337
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800338class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700339 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700340 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700341
Craig Tiller883064c2015-11-04 10:06:10 -0800342 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800343 with open('src/csharp/tests.json') as f:
344 tests_json = json.load(f)
345 assemblies = tests_json['assemblies']
346 tests = tests_json['tests']
347
348 msbuild_config = _WINDOWS_CONFIG[config.build_config]
349 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
350 for a in assemblies]
351
352 extra_args = ['-labels'] + assembly_files
353
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700354 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800355 script_name = 'tools\\run_tests\\run_csharp.bat'
356 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700357 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800358 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700359
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700360 if config.build_config == 'gcov':
361 # On Windows, we only collect C# code coverage.
362 # On Linux, we only collect coverage for native extension.
363 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800364 return [config.job_spec([script_name] + extra_args, None,
365 shortname='csharp.coverage',
366 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700367 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800368 specs = []
369 for test in tests:
370 cmdline = [script_name, '-run=%s' % test] + extra_args
371 if self.platform == 'windows':
372 # use different output directory for each test to prevent
373 # TestResult.xml clash between parallel test runs.
374 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
375 specs.append(config.job_spec(cmdline, None,
376 shortname='csharp.%s' % test,
377 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
378 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800379
murgatroid99256d3df2015-09-21 16:58:02 -0700380 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700381 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700382 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700383 else:
384 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700385
Craig Tiller883064c2015-11-04 10:06:10 -0800386 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700387 # For Windows, this target doesn't really build anything,
388 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700389 if self.platform == 'windows':
390 return []
391 else:
392 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800393
394 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700395 if self.platform == 'windows':
396 return [['src\\csharp\\buildall.bat']]
397 else:
398 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000399
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200400 def post_tests_steps(self):
401 return []
402
murgatroid99a3e244f2015-09-22 11:25:53 -0700403 def makefile_name(self):
404 return 'Makefile'
405
murgatroid99132ce6a2015-03-04 17:29:14 -0800406 def supports_multi_config(self):
407 return False
408
409 def __str__(self):
410 return 'csharp'
411
Craig Tillerd625d812015-04-08 15:52:35 -0700412
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700413class ObjCLanguage(object):
414
Craig Tiller883064c2015-11-04 10:06:10 -0800415 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700416 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
417 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
418
murgatroid99256d3df2015-09-21 16:58:02 -0700419 def pre_build_steps(self):
420 return []
421
Craig Tiller883064c2015-11-04 10:06:10 -0800422 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700423 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700424
425 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700426 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700427
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200428 def post_tests_steps(self):
429 return []
430
murgatroid99a3e244f2015-09-22 11:25:53 -0700431 def makefile_name(self):
432 return 'Makefile'
433
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700434 def supports_multi_config(self):
435 return False
436
437 def __str__(self):
438 return 'objc'
439
440
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100441class Sanity(object):
442
Craig Tiller883064c2015-11-04 10:06:10 -0800443 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800444 import yaml
445 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
446 return [config.job_spec([cmd], None, timeout_seconds=None, environ={'TEST': 'true'})
447 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100448
murgatroid99256d3df2015-09-21 16:58:02 -0700449 def pre_build_steps(self):
450 return []
451
Craig Tiller883064c2015-11-04 10:06:10 -0800452 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100453 return ['run_dep_checks']
454
455 def build_steps(self):
456 return []
457
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200458 def post_tests_steps(self):
459 return []
460
murgatroid99a3e244f2015-09-22 11:25:53 -0700461 def makefile_name(self):
462 return 'Makefile'
463
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100464 def supports_multi_config(self):
465 return False
466
467 def __str__(self):
468 return 'sanity'
469
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200470
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100471class Build(object):
472
Craig Tiller883064c2015-11-04 10:06:10 -0800473 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100474 return []
475
murgatroid99256d3df2015-09-21 16:58:02 -0700476 def pre_build_steps(self):
477 return []
478
Craig Tiller883064c2015-11-04 10:06:10 -0800479 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200480 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100481
482 def build_steps(self):
483 return []
484
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100485 def post_tests_steps(self):
486 return []
487
murgatroid99a3e244f2015-09-22 11:25:53 -0700488 def makefile_name(self):
489 return 'Makefile'
490
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100491 def supports_multi_config(self):
492 return True
493
494 def __str__(self):
495 return self.make_target
496
497
Craig Tiller738c3342015-01-12 14:28:33 -0800498# different configurations we can run under
499_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800500 'dbg': SimpleConfig('dbg'),
501 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700502 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700503 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700504 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800505 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700506 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800507 'ASAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:detect_leaks=1:color=always',
508 'LSAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700509 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800510 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800511 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800512 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800513 'helgrind': ValgrindConfig('dbg', 'helgrind')
514 }
Craig Tiller738c3342015-01-12 14:28:33 -0800515
516
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100517_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800518_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800519 'c++': CLanguage('cxx', 'c++'),
520 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800521 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000522 'php': PhpLanguage(),
523 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800524 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100525 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700526 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100527 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100528 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800529 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800530
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700531_WINDOWS_CONFIG = {
532 'dbg': 'Debug',
533 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800534 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700535 }
536
David Garcia Quintase90cd372015-05-31 18:15:26 -0700537
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800538def _windows_arch_option(arch):
539 """Returns msbuild cmdline option for selected architecture."""
540 if arch == 'default' or arch == 'windows_x86':
541 return '/p:Platform=Win32'
542 elif arch == 'windows_x64':
543 return '/p:Platform=x64'
544 else:
545 print 'Architecture %s not supported on current platform.' % arch
546 sys.exit(1)
547
548
549def _windows_build_bat(compiler):
550 """Returns name of build.bat for selected compiler."""
551 if compiler == 'default' or compiler == 'vs2013':
552 return 'vsprojects\\build_vs2013.bat'
553 elif compiler == 'vs2015':
554 return 'vsprojects\\build_vs2015.bat'
555 elif compiler == 'vs2010':
556 return 'vsprojects\\build_vs2010.bat'
557 else:
558 print 'Compiler %s not supported.' % compiler
559 sys.exit(1)
560
561
562def _windows_toolset_option(compiler):
563 """Returns msbuild PlatformToolset for selected compiler."""
564 if compiler == 'default' or compiler == 'vs2013':
565 return '/p:PlatformToolset=v120'
566 elif compiler == 'vs2015':
567 return '/p:PlatformToolset=v140'
568 elif compiler == 'vs2010':
569 return '/p:PlatformToolset=v100'
570 else:
571 print 'Compiler %s not supported.' % compiler
572 sys.exit(1)
573
574
David Garcia Quintase90cd372015-05-31 18:15:26 -0700575def runs_per_test_type(arg_str):
576 """Auxilary function to parse the "runs_per_test" flag.
577
578 Returns:
579 A positive integer or 0, the latter indicating an infinite number of
580 runs.
581
582 Raises:
583 argparse.ArgumentTypeError: Upon invalid input.
584 """
585 if arg_str == 'inf':
586 return 0
587 try:
588 n = int(arg_str)
589 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700590 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700591 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700592 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700593 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700594
595# parse command line
596argp = argparse.ArgumentParser(description='Run grpc tests.')
597argp.add_argument('-c', '--config',
598 choices=['all'] + sorted(_CONFIGS.keys()),
599 nargs='+',
600 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700601argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
602 help='A positive integer or "inf". If "inf", all tests will run in an '
603 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800604argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller83762ac2015-05-22 14:04:06 -0700605argp.add_argument('-j', '--jobs', default=2 * multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800606argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800607argp.add_argument('-f', '--forever',
608 default=False,
609 action='store_const',
610 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100611argp.add_argument('-t', '--travis',
612 default=False,
613 action='store_const',
614 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800615argp.add_argument('--newline_on_success',
616 default=False,
617 action='store_const',
618 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800619argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700620 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800621 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700622 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700623argp.add_argument('-S', '--stop_on_failure',
624 default=False,
625 action='store_const',
626 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700627argp.add_argument('--use_docker',
628 default=False,
629 action='store_const',
630 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700631 help='Run all the tests under docker. That provides ' +
632 'additional isolation and prevents the need to install ' +
633 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700634argp.add_argument('--allow_flakes',
635 default=False,
636 action='store_const',
637 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700638 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800639argp.add_argument('--arch',
640 choices=['default', 'windows_x86', 'windows_x64'],
641 default='default',
642 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
643argp.add_argument('--compiler',
644 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
645 default='default',
646 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
647argp.add_argument('--build_only',
648 default=False,
649 action='store_const',
650 const=True,
651 help='Perform all the build steps but dont run any tests.')
Craig Tiller1676f912016-01-05 10:49:44 -0800652argp.add_argument('--update_submodules', default=[], nargs='*',
653 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
654 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700655argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200656argp.add_argument('-x', '--xml_report', default=None, type=str,
657 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800658args = argp.parse_args()
659
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700660if args.use_docker:
661 if not args.travis:
662 print 'Seen --use_docker flag, will run tests under docker.'
663 print
664 print 'IMPORTANT: The changes you are testing need to be locally committed'
665 print 'because only the committed changes in the current branch will be'
666 print 'copied to the docker environment.'
667 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700668
669 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700670 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700671
672 # TODO(jtattermusch): revisit if we need special handling for arch here
673 # set arch command prefix in case we are working with different arch.
674 arch_env = os.getenv('arch')
675 if arch_env:
676 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
677
678 env = os.environ.copy()
679 env['RUN_TESTS_COMMAND'] = run_tests_cmd
680 if args.xml_report:
681 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700682 if not args.travis:
683 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700684
685 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
686 shell=True,
687 env=env)
688 sys.exit(0)
689
Craig Tiller1676f912016-01-05 10:49:44 -0800690# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800691need_to_regenerate_projects = False
692for spec in args.update_submodules:
693 spec = spec.split(':', 1)
694 if len(spec) == 1:
695 submodule = spec[0]
696 branch = 'master'
697 elif len(spec) == 2:
698 submodule = spec[0]
699 branch = spec[1]
700 cwd = 'third_party/%s' % submodule
701 def git(cmd, cwd=cwd):
702 print 'in %s: git %s' % (cwd, cmd)
703 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
704 git('fetch')
705 git('checkout %s' % branch)
706 git('pull origin %s' % branch)
707 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
708 need_to_regenerate_projects = True
709if need_to_regenerate_projects:
710 if jobset.platform_string() == 'linux':
711 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
712 else:
713 print 'WARNING: may need to regenerate projects, but since we are not on'
714 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800715
716
Nicolas Nobleddef2462015-01-06 18:08:25 -0800717# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800718run_configs = set(_CONFIGS[cfg]
719 for cfg in itertools.chain.from_iterable(
720 _CONFIGS.iterkeys() if x == 'all' else [x]
721 for x in args.config))
722build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800723
Craig Tiller06805272015-06-11 14:46:47 -0700724if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700725 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700726
Adele Zhou6b9527c2015-11-20 15:56:35 -0800727if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800728 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800729else:
730 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800731# We don't support code coverage on some languages
732if 'gcov' in args.config:
733 for bad in ['objc', 'sanity', 'build']:
734 if bad in lang_list:
735 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800736
737languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800738
739if len(build_configs) > 1:
740 for language in languages:
741 if not language.supports_multi_config():
742 print language, 'does not support multiple build configurations'
743 sys.exit(1)
744
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800745if platform_string() != 'windows':
746 if args.arch != 'default':
747 print 'Architecture %s not supported on current platform.' % args.arch
748 sys.exit(1)
749 if args.compiler != 'default':
750 print 'Compiler %s not supported on current platform.' % args.compiler
751 sys.exit(1)
752
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100753if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700754 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700755 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700756 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700757 # empirically /m:2 gives the best performance/price and should prevent
758 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700759 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700760 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700761 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700762 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800763 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700764 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800765 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
766 _windows_toolset_option(args.compiler),
767 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700768 extra_args,
Craig Tiller590105a2016-01-19 13:03:46 -0800769 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700770 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700771else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700772 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700773 if targets:
774 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
775 '-f', makefile,
776 '-j', '%d' % (multiprocessing.cpu_count() + 1),
Craig Tiller71a86042016-01-15 14:59:58 -0800777 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
778 'CONFIG=%s' % cfg] +
779 ([] if not args.travis else ['JENKINS_BUILD=1']) +
780 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800781 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700782 else:
783 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700784make_targets = {}
785for l in languages:
786 makefile = l.makefile_name()
787 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800788 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700789
Jan Tattermusche4a69182015-12-15 09:53:01 -0800790def build_step_environ(cfg):
791 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800792 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800793 if msbuild_cfg:
794 environ['MSBUILD_CONFIG'] = msbuild_cfg
795 return environ
796
murgatroid99fddac962015-09-22 09:20:11 -0700797build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800798 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700799 for cfg in build_configs
800 for l in languages
801 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700802if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700803 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 -0700804 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700805build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800806 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800807 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800808 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700809 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800810
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200811post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800812 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200813 for cfg in build_configs
814 for l in languages
815 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800816runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800817forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800818
Nicolas Nobleddef2462015-01-06 18:08:25 -0800819
Craig Tiller71735182015-01-15 17:07:13 -0800820class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800821 """Cache for running tests."""
822
David Klempner25739582015-02-11 15:57:32 -0800823 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800824 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800825 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700826 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800827
828 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800829 if cmdline not in self._last_successful_run:
830 return True
831 if self._last_successful_run[cmdline] != bin_hash:
832 return True
David Klempner25739582015-02-11 15:57:32 -0800833 if not self._use_cache_results:
834 return True
Craig Tiller71735182015-01-15 17:07:13 -0800835 return False
836
837 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800838 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700839 if time.time() - self._last_save > 1:
840 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800841
842 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800843 return [{'cmdline': k, 'hash': v}
844 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800845
846 def parse(self, exdump):
847 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
848
849 def save(self):
850 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800851 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700852 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800853
Craig Tiller1cc11db2015-01-15 22:50:50 -0800854 def maybe_load(self):
855 if os.path.exists('.run_tests_cache'):
856 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800857 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800858
859
Craig Tillerf53d9c82015-08-04 14:19:43 -0700860def _start_port_server(port_server_port):
861 # check if a compatible port server is running
862 # if incompatible (version mismatch) ==> start a new one
863 # if not running ==> start a new one
864 # otherwise, leave it up
865 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700866 version = int(urllib2.urlopen(
867 'http://localhost:%d/version_number' % port_server_port,
868 timeout=1).read())
869 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700870 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700871 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700872 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700873 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700874 running = False
875 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700876 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800877 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
878 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700879 print 'my port server is version %d' % current_version
880 running = (version >= current_version)
881 if not running:
882 print 'port_server version mismatch: killing the old one'
883 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
884 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700885 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700886 fd, logfile = tempfile.mkstemp()
887 os.close(fd)
888 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800889 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
890 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700891 env = dict(os.environ)
892 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100893 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800894 # Working directory of port server needs to be outside of Jenkins
895 # workspace to prevent file lock issues.
896 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700897 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700898 args,
899 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800900 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700901 creationflags = 0x00000008, # detached process
902 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700903 else:
904 port_server = subprocess.Popen(
905 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700906 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700907 preexec_fn=os.setsid,
908 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700909 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700910 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700911 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700912 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700913 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700914 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700915 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700916 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700917 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700918 # try one final time: maybe another build managed to start one
919 time.sleep(1)
920 try:
921 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
922 timeout=1).read()
923 print 'last ditch attempt to contact port server succeeded'
924 break
925 except:
926 traceback.print_exc();
927 port_log = open(logfile, 'r').read()
928 print port_log
929 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700930 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700931 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
932 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700933 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700934 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700935 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700936 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700937 traceback.print_exc();
938 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700939 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700940 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700941 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700942 traceback.print_exc();
943 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700944 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700945 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700946 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700947 port_server.kill()
948 raise
949
950
Adele Zhoud5fffa52015-10-23 15:51:42 -0700951def _calculate_num_runs_failures(list_of_results):
952 """Caculate number of runs and failures for a particular test.
953
954 Args:
955 list_of_results: (List) of JobResult object.
956 Returns:
957 A tuple of total number of runs and failures.
958 """
959 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
960 num_failures = 0
961 for jobresult in list_of_results:
962 if jobresult.retries > 0:
963 num_runs += jobresult.retries
964 if jobresult.num_failures > 0:
965 num_failures += jobresult.num_failures
966 return num_runs, num_failures
967
Adele Zhou6b9527c2015-11-20 15:56:35 -0800968
Craig Tillereb9de8b2016-01-08 08:57:41 -0800969# _build_and_run results
970class BuildAndRunError(object):
971
972 BUILD = object()
973 TEST = object()
974 POST_TEST = object()
975
976
977# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700978def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800979 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800980 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800981 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700982 num_failures, _ = jobset.run(
983 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800984 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700985 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800986 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -0800987
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800988 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800989 return []
ctiller3040cb72015-01-07 12:13:17 -0800990
Craig Tiller234b6e72015-05-23 10:12:40 -0700991 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700992 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700993 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700994 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700995 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800996 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800997 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700998 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700999 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001000 one_run = set(
1001 spec
1002 for config in run_configs
1003 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001004 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001005 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001006 # When running on travis, we want out test runs to be as similar as possible
1007 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001008 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001009 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1010 else:
1011 # whereas otherwise, we want to shuffle things up to give all tests a
1012 # chance to run.
1013 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1014 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001015 if infinite_runs:
1016 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 -07001017 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1018 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001019 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001020
Adele Zhou803af152015-11-30 15:16:16 -08001021 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001022 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001023 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001024 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001025 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001026 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001027 if resultset:
1028 for k, v in resultset.iteritems():
1029 num_runs, num_failures = _calculate_num_runs_failures(v)
1030 if num_failures == num_runs: # what about infinite_runs???
1031 jobset.message('FAILED', k, do_newline=True)
1032 elif num_failures > 0:
1033 jobset.message(
1034 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1035 do_newline=True)
1036 else:
1037 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001038 finally:
1039 for antagonist in antagonists:
1040 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001041 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001042 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001043
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001044 number_failures, _ = jobset.run(
1045 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001046 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001047
1048 out = []
1049 if number_failures:
1050 out.append(BuildAndRunError.POST_TEST)
1051 if num_test_failures:
1052 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001053
Craig Tiller69cd2372015-06-11 09:38:09 -07001054 if cache: cache.save()
1055
Craig Tillereb9de8b2016-01-08 08:57:41 -08001056 return out
ctiller3040cb72015-01-07 12:13:17 -08001057
1058
David Klempner25739582015-02-11 15:57:32 -08001059test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001060test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001061
ctiller3040cb72015-01-07 12:13:17 -08001062if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001063 success = True
ctiller3040cb72015-01-07 12:13:17 -08001064 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001065 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001066 initial_time = dw.most_recent_change()
1067 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001068 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001069 errors = _build_and_run(check_cancelled=have_files_changed,
1070 newline_on_success=False,
1071 cache=test_cache,
1072 build_only=args.build_only) == 0
1073 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001074 jobset.message('SUCCESS',
1075 'All tests are now passing properly',
1076 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001077 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001078 while not have_files_changed():
1079 time.sleep(1)
1080else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001081 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001082 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001083 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001084 xml_report=args.xml_report,
1085 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001086 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001087 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1088 else:
1089 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001090 exit_code = 0
1091 if BuildAndRunError.BUILD in errors:
1092 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001093 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001094 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001095 if BuildAndRunError.POST_TEST in errors:
1096 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001097 sys.exit(exit_code)