blob: d4a9cb264571a7d9b22f0e89aa88ede779c3ae44 [file] [log] [blame]
Mark Slee5299a952007-10-05 00:13:24 +00001#!/usr/bin/env python
2
David Reissea2cba82009-03-30 21:35:00 +00003#
4# Licensed to the Apache Software Foundation (ASF) under one
5# or more contributor license agreements. See the NOTICE file
6# distributed with this work for additional information
7# regarding copyright ownership. The ASF licenses this file
8# to you under the Apache License, Version 2.0 (the
9# "License"); you may not use this file except in compliance
10# with the License. You may obtain a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing,
15# software distributed under the License is distributed on an
16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17# KIND, either express or implied. See the License for the
18# specific language governing permissions and limitations
19# under the License.
20#
21
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000022from __future__ import division
Nobuaki Sukegawa760511f2015-11-06 21:24:16 +090023from __future__ import print_function
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090024import copy
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090025import os
26import signal
Roger Meier9b328532014-04-21 21:22:54 +020027import socket
Mark Slee5299a952007-10-05 00:13:24 +000028import subprocess
29import sys
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090030import time
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000031from optparse import OptionParser
32
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +090033from util import local_libpath
34
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090035SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000036
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090037SCRIPTS = [
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090038 'FastbinaryTest.py',
39 'TestFrozen.py',
40 'TSimpleJSONProtocolTest.py',
41 'SerializationTest.py',
42 'TestEof.py',
43 'TestSyntax.py',
44 'TestSocket.py',
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090045]
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000046FRAMED = ["TNonblockingServer"]
Bryan Duxbury16066592011-03-22 18:06:04 +000047SKIP_ZLIB = ['TNonblockingServer', 'THttpServer']
48SKIP_SSL = ['TNonblockingServer', 'THttpServer']
Roger Meier6857b7f2015-09-16 19:53:07 +020049EXTRA_DELAY = dict(TProcessPoolServer=5.5)
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000050
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090051PROTOS = [
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090052 'accel',
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +090053 'accelc',
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090054 'binary',
55 'compact',
56 'json',
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090057]
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000058
59SERVERS = [
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090060 "TSimpleServer",
61 "TThreadedServer",
62 "TThreadPoolServer",
63 "TProcessPoolServer",
64 "TForkingServer",
65 "TNonblockingServer",
66 "THttpServer",
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090067]
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000068
Mark Slee5299a952007-10-05 00:13:24 +000069
David Reiss2a4bfd62008-04-07 23:45:00 +000070def relfile(fname):
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090071 return os.path.join(SCRIPT_DIR, fname)
David Reiss2a4bfd62008-04-07 23:45:00 +000072
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090073
Nobuaki Sukegawaa3b88a02016-01-06 20:44:17 +090074def setup_pypath(libdir, gendir):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090075 dirs = [libdir, gendir]
76 env = copy.deepcopy(os.environ)
77 pypath = env.get('PYTHONPATH', None)
78 if pypath:
79 dirs.append(pypath)
80 env['PYTHONPATH'] = ':'.join(dirs)
81 if gendir.endswith('gen-py-no_utf8strings'):
82 env['THRIFT_TEST_PY_NO_UTF8STRINGS'] = '1'
83 return env
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090084
85
Nobuaki Sukegawaa3b88a02016-01-06 20:44:17 +090086def runScriptTest(libdir, genbase, genpydir, script):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090087 env = setup_pypath(libdir, os.path.join(genbase, genpydir))
88 script_args = [sys.executable, relfile(script)]
89 print('\nTesting script: %s\n----' % (' '.join(script_args)))
90 ret = subprocess.call(script_args, env=env)
91 if ret != 0:
92 print('*** FAILED ***', file=sys.stderr)
93 print('LIBDIR: %s' % libdir, file=sys.stderr)
94 print('PY_GEN: %s' % genpydir, file=sys.stderr)
95 print('SCRIPT: %s' % script, file=sys.stderr)
96 raise Exception("Script subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(script_args)))
Roger Meier76150722014-05-31 22:22:07 +020097
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +090098
Nobuaki Sukegawaa3b88a02016-01-06 20:44:17 +090099def runServiceTest(libdir, genbase, genpydir, server_class, proto, port, use_zlib, use_ssl, verbose):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900100 env = setup_pypath(libdir, os.path.join(genbase, genpydir))
101 # Build command line arguments
102 server_args = [sys.executable, relfile('TestServer.py')]
103 cli_args = [sys.executable, relfile('TestClient.py')]
104 for which in (server_args, cli_args):
105 which.append('--protocol=%s' % proto) # accel, binary, compact or json
106 which.append('--port=%d' % port) # default to 9090
107 if use_zlib:
108 which.append('--zlib')
109 if use_ssl:
110 which.append('--ssl')
111 if verbose == 0:
112 which.append('-q')
113 if verbose == 2:
114 which.append('-v')
115 # server-specific option to select server class
116 server_args.append(server_class)
117 # client-specific cmdline options
118 if server_class in FRAMED:
119 cli_args.append('--transport=framed')
120 else:
121 cli_args.append('--transport=buffered')
122 if server_class == 'THttpServer':
123 cli_args.append('--http=/')
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900124 if verbose > 0:
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900125 print('Testing server %s: %s' % (server_class, ' '.join(server_args)))
126 serverproc = subprocess.Popen(server_args, env=env)
127
128 def ensureServerAlive():
129 if serverproc.poll() is not None:
130 print(('FAIL: Server process (%s) failed with retcode %d')
131 % (' '.join(server_args), serverproc.returncode))
132 raise Exception('Server subprocess %s died, args: %s'
133 % (server_class, ' '.join(server_args)))
134
135 # Wait for the server to start accepting connections on the given port.
136 sock = socket.socket()
137 sleep_time = 0.1 # Seconds
138 max_attempts = 100
139 try:
140 attempt = 0
141 while sock.connect_ex(('127.0.0.1', port)) != 0:
142 attempt += 1
143 if attempt >= max_attempts:
144 raise Exception("TestServer not ready on port %d after %.2f seconds"
145 % (port, sleep_time * attempt))
146 ensureServerAlive()
147 time.sleep(sleep_time)
148 finally:
149 sock.close()
150
151 try:
152 if verbose > 0:
153 print('Testing client: %s' % (' '.join(cli_args)))
154 ret = subprocess.call(cli_args, env=env)
155 if ret != 0:
156 print('*** FAILED ***', file=sys.stderr)
157 print('LIBDIR: %s' % libdir, file=sys.stderr)
158 print('PY_GEN: %s' % genpydir, file=sys.stderr)
159 raise Exception("Client subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(cli_args)))
160 finally:
161 # check that server didn't die
162 ensureServerAlive()
163 extra_sleep = EXTRA_DELAY.get(server_class, 0)
164 if extra_sleep > 0 and verbose > 0:
165 print('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child'
166 'processes to terminate via alarm'
167 % (server_class, proto, use_zlib, use_ssl, extra_sleep))
168 time.sleep(extra_sleep)
169 os.kill(serverproc.pid, signal.SIGKILL)
170 serverproc.wait()
David Reissbcaa2ad2008-06-10 22:55:26 +0000171
Roger Meier76150722014-05-31 22:22:07 +0200172
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900173class TestCases(object):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900174 def __init__(self, genbase, libdir, port, gendirs, servers, verbose):
175 self.genbase = genbase
176 self.libdir = libdir
177 self.port = port
178 self.verbose = verbose
179 self.gendirs = gendirs
180 self.servers = servers
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900181
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900182 def default_conf(self):
183 return {
184 'gendir': self.gendirs[0],
185 'server': self.servers[0],
186 'proto': PROTOS[0],
187 'zlib': False,
188 'ssl': False,
189 }
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900190
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900191 def run(self, conf, test_count):
192 with_zlib = conf['zlib']
193 with_ssl = conf['ssl']
194 try_server = conf['server']
195 try_proto = conf['proto']
196 genpydir = conf['gendir']
197 # skip any servers that don't work with the Zlib transport
198 if with_zlib and try_server in SKIP_ZLIB:
199 return False
200 # skip any servers that don't work with SSL
201 if with_ssl and try_server in SKIP_SSL:
202 return False
203 if self.verbose > 0:
204 print('\nTest run #%d: (includes %s) Server=%s, Proto=%s, zlib=%s, SSL=%s'
205 % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
206 runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl, self.verbose)
207 if self.verbose > 0:
208 print('OK: Finished (includes %s) %s / %s proto / zlib=%s / SSL=%s. %d combinations tested.'
209 % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
210 return True
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900211
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900212 def test_feature(self, name, values):
213 test_count = 0
214 conf = self.default_conf()
215 for try_server in values:
216 conf[name] = try_server
217 if self.run(conf, test_count):
218 test_count += 1
219 return test_count
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900220
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900221 def run_all_tests(self):
222 test_count = 0
223 for try_server in self.servers:
224 for genpydir in self.gendirs:
225 for try_proto in PROTOS:
226 for with_zlib in (False, True):
227 # skip any servers that don't work with the Zlib transport
228 if with_zlib and try_server in SKIP_ZLIB:
229 continue
230 for with_ssl in (False, True):
231 # skip any servers that don't work with SSL
232 if with_ssl and try_server in SKIP_SSL:
233 continue
234 test_count += 1
235 if self.verbose > 0:
236 print('\nTest run #%d: (includes %s) Server=%s, Proto=%s, zlib=%s, SSL=%s'
237 % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
238 runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl)
239 if self.verbose > 0:
240 print('OK: Finished (includes %s) %s / %s proto / zlib=%s / SSL=%s. %d combinations tested.'
241 % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
242 return test_count
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900243
244
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900245def main():
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900246 parser = OptionParser()
247 parser.add_option('--all', action="store_true", dest='all')
248 parser.add_option('--genpydirs', type='string', dest='genpydirs',
249 default='default,slots,oldstyle,no_utf8strings,dynamic,dynamicslots',
250 help='directory extensions for generated code, used as suffixes for \"gen-py-*\" added sys.path for individual tests')
251 parser.add_option("--port", type="int", dest="port", default=9090,
252 help="port number for server to listen on")
253 parser.add_option('-v', '--verbose', action="store_const",
254 dest="verbose", const=2,
255 help="verbose output")
256 parser.add_option('-q', '--quiet', action="store_const",
257 dest="verbose", const=0,
258 help="minimal output")
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +0900259 parser.add_option('-L', '--libdir', dest="libdir", default=local_libpath(),
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900260 help="directory path that contains Thrift Python library")
261 parser.add_option('--gen-base', dest="gen_base", default=SCRIPT_DIR,
262 help="directory path that contains Thrift Python library")
263 parser.set_defaults(verbose=1)
264 options, args = parser.parse_args()
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900265
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900266 generated_dirs = []
267 for gp_dir in options.genpydirs.split(','):
268 generated_dirs.append('gen-py-%s' % (gp_dir))
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900269
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900270 # commandline permits a single class name to be specified to override SERVERS=[...]
271 servers = SERVERS
272 if len(args) == 1:
273 if args[0] in SERVERS:
274 servers = args
275 else:
276 print('Unavailable server type "%s", please choose one of: %s' % (args[0], servers))
277 sys.exit(0)
278
279 tests = TestCases(options.gen_base, options.libdir, options.port, generated_dirs, servers, options.verbose)
280
281 # run tests without a client/server first
282 print('----------------')
283 print(' Executing individual test scripts with various generated code directories')
284 print(' Directories to be tested: ' + ', '.join(generated_dirs))
285 print(' Scripts to be tested: ' + ', '.join(SCRIPTS))
286 print('----------------')
287 for genpydir in generated_dirs:
288 for script in SCRIPTS:
289 runScriptTest(options.libdir, options.gen_base, genpydir, script)
290
291 print('----------------')
292 print(' Executing Client/Server tests with various generated code directories')
293 print(' Servers to be tested: ' + ', '.join(servers))
294 print(' Directories to be tested: ' + ', '.join(generated_dirs))
295 print(' Protocols to be tested: ' + ', '.join(PROTOS))
296 print(' Options to be tested: ZLIB(yes/no), SSL(yes/no)')
297 print('----------------')
298
299 if options.all:
300 tests.run_all_tests()
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900301 else:
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900302 tests.test_feature('gendir', generated_dirs)
303 tests.test_feature('server', servers)
304 tests.test_feature('proto', PROTOS)
305 tests.test_feature('zlib', [False, True])
306 tests.test_feature('ssl', [False, True])
Nobuaki Sukegawacacce2f2015-11-08 23:43:55 +0900307
308
309if __name__ == '__main__':
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900310 sys.exit(main())