blob: a4aa2068c266b9a31c21dbe2c791d3371757afa0 [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
murgatroid99256d3df2015-09-21 16:58:02 -0700162 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700163 if self.platform == 'windows':
164 return [['tools\\run_tests\\pre_build_c.bat']]
165 else:
166 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700167
Craig Tillerc7449162015-01-16 14:42:10 -0800168 def build_steps(self):
169 return []
170
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200171 def post_tests_steps(self):
172 if self.platform == 'windows':
173 return []
174 else:
175 return [['tools/run_tests/post_tests_c.sh']]
176
murgatroid99a3e244f2015-09-22 11:25:53 -0700177 def makefile_name(self):
178 return 'Makefile'
179
murgatroid99132ce6a2015-03-04 17:29:14 -0800180 def supports_multi_config(self):
181 return True
182
183 def __str__(self):
184 return self.make_target
185
Craig Tillercc0535d2015-12-08 15:14:47 -0800186
murgatroid992c8d5162015-01-26 10:41:21 -0800187class NodeLanguage(object):
188
Craig Tiller883064c2015-11-04 10:06:10 -0800189 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700190 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700191 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800192
murgatroid99256d3df2015-09-21 16:58:02 -0700193 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700194 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700195 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700196
Craig Tiller883064c2015-11-04 10:06:10 -0800197 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700198 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800199
200 def build_steps(self):
201 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800202
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200203 def post_tests_steps(self):
204 return []
205
murgatroid99a3e244f2015-09-22 11:25:53 -0700206 def makefile_name(self):
207 return 'Makefile'
208
murgatroid99132ce6a2015-03-04 17:29:14 -0800209 def supports_multi_config(self):
210 return False
211
212 def __str__(self):
213 return 'node'
214
Craig Tiller99775822015-01-30 13:07:16 -0800215
Craig Tillerc7449162015-01-16 14:42:10 -0800216class PhpLanguage(object):
217
Craig Tiller883064c2015-11-04 10:06:10 -0800218 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700219 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700220 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800221
murgatroid99256d3df2015-09-21 16:58:02 -0700222 def pre_build_steps(self):
223 return []
224
Craig Tiller883064c2015-11-04 10:06:10 -0800225 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700226 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800227
228 def build_steps(self):
229 return [['tools/run_tests/build_php.sh']]
230
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200231 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800232 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200233
murgatroid99a3e244f2015-09-22 11:25:53 -0700234 def makefile_name(self):
235 return 'Makefile'
236
murgatroid99132ce6a2015-03-04 17:29:14 -0800237 def supports_multi_config(self):
238 return False
239
240 def __str__(self):
241 return 'php'
242
Craig Tillerc7449162015-01-16 14:42:10 -0800243
Nathaniel Manista840615e2015-01-22 20:31:47 +0000244class PythonLanguage(object):
245
Craig Tiller49f61322015-03-03 13:02:11 -0800246 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700247 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700248 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800249
Craig Tiller883064c2015-11-04 10:06:10 -0800250 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700251 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
252 environment['PYVER'] = '2.7'
253 return [config.job_spec(
254 ['tools/run_tests/run_python.sh'],
255 None,
256 environ=environment,
257 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700258 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700259 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000260
murgatroid99256d3df2015-09-21 16:58:02 -0700261 def pre_build_steps(self):
262 return []
263
Craig Tiller883064c2015-11-04 10:06:10 -0800264 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700265 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000266
267 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700268 commands = []
269 for python_version in self._build_python_versions:
270 try:
271 with open(os.devnull, 'w') as output:
272 subprocess.check_call(['which', 'python' + python_version],
273 stdout=output, stderr=output)
274 commands.append(['tools/run_tests/build_python.sh', python_version])
275 self._has_python_versions.append(python_version)
276 except:
277 jobset.message('WARNING', 'Missing Python ' + python_version,
278 do_newline=True)
279 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000280
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200281 def post_tests_steps(self):
282 return []
283
murgatroid99a3e244f2015-09-22 11:25:53 -0700284 def makefile_name(self):
285 return 'Makefile'
286
murgatroid99132ce6a2015-03-04 17:29:14 -0800287 def supports_multi_config(self):
288 return False
289
290 def __str__(self):
291 return 'python'
292
Craig Tillerd625d812015-04-08 15:52:35 -0700293
murgatroid996a4c4fa2015-02-27 12:08:57 -0800294class RubyLanguage(object):
295
Craig Tiller883064c2015-11-04 10:06:10 -0800296 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700297 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700298 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800299
murgatroid99256d3df2015-09-21 16:58:02 -0700300 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200301 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700302
Craig Tiller883064c2015-11-04 10:06:10 -0800303 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700304 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800305
306 def build_steps(self):
307 return [['tools/run_tests/build_ruby.sh']]
308
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200309 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100310 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200311
murgatroid99a3e244f2015-09-22 11:25:53 -0700312 def makefile_name(self):
313 return 'Makefile'
314
murgatroid99132ce6a2015-03-04 17:29:14 -0800315 def supports_multi_config(self):
316 return False
317
318 def __str__(self):
319 return 'ruby'
320
Craig Tillerd625d812015-04-08 15:52:35 -0700321
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800322class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700323 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700324 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700325
Craig Tiller883064c2015-11-04 10:06:10 -0800326 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800327 with open('src/csharp/tests.json') as f:
328 tests_json = json.load(f)
329 assemblies = tests_json['assemblies']
330 tests = tests_json['tests']
331
332 msbuild_config = _WINDOWS_CONFIG[config.build_config]
333 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
334 for a in assemblies]
335
336 extra_args = ['-labels'] + assembly_files
337
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700338 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800339 script_name = 'tools\\run_tests\\run_csharp.bat'
340 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700341 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800342 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700343
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700344 if config.build_config == 'gcov':
345 # On Windows, we only collect C# code coverage.
346 # On Linux, we only collect coverage for native extension.
347 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800348 return [config.job_spec([script_name] + extra_args, None,
349 shortname='csharp.coverage',
350 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700351 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800352 specs = []
353 for test in tests:
354 cmdline = [script_name, '-run=%s' % test] + extra_args
355 if self.platform == 'windows':
356 # use different output directory for each test to prevent
357 # TestResult.xml clash between parallel test runs.
358 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
359 specs.append(config.job_spec(cmdline, None,
360 shortname='csharp.%s' % test,
361 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
362 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800363
murgatroid99256d3df2015-09-21 16:58:02 -0700364 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700365 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700366 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700367 else:
368 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700369
Craig Tiller883064c2015-11-04 10:06:10 -0800370 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700371 # For Windows, this target doesn't really build anything,
372 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700373 if self.platform == 'windows':
374 return []
375 else:
376 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800377
378 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700379 if self.platform == 'windows':
380 return [['src\\csharp\\buildall.bat']]
381 else:
382 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000383
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200384 def post_tests_steps(self):
385 return []
386
murgatroid99a3e244f2015-09-22 11:25:53 -0700387 def makefile_name(self):
388 return 'Makefile'
389
murgatroid99132ce6a2015-03-04 17:29:14 -0800390 def supports_multi_config(self):
391 return False
392
393 def __str__(self):
394 return 'csharp'
395
Craig Tillerd625d812015-04-08 15:52:35 -0700396
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700397class ObjCLanguage(object):
398
Craig Tiller883064c2015-11-04 10:06:10 -0800399 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700400 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
401 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
402
murgatroid99256d3df2015-09-21 16:58:02 -0700403 def pre_build_steps(self):
404 return []
405
Craig Tiller883064c2015-11-04 10:06:10 -0800406 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700407 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700408
409 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700410 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700411
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200412 def post_tests_steps(self):
413 return []
414
murgatroid99a3e244f2015-09-22 11:25:53 -0700415 def makefile_name(self):
416 return 'Makefile'
417
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700418 def supports_multi_config(self):
419 return False
420
421 def __str__(self):
422 return 'objc'
423
424
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100425class Sanity(object):
426
Craig Tiller883064c2015-11-04 10:06:10 -0800427 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800428 import yaml
429 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
Craig Tillera7a9e402016-01-20 11:05:34 -0800430 return [config.job_spec([cmd['script']], None, timeout_seconds=None, environ={'TEST': 'true'}, cpu_cost=cmd.get('cpu_cost', 1))
Craig Tiller94d04a52016-01-20 10:58:23 -0800431 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100432
murgatroid99256d3df2015-09-21 16:58:02 -0700433 def pre_build_steps(self):
434 return []
435
Craig Tiller883064c2015-11-04 10:06:10 -0800436 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100437 return ['run_dep_checks']
438
439 def build_steps(self):
440 return []
441
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200442 def post_tests_steps(self):
443 return []
444
murgatroid99a3e244f2015-09-22 11:25:53 -0700445 def makefile_name(self):
446 return 'Makefile'
447
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100448 def supports_multi_config(self):
449 return False
450
451 def __str__(self):
452 return 'sanity'
453
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200454
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100455class Build(object):
456
Craig Tiller883064c2015-11-04 10:06:10 -0800457 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100458 return []
459
murgatroid99256d3df2015-09-21 16:58:02 -0700460 def pre_build_steps(self):
461 return []
462
Craig Tiller883064c2015-11-04 10:06:10 -0800463 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200464 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100465
466 def build_steps(self):
467 return []
468
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100469 def post_tests_steps(self):
470 return []
471
murgatroid99a3e244f2015-09-22 11:25:53 -0700472 def makefile_name(self):
473 return 'Makefile'
474
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100475 def supports_multi_config(self):
476 return True
477
478 def __str__(self):
479 return self.make_target
480
481
Craig Tiller738c3342015-01-12 14:28:33 -0800482# different configurations we can run under
Craig Tillera0f85172016-01-20 15:56:06 -0800483with open('tools/run_tests/configs.json') as f:
Craig Tiller1dce9062016-01-20 17:01:56 -0800484 _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))
Craig Tiller738c3342015-01-12 14:28:33 -0800485
486
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100487_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800488_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800489 'c++': CLanguage('cxx', 'c++'),
490 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800491 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000492 'php': PhpLanguage(),
493 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800494 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100495 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700496 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100497 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100498 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800499 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800500
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700501_WINDOWS_CONFIG = {
502 'dbg': 'Debug',
503 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800504 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700505 }
506
David Garcia Quintase90cd372015-05-31 18:15:26 -0700507
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800508def _windows_arch_option(arch):
509 """Returns msbuild cmdline option for selected architecture."""
510 if arch == 'default' or arch == 'windows_x86':
511 return '/p:Platform=Win32'
512 elif arch == 'windows_x64':
513 return '/p:Platform=x64'
514 else:
515 print 'Architecture %s not supported on current platform.' % arch
516 sys.exit(1)
517
518
519def _windows_build_bat(compiler):
520 """Returns name of build.bat for selected compiler."""
521 if compiler == 'default' or compiler == 'vs2013':
522 return 'vsprojects\\build_vs2013.bat'
523 elif compiler == 'vs2015':
524 return 'vsprojects\\build_vs2015.bat'
525 elif compiler == 'vs2010':
526 return 'vsprojects\\build_vs2010.bat'
527 else:
528 print 'Compiler %s not supported.' % compiler
529 sys.exit(1)
530
531
532def _windows_toolset_option(compiler):
533 """Returns msbuild PlatformToolset for selected compiler."""
534 if compiler == 'default' or compiler == 'vs2013':
535 return '/p:PlatformToolset=v120'
536 elif compiler == 'vs2015':
537 return '/p:PlatformToolset=v140'
538 elif compiler == 'vs2010':
539 return '/p:PlatformToolset=v100'
540 else:
541 print 'Compiler %s not supported.' % compiler
542 sys.exit(1)
543
544
David Garcia Quintase90cd372015-05-31 18:15:26 -0700545def runs_per_test_type(arg_str):
546 """Auxilary function to parse the "runs_per_test" flag.
547
548 Returns:
549 A positive integer or 0, the latter indicating an infinite number of
550 runs.
551
552 Raises:
553 argparse.ArgumentTypeError: Upon invalid input.
554 """
555 if arg_str == 'inf':
556 return 0
557 try:
558 n = int(arg_str)
559 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700560 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700561 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700562 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700563 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700564
565# parse command line
566argp = argparse.ArgumentParser(description='Run grpc tests.')
567argp.add_argument('-c', '--config',
568 choices=['all'] + sorted(_CONFIGS.keys()),
569 nargs='+',
570 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700571argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
572 help='A positive integer or "inf". If "inf", all tests will run in an '
573 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800574argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800575argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800576argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800577argp.add_argument('-f', '--forever',
578 default=False,
579 action='store_const',
580 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100581argp.add_argument('-t', '--travis',
582 default=False,
583 action='store_const',
584 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800585argp.add_argument('--newline_on_success',
586 default=False,
587 action='store_const',
588 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800589argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700590 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800591 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700592 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700593argp.add_argument('-S', '--stop_on_failure',
594 default=False,
595 action='store_const',
596 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700597argp.add_argument('--use_docker',
598 default=False,
599 action='store_const',
600 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700601 help='Run all the tests under docker. That provides ' +
602 'additional isolation and prevents the need to install ' +
603 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700604argp.add_argument('--allow_flakes',
605 default=False,
606 action='store_const',
607 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700608 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800609argp.add_argument('--arch',
610 choices=['default', 'windows_x86', 'windows_x64'],
611 default='default',
612 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
613argp.add_argument('--compiler',
614 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
615 default='default',
616 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
617argp.add_argument('--build_only',
618 default=False,
619 action='store_const',
620 const=True,
621 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800622argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
623 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800624argp.add_argument('--update_submodules', default=[], nargs='*',
625 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
626 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700627argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200628argp.add_argument('-x', '--xml_report', default=None, type=str,
629 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800630args = argp.parse_args()
631
Craig Tiller5f735a62016-01-20 09:31:15 -0800632jobset.measure_cpu_costs = args.measure_cpu_costs
633
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700634if args.use_docker:
635 if not args.travis:
636 print 'Seen --use_docker flag, will run tests under docker.'
637 print
638 print 'IMPORTANT: The changes you are testing need to be locally committed'
639 print 'because only the committed changes in the current branch will be'
640 print 'copied to the docker environment.'
641 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700642
643 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700644 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700645
646 # TODO(jtattermusch): revisit if we need special handling for arch here
647 # set arch command prefix in case we are working with different arch.
648 arch_env = os.getenv('arch')
649 if arch_env:
650 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
651
652 env = os.environ.copy()
653 env['RUN_TESTS_COMMAND'] = run_tests_cmd
654 if args.xml_report:
655 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700656 if not args.travis:
657 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700658
659 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
660 shell=True,
661 env=env)
662 sys.exit(0)
663
Craig Tiller1676f912016-01-05 10:49:44 -0800664# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800665need_to_regenerate_projects = False
666for spec in args.update_submodules:
667 spec = spec.split(':', 1)
668 if len(spec) == 1:
669 submodule = spec[0]
670 branch = 'master'
671 elif len(spec) == 2:
672 submodule = spec[0]
673 branch = spec[1]
674 cwd = 'third_party/%s' % submodule
675 def git(cmd, cwd=cwd):
676 print 'in %s: git %s' % (cwd, cmd)
677 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
678 git('fetch')
679 git('checkout %s' % branch)
680 git('pull origin %s' % branch)
681 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
682 need_to_regenerate_projects = True
683if need_to_regenerate_projects:
684 if jobset.platform_string() == 'linux':
685 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
686 else:
687 print 'WARNING: may need to regenerate projects, but since we are not on'
688 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800689
690
Nicolas Nobleddef2462015-01-06 18:08:25 -0800691# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800692run_configs = set(_CONFIGS[cfg]
693 for cfg in itertools.chain.from_iterable(
694 _CONFIGS.iterkeys() if x == 'all' else [x]
695 for x in args.config))
696build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800697
Craig Tiller06805272015-06-11 14:46:47 -0700698if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700699 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700700
Adele Zhou6b9527c2015-11-20 15:56:35 -0800701if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800702 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800703else:
704 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800705# We don't support code coverage on some languages
706if 'gcov' in args.config:
707 for bad in ['objc', 'sanity', 'build']:
708 if bad in lang_list:
709 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800710
711languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800712
713if len(build_configs) > 1:
714 for language in languages:
715 if not language.supports_multi_config():
716 print language, 'does not support multiple build configurations'
717 sys.exit(1)
718
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800719if platform_string() != 'windows':
720 if args.arch != 'default':
721 print 'Architecture %s not supported on current platform.' % args.arch
722 sys.exit(1)
723 if args.compiler != 'default':
724 print 'Compiler %s not supported on current platform.' % args.compiler
725 sys.exit(1)
726
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100727if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700728 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700729 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700730 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700731 # empirically /m:2 gives the best performance/price and should prevent
732 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700733 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700734 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700735 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700736 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800737 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700738 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800739 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
740 _windows_toolset_option(args.compiler),
741 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700742 extra_args,
Craig Tiller590105a2016-01-19 13:03:46 -0800743 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700744 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700745else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700746 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700747 if targets:
748 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
749 '-f', makefile,
750 '-j', '%d' % (multiprocessing.cpu_count() + 1),
Craig Tiller71a86042016-01-15 14:59:58 -0800751 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
752 'CONFIG=%s' % cfg] +
753 ([] if not args.travis else ['JENKINS_BUILD=1']) +
754 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800755 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700756 else:
757 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700758make_targets = {}
759for l in languages:
760 makefile = l.makefile_name()
761 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800762 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700763
Jan Tattermusche4a69182015-12-15 09:53:01 -0800764def build_step_environ(cfg):
765 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800766 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800767 if msbuild_cfg:
768 environ['MSBUILD_CONFIG'] = msbuild_cfg
769 return environ
770
murgatroid99fddac962015-09-22 09:20:11 -0700771build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800772 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700773 for cfg in build_configs
774 for l in languages
775 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700776if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700777 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 -0700778 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700779build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800780 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800781 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800782 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700783 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800784
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200785post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800786 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200787 for cfg in build_configs
788 for l in languages
789 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800790runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800791forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800792
Nicolas Nobleddef2462015-01-06 18:08:25 -0800793
Craig Tiller71735182015-01-15 17:07:13 -0800794class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800795 """Cache for running tests."""
796
David Klempner25739582015-02-11 15:57:32 -0800797 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800798 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800799 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700800 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800801
802 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800803 if cmdline not in self._last_successful_run:
804 return True
805 if self._last_successful_run[cmdline] != bin_hash:
806 return True
David Klempner25739582015-02-11 15:57:32 -0800807 if not self._use_cache_results:
808 return True
Craig Tiller71735182015-01-15 17:07:13 -0800809 return False
810
811 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800812 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700813 if time.time() - self._last_save > 1:
814 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800815
816 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800817 return [{'cmdline': k, 'hash': v}
818 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800819
820 def parse(self, exdump):
821 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
822
823 def save(self):
824 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800825 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700826 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800827
Craig Tiller1cc11db2015-01-15 22:50:50 -0800828 def maybe_load(self):
829 if os.path.exists('.run_tests_cache'):
830 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800831 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800832
833
Craig Tillerf53d9c82015-08-04 14:19:43 -0700834def _start_port_server(port_server_port):
835 # check if a compatible port server is running
836 # if incompatible (version mismatch) ==> start a new one
837 # if not running ==> start a new one
838 # otherwise, leave it up
839 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700840 version = int(urllib2.urlopen(
841 'http://localhost:%d/version_number' % port_server_port,
842 timeout=1).read())
843 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700844 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700845 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700846 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700847 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700848 running = False
849 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700850 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800851 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
852 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700853 print 'my port server is version %d' % current_version
854 running = (version >= current_version)
855 if not running:
856 print 'port_server version mismatch: killing the old one'
857 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
858 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700859 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700860 fd, logfile = tempfile.mkstemp()
861 os.close(fd)
862 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800863 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
864 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700865 env = dict(os.environ)
866 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100867 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800868 # Working directory of port server needs to be outside of Jenkins
869 # workspace to prevent file lock issues.
870 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700871 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700872 args,
873 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800874 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700875 creationflags = 0x00000008, # detached process
876 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700877 else:
878 port_server = subprocess.Popen(
879 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700880 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700881 preexec_fn=os.setsid,
882 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700883 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700884 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700885 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700886 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700887 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700888 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700889 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700890 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700891 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700892 # try one final time: maybe another build managed to start one
893 time.sleep(1)
894 try:
895 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
896 timeout=1).read()
897 print 'last ditch attempt to contact port server succeeded'
898 break
899 except:
900 traceback.print_exc();
901 port_log = open(logfile, 'r').read()
902 print port_log
903 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700904 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700905 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
906 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700907 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700908 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700909 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700910 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700911 traceback.print_exc();
912 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700913 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700914 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700915 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700916 traceback.print_exc();
917 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700918 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700919 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700920 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700921 port_server.kill()
922 raise
923
924
Adele Zhoud5fffa52015-10-23 15:51:42 -0700925def _calculate_num_runs_failures(list_of_results):
926 """Caculate number of runs and failures for a particular test.
927
928 Args:
929 list_of_results: (List) of JobResult object.
930 Returns:
931 A tuple of total number of runs and failures.
932 """
933 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
934 num_failures = 0
935 for jobresult in list_of_results:
936 if jobresult.retries > 0:
937 num_runs += jobresult.retries
938 if jobresult.num_failures > 0:
939 num_failures += jobresult.num_failures
940 return num_runs, num_failures
941
Adele Zhou6b9527c2015-11-20 15:56:35 -0800942
Craig Tillereb9de8b2016-01-08 08:57:41 -0800943# _build_and_run results
944class BuildAndRunError(object):
945
946 BUILD = object()
947 TEST = object()
948 POST_TEST = object()
949
950
951# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700952def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800953 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800954 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800955 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700956 num_failures, _ = jobset.run(
957 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800958 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700959 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800960 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -0800961
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800962 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800963 return []
ctiller3040cb72015-01-07 12:13:17 -0800964
Craig Tiller234b6e72015-05-23 10:12:40 -0700965 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700966 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700967 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700968 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700969 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800970 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800971 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700972 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700973 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700974 one_run = set(
975 spec
976 for config in run_configs
977 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800978 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700979 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700980 # When running on travis, we want out test runs to be as similar as possible
981 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800982 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700983 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
984 else:
985 # whereas otherwise, we want to shuffle things up to give all tests a
986 # chance to run.
987 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
988 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -0700989 if infinite_runs:
990 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 -0700991 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
992 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -0700993 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200994
Adele Zhou803af152015-11-30 15:16:16 -0800995 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -0700996 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -0800997 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -0700998 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700999 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001000 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001001 if resultset:
1002 for k, v in resultset.iteritems():
1003 num_runs, num_failures = _calculate_num_runs_failures(v)
1004 if num_failures == num_runs: # what about infinite_runs???
1005 jobset.message('FAILED', k, do_newline=True)
1006 elif num_failures > 0:
1007 jobset.message(
1008 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1009 do_newline=True)
1010 else:
1011 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001012 finally:
1013 for antagonist in antagonists:
1014 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001015 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001016 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001017
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001018 number_failures, _ = jobset.run(
1019 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001020 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001021
1022 out = []
1023 if number_failures:
1024 out.append(BuildAndRunError.POST_TEST)
1025 if num_test_failures:
1026 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001027
Craig Tiller69cd2372015-06-11 09:38:09 -07001028 if cache: cache.save()
1029
Craig Tillereb9de8b2016-01-08 08:57:41 -08001030 return out
ctiller3040cb72015-01-07 12:13:17 -08001031
1032
David Klempner25739582015-02-11 15:57:32 -08001033test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001034test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001035
ctiller3040cb72015-01-07 12:13:17 -08001036if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001037 success = True
ctiller3040cb72015-01-07 12:13:17 -08001038 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001039 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001040 initial_time = dw.most_recent_change()
1041 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001042 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001043 errors = _build_and_run(check_cancelled=have_files_changed,
1044 newline_on_success=False,
1045 cache=test_cache,
1046 build_only=args.build_only) == 0
1047 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001048 jobset.message('SUCCESS',
1049 'All tests are now passing properly',
1050 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001051 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001052 while not have_files_changed():
1053 time.sleep(1)
1054else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001055 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001056 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001057 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001058 xml_report=args.xml_report,
1059 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001060 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001061 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1062 else:
1063 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001064 exit_code = 0
1065 if BuildAndRunError.BUILD in errors:
1066 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001067 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001068 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001069 if BuildAndRunError.POST_TEST in errors:
1070 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001071 sys.exit(exit_code)
Craig Tillera0f85172016-01-20 15:56:06 -08001072