blob: a2f34247312b4b53be4a34a9796474ebbb28f4cd [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,
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070099 timeout_seconds=self.timeout_multiplier * timeout_seconds,
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):
248 return []
249
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 Tiller0fe5ee72015-12-22 12:50:36 -0800444 return [config.job_spec(['tools/run_tests/run_sanity.sh'], None, timeout_seconds=15*60),
Jan Tattermusche3d66252015-10-26 11:33:45 -0700445 config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100446
murgatroid99256d3df2015-09-21 16:58:02 -0700447 def pre_build_steps(self):
448 return []
449
Craig Tiller883064c2015-11-04 10:06:10 -0800450 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100451 return ['run_dep_checks']
452
453 def build_steps(self):
454 return []
455
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200456 def post_tests_steps(self):
457 return []
458
murgatroid99a3e244f2015-09-22 11:25:53 -0700459 def makefile_name(self):
460 return 'Makefile'
461
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100462 def supports_multi_config(self):
463 return False
464
465 def __str__(self):
466 return 'sanity'
467
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200468
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100469class Build(object):
470
Craig Tiller883064c2015-11-04 10:06:10 -0800471 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100472 return []
473
murgatroid99256d3df2015-09-21 16:58:02 -0700474 def pre_build_steps(self):
475 return []
476
Craig Tiller883064c2015-11-04 10:06:10 -0800477 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200478 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100479
480 def build_steps(self):
481 return []
482
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100483 def post_tests_steps(self):
484 return []
485
murgatroid99a3e244f2015-09-22 11:25:53 -0700486 def makefile_name(self):
487 return 'Makefile'
488
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100489 def supports_multi_config(self):
490 return True
491
492 def __str__(self):
493 return self.make_target
494
495
Craig Tiller738c3342015-01-12 14:28:33 -0800496# different configurations we can run under
497_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800498 'dbg': SimpleConfig('dbg'),
499 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700500 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700501 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700502 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800503 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700504 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800505 'ASAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:detect_leaks=1:color=always',
506 'LSAN_OPTIONS': 'suppressions=tools/asan_suppressions.txt:report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700507 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800508 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800509 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800510 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800511 'helgrind': ValgrindConfig('dbg', 'helgrind')
512 }
Craig Tiller738c3342015-01-12 14:28:33 -0800513
514
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100515_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800516_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800517 'c++': CLanguage('cxx', 'c++'),
518 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800519 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000520 'php': PhpLanguage(),
521 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800522 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100523 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700524 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100525 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100526 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800527 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800528
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700529_WINDOWS_CONFIG = {
530 'dbg': 'Debug',
531 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800532 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700533 }
534
David Garcia Quintase90cd372015-05-31 18:15:26 -0700535
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800536def _windows_arch_option(arch):
537 """Returns msbuild cmdline option for selected architecture."""
538 if arch == 'default' or arch == 'windows_x86':
539 return '/p:Platform=Win32'
540 elif arch == 'windows_x64':
541 return '/p:Platform=x64'
542 else:
543 print 'Architecture %s not supported on current platform.' % arch
544 sys.exit(1)
545
546
547def _windows_build_bat(compiler):
548 """Returns name of build.bat for selected compiler."""
549 if compiler == 'default' or compiler == 'vs2013':
550 return 'vsprojects\\build_vs2013.bat'
551 elif compiler == 'vs2015':
552 return 'vsprojects\\build_vs2015.bat'
553 elif compiler == 'vs2010':
554 return 'vsprojects\\build_vs2010.bat'
555 else:
556 print 'Compiler %s not supported.' % compiler
557 sys.exit(1)
558
559
560def _windows_toolset_option(compiler):
561 """Returns msbuild PlatformToolset for selected compiler."""
562 if compiler == 'default' or compiler == 'vs2013':
563 return '/p:PlatformToolset=v120'
564 elif compiler == 'vs2015':
565 return '/p:PlatformToolset=v140'
566 elif compiler == 'vs2010':
567 return '/p:PlatformToolset=v100'
568 else:
569 print 'Compiler %s not supported.' % compiler
570 sys.exit(1)
571
572
David Garcia Quintase90cd372015-05-31 18:15:26 -0700573def runs_per_test_type(arg_str):
574 """Auxilary function to parse the "runs_per_test" flag.
575
576 Returns:
577 A positive integer or 0, the latter indicating an infinite number of
578 runs.
579
580 Raises:
581 argparse.ArgumentTypeError: Upon invalid input.
582 """
583 if arg_str == 'inf':
584 return 0
585 try:
586 n = int(arg_str)
587 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700588 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700589 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700590 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700591 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700592
593# parse command line
594argp = argparse.ArgumentParser(description='Run grpc tests.')
595argp.add_argument('-c', '--config',
596 choices=['all'] + sorted(_CONFIGS.keys()),
597 nargs='+',
598 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700599argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
600 help='A positive integer or "inf". If "inf", all tests will run in an '
601 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800602argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller83762ac2015-05-22 14:04:06 -0700603argp.add_argument('-j', '--jobs', default=2 * multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800604argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800605argp.add_argument('-f', '--forever',
606 default=False,
607 action='store_const',
608 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100609argp.add_argument('-t', '--travis',
610 default=False,
611 action='store_const',
612 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800613argp.add_argument('--newline_on_success',
614 default=False,
615 action='store_const',
616 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800617argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700618 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800619 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700620 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700621argp.add_argument('-S', '--stop_on_failure',
622 default=False,
623 action='store_const',
624 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700625argp.add_argument('--use_docker',
626 default=False,
627 action='store_const',
628 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700629 help='Run all the tests under docker. That provides ' +
630 'additional isolation and prevents the need to install ' +
631 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700632argp.add_argument('--allow_flakes',
633 default=False,
634 action='store_const',
635 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700636 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800637argp.add_argument('--arch',
638 choices=['default', 'windows_x86', 'windows_x64'],
639 default='default',
640 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
641argp.add_argument('--compiler',
642 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
643 default='default',
644 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
645argp.add_argument('--build_only',
646 default=False,
647 action='store_const',
648 const=True,
649 help='Perform all the build steps but dont run any tests.')
Craig Tiller1676f912016-01-05 10:49:44 -0800650argp.add_argument('--update_submodules', default=[], nargs='*',
651 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
652 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700653argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200654argp.add_argument('-x', '--xml_report', default=None, type=str,
655 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800656args = argp.parse_args()
657
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700658if args.use_docker:
659 if not args.travis:
660 print 'Seen --use_docker flag, will run tests under docker.'
661 print
662 print 'IMPORTANT: The changes you are testing need to be locally committed'
663 print 'because only the committed changes in the current branch will be'
664 print 'copied to the docker environment.'
665 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700666
667 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700668 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700669
670 # TODO(jtattermusch): revisit if we need special handling for arch here
671 # set arch command prefix in case we are working with different arch.
672 arch_env = os.getenv('arch')
673 if arch_env:
674 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
675
676 env = os.environ.copy()
677 env['RUN_TESTS_COMMAND'] = run_tests_cmd
678 if args.xml_report:
679 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700680 if not args.travis:
681 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700682
683 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
684 shell=True,
685 env=env)
686 sys.exit(0)
687
Craig Tiller1676f912016-01-05 10:49:44 -0800688# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800689need_to_regenerate_projects = False
690for spec in args.update_submodules:
691 spec = spec.split(':', 1)
692 if len(spec) == 1:
693 submodule = spec[0]
694 branch = 'master'
695 elif len(spec) == 2:
696 submodule = spec[0]
697 branch = spec[1]
698 cwd = 'third_party/%s' % submodule
699 def git(cmd, cwd=cwd):
700 print 'in %s: git %s' % (cwd, cmd)
701 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
702 git('fetch')
703 git('checkout %s' % branch)
704 git('pull origin %s' % branch)
705 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
706 need_to_regenerate_projects = True
707if need_to_regenerate_projects:
708 if jobset.platform_string() == 'linux':
709 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
710 else:
711 print 'WARNING: may need to regenerate projects, but since we are not on'
712 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800713
714
Nicolas Nobleddef2462015-01-06 18:08:25 -0800715# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800716run_configs = set(_CONFIGS[cfg]
717 for cfg in itertools.chain.from_iterable(
718 _CONFIGS.iterkeys() if x == 'all' else [x]
719 for x in args.config))
720build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800721
Craig Tiller06805272015-06-11 14:46:47 -0700722if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700723 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700724
Adele Zhou6b9527c2015-11-20 15:56:35 -0800725if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800726 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800727else:
728 lang_list = args.language
729# We don't support code coverage on ObjC
730if 'gcov' in args.config and 'objc' in lang_list:
731 lang_list.remove('objc')
732
733languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800734
735if len(build_configs) > 1:
736 for language in languages:
737 if not language.supports_multi_config():
738 print language, 'does not support multiple build configurations'
739 sys.exit(1)
740
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800741if platform_string() != 'windows':
742 if args.arch != 'default':
743 print 'Architecture %s not supported on current platform.' % args.arch
744 sys.exit(1)
745 if args.compiler != 'default':
746 print 'Compiler %s not supported on current platform.' % args.compiler
747 sys.exit(1)
748
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100749if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700750 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700751 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700752 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700753 # empirically /m:2 gives the best performance/price and should prevent
754 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700755 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700756 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700757 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700758 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800759 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700760 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800761 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
762 _windows_toolset_option(args.compiler),
763 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700764 extra_args,
Craig Tillerdfc3eee2015-09-01 16:32:16 -0700765 shell=True, timeout_seconds=90*60)
Craig Tiller6fd23842015-09-01 07:36:31 -0700766 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700767else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700768 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700769 if targets:
770 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
771 '-f', makefile,
772 '-j', '%d' % (multiprocessing.cpu_count() + 1),
773 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
774 args.slowdown,
775 'CONFIG=%s' % cfg] + targets,
776 timeout_seconds=30*60)]
777 else:
778 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700779make_targets = {}
780for l in languages:
781 makefile = l.makefile_name()
782 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800783 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700784
Jan Tattermusche4a69182015-12-15 09:53:01 -0800785def build_step_environ(cfg):
786 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800787 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800788 if msbuild_cfg:
789 environ['MSBUILD_CONFIG'] = msbuild_cfg
790 return environ
791
murgatroid99fddac962015-09-22 09:20:11 -0700792build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800793 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700794 for cfg in build_configs
795 for l in languages
796 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700797if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700798 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 -0700799 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700800build_steps.extend(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800801 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=10*60)
murgatroid99132ce6a2015-03-04 17:29:14 -0800802 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800803 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700804 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800805
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200806post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800807 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200808 for cfg in build_configs
809 for l in languages
810 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800811runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800812forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800813
Nicolas Nobleddef2462015-01-06 18:08:25 -0800814
Craig Tiller71735182015-01-15 17:07:13 -0800815class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800816 """Cache for running tests."""
817
David Klempner25739582015-02-11 15:57:32 -0800818 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800819 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800820 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700821 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800822
823 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800824 if cmdline not in self._last_successful_run:
825 return True
826 if self._last_successful_run[cmdline] != bin_hash:
827 return True
David Klempner25739582015-02-11 15:57:32 -0800828 if not self._use_cache_results:
829 return True
Craig Tiller71735182015-01-15 17:07:13 -0800830 return False
831
832 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800833 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700834 if time.time() - self._last_save > 1:
835 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800836
837 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800838 return [{'cmdline': k, 'hash': v}
839 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800840
841 def parse(self, exdump):
842 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
843
844 def save(self):
845 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800846 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700847 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800848
Craig Tiller1cc11db2015-01-15 22:50:50 -0800849 def maybe_load(self):
850 if os.path.exists('.run_tests_cache'):
851 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800852 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800853
854
Craig Tillerf53d9c82015-08-04 14:19:43 -0700855def _start_port_server(port_server_port):
856 # check if a compatible port server is running
857 # if incompatible (version mismatch) ==> start a new one
858 # if not running ==> start a new one
859 # otherwise, leave it up
860 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700861 version = int(urllib2.urlopen(
862 'http://localhost:%d/version_number' % port_server_port,
863 timeout=1).read())
864 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700865 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700866 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700867 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700868 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700869 running = False
870 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700871 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800872 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
873 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700874 print 'my port server is version %d' % current_version
875 running = (version >= current_version)
876 if not running:
877 print 'port_server version mismatch: killing the old one'
878 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
879 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700880 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700881 fd, logfile = tempfile.mkstemp()
882 os.close(fd)
883 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800884 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
885 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700886 env = dict(os.environ)
887 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100888 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800889 # Working directory of port server needs to be outside of Jenkins
890 # workspace to prevent file lock issues.
891 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700892 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700893 args,
894 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800895 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700896 creationflags = 0x00000008, # detached process
897 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700898 else:
899 port_server = subprocess.Popen(
900 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700901 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700902 preexec_fn=os.setsid,
903 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700904 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700905 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700906 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700907 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700908 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700909 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700910 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700911 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700912 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700913 # try one final time: maybe another build managed to start one
914 time.sleep(1)
915 try:
916 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
917 timeout=1).read()
918 print 'last ditch attempt to contact port server succeeded'
919 break
920 except:
921 traceback.print_exc();
922 port_log = open(logfile, 'r').read()
923 print port_log
924 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700925 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700926 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
927 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700928 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700929 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700930 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700931 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700932 traceback.print_exc();
933 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700934 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700935 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700936 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700937 traceback.print_exc();
938 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700939 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700940 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700941 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700942 port_server.kill()
943 raise
944
945
Adele Zhoud5fffa52015-10-23 15:51:42 -0700946def _calculate_num_runs_failures(list_of_results):
947 """Caculate number of runs and failures for a particular test.
948
949 Args:
950 list_of_results: (List) of JobResult object.
951 Returns:
952 A tuple of total number of runs and failures.
953 """
954 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
955 num_failures = 0
956 for jobresult in list_of_results:
957 if jobresult.retries > 0:
958 num_runs += jobresult.retries
959 if jobresult.num_failures > 0:
960 num_failures += jobresult.num_failures
961 return num_runs, num_failures
962
Adele Zhou6b9527c2015-11-20 15:56:35 -0800963
Craig Tillereb9de8b2016-01-08 08:57:41 -0800964# _build_and_run results
965class BuildAndRunError(object):
966
967 BUILD = object()
968 TEST = object()
969 POST_TEST = object()
970
971
972# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700973def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800974 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800975 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800976 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700977 num_failures, _ = jobset.run(
978 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800979 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700980 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800981 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -0800982
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800983 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800984 return []
ctiller3040cb72015-01-07 12:13:17 -0800985
Craig Tiller234b6e72015-05-23 10:12:40 -0700986 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700987 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700988 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700989 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700990 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800991 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800992 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700993 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700994 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700995 one_run = set(
996 spec
997 for config in run_configs
998 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800999 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001000 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001001 # When running on travis, we want out test runs to be as similar as possible
1002 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001003 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001004 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1005 else:
1006 # whereas otherwise, we want to shuffle things up to give all tests a
1007 # chance to run.
1008 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1009 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001010 if infinite_runs:
1011 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 -07001012 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1013 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001014 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001015
Adele Zhou803af152015-11-30 15:16:16 -08001016 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001017 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001018 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001019 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001020 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001021 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001022 if resultset:
1023 for k, v in resultset.iteritems():
1024 num_runs, num_failures = _calculate_num_runs_failures(v)
1025 if num_failures == num_runs: # what about infinite_runs???
1026 jobset.message('FAILED', k, do_newline=True)
1027 elif num_failures > 0:
1028 jobset.message(
1029 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1030 do_newline=True)
1031 else:
1032 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001033 finally:
1034 for antagonist in antagonists:
1035 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001036 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001037 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001038
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001039 number_failures, _ = jobset.run(
1040 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001041 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001042
1043 out = []
1044 if number_failures:
1045 out.append(BuildAndRunError.POST_TEST)
1046 if num_test_failures:
1047 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001048
Craig Tiller69cd2372015-06-11 09:38:09 -07001049 if cache: cache.save()
1050
Craig Tillereb9de8b2016-01-08 08:57:41 -08001051 return out
ctiller3040cb72015-01-07 12:13:17 -08001052
1053
David Klempner25739582015-02-11 15:57:32 -08001054test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001055test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001056
ctiller3040cb72015-01-07 12:13:17 -08001057if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001058 success = True
ctiller3040cb72015-01-07 12:13:17 -08001059 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001060 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001061 initial_time = dw.most_recent_change()
1062 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001063 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001064 errors = _build_and_run(check_cancelled=have_files_changed,
1065 newline_on_success=False,
1066 cache=test_cache,
1067 build_only=args.build_only) == 0
1068 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001069 jobset.message('SUCCESS',
1070 'All tests are now passing properly',
1071 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001072 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001073 while not have_files_changed():
1074 time.sleep(1)
1075else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001076 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001077 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001078 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001079 xml_report=args.xml_report,
1080 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001081 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001082 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1083 else:
1084 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001085 exit_code = 0
1086 if BuildAndRunError.BUILD in errors:
1087 exit_code |= 1
1088 if BuildAndRunError.POST_TEST in errors:
1089 exit_code |= 2
1090 # BuildAndRunError.TEST does not cause run_tests.py to fail
1091 sys.exit(exit_code)