blob: a2fb1243cc49e3a247234ba94e8bd36ae8231597 [file] [log] [blame]
Jan Tattermusch320bd612015-09-15 12:44:35 -07001#!/usr/bin/env python
2# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Run interop (cross-language) tests in parallel."""
32
33import argparse
34import itertools
35import xml.etree.cElementTree as ET
36import jobset
Jan Tattermusch8266c672015-09-17 09:18:03 -070037import os
38import subprocess
39import sys
40import time
Jan Tattermusch320bd612015-09-15 12:44:35 -070041
Jan Tattermuschf49936a2015-09-16 15:44:26 -070042
43_CLOUD_TO_PROD_BASE_ARGS = [
44 '--server_host_override=grpc-test.sandbox.google.com',
45 '--server_host=grpc-test.sandbox.google.com',
46 '--server_port=443']
47
Jan Tattermusch8266c672015-09-17 09:18:03 -070048_CLOUD_TO_CLOUD_BASE_ARGS = [
49 '--server_host_override=foo.test.google.fr']
50
Jan Tattermuschf49936a2015-09-16 15:44:26 -070051# TOOD(jtattermusch) wrapped languages use this variable for location
52# of roots.pem. We might want to use GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
53# supported by C core SslCredentials instead.
54_SSL_CERT_ENV = { 'SSL_CERT_FILE':'/usr/local/share/grpc/roots.pem' }
55
56# TODO(jtatttermusch) unify usage of --enable_ssl, --use_tls and --use_tls=true
57
Jan Tattermusch8266c672015-09-17 09:18:03 -070058
Jan Tattermuschf49936a2015-09-16 15:44:26 -070059class CXXLanguage:
60
61 def __init__(self):
62 self.client_cmdline_base = ['bins/opt/interop_client']
63 self.client_cwd = None
64
65 def cloud_to_prod_args(self):
66 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
67 ['--enable_ssl','--use_prod_roots'])
68
Jan Tattermusch8266c672015-09-17 09:18:03 -070069 def cloud_to_cloud_args(self):
70 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
71 ['--enable_ssl'])
72
Jan Tattermuschf49936a2015-09-16 15:44:26 -070073 def cloud_to_prod_env(self):
74 return None
75
76 def __str__(self):
77 return 'c++'
78
79
80class CSharpLanguage:
81
82 def __init__(self):
83 self.client_cmdline_base = ['mono', 'Grpc.IntegrationTesting.Client.exe']
84 self.client_cwd = 'src/csharp/Grpc.IntegrationTesting.Client/bin/Debug'
85
86 def cloud_to_prod_args(self):
87 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
88 ['--use_tls'])
89
Jan Tattermusch8266c672015-09-17 09:18:03 -070090 def cloud_to_cloud_args(self):
91 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
92 ['--use_tls', '--use_test_ca'])
93
Jan Tattermuschf49936a2015-09-16 15:44:26 -070094 def cloud_to_prod_env(self):
95 return _SSL_CERT_ENV
96
97 def __str__(self):
98 return 'csharp'
99
100
101class NodeLanguage:
102
103 def __init__(self):
104 self.client_cmdline_base = ['node', 'src/node/interop/interop_client.js']
105 self.client_cwd = None
106
107 def cloud_to_prod_args(self):
108 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
109 ['--use_tls=true'])
110
Jan Tattermusch8266c672015-09-17 09:18:03 -0700111 def cloud_to_cloud_args(self):
112 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
113 ['--use_tls=true', '--use_test_ca=true'])
114
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700115 def cloud_to_prod_env(self):
116 return _SSL_CERT_ENV
117
118 def __str__(self):
119 return 'node'
120
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700121
122class PHPLanguage:
123
124 def __init__(self):
125 self.client_cmdline_base = ['src/php/bin/interop_client.sh']
126 self.client_cwd = None
127
128 def cloud_to_prod_args(self):
129 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
130 ['--use_tls'])
131
Jan Tattermusch8266c672015-09-17 09:18:03 -0700132 def cloud_to_cloud_args(self):
133 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
134 ['--use_tls', '--use_test_ca'])
135
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700136 def cloud_to_prod_env(self):
137 return _SSL_CERT_ENV
138
139 def __str__(self):
140 return 'php'
141
142
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700143class RubyLanguage:
144
145 def __init__(self):
146 self.client_cmdline_base = ['ruby', 'src/ruby/bin/interop/interop_client.rb']
147 self.client_cwd = None
148
149 def cloud_to_prod_args(self):
150 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
151 ['--use_tls'])
152
Jan Tattermusch8266c672015-09-17 09:18:03 -0700153 def cloud_to_cloud_args(self):
154 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
155 ['--use_tls', '--use_test_ca'])
156
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700157 def cloud_to_prod_env(self):
158 return _SSL_CERT_ENV
159
160 def __str__(self):
161 return 'ruby'
162
163
Jan Tattermusch320bd612015-09-15 12:44:35 -0700164# TODO(jtattermusch): add php and python once we get them working
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700165_LANGUAGES = {
166 'c++' : CXXLanguage(),
167 'csharp' : CSharpLanguage(),
168 'node' : NodeLanguage(),
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700169 'php' : PHPLanguage(),
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700170 'ruby' : RubyLanguage(),
171}
Jan Tattermusch320bd612015-09-15 12:44:35 -0700172
Jan Tattermusch8266c672015-09-17 09:18:03 -0700173# languages supported as cloud_to_cloud servers
174# TODO(jtattermusch): enable other languages as servers as well
175_SERVERS = { 'c++' : 8010, 'node' : 8040, 'csharp': 8070 }
176
Jan Tattermusch320bd612015-09-15 12:44:35 -0700177# TODO(jtattermusch): add empty_stream once C++ start supporting it.
178# TODO(jtattermusch): add support for auth tests.
179_TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
180 'client_streaming', 'server_streaming',
181 'cancel_after_begin', 'cancel_after_first_response',
182 'timeout_on_sleeping_server']
183
Jan Tattermusch8266c672015-09-17 09:18:03 -0700184
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700185def cloud_to_prod_jobspec(language, test_case):
186 """Creates jobspec for cloud-to-prod interop test"""
187 cmdline = language.cloud_to_prod_args() + ['--test_case=%s' % test_case]
188 test_job = jobset.JobSpec(
189 cmdline=cmdline,
190 cwd=language.client_cwd,
191 shortname="cloud_to_prod:%s:%s" % (language, test_case),
192 environ=language.cloud_to_prod_env(),
193 timeout_seconds=60)
194 return test_job
195
Jan Tattermusch8266c672015-09-17 09:18:03 -0700196
197def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
198 server_port):
199 """Creates jobspec for cloud-to-cloud interop test"""
200 cmdline = language.cloud_to_cloud_args() + ['--test_case=%s' % test_case,
201 '--server_host=%s' % server_host,
202 '--server_port=%s' % server_port ]
203 test_job = jobset.JobSpec(
204 cmdline=cmdline,
205 cwd=language.client_cwd,
206 shortname="cloud_to_cloud:%s:%s_server:%s" % (language, server_name,
207 test_case),
208 timeout_seconds=60)
209 return test_job
210
Jan Tattermusch320bd612015-09-15 12:44:35 -0700211argp = argparse.ArgumentParser(description='Run interop tests.')
212argp.add_argument('-l', '--language',
213 choices=['all'] + sorted(_LANGUAGES),
214 nargs='+',
Jan Tattermusch8266c672015-09-17 09:18:03 -0700215 default=['all'],
216 help='Clients to run.')
217argp.add_argument('-j', '--jobs', default=24, type=int)
218argp.add_argument('--cloud_to_prod',
219 default=False,
220 action='store_const',
221 const=True,
222 help='Run cloud_to_prod tests.')
223argp.add_argument('-s', '--server',
224 choices=['all'] + sorted(_SERVERS),
225 action='append',
226 help='Run cloud_to_cloud servers in a separate docker ' +
227 'image. Servers can only be started automatically if ' +
228 '--use_docker option is enabled.',
229 default=[])
230argp.add_argument('--override_server',
231 action='append',
232 type=lambda kv: kv.split("="),
233 help='Use servername=HOST:PORT to explicitly specify a server. E.g. csharp=localhost:50000',
234 default=[])
235argp.add_argument('-t', '--travis',
236 default=False,
237 action='store_const',
238 const=True)
239argp.add_argument('--use_docker',
240 default=False,
241 action='store_const',
242 const=True,
243 help='Run all the interop tests under docker. That provides ' +
244 'additional isolation and prevents the need to install ' +
245 'language specific prerequisites. Only available on Linux.')
Jan Tattermusch320bd612015-09-15 12:44:35 -0700246args = argp.parse_args()
247
Jan Tattermusch8266c672015-09-17 09:18:03 -0700248servers = set(s for s in itertools.chain.from_iterable(_SERVERS.iterkeys()
249 if x == 'all' else [x]
250 for x in args.server))
251
252if args.use_docker:
253 if not args.travis:
254 print 'Seen --use_docker flag, will run interop tests under docker.'
255 print
256 print 'IMPORTANT: The changes you are testing need to be locally committed'
257 print 'because only the committed changes in the current branch will be'
258 print 'copied to the docker environment.'
259 time.sleep(5)
260
261 child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]
262 run_tests_cmd = ('tools/run_tests/run_interop_tests.py %s' %
263 " ".join(child_argv[1:]))
264
265 # cmdline args to pass to the container running servers.
266 servers_extra_docker_args = ''
267 server_port_tuples = ''
268 for server in servers:
269 port = _SERVERS[server]
270 servers_extra_docker_args += ' -p %s' % port
271 servers_extra_docker_args += ' -e SERVER_PORT_%s=%s' % (server.replace("+", "x"), port)
272 server_port_tuples += ' %s:%s' % (server, port)
273
274 env = os.environ.copy()
275 env['RUN_TESTS_COMMAND'] = run_tests_cmd
276 env['SERVERS_DOCKER_EXTRA_ARGS'] = servers_extra_docker_args
277 env['SERVER_PORT_TUPLES'] = server_port_tuples
278 if not args.travis:
279 env['TTY_FLAG'] = '-t' # enables Ctrl-C when not on Jenkins.
280
281 subprocess.check_call(['tools/jenkins/build_docker_and_run_interop_tests.sh'],
282 shell=True,
283 env=env)
284 sys.exit(0)
285
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700286languages = set(_LANGUAGES[l]
287 for l in itertools.chain.from_iterable(
288 _LANGUAGES.iterkeys() if x == 'all' else [x]
289 for x in args.language))
Jan Tattermusch320bd612015-09-15 12:44:35 -0700290
291jobs = []
Jan Tattermusch8266c672015-09-17 09:18:03 -0700292if args.cloud_to_prod:
293 for language in languages:
294 for test_case in _TEST_CASES:
295 test_job = cloud_to_prod_jobspec(language, test_case)
296 jobs.append(test_job)
297
298# default servers to "localhost" and the default port
299server_addresses = dict((s, ("localhost", _SERVERS[s])) for s in servers)
300
301for server in args.override_server:
302 server_name = server[0]
303 (server_host, server_port) = server[1].split(":")
304 server_addresses[server_name] = (server_host, server_port)
305
306for server_name, server_address in server_addresses.iteritems():
307 (server_host, server_port) = server_address
308 for language in languages:
309 for test_case in _TEST_CASES:
310 test_job = cloud_to_cloud_jobspec(language,
311 test_case,
312 server_name,
313 server_host,
314 server_port)
315 jobs.append(test_job)
316
317if not jobs:
318 print "No jobs to run."
319 sys.exit(1)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700320
321root = ET.Element('testsuites')
322testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests')
323
Jan Tattermusch8266c672015-09-17 09:18:03 -0700324if jobset.run(jobs, newline_on_success=True, maxjobs=args.jobs, xml_report=testsuite):
325 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
326else:
327 jobset.message('FAILED', 'Some tests failed', do_newline=True)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700328
329tree = ET.ElementTree(root)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700330tree.write('report.xml', encoding='UTF-8')