blob: e3df912480389b105eaaef8e4c53f25e0125df6c [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tillerc2c79212015-02-16 12:00:01 -08002# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
Nicolas Nobleddef2462015-01-06 18:08:25 -080031"""Run tests in parallel."""
32
33import argparse
34import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070035import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080036import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080037import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080038import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080039import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070040import platform
41import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080042import re
Craig Tiller82875232015-09-25 13:57:34 -070043import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070044import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080045import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070046import tempfile
47import traceback
ctiller3040cb72015-01-07 12:13:17 -080048import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070049import urllib2
Nicolas Nobleddef2462015-01-06 18:08:25 -080050
51import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080052import report_utils
ctiller3040cb72015-01-07 12:13:17 -080053import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080054
Craig Tiller2cc2b842015-02-27 11:38:31 -080055ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
56os.chdir(ROOT)
57
58
Craig Tiller06805272015-06-11 14:46:47 -070059_FORCE_ENVIRON_FOR_WRAPPERS = {}
60
61
Craig Tillerd50993d2015-08-05 08:04:36 -070062def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010063 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070064
65
Craig Tiller738c3342015-01-12 14:28:33 -080066# SimpleConfig: just compile with CONFIG=config, and run the binary to test
67class SimpleConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080068
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070069 def __init__(self, config, environ=None, timeout_multiplier=1):
murgatroid99132ce6a2015-03-04 17:29:14 -080070 if environ is None:
71 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080072 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080073 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080074 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080075 self.environ['CONFIG'] = config
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070076 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080077
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070078 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
79 shortname=None, environ={}):
Craig Tiller49f61322015-03-03 13:02:11 -080080 """Construct a jobset.JobSpec for a test under this config
81
82 Args:
83 cmdline: a list of strings specifying the command line the test
84 would like to run
85 hash_targets: either None (don't do caching of test results), or
86 a list of strings specifying files to include in a
87 binary hash to check if a test has changed
88 -- if used, all artifacts needed to run the test must
89 be listed
90 """
Craig Tiller4fc90032015-05-21 10:39:52 -070091 actual_environ = self.environ.copy()
92 for k, v in environ.iteritems():
93 actual_environ[k] = v
Craig Tiller49f61322015-03-03 13:02:11 -080094 return jobset.JobSpec(cmdline=cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070095 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -070096 environ=actual_environ,
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070097 timeout_seconds=self.timeout_multiplier * timeout_seconds,
Craig Tiller547db2b2015-01-30 14:08:39 -080098 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -070099 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700100 flake_retries=5 if args.allow_flakes else 0,
101 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800102
103
104# ValgrindConfig: compile with some CONFIG=config, but use valgrind to run
105class ValgrindConfig(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800106
murgatroid99132ce6a2015-03-04 17:29:14 -0800107 def __init__(self, config, tool, args=None):
108 if args is None:
109 args = []
Craig Tiller738c3342015-01-12 14:28:33 -0800110 self.build_config = config
Craig Tiller2aa4d642015-01-14 15:59:44 -0800111 self.tool = tool
Craig Tiller1a305b12015-02-18 13:37:06 -0800112 self.args = args
Craig Tillerc7449162015-01-16 14:42:10 -0800113 self.allow_hashing = False
Craig Tiller738c3342015-01-12 14:28:33 -0800114
Craig Tiller49f61322015-03-03 13:02:11 -0800115 def job_spec(self, cmdline, hash_targets):
Craig Tiller1a305b12015-02-18 13:37:06 -0800116 return jobset.JobSpec(cmdline=['valgrind', '--tool=%s' % self.tool] +
Craig Tiller49f61322015-03-03 13:02:11 -0800117 self.args + cmdline,
Craig Tiller71ec6cb2015-06-03 00:51:11 -0700118 shortname='valgrind %s' % cmdline[0],
Craig Tillerd4509a12015-09-28 09:18:40 -0700119 hash_targets=None,
Craig Tiller95cc07b2015-09-28 13:41:30 -0700120 flake_retries=5 if args.allow_flakes else 0,
Craig Tiller35505de2015-10-08 13:31:33 -0700121 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800122
123
murgatroid99cf08daf2015-09-21 15:33:16 -0700124def get_c_tests(travis, test_lang) :
125 out = []
126 platforms_str = 'ci_platforms' if travis else 'platforms'
127 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700128 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700129 return [tgt
130 for tgt in js
131 if tgt['language'] == test_lang and
132 platform_string() in tgt[platforms_str] and
133 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700134
murgatroid99fafeeb32015-09-22 09:13:03 -0700135
Craig Tillerc7449162015-01-16 14:42:10 -0800136class CLanguage(object):
137
Craig Tillere9c959d2015-01-18 10:23:26 -0800138 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800139 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700140 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700141 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800142
Craig Tiller883064c2015-11-04 10:06:10 -0800143 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800144 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800145 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700146 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700147 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700148 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700149 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700150 binary = 'vsprojects/%s/%s.exe' % (
151 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700152 else:
153 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700154 if os.path.isfile(binary):
155 out.append(config.job_spec([binary], [binary]))
Craig Tiller883064c2015-11-04 10:06:10 -0800156 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700157 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700158 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800159
Craig Tiller883064c2015-11-04 10:06:10 -0800160 def make_targets(self, test_regex):
161 if platform_string() != 'windows' and test_regex != '.*':
162 # use the regex to minimize the number of things to build
163 return [target['name']
164 for target in get_c_tests(False, self.test_lang)
165 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700166 if platform_string() == 'windows':
167 # don't build tools on windows just yet
168 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700169 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800170
murgatroid99256d3df2015-09-21 16:58:02 -0700171 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700172 if self.platform == 'windows':
173 return [['tools\\run_tests\\pre_build_c.bat']]
174 else:
175 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700176
Craig Tillerc7449162015-01-16 14:42:10 -0800177 def build_steps(self):
178 return []
179
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200180 def post_tests_steps(self):
181 if self.platform == 'windows':
182 return []
183 else:
184 return [['tools/run_tests/post_tests_c.sh']]
185
murgatroid99a3e244f2015-09-22 11:25:53 -0700186 def makefile_name(self):
187 return 'Makefile'
188
murgatroid99132ce6a2015-03-04 17:29:14 -0800189 def supports_multi_config(self):
190 return True
191
192 def __str__(self):
193 return self.make_target
194
murgatroid992c8d5162015-01-26 10:41:21 -0800195class NodeLanguage(object):
196
Craig Tiller883064c2015-11-04 10:06:10 -0800197 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700198 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700199 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800200
murgatroid99256d3df2015-09-21 16:58:02 -0700201 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700202 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700203 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700204
Craig Tiller883064c2015-11-04 10:06:10 -0800205 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700206 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800207
208 def build_steps(self):
209 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800210
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200211 def post_tests_steps(self):
212 return []
213
murgatroid99a3e244f2015-09-22 11:25:53 -0700214 def makefile_name(self):
215 return 'Makefile'
216
murgatroid99132ce6a2015-03-04 17:29:14 -0800217 def supports_multi_config(self):
218 return False
219
220 def __str__(self):
221 return 'node'
222
Craig Tiller99775822015-01-30 13:07:16 -0800223
Craig Tillerc7449162015-01-16 14:42:10 -0800224class PhpLanguage(object):
225
Craig Tiller883064c2015-11-04 10:06:10 -0800226 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700227 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700228 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800229
murgatroid99256d3df2015-09-21 16:58:02 -0700230 def pre_build_steps(self):
231 return []
232
Craig Tiller883064c2015-11-04 10:06:10 -0800233 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700234 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800235
236 def build_steps(self):
237 return [['tools/run_tests/build_php.sh']]
238
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200239 def post_tests_steps(self):
240 return []
241
murgatroid99a3e244f2015-09-22 11:25:53 -0700242 def makefile_name(self):
243 return 'Makefile'
244
murgatroid99132ce6a2015-03-04 17:29:14 -0800245 def supports_multi_config(self):
246 return False
247
248 def __str__(self):
249 return 'php'
250
Craig Tillerc7449162015-01-16 14:42:10 -0800251
Nathaniel Manista840615e2015-01-22 20:31:47 +0000252class PythonLanguage(object):
253
Craig Tiller49f61322015-03-03 13:02:11 -0800254 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700255 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700256 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800257
Craig Tiller883064c2015-11-04 10:06:10 -0800258 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700259 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
260 environment['PYVER'] = '2.7'
261 return [config.job_spec(
262 ['tools/run_tests/run_python.sh'],
263 None,
264 environ=environment,
265 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700266 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700267 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000268
murgatroid99256d3df2015-09-21 16:58:02 -0700269 def pre_build_steps(self):
270 return []
271
Craig Tiller883064c2015-11-04 10:06:10 -0800272 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700273 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000274
275 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700276 commands = []
277 for python_version in self._build_python_versions:
278 try:
279 with open(os.devnull, 'w') as output:
280 subprocess.check_call(['which', 'python' + python_version],
281 stdout=output, stderr=output)
282 commands.append(['tools/run_tests/build_python.sh', python_version])
283 self._has_python_versions.append(python_version)
284 except:
285 jobset.message('WARNING', 'Missing Python ' + python_version,
286 do_newline=True)
287 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000288
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200289 def post_tests_steps(self):
290 return []
291
murgatroid99a3e244f2015-09-22 11:25:53 -0700292 def makefile_name(self):
293 return 'Makefile'
294
murgatroid99132ce6a2015-03-04 17:29:14 -0800295 def supports_multi_config(self):
296 return False
297
298 def __str__(self):
299 return 'python'
300
Craig Tillerd625d812015-04-08 15:52:35 -0700301
murgatroid996a4c4fa2015-02-27 12:08:57 -0800302class RubyLanguage(object):
303
Craig Tiller883064c2015-11-04 10:06:10 -0800304 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700305 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700306 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800307
murgatroid99256d3df2015-09-21 16:58:02 -0700308 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200309 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700310
Craig Tiller883064c2015-11-04 10:06:10 -0800311 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700312 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800313
314 def build_steps(self):
315 return [['tools/run_tests/build_ruby.sh']]
316
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200317 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100318 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200319
murgatroid99a3e244f2015-09-22 11:25:53 -0700320 def makefile_name(self):
321 return 'Makefile'
322
murgatroid99132ce6a2015-03-04 17:29:14 -0800323 def supports_multi_config(self):
324 return False
325
326 def __str__(self):
327 return 'ruby'
328
Craig Tillerd625d812015-04-08 15:52:35 -0700329
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800330class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700331 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700332 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700333
Craig Tiller883064c2015-11-04 10:06:10 -0800334 def test_specs(self, config, args):
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -0700335 assemblies = ['Grpc.Core.Tests',
336 'Grpc.Examples.Tests',
Jan Tattermusch9d67d8d2015-08-01 20:39:16 -0700337 'Grpc.HealthCheck.Tests',
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -0700338 'Grpc.IntegrationTesting']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700339 if self.platform == 'windows':
340 cmd = 'tools\\run_tests\\run_csharp.bat'
341 else:
342 cmd = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700343
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700344 if config.build_config == 'gcov':
345 # On Windows, we only collect C# code coverage.
346 # On Linux, we only collect coverage for native extension.
347 # For code coverage all tests need to run as one suite.
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700348 return [config.job_spec([cmd], None,
349 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
350 else:
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700351 return [config.job_spec([cmd, assembly],
352 None, shortname=assembly,
353 environ=_FORCE_ENVIRON_FOR_WRAPPERS)
354 for assembly in assemblies]
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800355
murgatroid99256d3df2015-09-21 16:58:02 -0700356 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700357 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700358 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700359 else:
360 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700361
Craig Tiller883064c2015-11-04 10:06:10 -0800362 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700363 # For Windows, this target doesn't really build anything,
364 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700365 if self.platform == 'windows':
366 return []
367 else:
368 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800369
370 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700371 if self.platform == 'windows':
372 return [['src\\csharp\\buildall.bat']]
373 else:
374 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000375
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200376 def post_tests_steps(self):
377 return []
378
murgatroid99a3e244f2015-09-22 11:25:53 -0700379 def makefile_name(self):
380 return 'Makefile'
381
murgatroid99132ce6a2015-03-04 17:29:14 -0800382 def supports_multi_config(self):
383 return False
384
385 def __str__(self):
386 return 'csharp'
387
Craig Tillerd625d812015-04-08 15:52:35 -0700388
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700389class ObjCLanguage(object):
390
Craig Tiller883064c2015-11-04 10:06:10 -0800391 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700392 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
393 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
394
murgatroid99256d3df2015-09-21 16:58:02 -0700395 def pre_build_steps(self):
396 return []
397
Craig Tiller883064c2015-11-04 10:06:10 -0800398 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700399 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700400
401 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700402 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700403
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200404 def post_tests_steps(self):
405 return []
406
murgatroid99a3e244f2015-09-22 11:25:53 -0700407 def makefile_name(self):
408 return 'Makefile'
409
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700410 def supports_multi_config(self):
411 return False
412
413 def __str__(self):
414 return 'objc'
415
416
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100417class Sanity(object):
418
Craig Tiller883064c2015-11-04 10:06:10 -0800419 def test_specs(self, config, args):
Jan Tattermusche3d66252015-10-26 11:33:45 -0700420 return [config.job_spec(['tools/run_tests/run_sanity.sh'], None),
421 config.job_spec(['tools/run_tests/check_sources_and_headers.py'], None)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100422
murgatroid99256d3df2015-09-21 16:58:02 -0700423 def pre_build_steps(self):
424 return []
425
Craig Tiller883064c2015-11-04 10:06:10 -0800426 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100427 return ['run_dep_checks']
428
429 def build_steps(self):
430 return []
431
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200432 def post_tests_steps(self):
433 return []
434
murgatroid99a3e244f2015-09-22 11:25:53 -0700435 def makefile_name(self):
436 return 'Makefile'
437
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100438 def supports_multi_config(self):
439 return False
440
441 def __str__(self):
442 return 'sanity'
443
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200444
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100445class Build(object):
446
Craig Tiller883064c2015-11-04 10:06:10 -0800447 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100448 return []
449
murgatroid99256d3df2015-09-21 16:58:02 -0700450 def pre_build_steps(self):
451 return []
452
Craig Tiller883064c2015-11-04 10:06:10 -0800453 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200454 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100455
456 def build_steps(self):
457 return []
458
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100459 def post_tests_steps(self):
460 return []
461
murgatroid99a3e244f2015-09-22 11:25:53 -0700462 def makefile_name(self):
463 return 'Makefile'
464
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100465 def supports_multi_config(self):
466 return True
467
468 def __str__(self):
469 return self.make_target
470
471
Craig Tiller738c3342015-01-12 14:28:33 -0800472# different configurations we can run under
473_CONFIGS = {
Craig Tillerb50d1662015-01-15 17:28:21 -0800474 'dbg': SimpleConfig('dbg'),
475 'opt': SimpleConfig('opt'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700476 'tsan': SimpleConfig('tsan', timeout_multiplier=2, environ={
Craig Tiller1ada6ad2015-07-16 16:19:14 -0700477 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt:halt_on_error=1:second_deadlock_stack=1'}),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700478 'msan': SimpleConfig('msan', timeout_multiplier=1.5),
Craig Tiller96bd5f62015-02-13 09:04:13 -0800479 'ubsan': SimpleConfig('ubsan'),
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700480 'asan': SimpleConfig('asan', timeout_multiplier=1.5, environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800481 'ASAN_OPTIONS': 'detect_leaks=1:color=always',
Craig Tillerd4b13622015-05-29 09:10:10 -0700482 'LSAN_OPTIONS': 'report_objects=1'}),
Craig Tiller810725c2015-05-12 09:44:41 -0700483 'asan-noleaks': SimpleConfig('asan', environ={
Craig Tiller2a2a6ed2015-11-18 15:52:46 -0800484 'ASAN_OPTIONS': 'detect_leaks=0:color=always'}),
Craig Tillerb50d1662015-01-15 17:28:21 -0800485 'gcov': SimpleConfig('gcov'),
Craig Tiller1a305b12015-02-18 13:37:06 -0800486 'memcheck': ValgrindConfig('valgrind', 'memcheck', ['--leak-check=full']),
Craig Tillerb50d1662015-01-15 17:28:21 -0800487 'helgrind': ValgrindConfig('dbg', 'helgrind')
488 }
Craig Tiller738c3342015-01-12 14:28:33 -0800489
490
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100491_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800492_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800493 'c++': CLanguage('cxx', 'c++'),
494 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800495 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000496 'php': PhpLanguage(),
497 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800498 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100499 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700500 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100501 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100502 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800503 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800504
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700505_WINDOWS_CONFIG = {
506 'dbg': 'Debug',
507 'opt': 'Release',
508 }
509
David Garcia Quintase90cd372015-05-31 18:15:26 -0700510
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800511def _windows_arch_option(arch):
512 """Returns msbuild cmdline option for selected architecture."""
513 if arch == 'default' or arch == 'windows_x86':
514 return '/p:Platform=Win32'
515 elif arch == 'windows_x64':
516 return '/p:Platform=x64'
517 else:
518 print 'Architecture %s not supported on current platform.' % arch
519 sys.exit(1)
520
521
522def _windows_build_bat(compiler):
523 """Returns name of build.bat for selected compiler."""
524 if compiler == 'default' or compiler == 'vs2013':
525 return 'vsprojects\\build_vs2013.bat'
526 elif compiler == 'vs2015':
527 return 'vsprojects\\build_vs2015.bat'
528 elif compiler == 'vs2010':
529 return 'vsprojects\\build_vs2010.bat'
530 else:
531 print 'Compiler %s not supported.' % compiler
532 sys.exit(1)
533
534
535def _windows_toolset_option(compiler):
536 """Returns msbuild PlatformToolset for selected compiler."""
537 if compiler == 'default' or compiler == 'vs2013':
538 return '/p:PlatformToolset=v120'
539 elif compiler == 'vs2015':
540 return '/p:PlatformToolset=v140'
541 elif compiler == 'vs2010':
542 return '/p:PlatformToolset=v100'
543 else:
544 print 'Compiler %s not supported.' % compiler
545 sys.exit(1)
546
547
David Garcia Quintase90cd372015-05-31 18:15:26 -0700548def runs_per_test_type(arg_str):
549 """Auxilary function to parse the "runs_per_test" flag.
550
551 Returns:
552 A positive integer or 0, the latter indicating an infinite number of
553 runs.
554
555 Raises:
556 argparse.ArgumentTypeError: Upon invalid input.
557 """
558 if arg_str == 'inf':
559 return 0
560 try:
561 n = int(arg_str)
562 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700563 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700564 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700565 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700566 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700567
568# parse command line
569argp = argparse.ArgumentParser(description='Run grpc tests.')
570argp.add_argument('-c', '--config',
571 choices=['all'] + sorted(_CONFIGS.keys()),
572 nargs='+',
573 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700574argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
575 help='A positive integer or "inf". If "inf", all tests will run in an '
576 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800577argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller83762ac2015-05-22 14:04:06 -0700578argp.add_argument('-j', '--jobs', default=2 * multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800579argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800580argp.add_argument('-f', '--forever',
581 default=False,
582 action='store_const',
583 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100584argp.add_argument('-t', '--travis',
585 default=False,
586 action='store_const',
587 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800588argp.add_argument('--newline_on_success',
589 default=False,
590 action='store_const',
591 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800592argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700593 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800594 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700595 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700596argp.add_argument('-S', '--stop_on_failure',
597 default=False,
598 action='store_const',
599 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700600argp.add_argument('--use_docker',
601 default=False,
602 action='store_const',
603 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700604 help='Run all the tests under docker. That provides ' +
605 'additional isolation and prevents the need to install ' +
606 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700607argp.add_argument('--allow_flakes',
608 default=False,
609 action='store_const',
610 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700611 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800612argp.add_argument('--arch',
613 choices=['default', 'windows_x86', 'windows_x64'],
614 default='default',
615 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
616argp.add_argument('--compiler',
617 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
618 default='default',
619 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
620argp.add_argument('--build_only',
621 default=False,
622 action='store_const',
623 const=True,
624 help='Perform all the build steps but dont run any tests.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700625argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200626argp.add_argument('-x', '--xml_report', default=None, type=str,
627 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800628args = argp.parse_args()
629
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700630if args.use_docker:
631 if not args.travis:
632 print 'Seen --use_docker flag, will run tests under docker.'
633 print
634 print 'IMPORTANT: The changes you are testing need to be locally committed'
635 print 'because only the committed changes in the current branch will be'
636 print 'copied to the docker environment.'
637 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700638
639 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700640 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700641
642 # TODO(jtattermusch): revisit if we need special handling for arch here
643 # set arch command prefix in case we are working with different arch.
644 arch_env = os.getenv('arch')
645 if arch_env:
646 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
647
648 env = os.environ.copy()
649 env['RUN_TESTS_COMMAND'] = run_tests_cmd
650 if args.xml_report:
651 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700652 if not args.travis:
653 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700654
655 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
656 shell=True,
657 env=env)
658 sys.exit(0)
659
Nicolas Nobleddef2462015-01-06 18:08:25 -0800660# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800661run_configs = set(_CONFIGS[cfg]
662 for cfg in itertools.chain.from_iterable(
663 _CONFIGS.iterkeys() if x == 'all' else [x]
664 for x in args.config))
665build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800666
Craig Tiller06805272015-06-11 14:46:47 -0700667if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700668 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700669
Adele Zhou6b9527c2015-11-20 15:56:35 -0800670if 'all' in args.language:
671 lang_list = _LANGUAGES.keys()
672else:
673 lang_list = args.language
674# We don't support code coverage on ObjC
675if 'gcov' in args.config and 'objc' in lang_list:
676 lang_list.remove('objc')
677
678languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800679
680if len(build_configs) > 1:
681 for language in languages:
682 if not language.supports_multi_config():
683 print language, 'does not support multiple build configurations'
684 sys.exit(1)
685
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800686if platform_string() != 'windows':
687 if args.arch != 'default':
688 print 'Architecture %s not supported on current platform.' % args.arch
689 sys.exit(1)
690 if args.compiler != 'default':
691 print 'Compiler %s not supported on current platform.' % args.compiler
692 sys.exit(1)
693
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100694if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700695 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700696 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700697 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700698 # empirically /m:2 gives the best performance/price and should prevent
699 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700700 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700701 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700702 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700703 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800704 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700705 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800706 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
707 _windows_toolset_option(args.compiler),
708 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700709 extra_args,
Craig Tillerdfc3eee2015-09-01 16:32:16 -0700710 shell=True, timeout_seconds=90*60)
Craig Tiller6fd23842015-09-01 07:36:31 -0700711 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700712else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700713 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700714 if targets:
715 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
716 '-f', makefile,
717 '-j', '%d' % (multiprocessing.cpu_count() + 1),
718 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' %
719 args.slowdown,
720 'CONFIG=%s' % cfg] + targets,
721 timeout_seconds=30*60)]
722 else:
723 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700724make_targets = {}
725for l in languages:
726 makefile = l.makefile_name()
727 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800728 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700729
murgatroid99fddac962015-09-22 09:20:11 -0700730build_steps = list(set(
Nicolas "Pixel" Noble351da8d2015-10-08 02:55:08 +0200731 jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700732 for cfg in build_configs
733 for l in languages
734 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700735if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700736 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 -0700737 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700738build_steps.extend(set(
Jan Tattermusch21897192015-10-07 15:42:21 -0700739 jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, timeout_seconds=10*60)
murgatroid99132ce6a2015-03-04 17:29:14 -0800740 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800741 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700742 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800743
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200744post_tests_steps = list(set(
745 jobset.JobSpec(cmdline, environ={'CONFIG': cfg})
746 for cfg in build_configs
747 for l in languages
748 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800749runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800750forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800751
Nicolas Nobleddef2462015-01-06 18:08:25 -0800752
Craig Tiller71735182015-01-15 17:07:13 -0800753class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800754 """Cache for running tests."""
755
David Klempner25739582015-02-11 15:57:32 -0800756 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800757 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800758 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700759 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800760
761 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800762 if cmdline not in self._last_successful_run:
763 return True
764 if self._last_successful_run[cmdline] != bin_hash:
765 return True
David Klempner25739582015-02-11 15:57:32 -0800766 if not self._use_cache_results:
767 return True
Craig Tiller71735182015-01-15 17:07:13 -0800768 return False
769
770 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800771 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700772 if time.time() - self._last_save > 1:
773 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800774
775 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800776 return [{'cmdline': k, 'hash': v}
777 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800778
779 def parse(self, exdump):
780 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
781
782 def save(self):
783 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800784 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700785 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800786
Craig Tiller1cc11db2015-01-15 22:50:50 -0800787 def maybe_load(self):
788 if os.path.exists('.run_tests_cache'):
789 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800790 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800791
792
Craig Tillerf53d9c82015-08-04 14:19:43 -0700793def _start_port_server(port_server_port):
794 # check if a compatible port server is running
795 # if incompatible (version mismatch) ==> start a new one
796 # if not running ==> start a new one
797 # otherwise, leave it up
798 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700799 version = int(urllib2.urlopen(
800 'http://localhost:%d/version_number' % port_server_port,
801 timeout=1).read())
802 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700803 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700804 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700805 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700806 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700807 running = False
808 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700809 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800810 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
811 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700812 print 'my port server is version %d' % current_version
813 running = (version >= current_version)
814 if not running:
815 print 'port_server version mismatch: killing the old one'
816 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
817 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700818 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700819 fd, logfile = tempfile.mkstemp()
820 os.close(fd)
821 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800822 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
823 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700824 env = dict(os.environ)
825 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100826 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800827 # Working directory of port server needs to be outside of Jenkins
828 # workspace to prevent file lock issues.
829 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700830 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700831 args,
832 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800833 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700834 creationflags = 0x00000008, # detached process
835 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700836 else:
837 port_server = subprocess.Popen(
838 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700839 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700840 preexec_fn=os.setsid,
841 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700842 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700843 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700844 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700845 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700846 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700847 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700848 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700849 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700850 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700851 # try one final time: maybe another build managed to start one
852 time.sleep(1)
853 try:
854 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
855 timeout=1).read()
856 print 'last ditch attempt to contact port server succeeded'
857 break
858 except:
859 traceback.print_exc();
860 port_log = open(logfile, 'r').read()
861 print port_log
862 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700863 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700864 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
865 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700866 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700867 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700868 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700869 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700870 traceback.print_exc();
871 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700872 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700873 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700874 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700875 traceback.print_exc();
876 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700877 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700878 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700879 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700880 port_server.kill()
881 raise
882
883
Adele Zhoud5fffa52015-10-23 15:51:42 -0700884def _calculate_num_runs_failures(list_of_results):
885 """Caculate number of runs and failures for a particular test.
886
887 Args:
888 list_of_results: (List) of JobResult object.
889 Returns:
890 A tuple of total number of runs and failures.
891 """
892 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
893 num_failures = 0
894 for jobresult in list_of_results:
895 if jobresult.retries > 0:
896 num_runs += jobresult.retries
897 if jobresult.num_failures > 0:
898 num_failures += jobresult.num_failures
899 return num_runs, num_failures
900
Adele Zhou6b9527c2015-11-20 15:56:35 -0800901
Craig Tillerf53d9c82015-08-04 14:19:43 -0700902def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800903 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800904 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800905 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700906 num_failures, _ = jobset.run(
907 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800908 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700909 if num_failures:
Craig Tillerd86a3942015-01-14 12:48:54 -0800910 return 1
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800911
912 if build_only:
913 return 0
ctiller3040cb72015-01-07 12:13:17 -0800914
Craig Tiller234b6e72015-05-23 10:12:40 -0700915 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700916 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700917 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700918 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700919 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800920 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800921 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700922 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700923 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700924 one_run = set(
925 spec
926 for config in run_configs
927 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800928 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700929 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700930 # When running on travis, we want out test runs to be as similar as possible
931 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800932 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700933 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
934 else:
935 # whereas otherwise, we want to shuffle things up to give all tests a
936 # chance to run.
937 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
938 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -0700939 if infinite_runs:
940 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 -0700941 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
942 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -0700943 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200944
Adele Zhou803af152015-11-30 15:16:16 -0800945 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -0700946 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -0800947 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -0700948 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700949 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700950 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -0700951 if resultset:
952 for k, v in resultset.iteritems():
953 num_runs, num_failures = _calculate_num_runs_failures(v)
954 if num_failures == num_runs: # what about infinite_runs???
955 jobset.message('FAILED', k, do_newline=True)
956 elif num_failures > 0:
957 jobset.message(
958 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
959 do_newline=True)
960 else:
961 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -0700962 finally:
963 for antagonist in antagonists:
964 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -0800965 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -0800966 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -0800967
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700968 number_failures, _ = jobset.run(
969 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800970 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhou803af152015-11-30 15:16:16 -0800971 if num_test_failures or number_failures:
972 return 2
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200973
Craig Tiller69cd2372015-06-11 09:38:09 -0700974 if cache: cache.save()
975
Craig Tillerd86a3942015-01-14 12:48:54 -0800976 return 0
ctiller3040cb72015-01-07 12:13:17 -0800977
978
David Klempner25739582015-02-11 15:57:32 -0800979test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -0800980test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -0800981
ctiller3040cb72015-01-07 12:13:17 -0800982if forever:
Nicolas Noble044db742015-01-14 16:57:24 -0800983 success = True
ctiller3040cb72015-01-07 12:13:17 -0800984 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -0800985 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -0800986 initial_time = dw.most_recent_change()
987 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -0800988 previous_success = success
Craig Tiller71735182015-01-15 17:07:13 -0800989 success = _build_and_run(check_cancelled=have_files_changed,
Nicolas Nobleb09078f2015-01-14 18:06:05 -0800990 newline_on_success=False,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800991 cache=test_cache,
992 build_only=args.build_only) == 0
Nicolas Noble044db742015-01-14 16:57:24 -0800993 if not previous_success and success:
Nicolas Nobleb09078f2015-01-14 18:06:05 -0800994 jobset.message('SUCCESS',
995 'All tests are now passing properly',
996 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800997 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -0800998 while not have_files_changed():
999 time.sleep(1)
1000else:
Craig Tiller71735182015-01-15 17:07:13 -08001001 result = _build_and_run(check_cancelled=lambda: False,
1002 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001003 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001004 xml_report=args.xml_report,
1005 build_only=args.build_only)
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001006 if result == 0:
1007 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1008 else:
1009 jobset.message('FAILED', 'Some tests failed', do_newline=True)
1010 sys.exit(result)