blob: bbee5c915756e8d5bcad055128895f72371b03b6 [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
Craig Tiller16900662016-01-07 19:30:54 -0800729# We don't support code coverage on some languages
730if 'gcov' in args.config:
731 for bad in ['objc', 'sanity', 'build']:
732 if bad in lang_list:
733 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800734
735languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800736
737if len(build_configs) > 1:
738 for language in languages:
739 if not language.supports_multi_config():
740 print language, 'does not support multiple build configurations'
741 sys.exit(1)
742
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800743if platform_string() != 'windows':
744 if args.arch != 'default':
745 print 'Architecture %s not supported on current platform.' % args.arch
746 sys.exit(1)
747 if args.compiler != 'default':
748 print 'Compiler %s not supported on current platform.' % args.compiler
749 sys.exit(1)
750
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100751if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700752 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700753 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700754 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700755 # empirically /m:2 gives the best performance/price and should prevent
756 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700757 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700758 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700759 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700760 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800761 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700762 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800763 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
764 _windows_toolset_option(args.compiler),
765 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700766 extra_args,
Craig Tillerdfc3eee2015-09-01 16:32:16 -0700767 shell=True, timeout_seconds=90*60)
Craig Tiller6fd23842015-09-01 07:36:31 -0700768 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700769else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700770 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700771 if targets:
772 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
773 '-f', makefile,
774 '-j', '%d' % (multiprocessing.cpu_count() + 1),
775 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
776 args.slowdown,
777 'CONFIG=%s' % cfg] + targets,
778 timeout_seconds=30*60)]
779 else:
780 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700781make_targets = {}
782for l in languages:
783 makefile = l.makefile_name()
784 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800785 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700786
Jan Tattermusche4a69182015-12-15 09:53:01 -0800787def build_step_environ(cfg):
788 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800789 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800790 if msbuild_cfg:
791 environ['MSBUILD_CONFIG'] = msbuild_cfg
792 return environ
793
murgatroid99fddac962015-09-22 09:20:11 -0700794build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800795 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700796 for cfg in build_configs
797 for l in languages
798 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700799if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700800 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 -0700801 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700802build_steps.extend(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800803 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=10*60)
murgatroid99132ce6a2015-03-04 17:29:14 -0800804 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800805 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700806 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800807
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200808post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800809 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200810 for cfg in build_configs
811 for l in languages
812 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800813runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800814forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800815
Nicolas Nobleddef2462015-01-06 18:08:25 -0800816
Craig Tiller71735182015-01-15 17:07:13 -0800817class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800818 """Cache for running tests."""
819
David Klempner25739582015-02-11 15:57:32 -0800820 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800821 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800822 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700823 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800824
825 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800826 if cmdline not in self._last_successful_run:
827 return True
828 if self._last_successful_run[cmdline] != bin_hash:
829 return True
David Klempner25739582015-02-11 15:57:32 -0800830 if not self._use_cache_results:
831 return True
Craig Tiller71735182015-01-15 17:07:13 -0800832 return False
833
834 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800835 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700836 if time.time() - self._last_save > 1:
837 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800838
839 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800840 return [{'cmdline': k, 'hash': v}
841 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800842
843 def parse(self, exdump):
844 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
845
846 def save(self):
847 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800848 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700849 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800850
Craig Tiller1cc11db2015-01-15 22:50:50 -0800851 def maybe_load(self):
852 if os.path.exists('.run_tests_cache'):
853 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800854 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800855
856
Craig Tillerf53d9c82015-08-04 14:19:43 -0700857def _start_port_server(port_server_port):
858 # check if a compatible port server is running
859 # if incompatible (version mismatch) ==> start a new one
860 # if not running ==> start a new one
861 # otherwise, leave it up
862 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700863 version = int(urllib2.urlopen(
864 'http://localhost:%d/version_number' % port_server_port,
865 timeout=1).read())
866 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700867 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700868 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700869 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700870 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700871 running = False
872 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700873 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800874 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
875 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700876 print 'my port server is version %d' % current_version
877 running = (version >= current_version)
878 if not running:
879 print 'port_server version mismatch: killing the old one'
880 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
881 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700882 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700883 fd, logfile = tempfile.mkstemp()
884 os.close(fd)
885 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800886 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
887 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700888 env = dict(os.environ)
889 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100890 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800891 # Working directory of port server needs to be outside of Jenkins
892 # workspace to prevent file lock issues.
893 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700894 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700895 args,
896 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800897 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700898 creationflags = 0x00000008, # detached process
899 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700900 else:
901 port_server = subprocess.Popen(
902 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700903 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700904 preexec_fn=os.setsid,
905 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700906 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700907 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700908 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700909 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700910 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700911 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700912 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700913 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700914 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700915 # try one final time: maybe another build managed to start one
916 time.sleep(1)
917 try:
918 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
919 timeout=1).read()
920 print 'last ditch attempt to contact port server succeeded'
921 break
922 except:
923 traceback.print_exc();
924 port_log = open(logfile, 'r').read()
925 print port_log
926 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700927 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700928 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
929 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700930 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700931 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700932 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700933 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700934 traceback.print_exc();
935 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700936 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700937 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700938 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700939 traceback.print_exc();
940 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700941 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700942 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700943 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700944 port_server.kill()
945 raise
946
947
Adele Zhoud5fffa52015-10-23 15:51:42 -0700948def _calculate_num_runs_failures(list_of_results):
949 """Caculate number of runs and failures for a particular test.
950
951 Args:
952 list_of_results: (List) of JobResult object.
953 Returns:
954 A tuple of total number of runs and failures.
955 """
956 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
957 num_failures = 0
958 for jobresult in list_of_results:
959 if jobresult.retries > 0:
960 num_runs += jobresult.retries
961 if jobresult.num_failures > 0:
962 num_failures += jobresult.num_failures
963 return num_runs, num_failures
964
Adele Zhou6b9527c2015-11-20 15:56:35 -0800965
Craig Tillerf53d9c82015-08-04 14:19:43 -0700966def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800967 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800968 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800969 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700970 num_failures, _ = jobset.run(
971 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800972 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700973 if num_failures:
Craig Tillerd86a3942015-01-14 12:48:54 -0800974 return 1
Craig Tillerb361b4e2016-01-06 11:44:17 -0800975
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800976 if build_only:
977 return 0
ctiller3040cb72015-01-07 12:13:17 -0800978
Craig Tiller234b6e72015-05-23 10:12:40 -0700979 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700980 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700981 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700982 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700983 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800984 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800985 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700986 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700987 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700988 one_run = set(
989 spec
990 for config in run_configs
991 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800992 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700993 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700994 # When running on travis, we want out test runs to be as similar as possible
995 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800996 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700997 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
998 else:
999 # whereas otherwise, we want to shuffle things up to give all tests a
1000 # chance to run.
1001 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1002 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001003 if infinite_runs:
1004 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 -07001005 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1006 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001007 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001008
Adele Zhou803af152015-11-30 15:16:16 -08001009 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001010 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001011 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001012 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001013 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001014 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001015 if resultset:
1016 for k, v in resultset.iteritems():
1017 num_runs, num_failures = _calculate_num_runs_failures(v)
1018 if num_failures == num_runs: # what about infinite_runs???
1019 jobset.message('FAILED', k, do_newline=True)
1020 elif num_failures > 0:
1021 jobset.message(
1022 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1023 do_newline=True)
1024 else:
1025 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001026 finally:
1027 for antagonist in antagonists:
1028 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001029 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001030 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001031
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001032 number_failures, _ = jobset.run(
1033 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001034 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhou803af152015-11-30 15:16:16 -08001035 if num_test_failures or number_failures:
1036 return 2
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001037
Craig Tiller69cd2372015-06-11 09:38:09 -07001038 if cache: cache.save()
1039
Craig Tillerd86a3942015-01-14 12:48:54 -08001040 return 0
ctiller3040cb72015-01-07 12:13:17 -08001041
1042
David Klempner25739582015-02-11 15:57:32 -08001043test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001044test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001045
ctiller3040cb72015-01-07 12:13:17 -08001046if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001047 success = True
ctiller3040cb72015-01-07 12:13:17 -08001048 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001049 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001050 initial_time = dw.most_recent_change()
1051 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001052 previous_success = success
Craig Tiller71735182015-01-15 17:07:13 -08001053 success = _build_and_run(check_cancelled=have_files_changed,
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001054 newline_on_success=False,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001055 cache=test_cache,
1056 build_only=args.build_only) == 0
Nicolas Noble044db742015-01-14 16:57:24 -08001057 if not previous_success and success:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001058 jobset.message('SUCCESS',
1059 'All tests are now passing properly',
1060 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001061 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001062 while not have_files_changed():
1063 time.sleep(1)
1064else:
Craig Tiller71735182015-01-15 17:07:13 -08001065 result = _build_and_run(check_cancelled=lambda: False,
1066 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001067 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001068 xml_report=args.xml_report,
1069 build_only=args.build_only)
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001070 if result == 0:
1071 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1072 else:
1073 jobset.message('FAILED', 'Some tests failed', do_newline=True)
1074 sys.exit(result)