blob: 4529f4ec5605bd4e0583a98543062e988d66331e [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."""
541 if arch == 'default' or arch == 'windows_x86':
542 return '/p:Platform=Win32'
543 elif arch == 'windows_x64':
544 return '/p:Platform=x64'
545 else:
546 print 'Architecture %s not supported on current platform.' % arch
547 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
576def _get_dockerfile_dir():
577 """Returns dockerfile to use"""
578 #TODO: don't depend on env var.
579 arch_env = os.getenv('arch')
580 if arch_env == 'i386':
581 return 'tools/jenkins/grpc_jenkins_slave_32bits'
582 else:
583 # TODO: support 32_bit dockerfile as well
584 return 'tools/jenkins/grpc_jenkins_slave'
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',
651 choices=['default', 'windows_x86', 'windows_x64'],
652 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':
739 if args.arch != 'default':
740 print 'Architecture %s not supported on current platform.' % args.arch
741 sys.exit(1)
742 if args.compiler != 'default':
743 print 'Compiler %s not supported on current platform.' % args.compiler
744 sys.exit(1)
745
Jan Tattermusch4dc9e722016-01-25 17:00:54 -0800746if len(languages) != 1 or len(build_configs) != 1:
747 print 'Multi-language and multi-config testing is not supported.'
748 sys.exit(1)
749
750if args.use_docker:
751 if not args.travis:
752 print 'Seen --use_docker flag, will run tests under docker.'
753 print
754 print 'IMPORTANT: The changes you are testing need to be locally committed'
755 print 'because only the committed changes in the current branch will be'
756 print 'copied to the docker environment.'
757 time.sleep(5)
758
759 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
760 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
761
762 # TODO(jtattermusch): revisit if we need special handling for arch here
763 # set arch command prefix in case we are working with different arch.
764 arch_env = os.getenv('arch')
765 if arch_env:
766 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
767
768 env = os.environ.copy()
769 env['RUN_TESTS_COMMAND'] = run_tests_cmd
770 # TODO: also support 32-bit, figure out the dockerfile properly
771 env['DOCKERFILE_DIR'] = _get_dockerfile_dir()
772 env['DOCKER_RUN_SCRIPT'] = 'tools/jenkins/docker_run_tests.sh'
773 if args.xml_report:
774 env['XML_REPORT'] = args.xml_report
775 if not args.travis:
776 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
777
778 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
779 shell=True,
780 env=env)
781 sys.exit(0)
782
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100783if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700784 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700785 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700786 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700787 # empirically /m:2 gives the best performance/price and should prevent
788 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700789 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700790 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700791 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700792 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800793 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700794 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800795 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
796 _windows_toolset_option(args.compiler),
797 _windows_arch_option(args.arch)] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800798 extra_args +
799 language_make_options,
Craig Tiller590105a2016-01-19 13:03:46 -0800800 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700801 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700802else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700803 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700804 if targets:
805 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
806 '-f', makefile,
Craig Tillerdd6f7ed2016-01-21 12:54:42 -0800807 '-j', '%d' % args.jobs,
Craig Tiller71a86042016-01-15 14:59:58 -0800808 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
809 'CONFIG=%s' % cfg] +
Jan Tattermuschc895fe02016-01-20 09:13:09 -0800810 language_make_options +
Craig Tiller71a86042016-01-15 14:59:58 -0800811 ([] if not args.travis else ['JENKINS_BUILD=1']) +
812 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800813 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700814 else:
815 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700816make_targets = {}
817for l in languages:
818 makefile = l.makefile_name()
819 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800820 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700821
Jan Tattermusche4a69182015-12-15 09:53:01 -0800822def build_step_environ(cfg):
823 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800824 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800825 if msbuild_cfg:
826 environ['MSBUILD_CONFIG'] = msbuild_cfg
827 return environ
828
murgatroid99fddac962015-09-22 09:20:11 -0700829build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800830 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700831 for cfg in build_configs
832 for l in languages
833 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700834if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700835 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 -0700836 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700837build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800838 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800839 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800840 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700841 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800842
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200843post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800844 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200845 for cfg in build_configs
846 for l in languages
847 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800848runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800849forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800850
Nicolas Nobleddef2462015-01-06 18:08:25 -0800851
Craig Tiller71735182015-01-15 17:07:13 -0800852class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800853 """Cache for running tests."""
854
David Klempner25739582015-02-11 15:57:32 -0800855 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800856 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800857 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700858 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800859
860 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800861 if cmdline not in self._last_successful_run:
862 return True
863 if self._last_successful_run[cmdline] != bin_hash:
864 return True
David Klempner25739582015-02-11 15:57:32 -0800865 if not self._use_cache_results:
866 return True
Craig Tiller71735182015-01-15 17:07:13 -0800867 return False
868
869 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800870 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700871 if time.time() - self._last_save > 1:
872 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800873
874 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800875 return [{'cmdline': k, 'hash': v}
876 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800877
878 def parse(self, exdump):
879 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
880
881 def save(self):
882 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800883 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700884 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800885
Craig Tiller1cc11db2015-01-15 22:50:50 -0800886 def maybe_load(self):
887 if os.path.exists('.run_tests_cache'):
888 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800889 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800890
891
Craig Tillerf53d9c82015-08-04 14:19:43 -0700892def _start_port_server(port_server_port):
893 # check if a compatible port server is running
894 # if incompatible (version mismatch) ==> start a new one
895 # if not running ==> start a new one
896 # otherwise, leave it up
897 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700898 version = int(urllib2.urlopen(
899 'http://localhost:%d/version_number' % port_server_port,
900 timeout=1).read())
901 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700902 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700903 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700904 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700905 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700906 running = False
907 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700908 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800909 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
910 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700911 print 'my port server is version %d' % current_version
912 running = (version >= current_version)
913 if not running:
914 print 'port_server version mismatch: killing the old one'
915 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
916 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700917 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700918 fd, logfile = tempfile.mkstemp()
919 os.close(fd)
920 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800921 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
922 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700923 env = dict(os.environ)
924 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100925 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800926 # Working directory of port server needs to be outside of Jenkins
927 # workspace to prevent file lock issues.
928 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700929 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700930 args,
931 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800932 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700933 creationflags = 0x00000008, # detached process
934 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700935 else:
936 port_server = subprocess.Popen(
937 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700938 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700939 preexec_fn=os.setsid,
940 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700941 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700942 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700943 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700944 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700945 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700946 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700947 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700948 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700949 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700950 # try one final time: maybe another build managed to start one
951 time.sleep(1)
952 try:
953 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
954 timeout=1).read()
955 print 'last ditch attempt to contact port server succeeded'
956 break
957 except:
958 traceback.print_exc();
959 port_log = open(logfile, 'r').read()
960 print port_log
961 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700962 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700963 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
964 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700965 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700966 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700967 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700968 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700969 traceback.print_exc();
970 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700971 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700972 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700973 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700974 traceback.print_exc();
975 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700976 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700977 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700978 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700979 port_server.kill()
980 raise
981
982
Adele Zhoud5fffa52015-10-23 15:51:42 -0700983def _calculate_num_runs_failures(list_of_results):
984 """Caculate number of runs and failures for a particular test.
985
986 Args:
987 list_of_results: (List) of JobResult object.
988 Returns:
989 A tuple of total number of runs and failures.
990 """
991 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
992 num_failures = 0
993 for jobresult in list_of_results:
994 if jobresult.retries > 0:
995 num_runs += jobresult.retries
996 if jobresult.num_failures > 0:
997 num_failures += jobresult.num_failures
998 return num_runs, num_failures
999
Adele Zhou6b9527c2015-11-20 15:56:35 -08001000
Craig Tillereb9de8b2016-01-08 08:57:41 -08001001# _build_and_run results
1002class BuildAndRunError(object):
1003
1004 BUILD = object()
1005 TEST = object()
1006 POST_TEST = object()
1007
1008
1009# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -07001010def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001011 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -08001012 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -08001013 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -07001014 num_failures, _ = jobset.run(
1015 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001016 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -07001017 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001018 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -08001019
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001020 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001021 return []
ctiller3040cb72015-01-07 12:13:17 -08001022
Craig Tiller234b6e72015-05-23 10:12:40 -07001023 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001024 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -07001025 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -07001026 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -07001027 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -08001028 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -08001029 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -07001030 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -07001031 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -07001032 one_run = set(
1033 spec
1034 for config in run_configs
1035 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -08001036 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -07001037 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001038 # When running on travis, we want out test runs to be as similar as possible
1039 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -08001040 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -07001041 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
1042 else:
1043 # whereas otherwise, we want to shuffle things up to give all tests a
1044 # chance to run.
1045 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
1046 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -07001047 if infinite_runs:
1048 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 -07001049 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
1050 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -07001051 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001052
Adele Zhou803af152015-11-30 15:16:16 -08001053 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -07001054 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -08001055 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -07001056 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001057 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001058 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001059 if resultset:
1060 for k, v in resultset.iteritems():
1061 num_runs, num_failures = _calculate_num_runs_failures(v)
1062 if num_failures == num_runs: # what about infinite_runs???
1063 jobset.message('FAILED', k, do_newline=True)
1064 elif num_failures > 0:
1065 jobset.message(
1066 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1067 do_newline=True)
1068 else:
1069 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001070 finally:
1071 for antagonist in antagonists:
1072 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001073 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001074 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001075
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001076 number_failures, _ = jobset.run(
1077 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001078 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001079
1080 out = []
1081 if number_failures:
1082 out.append(BuildAndRunError.POST_TEST)
1083 if num_test_failures:
1084 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001085
Craig Tiller69cd2372015-06-11 09:38:09 -07001086 if cache: cache.save()
1087
Craig Tillereb9de8b2016-01-08 08:57:41 -08001088 return out
ctiller3040cb72015-01-07 12:13:17 -08001089
1090
David Klempner25739582015-02-11 15:57:32 -08001091test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001092test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001093
ctiller3040cb72015-01-07 12:13:17 -08001094if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001095 success = True
ctiller3040cb72015-01-07 12:13:17 -08001096 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001097 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001098 initial_time = dw.most_recent_change()
1099 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001100 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001101 errors = _build_and_run(check_cancelled=have_files_changed,
1102 newline_on_success=False,
1103 cache=test_cache,
1104 build_only=args.build_only) == 0
1105 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001106 jobset.message('SUCCESS',
1107 'All tests are now passing properly',
1108 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001109 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001110 while not have_files_changed():
1111 time.sleep(1)
1112else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001113 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001114 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001115 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001116 xml_report=args.xml_report,
1117 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001118 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001119 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1120 else:
1121 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001122 exit_code = 0
1123 if BuildAndRunError.BUILD in errors:
1124 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001125 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001126 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001127 if BuildAndRunError.POST_TEST in errors:
1128 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001129 sys.exit(exit_code)
Craig Tillera0f85172016-01-20 15:56:06 -08001130