blob: 767b801c2af34c1813f10baaa03123770c227015 [file] [log] [blame]
Nathaniel Manistaae4fbcd2015-09-23 16:29:44 +00001#!/usr/bin/env python2.7
Craig Tiller40839772016-01-05 12:34:49 -08002# Copyright 2015-2016, Google Inc.
Craig Tillerc2c79212015-02-16 12:00:01 -08003# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
Nicolas Nobleddef2462015-01-06 18:08:25 -080031"""Run tests in parallel."""
32
33import argparse
Craig Tiller9279ac22016-01-20 17:05:23 -080034import ast
Nicolas Nobleddef2462015-01-06 18:08:25 -080035import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070036import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080037import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080038import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080039import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080040import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070041import platform
42import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080043import re
Craig Tiller82875232015-09-25 13:57:34 -070044import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070045import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080046import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070047import tempfile
48import traceback
ctiller3040cb72015-01-07 12:13:17 -080049import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070050import urllib2
Jan Tattermusch03c01062015-12-11 14:28:56 -080051import uuid
Nicolas Nobleddef2462015-01-06 18:08:25 -080052
53import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080054import report_utils
ctiller3040cb72015-01-07 12:13:17 -080055import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080056
Craig Tillerb361b4e2016-01-06 11:44:17 -080057
Craig Tiller2cc2b842015-02-27 11:38:31 -080058ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
59os.chdir(ROOT)
60
61
Craig Tiller06805272015-06-11 14:46:47 -070062_FORCE_ENVIRON_FOR_WRAPPERS = {}
63
64
Craig Tillerd50993d2015-08-05 08:04:36 -070065def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010066 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070067
68
Craig Tiller738c3342015-01-12 14:28:33 -080069# SimpleConfig: just compile with CONFIG=config, and run the binary to test
Craig Tillera0f85172016-01-20 15:56:06 -080070class Config(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080071
Craig Tillera0f85172016-01-20 15:56:06 -080072 def __init__(self, config, environ=None, timeout_multiplier=1, tool_prefix=[]):
murgatroid99132ce6a2015-03-04 17:29:14 -080073 if environ is None:
74 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080075 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080076 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080077 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080078 self.environ['CONFIG'] = config
Craig Tillera0f85172016-01-20 15:56:06 -080079 self.tool_prefix = tool_prefix
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070080 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080081
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070082 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
Craig Tiller56c6b6a2016-01-20 08:27:37 -080083 shortname=None, environ={}, cpu_cost=1.0):
Craig Tiller49f61322015-03-03 13:02:11 -080084 """Construct a jobset.JobSpec for a test under this config
85
86 Args:
87 cmdline: a list of strings specifying the command line the test
88 would like to run
89 hash_targets: either None (don't do caching of test results), or
90 a list of strings specifying files to include in a
91 binary hash to check if a test has changed
92 -- if used, all artifacts needed to run the test must
93 be listed
94 """
Craig Tiller4fc90032015-05-21 10:39:52 -070095 actual_environ = self.environ.copy()
96 for k, v in environ.iteritems():
97 actual_environ[k] = v
Craig Tillera0f85172016-01-20 15:56:06 -080098 return jobset.JobSpec(cmdline=self.tool_prefix + cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070099 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -0700100 environ=actual_environ,
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800101 cpu_cost=cpu_cost,
Craig Tiller94d04a52016-01-20 10:58:23 -0800102 timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),
Craig Tiller547db2b2015-01-30 14:08:39 -0800103 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700104 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700105 flake_retries=5 if args.allow_flakes else 0,
106 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800107
108
murgatroid99cf08daf2015-09-21 15:33:16 -0700109def get_c_tests(travis, test_lang) :
110 out = []
111 platforms_str = 'ci_platforms' if travis else 'platforms'
112 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700113 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700114 return [tgt
115 for tgt in js
116 if tgt['language'] == test_lang and
117 platform_string() in tgt[platforms_str] and
118 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700119
murgatroid99fafeeb32015-09-22 09:13:03 -0700120
Craig Tillerc7449162015-01-16 14:42:10 -0800121class CLanguage(object):
122
Craig Tillere9c959d2015-01-18 10:23:26 -0800123 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800124 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700125 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700126 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800127
Craig Tiller883064c2015-11-04 10:06:10 -0800128 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800129 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800130 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700131 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700132 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700133 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700134 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700135 binary = 'vsprojects/%s/%s.exe' % (
136 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700137 else:
138 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700139 if os.path.isfile(binary):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800140 cmdline = [binary] + target['args']
141 out.append(config.job_spec(cmdline, [binary],
142 shortname=' '.join(cmdline),
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800143 cpu_cost=target['cpu_cost'],
Craig Tillercc0535d2015-12-08 15:14:47 -0800144 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
145 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800146 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
Craig Tiller883064c2015-11-04 10:06:10 -0800147 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700148 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700149 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800150
Craig Tiller883064c2015-11-04 10:06:10 -0800151 def make_targets(self, test_regex):
152 if platform_string() != 'windows' and test_regex != '.*':
153 # use the regex to minimize the number of things to build
154 return [target['name']
155 for target in get_c_tests(False, self.test_lang)
156 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700157 if platform_string() == 'windows':
158 # don't build tools on windows just yet
159 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700160 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800161
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800162 def make_options(self):
163 return []
164
murgatroid99256d3df2015-09-21 16:58:02 -0700165 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700166 if self.platform == 'windows':
167 return [['tools\\run_tests\\pre_build_c.bat']]
168 else:
169 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700170
Craig Tillerc7449162015-01-16 14:42:10 -0800171 def build_steps(self):
172 return []
173
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200174 def post_tests_steps(self):
175 if self.platform == 'windows':
176 return []
177 else:
178 return [['tools/run_tests/post_tests_c.sh']]
179
murgatroid99a3e244f2015-09-22 11:25:53 -0700180 def makefile_name(self):
181 return 'Makefile'
182
murgatroid99132ce6a2015-03-04 17:29:14 -0800183 def supports_multi_config(self):
184 return True
185
186 def __str__(self):
187 return self.make_target
188
Craig Tillercc0535d2015-12-08 15:14:47 -0800189
murgatroid992c8d5162015-01-26 10:41:21 -0800190class NodeLanguage(object):
191
Craig Tiller883064c2015-11-04 10:06:10 -0800192 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700193 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700194 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800195
murgatroid99256d3df2015-09-21 16:58:02 -0700196 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700197 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700198 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700199
Craig Tiller883064c2015-11-04 10:06:10 -0800200 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700201 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800202
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800203 def make_options(self):
204 return []
205
murgatroid992c8d5162015-01-26 10:41:21 -0800206 def build_steps(self):
207 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800208
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200209 def post_tests_steps(self):
210 return []
211
murgatroid99a3e244f2015-09-22 11:25:53 -0700212 def makefile_name(self):
213 return 'Makefile'
214
murgatroid99132ce6a2015-03-04 17:29:14 -0800215 def supports_multi_config(self):
216 return False
217
218 def __str__(self):
219 return 'node'
220
Craig Tiller99775822015-01-30 13:07:16 -0800221
Craig Tillerc7449162015-01-16 14:42:10 -0800222class PhpLanguage(object):
223
Craig Tiller883064c2015-11-04 10:06:10 -0800224 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700225 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700226 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800227
murgatroid99256d3df2015-09-21 16:58:02 -0700228 def pre_build_steps(self):
229 return []
230
Craig Tiller883064c2015-11-04 10:06:10 -0800231 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700232 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800233
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800234 def make_options(self):
235 return []
236
Craig Tillerc7449162015-01-16 14:42:10 -0800237 def build_steps(self):
238 return [['tools/run_tests/build_php.sh']]
239
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200240 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800241 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200242
murgatroid99a3e244f2015-09-22 11:25:53 -0700243 def makefile_name(self):
244 return 'Makefile'
245
murgatroid99132ce6a2015-03-04 17:29:14 -0800246 def supports_multi_config(self):
247 return False
248
249 def __str__(self):
250 return 'php'
251
Craig Tillerc7449162015-01-16 14:42:10 -0800252
Nathaniel Manista840615e2015-01-22 20:31:47 +0000253class PythonLanguage(object):
254
Craig Tiller49f61322015-03-03 13:02:11 -0800255 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700256 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700257 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800258
Craig Tiller883064c2015-11-04 10:06:10 -0800259 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700260 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
261 environment['PYVER'] = '2.7'
262 return [config.job_spec(
263 ['tools/run_tests/run_python.sh'],
264 None,
265 environ=environment,
266 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700267 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700268 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000269
murgatroid99256d3df2015-09-21 16:58:02 -0700270 def pre_build_steps(self):
271 return []
272
Craig Tiller883064c2015-11-04 10:06:10 -0800273 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700274 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000275
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800276 def make_options(self):
277 return []
278
Nathaniel Manista840615e2015-01-22 20:31:47 +0000279 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700280 commands = []
281 for python_version in self._build_python_versions:
282 try:
283 with open(os.devnull, 'w') as output:
284 subprocess.check_call(['which', 'python' + python_version],
285 stdout=output, stderr=output)
286 commands.append(['tools/run_tests/build_python.sh', python_version])
287 self._has_python_versions.append(python_version)
288 except:
289 jobset.message('WARNING', 'Missing Python ' + python_version,
290 do_newline=True)
291 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000292
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200293 def post_tests_steps(self):
294 return []
295
murgatroid99a3e244f2015-09-22 11:25:53 -0700296 def makefile_name(self):
297 return 'Makefile'
298
murgatroid99132ce6a2015-03-04 17:29:14 -0800299 def supports_multi_config(self):
300 return False
301
302 def __str__(self):
303 return 'python'
304
Craig Tillerd625d812015-04-08 15:52:35 -0700305
murgatroid996a4c4fa2015-02-27 12:08:57 -0800306class RubyLanguage(object):
307
Craig Tiller883064c2015-11-04 10:06:10 -0800308 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700309 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700310 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800311
murgatroid99256d3df2015-09-21 16:58:02 -0700312 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200313 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700314
Craig Tiller883064c2015-11-04 10:06:10 -0800315 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700316 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800317
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800318 def make_options(self):
319 return []
320
murgatroid996a4c4fa2015-02-27 12:08:57 -0800321 def build_steps(self):
322 return [['tools/run_tests/build_ruby.sh']]
323
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200324 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100325 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200326
murgatroid99a3e244f2015-09-22 11:25:53 -0700327 def makefile_name(self):
328 return 'Makefile'
329
murgatroid99132ce6a2015-03-04 17:29:14 -0800330 def supports_multi_config(self):
331 return False
332
333 def __str__(self):
334 return 'ruby'
335
Craig Tillerd625d812015-04-08 15:52:35 -0700336
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800337class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700338 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700339 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700340
Craig Tiller883064c2015-11-04 10:06:10 -0800341 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800342 with open('src/csharp/tests.json') as f:
343 tests_json = json.load(f)
344 assemblies = tests_json['assemblies']
345 tests = tests_json['tests']
346
347 msbuild_config = _WINDOWS_CONFIG[config.build_config]
348 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
349 for a in assemblies]
350
351 extra_args = ['-labels'] + assembly_files
352
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700353 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800354 script_name = 'tools\\run_tests\\run_csharp.bat'
355 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700356 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800357 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700358
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700359 if config.build_config == 'gcov':
360 # On Windows, we only collect C# code coverage.
361 # On Linux, we only collect coverage for native extension.
362 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800363 return [config.job_spec([script_name] + extra_args, None,
364 shortname='csharp.coverage',
365 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700366 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800367 specs = []
368 for test in tests:
369 cmdline = [script_name, '-run=%s' % test] + extra_args
370 if self.platform == 'windows':
371 # use different output directory for each test to prevent
372 # TestResult.xml clash between parallel test runs.
373 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
374 specs.append(config.job_spec(cmdline, None,
375 shortname='csharp.%s' % test,
376 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
377 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800378
murgatroid99256d3df2015-09-21 16:58:02 -0700379 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700380 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700381 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700382 else:
383 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700384
Craig Tiller883064c2015-11-04 10:06:10 -0800385 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700386 # For Windows, this target doesn't really build anything,
387 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700388 if self.platform == 'windows':
389 return []
390 else:
391 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800392
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800393 def make_options(self):
394 if self.platform == 'mac':
395 # On Mac, official distribution of mono is 32bit.
396 return ['CFLAGS=-arch i386', 'LDFLAGS=-arch i386']
397 else:
398 return []
399
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800400 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700401 if self.platform == 'windows':
402 return [['src\\csharp\\buildall.bat']]
403 else:
404 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000405
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200406 def post_tests_steps(self):
407 return []
408
murgatroid99a3e244f2015-09-22 11:25:53 -0700409 def makefile_name(self):
410 return 'Makefile'
411
murgatroid99132ce6a2015-03-04 17:29:14 -0800412 def supports_multi_config(self):
413 return False
414
415 def __str__(self):
416 return 'csharp'
417
Craig Tillerd625d812015-04-08 15:52:35 -0700418
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700419class ObjCLanguage(object):
420
Craig Tiller883064c2015-11-04 10:06:10 -0800421 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700422 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
423 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
424
murgatroid99256d3df2015-09-21 16:58:02 -0700425 def pre_build_steps(self):
426 return []
427
Craig Tiller883064c2015-11-04 10:06:10 -0800428 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700429 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700430
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800431 def make_options(self):
432 return []
433
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700434 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700435 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700436
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200437 def post_tests_steps(self):
438 return []
439
murgatroid99a3e244f2015-09-22 11:25:53 -0700440 def makefile_name(self):
441 return 'Makefile'
442
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700443 def supports_multi_config(self):
444 return False
445
446 def __str__(self):
447 return 'objc'
448
449
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100450class Sanity(object):
451
Craig Tiller883064c2015-11-04 10:06:10 -0800452 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800453 import yaml
454 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
Craig Tiller528fd1e2016-01-21 15:27:37 -0800455 return [config.job_spec(cmd['script'].split(), None, timeout_seconds=None, environ={'TEST': 'true'}, cpu_cost=cmd.get('cpu_cost', 1))
Craig Tiller94d04a52016-01-20 10:58:23 -0800456 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100457
murgatroid99256d3df2015-09-21 16:58:02 -0700458 def pre_build_steps(self):
459 return []
460
Craig Tiller883064c2015-11-04 10:06:10 -0800461 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100462 return ['run_dep_checks']
463
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800464 def make_options(self):
465 return []
466
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100467 def build_steps(self):
468 return []
469
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200470 def post_tests_steps(self):
471 return []
472
murgatroid99a3e244f2015-09-22 11:25:53 -0700473 def makefile_name(self):
474 return 'Makefile'
475
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100476 def supports_multi_config(self):
477 return False
478
479 def __str__(self):
480 return 'sanity'
481
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200482
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100483class Build(object):
484
Craig Tiller883064c2015-11-04 10:06:10 -0800485 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100486 return []
487
murgatroid99256d3df2015-09-21 16:58:02 -0700488 def pre_build_steps(self):
489 return []
490
Craig Tiller883064c2015-11-04 10:06:10 -0800491 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200492 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100493
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800494 def make_options(self):
495 return []
496
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100497 def build_steps(self):
498 return []
499
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100500 def post_tests_steps(self):
501 return []
502
murgatroid99a3e244f2015-09-22 11:25:53 -0700503 def makefile_name(self):
504 return 'Makefile'
505
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100506 def supports_multi_config(self):
507 return True
508
509 def __str__(self):
510 return self.make_target
511
512
Craig Tiller738c3342015-01-12 14:28:33 -0800513# different configurations we can run under
Craig Tillera0f85172016-01-20 15:56:06 -0800514with open('tools/run_tests/configs.json') as f:
Craig Tiller1dce9062016-01-20 17:01:56 -0800515 _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))
Craig Tiller738c3342015-01-12 14:28:33 -0800516
517
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100518_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800519_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800520 'c++': CLanguage('cxx', 'c++'),
521 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800522 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000523 'php': PhpLanguage(),
524 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800525 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100526 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700527 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100528 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100529 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800530 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800531
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700532_WINDOWS_CONFIG = {
533 'dbg': 'Debug',
534 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800535 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700536 }
537
David Garcia Quintase90cd372015-05-31 18:15:26 -0700538
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800539def _windows_arch_option(arch):
540 """Returns msbuild cmdline option for selected architecture."""
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800541 if arch == 'default' or arch == 'x86':
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800542 return '/p:Platform=Win32'
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800543 elif arch == 'x64':
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800544 return '/p:Platform=x64'
545 else:
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800546 print 'Architecture %s not supported.' % arch
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800547 sys.exit(1)
548
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800549
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800550def _windows_build_bat(compiler):
551 """Returns name of build.bat for selected compiler."""
552 if compiler == 'default' or compiler == 'vs2013':
553 return 'vsprojects\\build_vs2013.bat'
554 elif compiler == 'vs2015':
555 return 'vsprojects\\build_vs2015.bat'
556 elif compiler == 'vs2010':
557 return 'vsprojects\\build_vs2010.bat'
558 else:
559 print 'Compiler %s not supported.' % compiler
560 sys.exit(1)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800561
562
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800563def _windows_toolset_option(compiler):
564 """Returns msbuild PlatformToolset for selected compiler."""
565 if compiler == 'default' or compiler == 'vs2013':
566 return '/p:PlatformToolset=v120'
567 elif compiler == 'vs2015':
568 return '/p:PlatformToolset=v140'
569 elif compiler == 'vs2010':
570 return '/p:PlatformToolset=v100'
571 else:
572 print 'Compiler %s not supported.' % compiler
573 sys.exit(1)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800574
575
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800576def _get_dockerfile_dir(arch):
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800577 """Returns dockerfile to use"""
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800578 if arch == 'default' or arch == 'x64':
579 return 'tools/jenkins/grpc_jenkins_slave'
580 elif arch == 'x86':
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800581 return 'tools/jenkins/grpc_jenkins_slave_32bits'
582 else:
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800583 print 'Architecture %s not supported with current settings.' % arch
584 sys.exit(1)
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800585
David Garcia Quintase90cd372015-05-31 18:15:26 -0700586def runs_per_test_type(arg_str):
587 """Auxilary function to parse the "runs_per_test" flag.
588
589 Returns:
590 A positive integer or 0, the latter indicating an infinite number of
591 runs.
592
593 Raises:
594 argparse.ArgumentTypeError: Upon invalid input.
595 """
596 if arg_str == 'inf':
597 return 0
598 try:
599 n = int(arg_str)
600 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700601 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700602 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700603 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700604 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700605
606# parse command line
607argp = argparse.ArgumentParser(description='Run grpc tests.')
608argp.add_argument('-c', '--config',
609 choices=['all'] + sorted(_CONFIGS.keys()),
610 nargs='+',
611 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700612argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
613 help='A positive integer or "inf". If "inf", all tests will run in an '
614 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800615argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800616argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800617argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800618argp.add_argument('-f', '--forever',
619 default=False,
620 action='store_const',
621 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100622argp.add_argument('-t', '--travis',
623 default=False,
624 action='store_const',
625 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800626argp.add_argument('--newline_on_success',
627 default=False,
628 action='store_const',
629 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800630argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700631 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800632 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700633 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700634argp.add_argument('-S', '--stop_on_failure',
635 default=False,
636 action='store_const',
637 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700638argp.add_argument('--use_docker',
639 default=False,
640 action='store_const',
641 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700642 help='Run all the tests under docker. That provides ' +
643 'additional isolation and prevents the need to install ' +
644 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700645argp.add_argument('--allow_flakes',
646 default=False,
647 action='store_const',
648 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700649 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800650argp.add_argument('--arch',
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800651 choices=['default', 'x86', 'x64'],
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800652 default='default',
653 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
654argp.add_argument('--compiler',
655 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
656 default='default',
657 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
658argp.add_argument('--build_only',
659 default=False,
660 action='store_const',
661 const=True,
662 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800663argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
664 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800665argp.add_argument('--update_submodules', default=[], nargs='*',
666 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
667 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700668argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200669argp.add_argument('-x', '--xml_report', default=None, type=str,
670 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800671args = argp.parse_args()
672
Craig Tiller5f735a62016-01-20 09:31:15 -0800673jobset.measure_cpu_costs = args.measure_cpu_costs
674
Craig Tiller1676f912016-01-05 10:49:44 -0800675# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800676need_to_regenerate_projects = False
677for spec in args.update_submodules:
678 spec = spec.split(':', 1)
679 if len(spec) == 1:
680 submodule = spec[0]
681 branch = 'master'
682 elif len(spec) == 2:
683 submodule = spec[0]
684 branch = spec[1]
685 cwd = 'third_party/%s' % submodule
686 def git(cmd, cwd=cwd):
687 print 'in %s: git %s' % (cwd, cmd)
688 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
689 git('fetch')
690 git('checkout %s' % branch)
691 git('pull origin %s' % branch)
692 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
693 need_to_regenerate_projects = True
694if need_to_regenerate_projects:
695 if jobset.platform_string() == 'linux':
696 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
697 else:
698 print 'WARNING: may need to regenerate projects, but since we are not on'
699 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800700
701
Nicolas Nobleddef2462015-01-06 18:08:25 -0800702# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800703run_configs = set(_CONFIGS[cfg]
704 for cfg in itertools.chain.from_iterable(
705 _CONFIGS.iterkeys() if x == 'all' else [x]
706 for x in args.config))
707build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800708
Craig Tiller06805272015-06-11 14:46:47 -0700709if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700710 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700711
Adele Zhou6b9527c2015-11-20 15:56:35 -0800712if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800713 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800714else:
715 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800716# We don't support code coverage on some languages
717if 'gcov' in args.config:
718 for bad in ['objc', 'sanity', 'build']:
719 if bad in lang_list:
720 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800721
722languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800723
724if len(build_configs) > 1:
725 for language in languages:
726 if not language.supports_multi_config():
727 print language, 'does not support multiple build configurations'
728 sys.exit(1)
729
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800730language_make_options=[]
731if any(language.make_options() for language in languages):
732 if len(languages) != 1:
733 print 'languages with custom make options cannot be built simultaneously with other languages'
734 sys.exit(1)
735 else:
736 language_make_options = next(iter(languages)).make_options()
737
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800738if platform_string() != 'windows':
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800739 if args.arch != 'default' and platform_string() != 'linux':
740 # TODO: check if the current arch is correct
741 print 'Architecture %s not supported on current platform.' % args.arch
742 sys.exit(1)
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800743 if args.compiler != 'default':
744 print 'Compiler %s not supported on current platform.' % args.compiler
745 sys.exit(1)
746
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800747if len(languages) != 1 or len(build_configs) != 1:
748 print 'Multi-language and multi-config testing is not supported.'
749 sys.exit(1)
750
751if args.use_docker:
752 if not args.travis:
753 print 'Seen --use_docker flag, will run tests under docker.'
754 print
755 print 'IMPORTANT: The changes you are testing need to be locally committed'
756 print 'because only the committed changes in the current branch will be'
757 print 'copied to the docker environment.'
758 time.sleep(5)
759
760 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
761 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
762
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800763 env = os.environ.copy()
764 env['RUN_TESTS_COMMAND'] = run_tests_cmd
Jan Tattermusch9be594f2016-01-25 18:08:47 -0800765 env['DOCKERFILE_DIR'] = _get_dockerfile_dir(args.arch)
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800766 env['DOCKER_RUN_SCRIPT'] = 'tools/jenkins/docker_run_tests.sh'
767 if args.xml_report:
768 env['XML_REPORT'] = args.xml_report
769 if not args.travis:
770 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
771
772 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
773 shell=True,
774 env=env)
775 sys.exit(0)
776
Jan Tattermuschfba65302016-01-25 18:21:14 -0800777def make_jobspec(cfg, targets, makefile='Makefile'):
778 if platform_string() == 'windows':
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700779 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700780 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700781 # empirically /m:2 gives the best performance/price and should prevent
782 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700783 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700784 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700785 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700786 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800787 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700788 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800789 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
790 _windows_toolset_option(args.compiler),
791 _windows_arch_option(args.arch)] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800792 extra_args +
793 language_make_options,
Craig Tiller590105a2016-01-19 13:03:46 -0800794 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700795 for target in targets]
Jan Tattermuschfba65302016-01-25 18:21:14 -0800796 else:
murgatroid998ae409f2015-10-26 16:39:00 -0700797 if targets:
798 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
799 '-f', makefile,
Craig Tillerdd6f7ed2016-01-21 12:54:42 -0800800 '-j', '%d' % args.jobs,
Craig Tiller71a86042016-01-15 14:59:58 -0800801 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
802 'CONFIG=%s' % cfg] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800803 language_make_options +
Craig Tiller71a86042016-01-15 14:59:58 -0800804 ([] if not args.travis else ['JENKINS_BUILD=1']) +
805 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800806 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700807 else:
808 return []
Jan Tattermuschfba65302016-01-25 18:21:14 -0800809
murgatroid99a3e244f2015-09-22 11:25:53 -0700810make_targets = {}
811for l in languages:
812 makefile = l.makefile_name()
813 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800814 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700815
Jan Tattermusche4a69182015-12-15 09:53:01 -0800816def build_step_environ(cfg):
817 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800818 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800819 if msbuild_cfg:
820 environ['MSBUILD_CONFIG'] = msbuild_cfg
821 return environ
822
murgatroid99fddac962015-09-22 09:20:11 -0700823build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800824 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700825 for cfg in build_configs
826 for l in languages
827 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700828if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700829 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 -0700830 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700831build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800832 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800833 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800834 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700835 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800836
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200837post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800838 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200839 for cfg in build_configs
840 for l in languages
841 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800842runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800843forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800844
Nicolas Nobleddef2462015-01-06 18:08:25 -0800845
Craig Tiller71735182015-01-15 17:07:13 -0800846class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800847 """Cache for running tests."""
848
David Klempner25739582015-02-11 15:57:32 -0800849 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800850 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800851 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700852 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800853
854 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800855 if cmdline not in self._last_successful_run:
856 return True
857 if self._last_successful_run[cmdline] != bin_hash:
858 return True
David Klempner25739582015-02-11 15:57:32 -0800859 if not self._use_cache_results:
860 return True
Craig Tiller71735182015-01-15 17:07:13 -0800861 return False
862
863 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800864 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700865 if time.time() - self._last_save > 1:
866 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800867
868 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800869 return [{'cmdline': k, 'hash': v}
870 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800871
872 def parse(self, exdump):
873 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
874
875 def save(self):
876 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800877 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700878 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800879
Craig Tiller1cc11db2015-01-15 22:50:50 -0800880 def maybe_load(self):
881 if os.path.exists('.run_tests_cache'):
882 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800883 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800884
885
Craig Tillerf53d9c82015-08-04 14:19:43 -0700886def _start_port_server(port_server_port):
887 # check if a compatible port server is running
888 # if incompatible (version mismatch) ==> start a new one
889 # if not running ==> start a new one
890 # otherwise, leave it up
891 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700892 version = int(urllib2.urlopen(
893 'http://localhost:%d/version_number' % port_server_port,
894 timeout=1).read())
895 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700896 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700897 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700898 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700899 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700900 running = False
901 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700902 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800903 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
904 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700905 print 'my port server is version %d' % current_version
906 running = (version >= current_version)
907 if not running:
908 print 'port_server version mismatch: killing the old one'
909 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
910 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700911 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700912 fd, logfile = tempfile.mkstemp()
913 os.close(fd)
914 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800915 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
916 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700917 env = dict(os.environ)
918 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100919 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800920 # Working directory of port server needs to be outside of Jenkins
921 # workspace to prevent file lock issues.
922 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700923 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700924 args,
925 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800926 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700927 creationflags = 0x00000008, # detached process
928 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700929 else:
930 port_server = subprocess.Popen(
931 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700932 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700933 preexec_fn=os.setsid,
934 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700935 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700936 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700937 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700938 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700939 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700940 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700941 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700942 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700943 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700944 # try one final time: maybe another build managed to start one
945 time.sleep(1)
946 try:
947 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
948 timeout=1).read()
949 print 'last ditch attempt to contact port server succeeded'
950 break
951 except:
952 traceback.print_exc();
953 port_log = open(logfile, 'r').read()
954 print port_log
955 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700956 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700957 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
958 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700959 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700960 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700961 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700962 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700963 traceback.print_exc();
964 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700965 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700966 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700967 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700968 traceback.print_exc();
969 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700970 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700971 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700972 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700973 port_server.kill()
974 raise
975
976
Adele Zhoud5fffa52015-10-23 15:51:42 -0700977def _calculate_num_runs_failures(list_of_results):
978 """Caculate number of runs and failures for a particular test.
979
980 Args:
981 list_of_results: (List) of JobResult object.
982 Returns:
983 A tuple of total number of runs and failures.
984 """
985 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
986 num_failures = 0
987 for jobresult in list_of_results:
988 if jobresult.retries > 0:
989 num_runs += jobresult.retries
990 if jobresult.num_failures > 0:
991 num_failures += jobresult.num_failures
992 return num_runs, num_failures
993
Adele Zhou6b9527c2015-11-20 15:56:35 -0800994
Craig Tillereb9de8b2016-01-08 08:57:41 -0800995# _build_and_run results
996class BuildAndRunError(object):
997
998 BUILD = object()
999 TEST = object()
1000 POST_TEST = object()
1001
1002
1003# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -07001004def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001005 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -08001006 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -08001007 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -07001008 num_failures, _ = jobset.run(
1009 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001010 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -07001011 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001012 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -08001013
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001014 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001015 return []
ctiller3040cb72015-01-07 12:13:17 -08001016
Craig Tiller234b6e72015-05-23 10:12:40 -07001017 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001018 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -07001019 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -07001020 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -07001021 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -08001022 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -08001023 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -07001024 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -07001025 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001026 one_run = set(
1027 spec
1028 for config in run_configs
1029 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001030 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001031 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001032 # When running on travis, we want out test runs to be as similar as possible
1033 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001034 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001035 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1036 else:
1037 # whereas otherwise, we want to shuffle things up to give all tests a
1038 # chance to run.
1039 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1040 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001041 if infinite_runs:
1042 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 -07001043 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1044 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001045 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001046
Adele Zhou803af152015-11-30 15:16:16 -08001047 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001048 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001049 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001050 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001051 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001052 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001053 if resultset:
1054 for k, v in resultset.iteritems():
1055 num_runs, num_failures = _calculate_num_runs_failures(v)
1056 if num_failures == num_runs: # what about infinite_runs???
1057 jobset.message('FAILED', k, do_newline=True)
1058 elif num_failures > 0:
1059 jobset.message(
1060 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1061 do_newline=True)
1062 else:
1063 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001064 finally:
1065 for antagonist in antagonists:
1066 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001067 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001068 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001069
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001070 number_failures, _ = jobset.run(
1071 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001072 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001073
1074 out = []
1075 if number_failures:
1076 out.append(BuildAndRunError.POST_TEST)
1077 if num_test_failures:
1078 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001079
Craig Tiller69cd2372015-06-11 09:38:09 -07001080 if cache: cache.save()
1081
Craig Tillereb9de8b2016-01-08 08:57:41 -08001082 return out
ctiller3040cb72015-01-07 12:13:17 -08001083
1084
David Klempner25739582015-02-11 15:57:32 -08001085test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001086test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001087
ctiller3040cb72015-01-07 12:13:17 -08001088if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001089 success = True
ctiller3040cb72015-01-07 12:13:17 -08001090 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001091 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001092 initial_time = dw.most_recent_change()
1093 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001094 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001095 errors = _build_and_run(check_cancelled=have_files_changed,
1096 newline_on_success=False,
1097 cache=test_cache,
1098 build_only=args.build_only) == 0
1099 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001100 jobset.message('SUCCESS',
1101 'All tests are now passing properly',
1102 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001103 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001104 while not have_files_changed():
1105 time.sleep(1)
1106else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001107 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001108 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001109 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001110 xml_report=args.xml_report,
1111 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001112 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001113 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1114 else:
1115 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001116 exit_code = 0
1117 if BuildAndRunError.BUILD in errors:
1118 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001119 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001120 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001121 if BuildAndRunError.POST_TEST in errors:
1122 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001123 sys.exit(exit_code)
Craig Tillera0f85172016-01-20 15:56:06 -08001124