blob: 8e8a05ed08d1ca8983de2d3f34740c0eac85e213 [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tillerc2c79212015-02-16 12:00:01 -08002# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
Nicolas Nobleddef2462015-01-06 18:08:25 -080031"""Run tests in parallel."""
32
33import argparse
34import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070035import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080036import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080037import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080038import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080039import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070040import platform
41import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080042import re
Craig Tiller82875232015-09-25 13:57:34 -070043import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070044import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080045import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070046import tempfile
47import traceback
ctiller3040cb72015-01-07 12:13:17 -080048import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070049import urllib2
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 Tiller2cc2b842015-02-27 11:38:31 -080056ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
57os.chdir(ROOT)
58
59
Craig Tiller06805272015-06-11 14:46:47 -070060_FORCE_ENVIRON_FOR_WRAPPERS = {}
61
62
Craig Tillerd50993d2015-08-05 08:04:36 -070063def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010064 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070065
66
Craig Tiller738c3342015-01-12 14:28:33 -080067# SimpleConfig: just compile with CONFIG=config, and run the binary to test
68class SimpleConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080069
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070070 def __init__(self, config, environ=None, timeout_multiplier=1):
murgatroid99132ce6a2015-03-04 17:29:14 -080071 if environ is None:
72 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080073 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080074 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080075 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080076 self.environ['CONFIG'] = config
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070077 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080078
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070079 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
80 shortname=None, environ={}):
Craig Tiller49f61322015-03-03 13:02:11 -080081 """Construct a jobset.JobSpec for a test under this config
82
83 Args:
84 cmdline: a list of strings specifying the command line the test
85 would like to run
86 hash_targets: either None (don't do caching of test results), or
87 a list of strings specifying files to include in a
88 binary hash to check if a test has changed
89 -- if used, all artifacts needed to run the test must
90 be listed
91 """
Craig Tiller4fc90032015-05-21 10:39:52 -070092 actual_environ = self.environ.copy()
93 for k, v in environ.iteritems():
94 actual_environ[k] = v
Craig Tiller49f61322015-03-03 13:02:11 -080095 return jobset.JobSpec(cmdline=cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070096 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -070097 environ=actual_environ,
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070098 timeout_seconds=self.timeout_multiplier * timeout_seconds,
Craig Tiller547db2b2015-01-30 14:08:39 -080099 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700100 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700101 flake_retries=5 if args.allow_flakes else 0,
102 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800103
104
105# ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
106class ValgrindConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800107
murgatroid99132ce6a2015-03-04 17:29:14 -0800108 def __init__(self, config, tool, args=None):
109 if args is None:
110 args = []
Craig Tiller738c3342015-01-12 14:28:33 -0800111 self.build_config = config
Craig Tiller2aa4d642015-01-14 15:59:44 -0800112 self.tool = tool
Craig Tiller1a305b12015-02-18 13:37:06 -0800113 self.args = args
Craig Tillerc7449162015-01-16 14:42:10 -0800114 self.allow_hashing = False
Craig Tiller738c3342015-01-12 14:28:33 -0800115
Craig Tiller49f61322015-03-03 13:02:11 -0800116 def job_spec(self, cmdline, hash_targets):
Craig Tiller1a305b12015-02-18 13:37:06 -0800117 return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
Craig Tiller49f61322015-03-03 13:02:11 -0800118 self.args + cmdline,
Craig Tiller71ec6cb2015-06-03 00:51:11 -0700119 shortname='valgrind %s' % cmdline[0],
Craig Tillerd4509a12015-09-28 09:18:40 -0700120 hash_targets=None,
Craig Tiller95cc07b2015-09-28 13:41:30 -0700121 flake_retries=5 if args.allow_flakes else 0,
Craig Tiller35505de2015-10-08 13:31:33 -0700122 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800123
124
murgatroid99cf08daf2015-09-21 15:33:16 -0700125def get_c_tests(travis, test_lang) :
126 out = []
127 platforms_str = 'ci_platforms' if travis else 'platforms'
128 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700129 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700130 return [tgt
131 for tgt in js
132 if tgt['language'] == test_lang and
133 platform_string() in tgt[platforms_str] and
134 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700135
murgatroid99fafeeb32015-09-22 09:13:03 -0700136
Craig Tillerc7449162015-01-16 14:42:10 -0800137class CLanguage(object):
138
Craig Tillere9c959d2015-01-18 10:23:26 -0800139 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800140 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700141 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700142 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800143
Craig Tiller883064c2015-11-04 10:06:10 -0800144 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800145 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800146 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700147 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700148 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700149 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700150 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700151 binary = 'vsprojects/%s/%s.exe' % (
152 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700153 else:
154 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700155 if os.path.isfile(binary):
Craig Tillercc0535d2015-12-08 15:14:47 -0800156 out.append(config.job_spec([binary], [binary],
157 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
158 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800159 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
Craig Tiller883064c2015-11-04 10:06:10 -0800160 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700161 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700162 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800163
Craig Tiller883064c2015-11-04 10:06:10 -0800164 def make_targets(self, test_regex):
165 if platform_string() != 'windows' and test_regex != '.*':
166 # use the regex to minimize the number of things to build
167 return [target['name']
168 for target in get_c_tests(False, self.test_lang)
169 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700170 if platform_string() == 'windows':
171 # don't build tools on windows just yet
172 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700173 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800174
murgatroid99256d3df2015-09-21 16:58:02 -0700175 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700176 if self.platform == 'windows':
177 return [['tools\\run_tests\\pre_build_c.bat']]
178 else:
179 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700180
Craig Tillerc7449162015-01-16 14:42:10 -0800181 def build_steps(self):
182 return []
183
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200184 def post_tests_steps(self):
185 if self.platform == 'windows':
186 return []
187 else:
188 return [['tools/run_tests/post_tests_c.sh']]
189
murgatroid99a3e244f2015-09-22 11:25:53 -0700190 def makefile_name(self):
191 return 'Makefile'
192
murgatroid99132ce6a2015-03-04 17:29:14 -0800193 def supports_multi_config(self):
194 return True
195
196 def __str__(self):
197 return self.make_target
198
Craig Tillercc0535d2015-12-08 15:14:47 -0800199
murgatroid992c8d5162015-01-26 10:41:21 -0800200class NodeLanguage(object):
201
Craig Tiller883064c2015-11-04 10:06:10 -0800202 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700203 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700204 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800205
murgatroid99256d3df2015-09-21 16:58:02 -0700206 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700207 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700208 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700209
Craig Tiller883064c2015-11-04 10:06:10 -0800210 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700211 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800212
213 def build_steps(self):
214 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800215
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200216 def post_tests_steps(self):
217 return []
218
murgatroid99a3e244f2015-09-22 11:25:53 -0700219 def makefile_name(self):
220 return 'Makefile'
221
murgatroid99132ce6a2015-03-04 17:29:14 -0800222 def supports_multi_config(self):
223 return False
224
225 def __str__(self):
226 return 'node'
227
Craig Tiller99775822015-01-30 13:07:16 -0800228
Craig Tillerc7449162015-01-16 14:42:10 -0800229class PhpLanguage(object):
230
Craig Tiller883064c2015-11-04 10:06:10 -0800231 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700232 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700233 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800234
murgatroid99256d3df2015-09-21 16:58:02 -0700235 def pre_build_steps(self):
236 return []
237
Craig Tiller883064c2015-11-04 10:06:10 -0800238 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700239 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800240
241 def build_steps(self):
242 return [['tools/run_tests/build_php.sh']]
243
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200244 def post_tests_steps(self):
245 return []
246
murgatroid99a3e244f2015-09-22 11:25:53 -0700247 def makefile_name(self):
248 return 'Makefile'
249
murgatroid99132ce6a2015-03-04 17:29:14 -0800250 def supports_multi_config(self):
251 return False
252
253 def __str__(self):
254 return 'php'
255
Craig Tillerc7449162015-01-16 14:42:10 -0800256
Nathaniel Manista840615e2015-01-22 20:31:47 +0000257class PythonLanguage(object):
258
Craig Tiller49f61322015-03-03 13:02:11 -0800259 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700260 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700261 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800262
Craig Tiller883064c2015-11-04 10:06:10 -0800263 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700264 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
265 environment['PYVER'] = '2.7'
266 return [config.job_spec(
267 ['tools/run_tests/run_python.sh'],
268 None,
269 environ=environment,
270 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700271 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700272 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000273
murgatroid99256d3df2015-09-21 16:58:02 -0700274 def pre_build_steps(self):
275 return []
276
Craig Tiller883064c2015-11-04 10:06:10 -0800277 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700278 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000279
280 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700281 commands = []
282 for python_version in self._build_python_versions:
283 try:
284 with open(os.devnull, 'w') as output:
285 subprocess.check_call(['which', 'python' + python_version],
286 stdout=output, stderr=output)
287 commands.append(['tools/run_tests/build_python.sh', python_version])
288 self._has_python_versions.append(python_version)
289 except:
290 jobset.message('WARNING', 'Missing Python ' + python_version,
291 do_newline=True)
292 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000293
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200294 def post_tests_steps(self):
295 return []
296
murgatroid99a3e244f2015-09-22 11:25:53 -0700297 def makefile_name(self):
298 return 'Makefile'
299
murgatroid99132ce6a2015-03-04 17:29:14 -0800300 def supports_multi_config(self):
301 return False
302
303 def __str__(self):
304 return 'python'
305
Craig Tillerd625d812015-04-08 15:52:35 -0700306
murgatroid996a4c4fa2015-02-27 12:08:57 -0800307class RubyLanguage(object):
308
Craig Tiller883064c2015-11-04 10:06:10 -0800309 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700310 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700311 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800312
murgatroid99256d3df2015-09-21 16:58:02 -0700313 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200314 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700315
Craig Tiller883064c2015-11-04 10:06:10 -0800316 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700317 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800318
319 def build_steps(self):
320 return [['tools/run_tests/build_ruby.sh']]
321
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200322 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100323 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200324
murgatroid99a3e244f2015-09-22 11:25:53 -0700325 def makefile_name(self):
326 return 'Makefile'
327
murgatroid99132ce6a2015-03-04 17:29:14 -0800328 def supports_multi_config(self):
329 return False
330
331 def __str__(self):
332 return 'ruby'
333
Craig Tillerd625d812015-04-08 15:52:35 -0700334
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800335class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700336 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700337 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700338
Craig Tiller883064c2015-11-04 10:06:10 -0800339 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800340 with open('src/csharp/tests.json') as f:
341 tests_json = json.load(f)
342 assemblies = tests_json['assemblies']
343 tests = tests_json['tests']
344
345 msbuild_config = _WINDOWS_CONFIG[config.build_config]
346 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
347 for a in assemblies]
348
349 extra_args = ['-labels'] + assembly_files
350
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700351 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800352 script_name = 'tools\\run_tests\\run_csharp.bat'
353 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700354 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800355 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700356
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700357 if config.build_config == 'gcov':
358 # On Windows, we only collect C# code coverage.
359 # On Linux, we only collect coverage for native extension.
360 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800361 return [config.job_spec([script_name] + extra_args, None,
362 shortname='csharp.coverage',
363 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700364 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800365 specs = []
366 for test in tests:
367 cmdline = [script_name, '-run=%s' % test] + extra_args
368 if self.platform == 'windows':
369 # use different output directory for each test to prevent
370 # TestResult.xml clash between parallel test runs.
371 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
372 specs.append(config.job_spec(cmdline, None,
373 shortname='csharp.%s' % test,
374 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
375 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800376
murgatroid99256d3df2015-09-21 16:58:02 -0700377 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700378 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700379 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700380 else:
381 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700382
Craig Tiller883064c2015-11-04 10:06:10 -0800383 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700384 # For Windows, this target doesn't really build anything,
385 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700386 if self.platform == 'windows':
387 return []
388 else:
389 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800390
391 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700392 if self.platform == 'windows':
393 return [['src\\csharp\\buildall.bat']]
394 else:
395 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000396
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200397 def post_tests_steps(self):
398 return []
399
murgatroid99a3e244f2015-09-22 11:25:53 -0700400 def makefile_name(self):
401 return 'Makefile'
402
murgatroid99132ce6a2015-03-04 17:29:14 -0800403 def supports_multi_config(self):
404 return False
405
406 def __str__(self):
407 return 'csharp'
408
Craig Tillerd625d812015-04-08 15:52:35 -0700409
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700410class ObjCLanguage(object):
411
Craig Tiller883064c2015-11-04 10:06:10 -0800412 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700413 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
414 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
415
murgatroid99256d3df2015-09-21 16:58:02 -0700416 def pre_build_steps(self):
417 return []
418
Craig Tiller883064c2015-11-04 10:06:10 -0800419 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700420 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700421
422 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700423 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700424
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200425 def post_tests_steps(self):
426 return []
427
murgatroid99a3e244f2015-09-22 11:25:53 -0700428 def makefile_name(self):
429 return 'Makefile'
430
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700431 def supports_multi_config(self):
432 return False
433
434 def __str__(self):
435 return 'objc'
436
437
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100438class Sanity(object):
439
Craig Tiller883064c2015-11-04 10:06:10 -0800440 def test_specs(self, config, args):
Jan Tattermusche3d66252015-10-26 11:33:45 -0700441 return [config.job_spec(['tools/run_tests/run_sanity.sh'], None),
442 config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100443
murgatroid99256d3df2015-09-21 16:58:02 -0700444 def pre_build_steps(self):
445 return []
446
Craig Tiller883064c2015-11-04 10:06:10 -0800447 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100448 return ['run_dep_checks']
449
450 def build_steps(self):
451 return []
452
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200453 def post_tests_steps(self):
454 return []
455
murgatroid99a3e244f2015-09-22 11:25:53 -0700456 def makefile_name(self):
457 return 'Makefile'
458
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100459 def supports_multi_config(self):
460 return False
461
462 def __str__(self):
463 return 'sanity'
464
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200465
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100466class Build(object):
467
Craig Tiller883064c2015-11-04 10:06:10 -0800468 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100469 return []
470
murgatroid99256d3df2015-09-21 16:58:02 -0700471 def pre_build_steps(self):
472 return []
473
Craig Tiller883064c2015-11-04 10:06:10 -0800474 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200475 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100476
477 def build_steps(self):
478 return []
479
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100480 def post_tests_steps(self):
481 return []
482
murgatroid99a3e244f2015-09-22 11:25:53 -0700483 def makefile_name(self):
484 return 'Makefile'
485
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100486 def supports_multi_config(self):
487 return True
488
489 def __str__(self):
490 return self.make_target
491
492
Craig Tiller738c3342015-01-12 14:28:33 -0800493# different configurations we can run under
494_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800495 'dbg': SimpleConfig('dbg'),
496 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700497 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700498 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700499 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800500 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700501 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800502 'ASAN_OPTIONS': 'detect_leaks=1:color=always',
Craig Tillerd4b13622015-05-29 09:10:10 -0700503 'LSAN_OPTIONS': 'report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700504 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800505 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800506 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800507 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800508 'helgrind': ValgrindConfig('dbg', 'helgrind')
509 }
Craig Tiller738c3342015-01-12 14:28:33 -0800510
511
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100512_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800513_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800514 'c++': CLanguage('cxx', 'c++'),
515 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800516 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000517 'php': PhpLanguage(),
518 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800519 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100520 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700521 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100522 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100523 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800524 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800525
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700526_WINDOWS_CONFIG = {
527 'dbg': 'Debug',
528 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800529 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700530 }
531
David Garcia Quintase90cd372015-05-31 18:15:26 -0700532
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800533def _windows_arch_option(arch):
534 """Returns msbuild cmdline option for selected architecture."""
535 if arch == 'default' or arch == 'windows_x86':
536 return '/p:Platform=Win32'
537 elif arch == 'windows_x64':
538 return '/p:Platform=x64'
539 else:
540 print 'Architecture %s not supported on current platform.' % arch
541 sys.exit(1)
542
543
544def _windows_build_bat(compiler):
545 """Returns name of build.bat for selected compiler."""
546 if compiler == 'default' or compiler == 'vs2013':
547 return 'vsprojects\\build_vs2013.bat'
548 elif compiler == 'vs2015':
549 return 'vsprojects\\build_vs2015.bat'
550 elif compiler == 'vs2010':
551 return 'vsprojects\\build_vs2010.bat'
552 else:
553 print 'Compiler %s not supported.' % compiler
554 sys.exit(1)
555
556
557def _windows_toolset_option(compiler):
558 """Returns msbuild PlatformToolset for selected compiler."""
559 if compiler == 'default' or compiler == 'vs2013':
560 return '/p:PlatformToolset=v120'
561 elif compiler == 'vs2015':
562 return '/p:PlatformToolset=v140'
563 elif compiler == 'vs2010':
564 return '/p:PlatformToolset=v100'
565 else:
566 print 'Compiler %s not supported.' % compiler
567 sys.exit(1)
568
569
David Garcia Quintase90cd372015-05-31 18:15:26 -0700570def runs_per_test_type(arg_str):
571 """Auxilary function to parse the "runs_per_test" flag.
572
573 Returns:
574 A positive integer or 0, the latter indicating an infinite number of
575 runs.
576
577 Raises:
578 argparse.ArgumentTypeError: Upon invalid input.
579 """
580 if arg_str == 'inf':
581 return 0
582 try:
583 n = int(arg_str)
584 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700585 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700586 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700587 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700588 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700589
590# parse command line
591argp = argparse.ArgumentParser(description='Run grpc tests.')
592argp.add_argument('-c', '--config',
593 choices=['all'] + sorted(_CONFIGS.keys()),
594 nargs='+',
595 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700596argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
597 help='A positive integer or "inf". If "inf", all tests will run in an '
598 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800599argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller83762ac2015-05-22 14:04:06 -0700600argp.add_argument('-j', '--jobs', default=2 * multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800601argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800602argp.add_argument('-f', '--forever',
603 default=False,
604 action='store_const',
605 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100606argp.add_argument('-t', '--travis',
607 default=False,
608 action='store_const',
609 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800610argp.add_argument('--newline_on_success',
611 default=False,
612 action='store_const',
613 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800614argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700615 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800616 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700617 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700618argp.add_argument('-S', '--stop_on_failure',
619 default=False,
620 action='store_const',
621 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700622argp.add_argument('--use_docker',
623 default=False,
624 action='store_const',
625 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700626 help='Run all the tests under docker. That provides ' +
627 'additional isolation and prevents the need to install ' +
628 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700629argp.add_argument('--allow_flakes',
630 default=False,
631 action='store_const',
632 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700633 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800634argp.add_argument('--arch',
635 choices=['default', 'windows_x86', 'windows_x64'],
636 default='default',
637 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
638argp.add_argument('--compiler',
639 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
640 default='default',
641 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
642argp.add_argument('--build_only',
643 default=False,
644 action='store_const',
645 const=True,
646 help='Perform all the build steps but dont run any tests.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700647argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200648argp.add_argument('-x', '--xml_report', default=None, type=str,
649 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800650args = argp.parse_args()
651
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700652if args.use_docker:
653 if not args.travis:
654 print 'Seen --use_docker flag, will run tests under docker.'
655 print
656 print 'IMPORTANT: The changes you are testing need to be locally committed'
657 print 'because only the committed changes in the current branch will be'
658 print 'copied to the docker environment.'
659 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700660
661 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700662 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700663
664 # TODO(jtattermusch): revisit if we need special handling for arch here
665 # set arch command prefix in case we are working with different arch.
666 arch_env = os.getenv('arch')
667 if arch_env:
668 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
669
670 env = os.environ.copy()
671 env['RUN_TESTS_COMMAND'] = run_tests_cmd
672 if args.xml_report:
673 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700674 if not args.travis:
675 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700676
677 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
678 shell=True,
679 env=env)
680 sys.exit(0)
681
Nicolas Nobleddef2462015-01-06 18:08:25 -0800682# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800683run_configs = set(_CONFIGS[cfg]
684 for cfg in itertools.chain.from_iterable(
685 _CONFIGS.iterkeys() if x == 'all' else [x]
686 for x in args.config))
687build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800688
Craig Tiller06805272015-06-11 14:46:47 -0700689if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700690 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700691
Adele Zhou6b9527c2015-11-20 15:56:35 -0800692if 'all' in args.language:
693 lang_list = _LANGUAGES.keys()
694else:
695 lang_list = args.language
696# We don't support code coverage on ObjC
697if 'gcov' in args.config and 'objc' in lang_list:
698 lang_list.remove('objc')
699
700languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800701
702if len(build_configs) > 1:
703 for language in languages:
704 if not language.supports_multi_config():
705 print language, 'does not support multiple build configurations'
706 sys.exit(1)
707
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800708if platform_string() != 'windows':
709 if args.arch != 'default':
710 print 'Architecture %s not supported on current platform.' % args.arch
711 sys.exit(1)
712 if args.compiler != 'default':
713 print 'Compiler %s not supported on current platform.' % args.compiler
714 sys.exit(1)
715
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100716if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700717 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700718 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700719 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700720 # empirically /m:2 gives the best performance/price and should prevent
721 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700722 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700723 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700724 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700725 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800726 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700727 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800728 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
729 _windows_toolset_option(args.compiler),
730 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700731 extra_args,
Craig Tillerdfc3eee2015-09-01 16:32:16 -0700732 shell=True, timeout_seconds=90*60)
Craig Tiller6fd23842015-09-01 07:36:31 -0700733 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700734else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700735 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700736 if targets:
737 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
738 '-f', makefile,
739 '-j', '%d' % (multiprocessing.cpu_count() + 1),
740 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
741 args.slowdown,
742 'CONFIG=%s' % cfg] + targets,
743 timeout_seconds=30*60)]
744 else:
745 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700746make_targets = {}
747for l in languages:
748 makefile = l.makefile_name()
749 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800750 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700751
Jan Tattermusche4a69182015-12-15 09:53:01 -0800752def build_step_environ(cfg):
753 environ = {'CONFIG': cfg}
754 msbuild_cfg = _WINDOWS_CONFIG[cfg]
755 if msbuild_cfg:
756 environ['MSBUILD_CONFIG'] = msbuild_cfg
757 return environ
758
murgatroid99fddac962015-09-22 09:20:11 -0700759build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800760 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700761 for cfg in build_configs
762 for l in languages
763 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700764if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700765 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 -0700766 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700767build_steps.extend(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800768 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=10*60)
murgatroid99132ce6a2015-03-04 17:29:14 -0800769 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800770 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700771 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800772
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200773post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800774 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200775 for cfg in build_configs
776 for l in languages
777 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800778runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800779forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800780
Nicolas Nobleddef2462015-01-06 18:08:25 -0800781
Craig Tiller71735182015-01-15 17:07:13 -0800782class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800783 """Cache for running tests."""
784
David Klempner25739582015-02-11 15:57:32 -0800785 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800786 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800787 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700788 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800789
790 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800791 if cmdline not in self._last_successful_run:
792 return True
793 if self._last_successful_run[cmdline] != bin_hash:
794 return True
David Klempner25739582015-02-11 15:57:32 -0800795 if not self._use_cache_results:
796 return True
Craig Tiller71735182015-01-15 17:07:13 -0800797 return False
798
799 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800800 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700801 if time.time() - self._last_save > 1:
802 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800803
804 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800805 return [{'cmdline': k, 'hash': v}
806 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800807
808 def parse(self, exdump):
809 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
810
811 def save(self):
812 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800813 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700814 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800815
Craig Tiller1cc11db2015-01-15 22:50:50 -0800816 def maybe_load(self):
817 if os.path.exists('.run_tests_cache'):
818 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800819 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800820
821
Craig Tillerf53d9c82015-08-04 14:19:43 -0700822def _start_port_server(port_server_port):
823 # check if a compatible port server is running
824 # if incompatible (version mismatch) ==> start a new one
825 # if not running ==> start a new one
826 # otherwise, leave it up
827 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700828 version = int(urllib2.urlopen(
829 'http://localhost:%d/version_number' % port_server_port,
830 timeout=1).read())
831 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700832 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700833 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700834 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700835 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700836 running = False
837 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700838 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800839 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
840 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700841 print 'my port server is version %d' % current_version
842 running = (version >= current_version)
843 if not running:
844 print 'port_server version mismatch: killing the old one'
845 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
846 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700847 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700848 fd, logfile = tempfile.mkstemp()
849 os.close(fd)
850 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800851 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
852 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700853 env = dict(os.environ)
854 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100855 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800856 # Working directory of port server needs to be outside of Jenkins
857 # workspace to prevent file lock issues.
858 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700859 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700860 args,
861 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800862 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700863 creationflags = 0x00000008, # detached process
864 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700865 else:
866 port_server = subprocess.Popen(
867 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700868 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700869 preexec_fn=os.setsid,
870 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700871 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700872 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700873 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700874 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700875 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700876 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700877 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700878 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700879 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700880 # try one final time: maybe another build managed to start one
881 time.sleep(1)
882 try:
883 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
884 timeout=1).read()
885 print 'last ditch attempt to contact port server succeeded'
886 break
887 except:
888 traceback.print_exc();
889 port_log = open(logfile, 'r').read()
890 print port_log
891 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700892 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700893 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
894 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700895 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700896 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700897 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700898 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700899 traceback.print_exc();
900 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700901 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700902 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700903 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700904 traceback.print_exc();
905 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700906 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700907 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700908 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700909 port_server.kill()
910 raise
911
912
Adele Zhoud5fffa52015-10-23 15:51:42 -0700913def _calculate_num_runs_failures(list_of_results):
914 """Caculate number of runs and failures for a particular test.
915
916 Args:
917 list_of_results: (List) of JobResult object.
918 Returns:
919 A tuple of total number of runs and failures.
920 """
921 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
922 num_failures = 0
923 for jobresult in list_of_results:
924 if jobresult.retries > 0:
925 num_runs += jobresult.retries
926 if jobresult.num_failures > 0:
927 num_failures += jobresult.num_failures
928 return num_runs, num_failures
929
Adele Zhou6b9527c2015-11-20 15:56:35 -0800930
Craig Tillerf53d9c82015-08-04 14:19:43 -0700931def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800932 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800933 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800934 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700935 num_failures, _ = jobset.run(
936 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800937 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700938 if num_failures:
Craig Tillerd86a3942015-01-14 12:48:54 -0800939 return 1
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800940
941 if build_only:
942 return 0
ctiller3040cb72015-01-07 12:13:17 -0800943
Craig Tiller234b6e72015-05-23 10:12:40 -0700944 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700945 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700946 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700947 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700948 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800949 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800950 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700951 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700952 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700953 one_run = set(
954 spec
955 for config in run_configs
956 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800957 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700958 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700959 # When running on travis, we want out test runs to be as similar as possible
960 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800961 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700962 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
963 else:
964 # whereas otherwise, we want to shuffle things up to give all tests a
965 # chance to run.
966 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
967 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -0700968 if infinite_runs:
969 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 -0700970 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
971 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -0700972 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200973
Adele Zhou803af152015-11-30 15:16:16 -0800974 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -0700975 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -0800976 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -0700977 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700978 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700979 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -0700980 if resultset:
981 for k, v in resultset.iteritems():
982 num_runs, num_failures = _calculate_num_runs_failures(v)
983 if num_failures == num_runs: # what about infinite_runs???
984 jobset.message('FAILED', k, do_newline=True)
985 elif num_failures > 0:
986 jobset.message(
987 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
988 do_newline=True)
989 else:
990 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -0700991 finally:
992 for antagonist in antagonists:
993 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -0800994 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -0800995 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -0800996
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700997 number_failures, _ = jobset.run(
998 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800999 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhou803af152015-11-30 15:16:16 -08001000 if num_test_failures or number_failures:
1001 return 2
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001002
Craig Tiller69cd2372015-06-11 09:38:09 -07001003 if cache: cache.save()
1004
Craig Tillerd86a3942015-01-14 12:48:54 -08001005 return 0
ctiller3040cb72015-01-07 12:13:17 -08001006
1007
David Klempner25739582015-02-11 15:57:32 -08001008test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001009test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001010
ctiller3040cb72015-01-07 12:13:17 -08001011if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001012 success = True
ctiller3040cb72015-01-07 12:13:17 -08001013 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001014 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001015 initial_time = dw.most_recent_change()
1016 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001017 previous_success = success
Craig Tiller71735182015-01-15 17:07:13 -08001018 success = _build_and_run(check_cancelled=have_files_changed,
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001019 newline_on_success=False,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001020 cache=test_cache,
1021 build_only=args.build_only) == 0
Nicolas Noble044db742015-01-14 16:57:24 -08001022 if not previous_success and success:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001023 jobset.message('SUCCESS',
1024 'All tests are now passing properly',
1025 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001026 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001027 while not have_files_changed():
1028 time.sleep(1)
1029else:
Craig Tiller71735182015-01-15 17:07:13 -08001030 result = _build_and_run(check_cancelled=lambda: False,
1031 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001032 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001033 xml_report=args.xml_report,
1034 build_only=args.build_only)
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001035 if result == 0:
1036 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1037 else:
1038 jobset.message('FAILED', 'Some tests failed', do_newline=True)
1039 sys.exit(result)