blob: 6e4a1499e721b0463a02c27cc3d28a0d83b52c19 [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
34import glob
Craig Tillerf53d9c82015-08-04 14:19:43 -070035import hashlib
Nicolas Nobleddef2462015-01-06 18:08:25 -080036import itertools
Craig Tiller261dd982015-01-16 16:41:45 -080037import json
Nicolas Nobleddef2462015-01-06 18:08:25 -080038import multiprocessing
Craig Tiller1cc11db2015-01-15 22:50:50 -080039import os
David Garcia Quintas79e389f2015-06-02 17:49:42 -070040import platform
41import random
Craig Tillerfe406ec2015-02-24 13:55:12 -080042import re
Craig Tiller82875232015-09-25 13:57:34 -070043import socket
David Garcia Quintas79e389f2015-06-02 17:49:42 -070044import subprocess
Nicolas Nobleddef2462015-01-06 18:08:25 -080045import sys
Craig Tillerf0a293e2015-10-12 10:05:50 -070046import tempfile
47import traceback
ctiller3040cb72015-01-07 12:13:17 -080048import time
Craig Tillerf53d9c82015-08-04 14:19:43 -070049import urllib2
Jan Tattermusch03c01062015-12-11 14:28:56 -080050import uuid
Nicolas Nobleddef2462015-01-06 18:08:25 -080051
52import jobset
Adele Zhoua30f8292015-11-02 13:15:46 -080053import report_utils
ctiller3040cb72015-01-07 12:13:17 -080054import watch_dirs
Nicolas Nobleddef2462015-01-06 18:08:25 -080055
Craig Tillerb361b4e2016-01-06 11:44:17 -080056
Craig Tiller2cc2b842015-02-27 11:38:31 -080057ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
58os.chdir(ROOT)
59
60
Craig Tiller06805272015-06-11 14:46:47 -070061_FORCE_ENVIRON_FOR_WRAPPERS = {}
62
63
Craig Tillerd50993d2015-08-05 08:04:36 -070064def platform_string():
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +010065 return jobset.platform_string()
Craig Tillerd50993d2015-08-05 08:04:36 -070066
67
Craig Tiller738c3342015-01-12 14:28:33 -080068# SimpleConfig: just compile with CONFIG=config, and run the binary to test
Craig Tillera0f85172016-01-20 15:56:06 -080069class Config(object):
Craig Tillerb50d1662015-01-15 17:28:21 -080070
Craig Tillera0f85172016-01-20 15:56:06 -080071 def __init__(self, config, environ=None, timeout_multiplier=1, tool_prefix=[]):
murgatroid99132ce6a2015-03-04 17:29:14 -080072 if environ is None:
73 environ = {}
Craig Tiller738c3342015-01-12 14:28:33 -080074 self.build_config = config
Craig Tillerc7449162015-01-16 14:42:10 -080075 self.allow_hashing = (config != 'gcov')
Craig Tiller547db2b2015-01-30 14:08:39 -080076 self.environ = environ
murgatroid99132ce6a2015-03-04 17:29:14 -080077 self.environ['CONFIG'] = config
Craig Tillera0f85172016-01-20 15:56:06 -080078 self.tool_prefix = tool_prefix
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070079 self.timeout_multiplier = timeout_multiplier
Craig Tiller738c3342015-01-12 14:28:33 -080080
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -070081 def job_spec(self, cmdline, hash_targets, timeout_seconds=5*60,
Craig Tiller56c6b6a2016-01-20 08:27:37 -080082 shortname=None, environ={}, cpu_cost=1.0):
Craig Tiller49f61322015-03-03 13:02:11 -080083 """Construct a jobset.JobSpec for a test under this config
84
85 Args:
86 cmdline: a list of strings specifying the command line the test
87 would like to run
88 hash_targets: either None (don't do caching of test results), or
89 a list of strings specifying files to include in a
90 binary hash to check if a test has changed
91 -- if used, all artifacts needed to run the test must
92 be listed
93 """
Craig Tiller4fc90032015-05-21 10:39:52 -070094 actual_environ = self.environ.copy()
95 for k, v in environ.iteritems():
96 actual_environ[k] = v
Craig Tillera0f85172016-01-20 15:56:06 -080097 return jobset.JobSpec(cmdline=self.tool_prefix + cmdline,
Jan Tattermusch9a7d30c2015-04-23 16:12:55 -070098 shortname=shortname,
Craig Tiller4fc90032015-05-21 10:39:52 -070099 environ=actual_environ,
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800100 cpu_cost=cpu_cost,
Craig Tiller94d04a52016-01-20 10:58:23 -0800101 timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),
Craig Tiller547db2b2015-01-30 14:08:39 -0800102 hash_targets=hash_targets
Craig Tillerd4509a12015-09-28 09:18:40 -0700103 if self.allow_hashing else None,
Craig Tiller35505de2015-10-08 13:31:33 -0700104 flake_retries=5 if args.allow_flakes else 0,
105 timeout_retries=3 if args.allow_flakes else 0)
Craig Tiller738c3342015-01-12 14:28:33 -0800106
107
murgatroid99cf08daf2015-09-21 15:33:16 -0700108def get_c_tests(travis, test_lang) :
109 out = []
110 platforms_str = 'ci_platforms' if travis else 'platforms'
111 with open('tools/run_tests/tests.json') as f:
murgatroid9989899b12015-09-22 09:14:48 -0700112 js = json.load(f)
murgatroid99a3e244f2015-09-22 11:25:53 -0700113 return [tgt
114 for tgt in js
115 if tgt['language'] == test_lang and
116 platform_string() in tgt[platforms_str] and
117 not (travis and tgt['flaky'])]
murgatroid99cf08daf2015-09-21 15:33:16 -0700118
murgatroid99fafeeb32015-09-22 09:13:03 -0700119
Craig Tillerc7449162015-01-16 14:42:10 -0800120class CLanguage(object):
121
Craig Tillere9c959d2015-01-18 10:23:26 -0800122 def __init__(self, make_target, test_lang):
Craig Tillerc7449162015-01-16 14:42:10 -0800123 self.make_target = make_target
Craig Tillerd50993d2015-08-05 08:04:36 -0700124 self.platform = platform_string()
Craig Tiller711bbe62015-08-19 12:35:16 -0700125 self.test_lang = test_lang
Craig Tillerc7449162015-01-16 14:42:10 -0800126
Craig Tiller883064c2015-11-04 10:06:10 -0800127 def test_specs(self, config, args):
Craig Tiller547db2b2015-01-30 14:08:39 -0800128 out = []
Craig Tiller883064c2015-11-04 10:06:10 -0800129 binaries = get_c_tests(args.travis, self.test_lang)
Craig Tiller711bbe62015-08-19 12:35:16 -0700130 for target in binaries:
murgatroid99a3e244f2015-09-22 11:25:53 -0700131 if config.build_config in target['exclude_configs']:
murgatroid99fafeeb32015-09-22 09:13:03 -0700132 continue
Nicolas Noblee1445362015-05-11 17:40:26 -0700133 if self.platform == 'windows':
Craig Tillerf4182602015-09-01 12:23:16 -0700134 binary = 'vsprojects/%s/%s.exe' % (
135 _WINDOWS_CONFIG[config.build_config], target['name'])
Nicolas Noblee1445362015-05-11 17:40:26 -0700136 else:
137 binary = 'bins/%s/%s' % (config.build_config, target['name'])
yang-g6c1fdc62015-08-18 11:57:42 -0700138 if os.path.isfile(binary):
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800139 cmdline = [binary] + target['args']
140 out.append(config.job_spec(cmdline, [binary],
141 shortname=' '.join(cmdline),
Craig Tiller56c6b6a2016-01-20 08:27:37 -0800142 cpu_cost=target['cpu_cost'],
Craig Tillercc0535d2015-12-08 15:14:47 -0800143 environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
144 os.path.abspath(os.path.dirname(
Craig Tillered2164d2015-12-08 22:03:36 -0800145 sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')}))
Craig Tiller883064c2015-11-04 10:06:10 -0800146 elif args.regex == '.*' or platform_string() == 'windows':
Adele Zhoue4c35612015-10-16 15:34:23 -0700147 print '\nWARNING: binary not found, skipping', binary
Nicolas Noblee1445362015-05-11 17:40:26 -0700148 return sorted(out)
Craig Tillerc7449162015-01-16 14:42:10 -0800149
Craig Tiller883064c2015-11-04 10:06:10 -0800150 def make_targets(self, test_regex):
151 if platform_string() != 'windows' and test_regex != '.*':
152 # use the regex to minimize the number of things to build
153 return [target['name']
154 for target in get_c_tests(False, self.test_lang)
155 if re.search(test_regex, target['name'])]
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700156 if platform_string() == 'windows':
157 # don't build tools on windows just yet
158 return ['buildtests_%s' % self.make_target]
Craig Tiller7552f0f2015-06-19 17:46:20 -0700159 return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
Craig Tillerc7449162015-01-16 14:42:10 -0800160
murgatroid99256d3df2015-09-21 16:58:02 -0700161 def pre_build_steps(self):
Jan Tattermusch874aec02015-10-07 19:26:19 -0700162 if self.platform == 'windows':
163 return [['tools\\run_tests\\pre_build_c.bat']]
164 else:
165 return []
murgatroid99256d3df2015-09-21 16:58:02 -0700166
Craig Tillerc7449162015-01-16 14:42:10 -0800167 def build_steps(self):
168 return []
169
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200170 def post_tests_steps(self):
171 if self.platform == 'windows':
172 return []
173 else:
174 return [['tools/run_tests/post_tests_c.sh']]
175
murgatroid99a3e244f2015-09-22 11:25:53 -0700176 def makefile_name(self):
177 return 'Makefile'
178
murgatroid99132ce6a2015-03-04 17:29:14 -0800179 def supports_multi_config(self):
180 return True
181
182 def __str__(self):
183 return self.make_target
184
Craig Tillercc0535d2015-12-08 15:14:47 -0800185
murgatroid992c8d5162015-01-26 10:41:21 -0800186class NodeLanguage(object):
187
Craig Tiller883064c2015-11-04 10:06:10 -0800188 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700189 return [config.job_spec(['tools/run_tests/run_node.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700190 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid992c8d5162015-01-26 10:41:21 -0800191
murgatroid99256d3df2015-09-21 16:58:02 -0700192 def pre_build_steps(self):
murgatroid99ae369de2015-10-09 15:40:48 -0700193 # Default to 1 week cache expiration
murgatroid9993758952015-10-14 11:51:05 -0700194 return [['tools/run_tests/pre_build_node.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700195
Craig Tiller883064c2015-11-04 10:06:10 -0800196 def make_targets(self, test_regex):
murgatroid99db5b1602015-10-01 13:20:11 -0700197 return []
murgatroid992c8d5162015-01-26 10:41:21 -0800198
199 def build_steps(self):
200 return [['tools/run_tests/build_node.sh']]
Craig Tillerc7449162015-01-16 14:42:10 -0800201
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200202 def post_tests_steps(self):
203 return []
204
murgatroid99a3e244f2015-09-22 11:25:53 -0700205 def makefile_name(self):
206 return 'Makefile'
207
murgatroid99132ce6a2015-03-04 17:29:14 -0800208 def supports_multi_config(self):
209 return False
210
211 def __str__(self):
212 return 'node'
213
Craig Tiller99775822015-01-30 13:07:16 -0800214
Craig Tillerc7449162015-01-16 14:42:10 -0800215class PhpLanguage(object):
216
Craig Tiller883064c2015-11-04 10:06:10 -0800217 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700218 return [config.job_spec(['src/php/bin/run_tests.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700219 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Craig Tillerc7449162015-01-16 14:42:10 -0800220
murgatroid99256d3df2015-09-21 16:58:02 -0700221 def pre_build_steps(self):
222 return []
223
Craig Tiller883064c2015-11-04 10:06:10 -0800224 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700225 return ['static_c', 'shared_c']
Craig Tillerc7449162015-01-16 14:42:10 -0800226
227 def build_steps(self):
228 return [['tools/run_tests/build_php.sh']]
229
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200230 def post_tests_steps(self):
Stanley Cheunga6b95482016-01-13 16:10:48 -0800231 return [['tools/run_tests/post_tests_php.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200232
murgatroid99a3e244f2015-09-22 11:25:53 -0700233 def makefile_name(self):
234 return 'Makefile'
235
murgatroid99132ce6a2015-03-04 17:29:14 -0800236 def supports_multi_config(self):
237 return False
238
239 def __str__(self):
240 return 'php'
241
Craig Tillerc7449162015-01-16 14:42:10 -0800242
Nathaniel Manista840615e2015-01-22 20:31:47 +0000243class PythonLanguage(object):
244
Craig Tiller49f61322015-03-03 13:02:11 -0800245 def __init__(self):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700246 self._build_python_versions = ['2.7']
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700247 self._has_python_versions = []
Craig Tiller49f61322015-03-03 13:02:11 -0800248
Craig Tiller883064c2015-11-04 10:06:10 -0800249 def test_specs(self, config, args):
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700250 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
251 environment['PYVER'] = '2.7'
252 return [config.job_spec(
253 ['tools/run_tests/run_python.sh'],
254 None,
255 environ=environment,
256 shortname='py.test',
Masood Malekghassemi26ea9e22015-10-09 15:19:17 -0700257 timeout_seconds=15*60
Masood Malekghassemi2b841622015-07-28 17:39:02 -0700258 )]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000259
murgatroid99256d3df2015-09-21 16:58:02 -0700260 def pre_build_steps(self):
261 return []
262
Craig Tiller883064c2015-11-04 10:06:10 -0800263 def make_targets(self, test_regex):
Craig Tilleraf7cf542015-05-22 10:07:34 -0700264 return ['static_c', 'grpc_python_plugin', 'shared_c']
Nathaniel Manista840615e2015-01-22 20:31:47 +0000265
266 def build_steps(self):
Masood Malekghassemie5f70022015-06-29 09:20:26 -0700267 commands = []
268 for python_version in self._build_python_versions:
269 try:
270 with open(os.devnull, 'w') as output:
271 subprocess.check_call(['which', 'python' + python_version],
272 stdout=output, stderr=output)
273 commands.append(['tools/run_tests/build_python.sh', python_version])
274 self._has_python_versions.append(python_version)
275 except:
276 jobset.message('WARNING', 'Missing Python ' + python_version,
277 do_newline=True)
278 return commands
Nathaniel Manista840615e2015-01-22 20:31:47 +0000279
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200280 def post_tests_steps(self):
281 return []
282
murgatroid99a3e244f2015-09-22 11:25:53 -0700283 def makefile_name(self):
284 return 'Makefile'
285
murgatroid99132ce6a2015-03-04 17:29:14 -0800286 def supports_multi_config(self):
287 return False
288
289 def __str__(self):
290 return 'python'
291
Craig Tillerd625d812015-04-08 15:52:35 -0700292
murgatroid996a4c4fa2015-02-27 12:08:57 -0800293class RubyLanguage(object):
294
Craig Tiller883064c2015-11-04 10:06:10 -0800295 def test_specs(self, config, args):
Craig Tiller4fc90032015-05-21 10:39:52 -0700296 return [config.job_spec(['tools/run_tests/run_ruby.sh'], None,
Craig Tiller06805272015-06-11 14:46:47 -0700297 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
murgatroid996a4c4fa2015-02-27 12:08:57 -0800298
murgatroid99256d3df2015-09-21 16:58:02 -0700299 def pre_build_steps(self):
Nicolas "Pixel" Noblebcf988f2015-10-08 03:00:42 +0200300 return [['tools/run_tests/pre_build_ruby.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700301
Craig Tiller883064c2015-11-04 10:06:10 -0800302 def make_targets(self, test_regex):
murgatroid99a43c14f2015-07-30 13:31:23 -0700303 return ['static_c']
murgatroid996a4c4fa2015-02-27 12:08:57 -0800304
305 def build_steps(self):
306 return [['tools/run_tests/build_ruby.sh']]
307
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200308 def post_tests_steps(self):
Nicolas "Pixel" Noble7ef1e532015-12-02 00:55:33 +0100309 return [['tools/run_tests/post_tests_ruby.sh']]
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200310
murgatroid99a3e244f2015-09-22 11:25:53 -0700311 def makefile_name(self):
312 return 'Makefile'
313
murgatroid99132ce6a2015-03-04 17:29:14 -0800314 def supports_multi_config(self):
315 return False
316
317 def __str__(self):
318 return 'ruby'
319
Craig Tillerd625d812015-04-08 15:52:35 -0700320
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800321class CSharpLanguage(object):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700322 def __init__(self):
Craig Tillerd50993d2015-08-05 08:04:36 -0700323 self.platform = platform_string()
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700324
Craig Tiller883064c2015-11-04 10:06:10 -0800325 def test_specs(self, config, args):
Jan Tattermusch03c01062015-12-11 14:28:56 -0800326 with open('src/csharp/tests.json') as f:
327 tests_json = json.load(f)
328 assemblies = tests_json['assemblies']
329 tests = tests_json['tests']
330
331 msbuild_config = _WINDOWS_CONFIG[config.build_config]
332 assembly_files = ['%s/bin/%s/%s.dll' % (a, msbuild_config, a)
333 for a in assemblies]
334
335 extra_args = ['-labels'] + assembly_files
336
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700337 if self.platform == 'windows':
Jan Tattermusch03c01062015-12-11 14:28:56 -0800338 script_name = 'tools\\run_tests\\run_csharp.bat'
339 extra_args += ['-domain=None']
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700340 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800341 script_name = 'tools/run_tests/run_csharp.sh'
Jan Tattermuschbf3b1532015-10-26 10:24:42 -0700342
Jan Tattermuschbdf4b2e2015-10-28 08:22:34 -0700343 if config.build_config == 'gcov':
344 # On Windows, we only collect C# code coverage.
345 # On Linux, we only collect coverage for native extension.
346 # For code coverage all tests need to run as one suite.
Jan Tattermusch03c01062015-12-11 14:28:56 -0800347 return [config.job_spec([script_name] + extra_args, None,
348 shortname='csharp.coverage',
349 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
Jan Tattermusch61c3a832015-10-27 17:54:50 -0700350 else:
Jan Tattermusch03c01062015-12-11 14:28:56 -0800351 specs = []
352 for test in tests:
353 cmdline = [script_name, '-run=%s' % test] + extra_args
354 if self.platform == 'windows':
355 # use different output directory for each test to prevent
356 # TestResult.xml clash between parallel test runs.
357 cmdline += ['-work=test-result/%s' % uuid.uuid4()]
358 specs.append(config.job_spec(cmdline, None,
359 shortname='csharp.%s' % test,
360 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
361 return specs
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800362
murgatroid99256d3df2015-09-21 16:58:02 -0700363 def pre_build_steps(self):
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700364 if self.platform == 'windows':
Jan Tattermusch874aec02015-10-07 19:26:19 -0700365 return [['tools\\run_tests\\pre_build_csharp.bat']]
Jan Tattermusch48423fc2015-10-07 18:59:16 -0700366 else:
367 return [['tools/run_tests/pre_build_csharp.sh']]
murgatroid99256d3df2015-09-21 16:58:02 -0700368
Craig Tiller883064c2015-11-04 10:06:10 -0800369 def make_targets(self, test_regex):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700370 # For Windows, this target doesn't really build anything,
371 # everything is build by buildall script later.
Craig Tillerd5904822015-08-31 21:30:58 -0700372 if self.platform == 'windows':
373 return []
374 else:
375 return ['grpc_csharp_ext']
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800376
377 def build_steps(self):
Jan Tattermuschb00aa672015-06-01 15:48:03 -0700378 if self.platform == 'windows':
379 return [['src\\csharp\\buildall.bat']]
380 else:
381 return [['tools/run_tests/build_csharp.sh']]
Nathaniel Manista840615e2015-01-22 20:31:47 +0000382
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200383 def post_tests_steps(self):
384 return []
385
murgatroid99a3e244f2015-09-22 11:25:53 -0700386 def makefile_name(self):
387 return 'Makefile'
388
murgatroid99132ce6a2015-03-04 17:29:14 -0800389 def supports_multi_config(self):
390 return False
391
392 def __str__(self):
393 return 'csharp'
394
Craig Tillerd625d812015-04-08 15:52:35 -0700395
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700396class ObjCLanguage(object):
397
Craig Tiller883064c2015-11-04 10:06:10 -0800398 def test_specs(self, config, args):
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700399 return [config.job_spec(['src/objective-c/tests/run_tests.sh'], None,
400 environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
401
murgatroid99256d3df2015-09-21 16:58:02 -0700402 def pre_build_steps(self):
403 return []
404
Craig Tiller883064c2015-11-04 10:06:10 -0800405 def make_targets(self, test_regex):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700406 return ['grpc_objective_c_plugin', 'interop_server']
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700407
408 def build_steps(self):
Jorge Canizalesd0b32e92015-07-30 23:08:43 -0700409 return [['src/objective-c/tests/build_tests.sh']]
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700410
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200411 def post_tests_steps(self):
412 return []
413
murgatroid99a3e244f2015-09-22 11:25:53 -0700414 def makefile_name(self):
415 return 'Makefile'
416
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700417 def supports_multi_config(self):
418 return False
419
420 def __str__(self):
421 return 'objc'
422
423
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100424class Sanity(object):
425
Craig Tiller883064c2015-11-04 10:06:10 -0800426 def test_specs(self, config, args):
Craig Tiller94d04a52016-01-20 10:58:23 -0800427 import yaml
428 with open('tools/run_tests/sanity_tests.yaml', 'r') as f:
Craig Tillera7a9e402016-01-20 11:05:34 -0800429 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 -0800430 for cmd in yaml.load(f)]
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100431
murgatroid99256d3df2015-09-21 16:58:02 -0700432 def pre_build_steps(self):
433 return []
434
Craig Tiller883064c2015-11-04 10:06:10 -0800435 def make_targets(self, test_regex):
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100436 return ['run_dep_checks']
437
438 def build_steps(self):
439 return []
440
Nicolas "Pixel" Noble87879b32015-10-12 23:28:53 +0200441 def post_tests_steps(self):
442 return []
443
murgatroid99a3e244f2015-09-22 11:25:53 -0700444 def makefile_name(self):
445 return 'Makefile'
446
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100447 def supports_multi_config(self):
448 return False
449
450 def __str__(self):
451 return 'sanity'
452
Nicolas "Pixel" Noblee55cd7f2015-04-14 17:59:13 +0200453
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100454class Build(object):
455
Craig Tiller883064c2015-11-04 10:06:10 -0800456 def test_specs(self, config, args):
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100457 return []
458
murgatroid99256d3df2015-09-21 16:58:02 -0700459 def pre_build_steps(self):
460 return []
461
Craig Tiller883064c2015-11-04 10:06:10 -0800462 def make_targets(self, test_regex):
Nicolas "Pixel" Noblec23827b2015-04-23 06:17:55 +0200463 return ['static']
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100464
465 def build_steps(self):
466 return []
467
Nicolas "Pixel" Noblefe300452015-10-27 23:05:10 +0100468 def post_tests_steps(self):
469 return []
470
murgatroid99a3e244f2015-09-22 11:25:53 -0700471 def makefile_name(self):
472 return 'Makefile'
473
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100474 def supports_multi_config(self):
475 return True
476
477 def __str__(self):
478 return self.make_target
479
480
Craig Tiller738c3342015-01-12 14:28:33 -0800481# different configurations we can run under
Craig Tillera0f85172016-01-20 15:56:06 -0800482with open('tools/run_tests/configs.json') as f:
Craig Tiller1dce9062016-01-20 17:01:56 -0800483 _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))
Craig Tiller738c3342015-01-12 14:28:33 -0800484
485
Nicolas "Pixel" Noble1fb5e822015-03-16 06:20:37 +0100486_DEFAULT = ['opt']
Craig Tillerc7449162015-01-16 14:42:10 -0800487_LANGUAGES = {
Craig Tillere9c959d2015-01-18 10:23:26 -0800488 'c++': CLanguage('cxx', 'c++'),
489 'c': CLanguage('c', 'c'),
murgatroid992c8d5162015-01-26 10:41:21 -0800490 'node': NodeLanguage(),
Nathaniel Manista840615e2015-01-22 20:31:47 +0000491 'php': PhpLanguage(),
492 'python': PythonLanguage(),
Jan Tattermusch1970a5b2015-03-03 15:17:25 -0800493 'ruby': RubyLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100494 'csharp': CSharpLanguage(),
Jorge Canizalesa0b3bfa2015-07-30 19:25:52 -0700495 'objc' : ObjCLanguage(),
Nicolas "Pixel" Noble9f728642015-03-24 18:50:30 +0100496 'sanity': Sanity(),
Nicolas "Pixel" Noblefd2b0932015-03-26 00:26:29 +0100497 'build': Build(),
Craig Tillereb272bc2015-01-30 13:13:14 -0800498 }
Nicolas Nobleddef2462015-01-06 18:08:25 -0800499
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700500_WINDOWS_CONFIG = {
501 'dbg': 'Debug',
502 'opt': 'Release',
Jan Tattermusche4a69182015-12-15 09:53:01 -0800503 'gcov': 'Debug',
Craig Tiller7bb3efd2015-09-01 08:04:03 -0700504 }
505
David Garcia Quintase90cd372015-05-31 18:15:26 -0700506
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800507def _windows_arch_option(arch):
508 """Returns msbuild cmdline option for selected architecture."""
509 if arch == 'default' or arch == 'windows_x86':
510 return '/p:Platform=Win32'
511 elif arch == 'windows_x64':
512 return '/p:Platform=x64'
513 else:
514 print 'Architecture %s not supported on current platform.' % arch
515 sys.exit(1)
516
517
518def _windows_build_bat(compiler):
519 """Returns name of build.bat for selected compiler."""
520 if compiler == 'default' or compiler == 'vs2013':
521 return 'vsprojects\\build_vs2013.bat'
522 elif compiler == 'vs2015':
523 return 'vsprojects\\build_vs2015.bat'
524 elif compiler == 'vs2010':
525 return 'vsprojects\\build_vs2010.bat'
526 else:
527 print 'Compiler %s not supported.' % compiler
528 sys.exit(1)
529
530
531def _windows_toolset_option(compiler):
532 """Returns msbuild PlatformToolset for selected compiler."""
533 if compiler == 'default' or compiler == 'vs2013':
534 return '/p:PlatformToolset=v120'
535 elif compiler == 'vs2015':
536 return '/p:PlatformToolset=v140'
537 elif compiler == 'vs2010':
538 return '/p:PlatformToolset=v100'
539 else:
540 print 'Compiler %s not supported.' % compiler
541 sys.exit(1)
542
543
David Garcia Quintase90cd372015-05-31 18:15:26 -0700544def runs_per_test_type(arg_str):
545 """Auxilary function to parse the "runs_per_test" flag.
546
547 Returns:
548 A positive integer or 0, the latter indicating an infinite number of
549 runs.
550
551 Raises:
552 argparse.ArgumentTypeError: Upon invalid input.
553 """
554 if arg_str == 'inf':
555 return 0
556 try:
557 n = int(arg_str)
558 if n <= 0: raise ValueError
Craig Tiller50e53e22015-06-01 20:18:21 -0700559 return n
David Garcia Quintase90cd372015-05-31 18:15:26 -0700560 except:
Adele Zhoue4c35612015-10-16 15:34:23 -0700561 msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700562 raise argparse.ArgumentTypeError(msg)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700563
564# parse command line
565argp = argparse.ArgumentParser(description='Run grpc tests.')
566argp.add_argument('-c', '--config',
567 choices=['all'] + sorted(_CONFIGS.keys()),
568 nargs='+',
569 default=_DEFAULT)
David Garcia Quintase90cd372015-05-31 18:15:26 -0700570argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,
571 help='A positive integer or "inf". If "inf", all tests will run in an '
572 'infinite loop. Especially useful in combination with "-f"')
Craig Tillerfe406ec2015-02-24 13:55:12 -0800573argp.add_argument('-r', '--regex', default='.*', type=str)
Craig Tiller5f735a62016-01-20 09:31:15 -0800574argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Craig Tiller8451e872015-02-27 09:25:51 -0800575argp.add_argument('-s', '--slowdown', default=1.0, type=float)
ctiller3040cb72015-01-07 12:13:17 -0800576argp.add_argument('-f', '--forever',
577 default=False,
578 action='store_const',
579 const=True)
Nicolas "Pixel" Noblea7df3f92015-02-26 22:07:04 +0100580argp.add_argument('-t', '--travis',
581 default=False,
582 action='store_const',
583 const=True)
Nicolas Noble044db742015-01-14 16:57:24 -0800584argp.add_argument('--newline_on_success',
585 default=False,
586 action='store_const',
587 const=True)
Craig Tiller686fb262015-01-15 07:39:09 -0800588argp.add_argument('-l', '--language',
Craig Tiller60f15e62015-05-13 09:05:17 -0700589 choices=['all'] + sorted(_LANGUAGES.keys()),
Craig Tiller686fb262015-01-15 07:39:09 -0800590 nargs='+',
Craig Tiller60f15e62015-05-13 09:05:17 -0700591 default=['all'])
Craig Tillercd43da82015-05-29 08:41:29 -0700592argp.add_argument('-S', '--stop_on_failure',
593 default=False,
594 action='store_const',
595 const=True)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700596argp.add_argument('--use_docker',
597 default=False,
598 action='store_const',
599 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700600 help='Run all the tests under docker. That provides ' +
601 'additional isolation and prevents the need to install ' +
602 'language specific prerequisites. Only available on Linux.')
Craig Tillerd4509a12015-09-28 09:18:40 -0700603argp.add_argument('--allow_flakes',
604 default=False,
605 action='store_const',
606 const=True,
Adele Zhoue4c35612015-10-16 15:34:23 -0700607 help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800608argp.add_argument('--arch',
609 choices=['default', 'windows_x86', 'windows_x64'],
610 default='default',
611 help='Selects architecture to target. For some platforms "default" is the only supported choice.')
612argp.add_argument('--compiler',
613 choices=['default', 'vs2010', 'vs2013', 'vs2015'],
614 default='default',
615 help='Selects compiler to use. For some platforms "default" is the only supported choice.')
616argp.add_argument('--build_only',
617 default=False,
618 action='store_const',
619 const=True,
620 help='Perform all the build steps but dont run any tests.')
Craig Tiller5f735a62016-01-20 09:31:15 -0800621argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,
622 help='Measure the cpu costs of tests')
Craig Tiller1676f912016-01-05 10:49:44 -0800623argp.add_argument('--update_submodules', default=[], nargs='*',
624 help='Update some submodules before building. If any are updated, also run generate_projects. ' +
625 'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')
Craig Tiller234b6e72015-05-23 10:12:40 -0700626argp.add_argument('-a', '--antagonists', default=0, type=int)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200627argp.add_argument('-x', '--xml_report', default=None, type=str,
628 help='Generates a JUnit-compatible XML report')
Nicolas Nobleddef2462015-01-06 18:08:25 -0800629args = argp.parse_args()
630
Craig Tiller5f735a62016-01-20 09:31:15 -0800631jobset.measure_cpu_costs = args.measure_cpu_costs
632
Jan Tattermuschc96b9eb2015-09-18 16:01:21 -0700633if args.use_docker:
634 if not args.travis:
635 print 'Seen --use_docker flag, will run tests under docker.'
636 print
637 print 'IMPORTANT: The changes you are testing need to be locally committed'
638 print 'because only the committed changes in the current branch will be'
639 print 'copied to the docker environment.'
640 time.sleep(5)
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700641
642 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
Adele Zhoue4c35612015-10-16 15:34:23 -0700643 run_tests_cmd = 'tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700644
645 # TODO(jtattermusch): revisit if we need special handling for arch here
646 # set arch command prefix in case we are working with different arch.
647 arch_env = os.getenv('arch')
648 if arch_env:
649 run_test_cmd = 'arch %s %s' % (arch_env, run_test_cmd)
650
651 env = os.environ.copy()
652 env['RUN_TESTS_COMMAND'] = run_tests_cmd
653 if args.xml_report:
654 env['XML_REPORT'] = args.xml_report
Jan Tattermusch261b58c2015-09-18 17:15:48 -0700655 if not args.travis:
656 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
Jan Tattermuschc95eead2015-09-18 13:03:50 -0700657
658 subprocess.check_call(['tools/jenkins/build_docker_and_run_tests.sh'],
659 shell=True,
660 env=env)
661 sys.exit(0)
662
Craig Tiller1676f912016-01-05 10:49:44 -0800663# update submodules if necessary
Craig Tillerb361b4e2016-01-06 11:44:17 -0800664need_to_regenerate_projects = False
665for spec in args.update_submodules:
666 spec = spec.split(':', 1)
667 if len(spec) == 1:
668 submodule = spec[0]
669 branch = 'master'
670 elif len(spec) == 2:
671 submodule = spec[0]
672 branch = spec[1]
673 cwd = 'third_party/%s' % submodule
674 def git(cmd, cwd=cwd):
675 print 'in %s: git %s' % (cwd, cmd)
676 subprocess.check_call('git %s' % cmd, cwd=cwd, shell=True)
677 git('fetch')
678 git('checkout %s' % branch)
679 git('pull origin %s' % branch)
680 if os.path.exists('src/%s/gen_build_yaml.py' % submodule):
681 need_to_regenerate_projects = True
682if need_to_regenerate_projects:
683 if jobset.platform_string() == 'linux':
684 subprocess.check_call('tools/buildgen/generate_projects.sh', shell=True)
685 else:
686 print 'WARNING: may need to regenerate projects, but since we are not on'
687 print ' Linux this step is being skipped. Compilation MAY fail.'
Craig Tiller1676f912016-01-05 10:49:44 -0800688
689
Nicolas Nobleddef2462015-01-06 18:08:25 -0800690# grab config
Craig Tiller738c3342015-01-12 14:28:33 -0800691run_configs = set(_CONFIGS[cfg]
692 for cfg in itertools.chain.from_iterable(
693 _CONFIGS.iterkeys() if x == 'all' else [x]
694 for x in args.config))
695build_configs = set(cfg.build_config for cfg in run_configs)
Craig Tillerf1973b02015-01-16 12:32:13 -0800696
Craig Tiller06805272015-06-11 14:46:47 -0700697if args.travis:
murgatroid99d3b5b7f2015-10-06 17:02:03 -0700698 _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}
Craig Tiller06805272015-06-11 14:46:47 -0700699
Adele Zhou6b9527c2015-11-20 15:56:35 -0800700if 'all' in args.language:
Craig Tiller1676f912016-01-05 10:49:44 -0800701 lang_list = _LANGUAGES.keys()
Adele Zhou6b9527c2015-11-20 15:56:35 -0800702else:
703 lang_list = args.language
Craig Tiller16900662016-01-07 19:30:54 -0800704# We don't support code coverage on some languages
705if 'gcov' in args.config:
706 for bad in ['objc', 'sanity', 'build']:
707 if bad in lang_list:
708 lang_list.remove(bad)
Adele Zhou6b9527c2015-11-20 15:56:35 -0800709
710languages = set(_LANGUAGES[l] for l in lang_list)
murgatroid99132ce6a2015-03-04 17:29:14 -0800711
712if len(build_configs) > 1:
713 for language in languages:
714 if not language.supports_multi_config():
715 print language, 'does not support multiple build configurations'
716 sys.exit(1)
717
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800718if platform_string() != 'windows':
719 if args.arch != 'default':
720 print 'Architecture %s not supported on current platform.' % args.arch
721 sys.exit(1)
722 if args.compiler != 'default':
723 print 'Compiler %s not supported on current platform.' % args.compiler
724 sys.exit(1)
725
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100726if platform_string() == 'windows':
murgatroid99a3e244f2015-09-22 11:25:53 -0700727 def make_jobspec(cfg, targets, makefile='Makefile'):
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700728 extra_args = []
Craig Tillerb5391e12015-09-03 14:35:18 -0700729 # better do parallel compilation
Jan Tattermusch47eeb2b2015-10-07 14:09:18 -0700730 # empirically /m:2 gives the best performance/price and should prevent
731 # overloading the windows workers.
Adele Zhoue4c35612015-10-16 15:34:23 -0700732 extra_args.extend(['/m:2'])
Craig Tillerb5391e12015-09-03 14:35:18 -0700733 # disable PDB generation: it's broken, and we don't need it during CI
Adele Zhoue4c35612015-10-16 15:34:23 -0700734 extra_args.extend(['/p:Jenkins=true'])
Craig Tiller6fd23842015-09-01 07:36:31 -0700735 return [
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800736 jobset.JobSpec([_windows_build_bat(args.compiler),
murgatroid99cf08daf2015-09-21 15:33:16 -0700737 'vsprojects\\%s.sln' % target,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800738 '/p:Configuration=%s' % _WINDOWS_CONFIG[cfg],
739 _windows_toolset_option(args.compiler),
740 _windows_arch_option(args.arch)] +
Craig Tillerfc3c0c42015-09-01 16:47:54 -0700741 extra_args,
Craig Tiller590105a2016-01-19 13:03:46 -0800742 shell=True, timeout_seconds=None)
Craig Tiller6fd23842015-09-01 07:36:31 -0700743 for target in targets]
Craig Tiller5058c692015-04-08 09:42:04 -0700744else:
murgatroid99a3e244f2015-09-22 11:25:53 -0700745 def make_jobspec(cfg, targets, makefile='Makefile'):
murgatroid998ae409f2015-10-26 16:39:00 -0700746 if targets:
747 return [jobset.JobSpec([os.getenv('MAKE', 'make'),
748 '-f', makefile,
749 '-j', '%d' % (multiprocessing.cpu_count() + 1),
Craig Tiller71a86042016-01-15 14:59:58 -0800750 'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,
751 'CONFIG=%s' % cfg] +
752 ([] if not args.travis else ['JENKINS_BUILD=1']) +
753 targets,
Craig Tiller590105a2016-01-19 13:03:46 -0800754 timeout_seconds=None)]
murgatroid998ae409f2015-10-26 16:39:00 -0700755 else:
756 return []
murgatroid99a3e244f2015-09-22 11:25:53 -0700757make_targets = {}
758for l in languages:
759 makefile = l.makefile_name()
760 make_targets[makefile] = make_targets.get(makefile, set()).union(
Craig Tiller883064c2015-11-04 10:06:10 -0800761 set(l.make_targets(args.regex)))
Craig Tiller5058c692015-04-08 09:42:04 -0700762
Jan Tattermusche4a69182015-12-15 09:53:01 -0800763def build_step_environ(cfg):
764 environ = {'CONFIG': cfg}
Jan Tattermusch68016a12015-12-16 15:31:33 -0800765 msbuild_cfg = _WINDOWS_CONFIG.get(cfg)
Jan Tattermusche4a69182015-12-15 09:53:01 -0800766 if msbuild_cfg:
767 environ['MSBUILD_CONFIG'] = msbuild_cfg
768 return environ
769
murgatroid99fddac962015-09-22 09:20:11 -0700770build_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800771 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), flake_retries=5)
murgatroid99256d3df2015-09-21 16:58:02 -0700772 for cfg in build_configs
773 for l in languages
774 for cmdline in l.pre_build_steps()))
Craig Tillerbd4e3782015-09-01 06:48:55 -0700775if make_targets:
murgatroid99a3e244f2015-09-22 11:25:53 -0700776 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 -0700777 build_steps.extend(set(make_commands))
Craig Tiller5058c692015-04-08 09:42:04 -0700778build_steps.extend(set(
Craig Tiller590105a2016-01-19 13:03:46 -0800779 jobset.JobSpec(cmdline, environ=build_step_environ(cfg), timeout_seconds=None)
murgatroid99132ce6a2015-03-04 17:29:14 -0800780 for cfg in build_configs
Craig Tiller547db2b2015-01-30 14:08:39 -0800781 for l in languages
Craig Tiller533b1a22015-05-29 08:41:29 -0700782 for cmdline in l.build_steps()))
Craig Tillerf1973b02015-01-16 12:32:13 -0800783
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200784post_tests_steps = list(set(
Jan Tattermusche4a69182015-12-15 09:53:01 -0800785 jobset.JobSpec(cmdline, environ=build_step_environ(cfg))
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +0200786 for cfg in build_configs
787 for l in languages
788 for cmdline in l.post_tests_steps()))
Nicolas Nobleddef2462015-01-06 18:08:25 -0800789runs_per_test = args.runs_per_test
ctiller3040cb72015-01-07 12:13:17 -0800790forever = args.forever
Nicolas Nobleddef2462015-01-06 18:08:25 -0800791
Nicolas Nobleddef2462015-01-06 18:08:25 -0800792
Craig Tiller71735182015-01-15 17:07:13 -0800793class TestCache(object):
Craig Tillerb50d1662015-01-15 17:28:21 -0800794 """Cache for running tests."""
795
David Klempner25739582015-02-11 15:57:32 -0800796 def __init__(self, use_cache_results):
Craig Tiller71735182015-01-15 17:07:13 -0800797 self._last_successful_run = {}
David Klempner25739582015-02-11 15:57:32 -0800798 self._use_cache_results = use_cache_results
Craig Tiller69cd2372015-06-11 09:38:09 -0700799 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800800
801 def should_run(self, cmdline, bin_hash):
Craig Tiller71735182015-01-15 17:07:13 -0800802 if cmdline not in self._last_successful_run:
803 return True
804 if self._last_successful_run[cmdline] != bin_hash:
805 return True
David Klempner25739582015-02-11 15:57:32 -0800806 if not self._use_cache_results:
807 return True
Craig Tiller71735182015-01-15 17:07:13 -0800808 return False
809
810 def finished(self, cmdline, bin_hash):
Craig Tiller547db2b2015-01-30 14:08:39 -0800811 self._last_successful_run[cmdline] = bin_hash
Craig Tiller69cd2372015-06-11 09:38:09 -0700812 if time.time() - self._last_save > 1:
813 self.save()
Craig Tiller71735182015-01-15 17:07:13 -0800814
815 def dump(self):
Craig Tillerb50d1662015-01-15 17:28:21 -0800816 return [{'cmdline': k, 'hash': v}
817 for k, v in self._last_successful_run.iteritems()]
Craig Tiller71735182015-01-15 17:07:13 -0800818
819 def parse(self, exdump):
820 self._last_successful_run = dict((o['cmdline'], o['hash']) for o in exdump)
821
822 def save(self):
823 with open('.run_tests_cache', 'w') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800824 f.write(json.dumps(self.dump()))
Craig Tiller69cd2372015-06-11 09:38:09 -0700825 self._last_save = time.time()
Craig Tiller71735182015-01-15 17:07:13 -0800826
Craig Tiller1cc11db2015-01-15 22:50:50 -0800827 def maybe_load(self):
828 if os.path.exists('.run_tests_cache'):
829 with open('.run_tests_cache') as f:
Craig Tiller261dd982015-01-16 16:41:45 -0800830 self.parse(json.loads(f.read()))
Craig Tiller71735182015-01-15 17:07:13 -0800831
832
Craig Tillerf53d9c82015-08-04 14:19:43 -0700833def _start_port_server(port_server_port):
834 # check if a compatible port server is running
835 # if incompatible (version mismatch) ==> start a new one
836 # if not running ==> start a new one
837 # otherwise, leave it up
838 try:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700839 version = int(urllib2.urlopen(
840 'http://localhost:%d/version_number' % port_server_port,
841 timeout=1).read())
842 print 'detected port server running version %d' % version
Craig Tillerf53d9c82015-08-04 14:19:43 -0700843 running = True
Craig Tillerfe4939f2015-10-06 12:55:36 -0700844 except Exception as e:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700845 print 'failed to detect port server: %s' % sys.exc_info()[0]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700846 print e.strerror
Craig Tillerf53d9c82015-08-04 14:19:43 -0700847 running = False
848 if running:
Craig Tillerfe4939f2015-10-06 12:55:36 -0700849 current_version = int(subprocess.check_output(
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800850 [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
851 'dump_version']))
Craig Tillerfe4939f2015-10-06 12:55:36 -0700852 print 'my port server is version %d' % current_version
853 running = (version >= current_version)
854 if not running:
855 print 'port_server version mismatch: killing the old one'
856 urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
857 time.sleep(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700858 if not running:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700859 fd, logfile = tempfile.mkstemp()
860 os.close(fd)
861 print 'starting port_server, with log file %s' % logfile
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800862 args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'),
863 '-p', '%d' % port_server_port, '-l', logfile]
Craig Tiller367d41d2015-10-12 13:00:22 -0700864 env = dict(os.environ)
865 env['BUILD_ID'] = 'pleaseDontKillMeJenkins'
Nicolas "Pixel" Noblef72d7b52015-12-03 03:07:43 +0100866 if platform_string() == 'windows':
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800867 # Working directory of port server needs to be outside of Jenkins
868 # workspace to prevent file lock issues.
869 tempdir = tempfile.mkdtemp()
Craig Tillerd2c39712015-10-12 11:08:49 -0700870 port_server = subprocess.Popen(
Craig Tiller367d41d2015-10-12 13:00:22 -0700871 args,
872 env=env,
Jan Tattermusch3bd08272015-11-04 19:24:37 -0800873 cwd=tempdir,
Craig Tiller367d41d2015-10-12 13:00:22 -0700874 creationflags = 0x00000008, # detached process
875 close_fds=True)
Craig Tillerd2c39712015-10-12 11:08:49 -0700876 else:
877 port_server = subprocess.Popen(
878 args,
Craig Tiller367d41d2015-10-12 13:00:22 -0700879 env=env,
Craig Tillerd2c39712015-10-12 11:08:49 -0700880 preexec_fn=os.setsid,
881 close_fds=True)
Craig Tillerf0a293e2015-10-12 10:05:50 -0700882 time.sleep(1)
Craig Tiller8b5f4dc2015-08-26 08:02:01 -0700883 # ensure port server is up
Craig Tillerabd37fd2015-08-26 07:54:01 -0700884 waits = 0
Craig Tillerf53d9c82015-08-04 14:19:43 -0700885 while True:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700886 if waits > 10:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700887 print 'killing port server due to excessive start up waits'
Craig Tillerabd37fd2015-08-26 07:54:01 -0700888 port_server.kill()
Craig Tillera2f38b02015-09-24 11:19:17 -0700889 if port_server.poll() is not None:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700890 print 'port_server failed to start'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700891 # try one final time: maybe another build managed to start one
892 time.sleep(1)
893 try:
894 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
895 timeout=1).read()
896 print 'last ditch attempt to contact port server succeeded'
897 break
898 except:
899 traceback.print_exc();
900 port_log = open(logfile, 'r').read()
901 print port_log
902 sys.exit(1)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700903 try:
Craig Tillerabd37fd2015-08-26 07:54:01 -0700904 urllib2.urlopen('http://localhost:%d/get' % port_server_port,
905 timeout=1).read()
Craig Tillerf0a293e2015-10-12 10:05:50 -0700906 print 'port server is up and ready'
Craig Tillerf53d9c82015-08-04 14:19:43 -0700907 break
Craig Tiller31fdaa42015-09-25 13:09:59 -0700908 except socket.timeout:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700909 print 'waiting for port_server: timeout'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700910 traceback.print_exc();
911 time.sleep(1)
Craig Tiller31fdaa42015-09-25 13:09:59 -0700912 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700913 except urllib2.URLError:
Craig Tiller0d6499e2015-10-05 14:00:47 -0700914 print 'waiting for port_server: urlerror'
Craig Tillerf0a293e2015-10-12 10:05:50 -0700915 traceback.print_exc();
916 time.sleep(1)
Craig Tillerabd37fd2015-08-26 07:54:01 -0700917 waits += 1
Craig Tillerf53d9c82015-08-04 14:19:43 -0700918 except:
Craig Tillerf0a293e2015-10-12 10:05:50 -0700919 traceback.print_exc();
Craig Tillerf53d9c82015-08-04 14:19:43 -0700920 port_server.kill()
921 raise
922
923
Adele Zhoud5fffa52015-10-23 15:51:42 -0700924def _calculate_num_runs_failures(list_of_results):
925 """Caculate number of runs and failures for a particular test.
926
927 Args:
928 list_of_results: (List) of JobResult object.
929 Returns:
930 A tuple of total number of runs and failures.
931 """
932 num_runs = len(list_of_results) # By default, there is 1 run per JobResult.
933 num_failures = 0
934 for jobresult in list_of_results:
935 if jobresult.retries > 0:
936 num_runs += jobresult.retries
937 if jobresult.num_failures > 0:
938 num_failures += jobresult.num_failures
939 return num_runs, num_failures
940
Adele Zhou6b9527c2015-11-20 15:56:35 -0800941
Craig Tillereb9de8b2016-01-08 08:57:41 -0800942# _build_and_run results
943class BuildAndRunError(object):
944
945 BUILD = object()
946 TEST = object()
947 POST_TEST = object()
948
949
950# returns a list of things that failed (or an empty list on success)
Craig Tillerf53d9c82015-08-04 14:19:43 -0700951def _build_and_run(
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800952 check_cancelled, newline_on_success, cache, xml_report=None, build_only=False):
ctiller3040cb72015-01-07 12:13:17 -0800953 """Do one pass of building & running tests."""
murgatroid99666450e2015-01-26 13:03:31 -0800954 # build latest sequentially
Adele Zhoue4c35612015-10-16 15:34:23 -0700955 num_failures, _ = jobset.run(
956 build_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -0800957 newline_on_success=newline_on_success, travis=args.travis)
Adele Zhoue4c35612015-10-16 15:34:23 -0700958 if num_failures:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800959 return [BuildAndRunError.BUILD]
Craig Tillerb361b4e2016-01-06 11:44:17 -0800960
Jan Tattermusch2dd156e2015-12-04 18:26:17 -0800961 if build_only:
Craig Tillereb9de8b2016-01-08 08:57:41 -0800962 return []
ctiller3040cb72015-01-07 12:13:17 -0800963
Craig Tiller234b6e72015-05-23 10:12:40 -0700964 # start antagonists
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700965 antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
Craig Tiller234b6e72015-05-23 10:12:40 -0700966 for _ in range(0, args.antagonists)]
Craig Tillerfe4939f2015-10-06 12:55:36 -0700967 port_server_port = 32767
Craig Tillerf53d9c82015-08-04 14:19:43 -0700968 _start_port_server(port_server_port)
Adele Zhou7cf72112015-11-04 11:18:43 -0800969 resultset = None
Adele Zhou803af152015-11-30 15:16:16 -0800970 num_test_failures = 0
Craig Tiller234b6e72015-05-23 10:12:40 -0700971 try:
David Garcia Quintase90cd372015-05-31 18:15:26 -0700972 infinite_runs = runs_per_test == 0
yang-g6c1fdc62015-08-18 11:57:42 -0700973 one_run = set(
974 spec
975 for config in run_configs
976 for language in languages
Craig Tiller883064c2015-11-04 10:06:10 -0800977 for spec in language.test_specs(config, args)
yang-g6c1fdc62015-08-18 11:57:42 -0700978 if re.search(args.regex, spec.shortname))
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700979 # When running on travis, we want out test runs to be as similar as possible
980 # for reproducibility purposes.
Craig Tiller883064c2015-11-04 10:06:10 -0800981 if args.travis:
David Garcia Quintas79e389f2015-06-02 17:49:42 -0700982 massaged_one_run = sorted(one_run, key=lambda x: x.shortname)
983 else:
984 # whereas otherwise, we want to shuffle things up to give all tests a
985 # chance to run.
986 massaged_one_run = list(one_run) # random.shuffle needs an indexable seq.
987 random.shuffle(massaged_one_run) # which it modifies in-place.
Craig Tillerf7b7c892015-06-22 14:33:25 -0700988 if infinite_runs:
989 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 -0700990 runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs
991 else itertools.repeat(massaged_one_run, runs_per_test))
David Garcia Quintase90cd372015-05-31 18:15:26 -0700992 all_runs = itertools.chain.from_iterable(runs_sequence)
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +0200993
Adele Zhou803af152015-11-30 15:16:16 -0800994 num_test_failures, resultset = jobset.run(
Adele Zhou2271ab52015-10-28 13:59:14 -0700995 all_runs, check_cancelled, newline_on_success=newline_on_success,
Craig Tiller883064c2015-11-04 10:06:10 -0800996 travis=args.travis, infinite_runs=infinite_runs, maxjobs=args.jobs,
murgatroid998ae409f2015-10-26 16:39:00 -0700997 stop_on_failure=args.stop_on_failure,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700998 cache=cache if not xml_report else None,
Adele Zhouf2ca7bc2015-10-23 15:38:00 -0700999 add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
Adele Zhoud5fffa52015-10-23 15:51:42 -07001000 if resultset:
1001 for k, v in resultset.iteritems():
1002 num_runs, num_failures = _calculate_num_runs_failures(v)
1003 if num_failures == num_runs: # what about infinite_runs???
1004 jobset.message('FAILED', k, do_newline=True)
1005 elif num_failures > 0:
1006 jobset.message(
1007 'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),
1008 do_newline=True)
1009 else:
1010 jobset.message('PASSED', k, do_newline=True)
Craig Tiller234b6e72015-05-23 10:12:40 -07001011 finally:
1012 for antagonist in antagonists:
1013 antagonist.kill()
Adele Zhou7cf72112015-11-04 11:18:43 -08001014 if xml_report and resultset:
Adele Zhou3bc7ba42015-11-05 10:21:58 -08001015 report_utils.render_junit_xml_report(resultset, xml_report)
Craig Tillerd86a3942015-01-14 12:48:54 -08001016
Adele Zhouf2ca7bc2015-10-23 15:38:00 -07001017 number_failures, _ = jobset.run(
1018 post_tests_steps, maxjobs=1, stop_on_failure=True,
Craig Tiller883064c2015-11-04 10:06:10 -08001019 newline_on_success=newline_on_success, travis=args.travis)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001020
1021 out = []
1022 if number_failures:
1023 out.append(BuildAndRunError.POST_TEST)
1024 if num_test_failures:
1025 out.append(BuildAndRunError.TEST)
Nicolas "Pixel" Noble3fcd3bf2015-10-10 02:30:38 +02001026
Craig Tiller69cd2372015-06-11 09:38:09 -07001027 if cache: cache.save()
1028
Craig Tillereb9de8b2016-01-08 08:57:41 -08001029 return out
ctiller3040cb72015-01-07 12:13:17 -08001030
1031
David Klempner25739582015-02-11 15:57:32 -08001032test_cache = TestCache(runs_per_test == 1)
Craig Tiller547db2b2015-01-30 14:08:39 -08001033test_cache.maybe_load()
Craig Tiller71735182015-01-15 17:07:13 -08001034
ctiller3040cb72015-01-07 12:13:17 -08001035if forever:
Nicolas Noble044db742015-01-14 16:57:24 -08001036 success = True
ctiller3040cb72015-01-07 12:13:17 -08001037 while True:
Craig Tiller42bc87c2015-02-23 08:50:19 -08001038 dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])
ctiller3040cb72015-01-07 12:13:17 -08001039 initial_time = dw.most_recent_change()
1040 have_files_changed = lambda: dw.most_recent_change() != initial_time
Nicolas Noble044db742015-01-14 16:57:24 -08001041 previous_success = success
Craig Tillereb9de8b2016-01-08 08:57:41 -08001042 errors = _build_and_run(check_cancelled=have_files_changed,
1043 newline_on_success=False,
1044 cache=test_cache,
1045 build_only=args.build_only) == 0
1046 if not previous_success and not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001047 jobset.message('SUCCESS',
1048 'All tests are now passing properly',
1049 do_newline=True)
Nicolas Noble044db742015-01-14 16:57:24 -08001050 jobset.message('IDLE', 'No change detected')
ctiller3040cb72015-01-07 12:13:17 -08001051 while not have_files_changed():
1052 time.sleep(1)
1053else:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001054 errors = _build_and_run(check_cancelled=lambda: False,
Craig Tiller71735182015-01-15 17:07:13 -08001055 newline_on_success=args.newline_on_success,
Nicolas "Pixel" Noble5937b5b2015-06-26 02:04:12 +02001056 cache=test_cache,
Jan Tattermusch2dd156e2015-12-04 18:26:17 -08001057 xml_report=args.xml_report,
1058 build_only=args.build_only)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001059 if not errors:
Nicolas Nobleb09078f2015-01-14 18:06:05 -08001060 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
1061 else:
1062 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Craig Tillereb9de8b2016-01-08 08:57:41 -08001063 exit_code = 0
1064 if BuildAndRunError.BUILD in errors:
1065 exit_code |= 1
Craig Tiller4f2be362016-01-08 08:59:20 -08001066 if BuildAndRunError.TEST in errors and not args.travis:
Craig Tillereb9de8b2016-01-08 08:57:41 -08001067 exit_code |= 2
Craig Tiller4f2be362016-01-08 08:59:20 -08001068 if BuildAndRunError.POST_TEST in errors:
1069 exit_code |= 4
Craig Tillereb9de8b2016-01-08 08:57:41 -08001070 sys.exit(exit_code)
Craig Tillera0f85172016-01-20 15:56:06 -08001071