summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/test/py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/thrift/test/py')
-rw-r--r--src/jaegertracing/thrift/test/py/CMakeLists.txt34
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/FastbinaryTest.py256
-rw-r--r--src/jaegertracing/thrift/test/py/Makefile.am105
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/RunClientServer.py323
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/SerializationTest.py457
-rw-r--r--src/jaegertracing/thrift/test/py/TSimpleJSONProtocolTest.py108
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestClient.py500
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestEof.py132
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestFrozen.py123
-rw-r--r--src/jaegertracing/thrift/test/py/TestRenderedDoubleConstants.py177
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestServer.py412
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestSocket.py78
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/TestSyntax.py24
-rwxr-xr-xsrc/jaegertracing/thrift/test/py/explicit_module/runtest.sh35
-rw-r--r--src/jaegertracing/thrift/test/py/explicit_module/test1.thrift24
-rw-r--r--src/jaegertracing/thrift/test/py/explicit_module/test2.thrift24
-rw-r--r--src/jaegertracing/thrift/test/py/explicit_module/test3.thrift25
-rw-r--r--src/jaegertracing/thrift/test/py/generate.cmake36
-rw-r--r--src/jaegertracing/thrift/test/py/setup.cfg2
-rw-r--r--src/jaegertracing/thrift/test/py/util.py32
20 files changed, 2907 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/test/py/CMakeLists.txt b/src/jaegertracing/thrift/test/py/CMakeLists.txt
new file mode 100644
index 000000000..fbc221738
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+add_test(NAME python_test_generate
+ COMMAND ${CMAKE_COMMAND}
+ -DTHRIFTCOMPILER=$<TARGET_FILE:thrift-compiler>
+ -DMY_PROJECT_DIR=${PROJECT_SOURCE_DIR}
+ -DMY_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
+ -DMY_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/generate.cmake
+)
+
+add_test(NAME python_test
+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/RunClientServer.py --gen-base=${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS python_test_generate
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
diff --git a/src/jaegertracing/thrift/test/py/FastbinaryTest.py b/src/jaegertracing/thrift/test/py/FastbinaryTest.py
new file mode 100755
index 000000000..05c0bb6d1
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/FastbinaryTest.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+r"""
+PYTHONPATH=./gen-py:../../lib/py/build/lib... ./FastbinaryTest.py
+"""
+
+# TODO(dreiss): Test error cases. Check for memory leaks.
+
+from __future__ import print_function
+
+import math
+import os
+import sys
+import timeit
+
+from copy import deepcopy
+from pprint import pprint
+
+from thrift.transport import TTransport
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol, TBinaryProtocolAccelerated
+from thrift.protocol.TCompactProtocol import TCompactProtocol, TCompactProtocolAccelerated
+
+from DebugProtoTest import Srv
+from DebugProtoTest.ttypes import Backwards, Bonk, Empty, HolyMoley, OneOfEach, RandomStuff, Wrapper
+
+
+class TDevNullTransport(TTransport.TTransportBase):
+ def __init__(self):
+ pass
+
+ def isOpen(self):
+ return True
+
+
+ooe1 = OneOfEach()
+ooe1.im_true = True
+ooe1.im_false = False
+ooe1.a_bite = 0xd6
+ooe1.integer16 = 27000
+ooe1.integer32 = 1 << 24
+ooe1.integer64 = 6000 * 1000 * 1000
+ooe1.double_precision = math.pi
+ooe1.some_characters = "Debug THIS!"
+ooe1.zomg_unicode = u"\xd7\n\a\t"
+
+ooe2 = OneOfEach()
+ooe2.integer16 = 16
+ooe2.integer32 = 32
+ooe2.integer64 = 64
+ooe2.double_precision = (math.sqrt(5) + 1) / 2
+ooe2.some_characters = ":R (me going \"rrrr\")"
+ooe2.zomg_unicode = u"\xd3\x80\xe2\x85\xae\xce\x9d\x20"\
+ u"\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\
+ u"\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\
+ u"\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\
+ u"\xc7\x83\xe2\x80\xbc"
+
+if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
+ ooe1.zomg_unicode = ooe1.zomg_unicode.encode('utf8')
+ ooe2.zomg_unicode = ooe2.zomg_unicode.encode('utf8')
+
+hm = HolyMoley(**{"big": [], "contain": set(), "bonks": {}})
+hm.big.append(ooe1)
+hm.big.append(ooe2)
+hm.big[0].a_bite = 0x22
+hm.big[1].a_bite = 0x22
+
+hm.contain.add(("and a one", "and a two"))
+hm.contain.add(("then a one, two", "three!", "FOUR!"))
+if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
+ hm.contain.add((u"\xd7\n\a\t".encode('utf8'),))
+else:
+ hm.contain.add((u"\xd7\n\a\t",))
+hm.contain.add(())
+
+hm.bonks["nothing"] = []
+hm.bonks["something"] = [
+ Bonk(**{"type": 1, "message": "Wait."}),
+ Bonk(**{"type": 2, "message": "What?"}),
+]
+hm.bonks["poe"] = [
+ Bonk(**{"type": 3, "message": "quoth"}),
+ Bonk(**{"type": 4, "message": "the raven"}),
+ Bonk(**{"type": 5, "message": "nevermore"}),
+]
+
+rs = RandomStuff()
+rs.a = 1
+rs.b = 2
+rs.c = 3
+rs.myintlist = list(range(20))
+rs.maps = {1: Wrapper(**{"foo": Empty()}), 2: Wrapper(**{"foo": Empty()})}
+rs.bigint = 124523452435
+rs.triple = 3.14
+
+# make sure this splits two buffers in a buffered protocol
+rshuge = RandomStuff()
+rshuge.myintlist = list(range(10000))
+
+my_zero = Srv.Janky_result(**{"success": 5})
+
+
+class Test(object):
+ def __init__(self, fast, slow):
+ self._fast = fast
+ self._slow = slow
+
+ def _check_write(self, o):
+ trans_fast = TTransport.TMemoryBuffer()
+ trans_slow = TTransport.TMemoryBuffer()
+ prot_fast = self._fast(trans_fast, fallback=False)
+ prot_slow = self._slow(trans_slow)
+
+ o.write(prot_fast)
+ o.write(prot_slow)
+ ORIG = trans_slow.getvalue()
+ MINE = trans_fast.getvalue()
+ if ORIG != MINE:
+ print("actual : %s\nexpected: %s" % (repr(MINE), repr(ORIG)))
+ raise Exception('write value mismatch')
+
+ def _check_read(self, o):
+ prot = self._slow(TTransport.TMemoryBuffer())
+ o.write(prot)
+
+ slow_version_binary = prot.trans.getvalue()
+
+ prot = self._fast(
+ TTransport.TMemoryBuffer(slow_version_binary), fallback=False)
+ c = o.__class__()
+ c.read(prot)
+ if c != o:
+ print("actual : ")
+ pprint(repr(c))
+ print("expected: ")
+ pprint(repr(o))
+ raise Exception('read value mismatch')
+
+ prot = self._fast(
+ TTransport.TBufferedTransport(
+ TTransport.TMemoryBuffer(slow_version_binary)), fallback=False)
+ c = o.__class__()
+ c.read(prot)
+ if c != o:
+ print("actual : ")
+ pprint(repr(c))
+ print("expected: ")
+ pprint(repr(o))
+ raise Exception('read value mismatch')
+
+ def do_test(self):
+ self._check_write(HolyMoley())
+ self._check_read(HolyMoley())
+
+ self._check_write(hm)
+ no_set = deepcopy(hm)
+ no_set.contain = set()
+ self._check_read(no_set)
+ self._check_read(hm)
+
+ self._check_write(rs)
+ self._check_read(rs)
+
+ self._check_write(rshuge)
+ self._check_read(rshuge)
+
+ self._check_write(my_zero)
+ self._check_read(my_zero)
+
+ self._check_read(Backwards(**{"first_tag2": 4, "second_tag1": 2}))
+
+ # One case where the serialized form changes, but only superficially.
+ o = Backwards(**{"first_tag2": 4, "second_tag1": 2})
+ trans_fast = TTransport.TMemoryBuffer()
+ trans_slow = TTransport.TMemoryBuffer()
+ prot_fast = self._fast(trans_fast, fallback=False)
+ prot_slow = self._slow(trans_slow)
+
+ o.write(prot_fast)
+ o.write(prot_slow)
+ ORIG = trans_slow.getvalue()
+ MINE = trans_fast.getvalue()
+ assert id(ORIG) != id(MINE)
+
+ prot = self._fast(TTransport.TMemoryBuffer(), fallback=False)
+ o.write(prot)
+ prot = self._slow(
+ TTransport.TMemoryBuffer(prot.trans.getvalue()))
+ c = o.__class__()
+ c.read(prot)
+ if c != o:
+ print("copy: ")
+ pprint(repr(c))
+ print("orig: ")
+ pprint(repr(o))
+
+
+def do_test(fast, slow):
+ Test(fast, slow).do_test()
+
+
+def do_benchmark(protocol, iters=5000, skip_slow=False):
+ setup = """
+from __main__ import hm, rs, TDevNullTransport
+from thrift.protocol.{0} import {0}{1}
+trans = TDevNullTransport()
+prot = {0}{1}(trans{2})
+"""
+
+ setup_fast = setup.format(protocol, 'Accelerated', ', fallback=False')
+ if not skip_slow:
+ setup_slow = setup.format(protocol, '', '')
+
+ print("Starting Benchmarks")
+
+ if not skip_slow:
+ print("HolyMoley Standard = %f" %
+ timeit.Timer('hm.write(prot)', setup_slow).timeit(number=iters))
+
+ print("HolyMoley Acceler. = %f" %
+ timeit.Timer('hm.write(prot)', setup_fast).timeit(number=iters))
+
+ if not skip_slow:
+ print("FastStruct Standard = %f" %
+ timeit.Timer('rs.write(prot)', setup_slow).timeit(number=iters))
+
+ print("FastStruct Acceler. = %f" %
+ timeit.Timer('rs.write(prot)', setup_fast).timeit(number=iters))
+
+
+if __name__ == '__main__':
+ print('Testing TBinaryAccelerated')
+ do_test(TBinaryProtocolAccelerated, TBinaryProtocol)
+ do_benchmark('TBinaryProtocol')
+ print('Testing TCompactAccelerated')
+ do_test(TCompactProtocolAccelerated, TCompactProtocol)
+ do_benchmark('TCompactProtocol')
diff --git a/src/jaegertracing/thrift/test/py/Makefile.am b/src/jaegertracing/thrift/test/py/Makefile.am
new file mode 100644
index 000000000..9433e5907
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/Makefile.am
@@ -0,0 +1,105 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = serial-tests
+
+py_unit_tests = RunClientServer.py
+
+thrift_gen = \
+ gen-py/ThriftTest/__init__.py \
+ gen-py/DebugProtoTest/__init__.py \
+ gen-py/DoubleConstantsTest/__init__.py \
+ gen-py/Recursive/__init__.py \
+ gen-py-default/ThriftTest/__init__.py \
+ gen-py-default/DebugProtoTest/__init__.py \
+ gen-py-default/DoubleConstantsTest/__init__.py \
+ gen-py-default/Recursive/__init__.py \
+ gen-py-slots/ThriftTest/__init__.py \
+ gen-py-slots/DebugProtoTest/__init__.py \
+ gen-py-slots/DoubleConstantsTest/__init__.py \
+ gen-py-slots/Recursive/__init__.py \
+ gen-py-oldstyle/ThriftTest/__init__.py \
+ gen-py-oldstyle/DebugProtoTest/__init__.py \
+ gen-py-oldstyle/DoubleConstantsTest/__init__.py \
+ gen-py-oldstyle/Recursive/__init__.py \
+ gen-py-no_utf8strings/ThriftTest/__init__.py \
+ gen-py-no_utf8strings/DebugProtoTest/__init__.py \
+ gen-py-no_utf8strings/DoubleConstantsTest/__init__.py \
+ gen-py-no_utf8strings/Recursive/__init__.py \
+ gen-py-dynamic/ThriftTest/__init__.py \
+ gen-py-dynamic/DebugProtoTest/__init__.py \
+ gen-py-dynamic/DoubleConstantsTest/__init__.py \
+ gen-py-dynamic/Recursive/__init__.py \
+ gen-py-dynamicslots/ThriftTest/__init__.py \
+ gen-py-dynamicslots/DebugProtoTest/__init__.py \
+ gen-py-dynamicslots/DoubleConstantsTest/__init__.py \
+ gen-py-dynamicslots/Recursive/__init__.py
+
+
+precross: $(thrift_gen)
+BUILT_SOURCES = $(thrift_gen)
+
+helper_scripts= \
+ TestClient.py \
+ TestServer.py
+
+check_SCRIPTS= \
+ $(thrift_gen) \
+ $(py_unit_tests) \
+ $(helper_scripts)
+
+TESTS= $(py_unit_tests)
+
+
+gen-py/%/__init__.py: ../%.thrift $(THRIFT)
+ $(THRIFT) --gen py $<
+
+gen-py-default/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-default || $(MKDIR_P) gen-py-default
+ $(THRIFT) --gen py -out gen-py-default $<
+
+gen-py-slots/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-slots || $(MKDIR_P) gen-py-slots
+ $(THRIFT) --gen py:slots -out gen-py-slots $<
+
+gen-py-oldstyle/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-oldstyle || $(MKDIR_P) gen-py-oldstyle
+ $(THRIFT) --gen py:old_style -out gen-py-oldstyle $<
+
+gen-py-no_utf8strings/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-no_utf8strings || $(MKDIR_P) gen-py-no_utf8strings
+ $(THRIFT) --gen py:no_utf8strings -out gen-py-no_utf8strings $<
+
+gen-py-dynamic/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-dynamic || $(MKDIR_P) gen-py-dynamic
+ $(THRIFT) --gen py:dynamic -out gen-py-dynamic $<
+
+gen-py-dynamicslots/%/__init__.py: ../%.thrift $(THRIFT)
+ test -d gen-py-dynamicslots || $(MKDIR_P) gen-py-dynamicslots
+ $(THRIFT) --gen py:dynamic,slots -out gen-py-dynamicslots $<
+
+clean-local:
+ $(RM) -r build
+ find . -type f \( -iname "*.pyc" \) | xargs rm -f
+ find . -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf
+ $(RM) -r gen-py*/
+
+dist-hook:
+ find $(distdir) -type f \( -iname "*.pyc" \) | xargs rm -f
+ find $(distdir) -type d \( -iname "__pycache__" -or -iname "_trial_temp" \) | xargs rm -rf
+ $(RM) -r $(distdir)/gen-py*/
diff --git a/src/jaegertracing/thrift/test/py/RunClientServer.py b/src/jaegertracing/thrift/test/py/RunClientServer.py
new file mode 100755
index 000000000..56a408e60
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/RunClientServer.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import division
+from __future__ import print_function
+import platform
+import copy
+import os
+import signal
+import socket
+import subprocess
+import sys
+import time
+from optparse import OptionParser
+
+from util import local_libpath
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+SCRIPTS = [
+ 'FastbinaryTest.py',
+ 'TestFrozen.py',
+ 'TestRenderedDoubleConstants.py',
+ 'TSimpleJSONProtocolTest.py',
+ 'SerializationTest.py',
+ 'TestEof.py',
+ 'TestSyntax.py',
+ 'TestSocket.py',
+]
+FRAMED = ["TNonblockingServer"]
+SKIP_ZLIB = ['TNonblockingServer', 'THttpServer']
+SKIP_SSL = ['THttpServer']
+EXTRA_DELAY = dict(TProcessPoolServer=5.5)
+
+PROTOS = [
+ 'accel',
+ 'accelc',
+ 'binary',
+ 'compact',
+ 'json',
+ 'header',
+]
+
+
+def default_servers():
+ servers = [
+ 'TSimpleServer',
+ 'TThreadedServer',
+ 'TThreadPoolServer',
+ 'TNonblockingServer',
+ 'THttpServer',
+ ]
+ if platform.system() != 'Windows':
+ servers.append('TProcessPoolServer')
+ servers.append('TForkingServer')
+ return servers
+
+
+def relfile(fname):
+ return os.path.join(SCRIPT_DIR, fname)
+
+
+def setup_pypath(libdir, gendir):
+ dirs = [libdir, gendir]
+ env = copy.deepcopy(os.environ)
+ pypath = env.get('PYTHONPATH', None)
+ if pypath:
+ dirs.append(pypath)
+ env['PYTHONPATH'] = os.pathsep.join(dirs)
+ if gendir.endswith('gen-py-no_utf8strings'):
+ env['THRIFT_TEST_PY_NO_UTF8STRINGS'] = '1'
+ return env
+
+
+def runScriptTest(libdir, genbase, genpydir, script):
+ env = setup_pypath(libdir, os.path.join(genbase, genpydir))
+ script_args = [sys.executable, relfile(script)]
+ print('\nTesting script: %s\n----' % (' '.join(script_args)))
+ ret = subprocess.call(script_args, env=env)
+ if ret != 0:
+ print('*** FAILED ***', file=sys.stderr)
+ print('LIBDIR: %s' % libdir, file=sys.stderr)
+ print('PY_GEN: %s' % genpydir, file=sys.stderr)
+ print('SCRIPT: %s' % script, file=sys.stderr)
+ raise Exception("Script subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(script_args)))
+
+
+def runServiceTest(libdir, genbase, genpydir, server_class, proto, port, use_zlib, use_ssl, verbose):
+ env = setup_pypath(libdir, os.path.join(genbase, genpydir))
+ # Build command line arguments
+ server_args = [sys.executable, relfile('TestServer.py')]
+ cli_args = [sys.executable, relfile('TestClient.py')]
+ for which in (server_args, cli_args):
+ which.append('--protocol=%s' % proto) # accel, binary, compact or json
+ which.append('--port=%d' % port) # default to 9090
+ if use_zlib:
+ which.append('--zlib')
+ if use_ssl:
+ which.append('--ssl')
+ if verbose == 0:
+ which.append('-q')
+ if verbose == 2:
+ which.append('-v')
+ # server-specific option to select server class
+ server_args.append(server_class)
+ # client-specific cmdline options
+ if server_class in FRAMED:
+ cli_args.append('--transport=framed')
+ else:
+ cli_args.append('--transport=buffered')
+ if server_class == 'THttpServer':
+ cli_args.append('--http=/')
+ if verbose > 0:
+ print('Testing server %s: %s' % (server_class, ' '.join(server_args)))
+ serverproc = subprocess.Popen(server_args, env=env)
+
+ def ensureServerAlive():
+ if serverproc.poll() is not None:
+ print(('FAIL: Server process (%s) failed with retcode %d')
+ % (' '.join(server_args), serverproc.returncode))
+ raise Exception('Server subprocess %s died, args: %s'
+ % (server_class, ' '.join(server_args)))
+
+ # Wait for the server to start accepting connections on the given port.
+ sleep_time = 0.1 # Seconds
+ max_attempts = 100
+ attempt = 0
+ while True:
+ sock4 = socket.socket()
+ sock6 = socket.socket(socket.AF_INET6)
+ try:
+ if sock4.connect_ex(('127.0.0.1', port)) == 0 \
+ or sock6.connect_ex(('::1', port)) == 0:
+ break
+ attempt += 1
+ if attempt >= max_attempts:
+ raise Exception("TestServer not ready on port %d after %.2f seconds"
+ % (port, sleep_time * attempt))
+ ensureServerAlive()
+ time.sleep(sleep_time)
+ finally:
+ sock4.close()
+ sock6.close()
+
+ try:
+ if verbose > 0:
+ print('Testing client: %s' % (' '.join(cli_args)))
+ ret = subprocess.call(cli_args, env=env)
+ if ret != 0:
+ print('*** FAILED ***', file=sys.stderr)
+ print('LIBDIR: %s' % libdir, file=sys.stderr)
+ print('PY_GEN: %s' % genpydir, file=sys.stderr)
+ raise Exception("Client subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(cli_args)))
+ finally:
+ # check that server didn't die
+ ensureServerAlive()
+ extra_sleep = EXTRA_DELAY.get(server_class, 0)
+ if extra_sleep > 0 and verbose > 0:
+ print('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child'
+ 'processes to terminate via alarm'
+ % (server_class, proto, use_zlib, use_ssl, extra_sleep))
+ time.sleep(extra_sleep)
+ sig = signal.SIGKILL if platform.system() != 'Windows' else signal.SIGABRT
+ os.kill(serverproc.pid, sig)
+ serverproc.wait()
+
+
+class TestCases(object):
+ def __init__(self, genbase, libdir, port, gendirs, servers, verbose):
+ self.genbase = genbase
+ self.libdir = libdir
+ self.port = port
+ self.verbose = verbose
+ self.gendirs = gendirs
+ self.servers = servers
+
+ def default_conf(self):
+ return {
+ 'gendir': self.gendirs[0],
+ 'server': self.servers[0],
+ 'proto': PROTOS[0],
+ 'zlib': False,
+ 'ssl': False,
+ }
+
+ def run(self, conf, test_count):
+ with_zlib = conf['zlib']
+ with_ssl = conf['ssl']
+ try_server = conf['server']
+ try_proto = conf['proto']
+ genpydir = conf['gendir']
+ # skip any servers that don't work with the Zlib transport
+ if with_zlib and try_server in SKIP_ZLIB:
+ return False
+ # skip any servers that don't work with SSL
+ if with_ssl and try_server in SKIP_SSL:
+ return False
+ if self.verbose > 0:
+ print('\nTest run #%d: (includes %s) Server=%s, Proto=%s, zlib=%s, SSL=%s'
+ % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
+ runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl, self.verbose)
+ if self.verbose > 0:
+ print('OK: Finished (includes %s) %s / %s proto / zlib=%s / SSL=%s. %d combinations tested.'
+ % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
+ return True
+
+ def test_feature(self, name, values):
+ test_count = 0
+ conf = self.default_conf()
+ for try_server in values:
+ conf[name] = try_server
+ if self.run(conf, test_count):
+ test_count += 1
+ return test_count
+
+ def run_all_tests(self):
+ test_count = 0
+ for try_server in self.servers:
+ for genpydir in self.gendirs:
+ for try_proto in PROTOS:
+ for with_zlib in (False, True):
+ # skip any servers that don't work with the Zlib transport
+ if with_zlib and try_server in SKIP_ZLIB:
+ continue
+ for with_ssl in (False, True):
+ # skip any servers that don't work with SSL
+ if with_ssl and try_server in SKIP_SSL:
+ continue
+ test_count += 1
+ if self.verbose > 0:
+ print('\nTest run #%d: (includes %s) Server=%s, Proto=%s, zlib=%s, SSL=%s'
+ % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
+ runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl)
+ if self.verbose > 0:
+ print('OK: Finished (includes %s) %s / %s proto / zlib=%s / SSL=%s. %d combinations tested.'
+ % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
+ return test_count
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option('--all', action="store_true", dest='all')
+ parser.add_option('--genpydirs', type='string', dest='genpydirs',
+ default='default,slots,oldstyle,no_utf8strings,dynamic,dynamicslots',
+ help='directory extensions for generated code, used as suffixes for \"gen-py-*\" added sys.path for individual tests')
+ parser.add_option("--port", type="int", dest="port", default=9090,
+ help="port number for server to listen on")
+ parser.add_option('-v', '--verbose', action="store_const",
+ dest="verbose", const=2,
+ help="verbose output")
+ parser.add_option('-q', '--quiet', action="store_const",
+ dest="verbose", const=0,
+ help="minimal output")
+ parser.add_option('-L', '--libdir', dest="libdir", default=local_libpath(),
+ help="directory path that contains Thrift Python library")
+ parser.add_option('--gen-base', dest="gen_base", default=SCRIPT_DIR,
+ help="directory path that contains Thrift Python library")
+ parser.set_defaults(verbose=1)
+ options, args = parser.parse_args()
+
+ generated_dirs = []
+ for gp_dir in options.genpydirs.split(','):
+ generated_dirs.append('gen-py-%s' % (gp_dir))
+
+ # commandline permits a single class name to be specified to override SERVERS=[...]
+ servers = default_servers()
+ if len(args) == 1:
+ if args[0] in servers:
+ servers = args
+ else:
+ print('Unavailable server type "%s", please choose one of: %s' % (args[0], servers))
+ sys.exit(0)
+
+ tests = TestCases(options.gen_base, options.libdir, options.port, generated_dirs, servers, options.verbose)
+
+ # run tests without a client/server first
+ print('----------------')
+ print(' Executing individual test scripts with various generated code directories')
+ print(' Directories to be tested: ' + ', '.join(generated_dirs))
+ print(' Scripts to be tested: ' + ', '.join(SCRIPTS))
+ print('----------------')
+ for genpydir in generated_dirs:
+ for script in SCRIPTS:
+ runScriptTest(options.libdir, options.gen_base, genpydir, script)
+
+ print('----------------')
+ print(' Executing Client/Server tests with various generated code directories')
+ print(' Servers to be tested: ' + ', '.join(servers))
+ print(' Directories to be tested: ' + ', '.join(generated_dirs))
+ print(' Protocols to be tested: ' + ', '.join(PROTOS))
+ print(' Options to be tested: ZLIB(yes/no), SSL(yes/no)')
+ print('----------------')
+
+ if options.all:
+ tests.run_all_tests()
+ else:
+ tests.test_feature('gendir', generated_dirs)
+ tests.test_feature('server', servers)
+ tests.test_feature('proto', PROTOS)
+ tests.test_feature('zlib', [False, True])
+ tests.test_feature('ssl', [False, True])
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src/jaegertracing/thrift/test/py/SerializationTest.py b/src/jaegertracing/thrift/test/py/SerializationTest.py
new file mode 100755
index 000000000..ef7983568
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/SerializationTest.py
@@ -0,0 +1,457 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import (
+ Bonk,
+ Bools,
+ LargeDeltas,
+ ListBonks,
+ NestedListsBonk,
+ NestedListsI32x2,
+ NestedListsI32x3,
+ NestedMixedx2,
+ Numberz,
+ VersioningTestV1,
+ VersioningTestV2,
+ Xtruct,
+ Xtruct2,
+)
+
+from Recursive.ttypes import RecTree
+from Recursive.ttypes import RecList
+from Recursive.ttypes import CoRec
+from Recursive.ttypes import CoRec2
+from Recursive.ttypes import VectorTest
+from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol, TCompactProtocol, TJSONProtocol
+from thrift.TSerialization import serialize, deserialize
+import sys
+import unittest
+
+
+class AbstractTest(unittest.TestCase):
+
+ def setUp(self):
+ self.v1obj = VersioningTestV1(
+ begin_in_both=12345,
+ old_string='aaa',
+ end_in_both=54321,
+ )
+
+ self.v2obj = VersioningTestV2(
+ begin_in_both=12345,
+ newint=1,
+ newbyte=2,
+ newshort=3,
+ newlong=4,
+ newdouble=5.0,
+ newstruct=Bonk(message="Hello!", type=123),
+ newlist=[7, 8, 9],
+ newset=set([42, 1, 8]),
+ newmap={1: 2, 2: 3},
+ newstring="Hola!",
+ end_in_both=54321,
+ )
+
+ self.bools = Bools(im_true=True, im_false=False)
+ self.bools_flipped = Bools(im_true=False, im_false=True)
+
+ self.large_deltas = LargeDeltas(
+ b1=self.bools,
+ b10=self.bools_flipped,
+ b100=self.bools,
+ check_true=True,
+ b1000=self.bools_flipped,
+ check_false=False,
+ vertwo2000=VersioningTestV2(newstruct=Bonk(message='World!', type=314)),
+ a_set2500=set(['lazy', 'brown', 'cow']),
+ vertwo3000=VersioningTestV2(newset=set([2, 3, 5, 7, 11])),
+ big_numbers=[2 ** 8, 2 ** 16, 2 ** 31 - 1, -(2 ** 31 - 1)]
+ )
+
+ self.compact_struct = CompactProtoTestStruct(
+ a_byte=127,
+ a_i16=32000,
+ a_i32=1000000000,
+ a_i64=0xffffffffff,
+ a_double=5.6789,
+ a_string="my string",
+ true_field=True,
+ false_field=False,
+ empty_struct_field=Empty(),
+ byte_list=[-127, -1, 0, 1, 127],
+ i16_list=[-1, 0, 1, 0x7fff],
+ i32_list=[-1, 0, 0xff, 0xffff, 0xffffff, 0x7fffffff],
+ i64_list=[-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff],
+ double_list=[0.1, 0.2, 0.3],
+ string_list=["first", "second", "third"],
+ boolean_list=[True, True, True, False, False, False],
+ struct_list=[Empty(), Empty()],
+ byte_set=set([-127, -1, 0, 1, 127]),
+ i16_set=set([-1, 0, 1, 0x7fff]),
+ i32_set=set([1, 2, 3]),
+ i64_set=set([-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff]),
+ double_set=set([0.1, 0.2, 0.3]),
+ string_set=set(["first", "second", "third"]),
+ boolean_set=set([True, False]),
+ # struct_set=set([Empty()]), # unhashable instance
+ byte_byte_map={1: 2},
+ i16_byte_map={1: 1, -1: 1, 0x7fff: 1},
+ i32_byte_map={1: 1, -1: 1, 0x7fffffff: 1},
+ i64_byte_map={0: 1, 1: 1, -1: 1, 0x7fffffffffffffff: 1},
+ double_byte_map={-1.1: 1, 1.1: 1},
+ string_byte_map={"first": 1, "second": 2, "third": 3, "": 0},
+ boolean_byte_map={True: 1, False: 0},
+ byte_i16_map={1: 1, 2: -1, 3: 0x7fff},
+ byte_i32_map={1: 1, 2: -1, 3: 0x7fffffff},
+ byte_i64_map={1: 1, 2: -1, 3: 0x7fffffffffffffff},
+ byte_double_map={1: 0.1, 2: -0.1, 3: 1000000.1},
+ byte_string_map={1: "", 2: "blah", 3: "loooooooooooooong string"},
+ byte_boolean_map={1: True, 2: False},
+ # list_byte_map # unhashable
+ # set_byte_map={set([1, 2, 3]) : 1, set([0, 1]) : 2, set([]) : 0}, # unhashable
+ # map_byte_map # unhashable
+ byte_map_map={0: {}, 1: {1: 1}, 2: {1: 1, 2: 2}},
+ byte_set_map={0: set([]), 1: set([1]), 2: set([1, 2])},
+ byte_list_map={0: [], 1: [1], 2: [1, 2]},
+ )
+
+ self.nested_lists_i32x2 = NestedListsI32x2(
+ [
+ [1, 1, 2],
+ [2, 7, 9],
+ [3, 5, 8]
+ ]
+ )
+
+ self.nested_lists_i32x3 = NestedListsI32x3(
+ [
+ [
+ [2, 7, 9],
+ [3, 5, 8]
+ ],
+ [
+ [1, 1, 2],
+ [1, 4, 9]
+ ]
+ ]
+ )
+
+ self.nested_mixedx2 = NestedMixedx2(int_set_list=[
+ set([1, 2, 3]),
+ set([1, 4, 9]),
+ set([1, 2, 3, 5, 8, 13, 21]),
+ set([-1, 0, 1])
+ ],
+ # note, the sets below are sets of chars, since the strings are iterated
+ map_int_strset={10: set('abc'), 20: set('def'), 30: set('GHI')},
+ map_int_strset_list=[
+ {10: set('abc'), 20: set('def'), 30: set('GHI')},
+ {100: set('lmn'), 200: set('opq'), 300: set('RST')},
+ {1000: set('uvw'), 2000: set('wxy'), 3000: set('XYZ')}]
+ )
+
+ self.nested_lists_bonk = NestedListsBonk(
+ [
+ [
+ [
+ Bonk(message='inner A first', type=1),
+ Bonk(message='inner A second', type=1)
+ ],
+ [
+ Bonk(message='inner B first', type=2),
+ Bonk(message='inner B second', type=2)
+ ]
+ ]
+ ]
+ )
+
+ self.list_bonks = ListBonks(
+ [
+ Bonk(message='inner A', type=1),
+ Bonk(message='inner B', type=2),
+ Bonk(message='inner C', type=0)
+ ]
+ )
+
+ def _serialize(self, obj):
+ trans = TTransport.TMemoryBuffer()
+ prot = self.protocol_factory.getProtocol(trans)
+ obj.write(prot)
+ return trans.getvalue()
+
+ def _deserialize(self, objtype, data):
+ prot = self.protocol_factory.getProtocol(TTransport.TMemoryBuffer(data))
+ ret = objtype()
+ ret.read(prot)
+ return ret
+
+ def testForwards(self):
+ obj = self._deserialize(VersioningTestV2, self._serialize(self.v1obj))
+ self.assertEquals(obj.begin_in_both, self.v1obj.begin_in_both)
+ self.assertEquals(obj.end_in_both, self.v1obj.end_in_both)
+
+ def testBackwards(self):
+ obj = self._deserialize(VersioningTestV1, self._serialize(self.v2obj))
+ self.assertEquals(obj.begin_in_both, self.v2obj.begin_in_both)
+ self.assertEquals(obj.end_in_both, self.v2obj.end_in_both)
+
+ def testSerializeV1(self):
+ obj = self._deserialize(VersioningTestV1, self._serialize(self.v1obj))
+ self.assertEquals(obj, self.v1obj)
+
+ def testSerializeV2(self):
+ obj = self._deserialize(VersioningTestV2, self._serialize(self.v2obj))
+ self.assertEquals(obj, self.v2obj)
+
+ def testBools(self):
+ self.assertNotEquals(self.bools, self.bools_flipped)
+ self.assertNotEquals(self.bools, self.v1obj)
+ obj = self._deserialize(Bools, self._serialize(self.bools))
+ self.assertEquals(obj, self.bools)
+ obj = self._deserialize(Bools, self._serialize(self.bools_flipped))
+ self.assertEquals(obj, self.bools_flipped)
+ rep = repr(self.bools)
+ self.assertTrue(len(rep) > 0)
+
+ def testLargeDeltas(self):
+ # test large field deltas (meaningful in CompactProto only)
+ obj = self._deserialize(LargeDeltas, self._serialize(self.large_deltas))
+ self.assertEquals(obj, self.large_deltas)
+ rep = repr(self.large_deltas)
+ self.assertTrue(len(rep) > 0)
+
+ def testNestedListsI32x2(self):
+ obj = self._deserialize(NestedListsI32x2, self._serialize(self.nested_lists_i32x2))
+ self.assertEquals(obj, self.nested_lists_i32x2)
+ rep = repr(self.nested_lists_i32x2)
+ self.assertTrue(len(rep) > 0)
+
+ def testNestedListsI32x3(self):
+ obj = self._deserialize(NestedListsI32x3, self._serialize(self.nested_lists_i32x3))
+ self.assertEquals(obj, self.nested_lists_i32x3)
+ rep = repr(self.nested_lists_i32x3)
+ self.assertTrue(len(rep) > 0)
+
+ def testNestedMixedx2(self):
+ obj = self._deserialize(NestedMixedx2, self._serialize(self.nested_mixedx2))
+ self.assertEquals(obj, self.nested_mixedx2)
+ rep = repr(self.nested_mixedx2)
+ self.assertTrue(len(rep) > 0)
+
+ def testNestedListsBonk(self):
+ obj = self._deserialize(NestedListsBonk, self._serialize(self.nested_lists_bonk))
+ self.assertEquals(obj, self.nested_lists_bonk)
+ rep = repr(self.nested_lists_bonk)
+ self.assertTrue(len(rep) > 0)
+
+ def testListBonks(self):
+ obj = self._deserialize(ListBonks, self._serialize(self.list_bonks))
+ self.assertEquals(obj, self.list_bonks)
+ rep = repr(self.list_bonks)
+ self.assertTrue(len(rep) > 0)
+
+ def testCompactStruct(self):
+ # test large field deltas (meaningful in CompactProto only)
+ obj = self._deserialize(CompactProtoTestStruct, self._serialize(self.compact_struct))
+ self.assertEquals(obj, self.compact_struct)
+ rep = repr(self.compact_struct)
+ self.assertTrue(len(rep) > 0)
+
+ def testIntegerLimits(self):
+ if (sys.version_info[0] == 2 and sys.version_info[1] <= 6):
+ print('Skipping testIntegerLimits for Python 2.6')
+ return
+ bad_values = [CompactProtoTestStruct(a_byte=128), CompactProtoTestStruct(a_byte=-129),
+ CompactProtoTestStruct(a_i16=32768), CompactProtoTestStruct(a_i16=-32769),
+ CompactProtoTestStruct(a_i32=2147483648), CompactProtoTestStruct(a_i32=-2147483649),
+ CompactProtoTestStruct(a_i64=9223372036854775808), CompactProtoTestStruct(a_i64=-9223372036854775809)
+ ]
+
+ for value in bad_values:
+ self.assertRaises(Exception, self._serialize, value)
+
+ def testRecTree(self):
+ """Ensure recursive tree node can be created."""
+ children = []
+ for idx in range(1, 5):
+ node = RecTree(item=idx, children=None)
+ children.append(node)
+
+ parent = RecTree(item=0, children=children)
+ serde_parent = self._deserialize(RecTree, self._serialize(parent))
+ self.assertEquals(0, serde_parent.item)
+ self.assertEquals(4, len(serde_parent.children))
+ for child in serde_parent.children:
+ # Cannot use assertIsInstance in python 2.6?
+ self.assertTrue(isinstance(child, RecTree))
+
+ def _buildLinkedList(self):
+ head = cur = RecList(item=0)
+ for idx in range(1, 5):
+ node = RecList(item=idx)
+ cur.nextitem = node
+ cur = node
+ return head
+
+ def _collapseLinkedList(self, head):
+ out_list = []
+ cur = head
+ while cur is not None:
+ out_list.append(cur.item)
+ cur = cur.nextitem
+ return out_list
+
+ def testRecList(self):
+ """Ensure recursive linked list can be created."""
+ rec_list = self._buildLinkedList()
+ serde_list = self._deserialize(RecList, self._serialize(rec_list))
+ out_list = self._collapseLinkedList(serde_list)
+ self.assertEquals([0, 1, 2, 3, 4], out_list)
+
+ def testCoRec(self):
+ """Ensure co-recursive structures can be created."""
+ item1 = CoRec()
+ item2 = CoRec2()
+
+ item1.other = item2
+ item2.other = item1
+
+ # NOTE [econner724,2017-06-21]: These objects cannot be serialized as serialization
+ # results in an infinite loop. fbthrift also suffers from this
+ # problem.
+
+ def testRecVector(self):
+ """Ensure a list of recursive nodes can be created."""
+ mylist = [self._buildLinkedList(), self._buildLinkedList()]
+ myvec = VectorTest(lister=mylist)
+
+ serde_vec = self._deserialize(VectorTest, self._serialize(myvec))
+ golden_list = [0, 1, 2, 3, 4]
+ for cur_list in serde_vec.lister:
+ out_list = self._collapseLinkedList(cur_list)
+ self.assertEqual(golden_list, out_list)
+
+
+class NormalBinaryTest(AbstractTest):
+ protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()
+
+
+class AcceleratedBinaryTest(AbstractTest):
+ protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False)
+
+
+class CompactProtocolTest(AbstractTest):
+ protocol_factory = TCompactProtocol.TCompactProtocolFactory()
+
+
+class AcceleratedCompactTest(AbstractTest):
+ protocol_factory = TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False)
+
+
+class JSONProtocolTest(AbstractTest):
+ protocol_factory = TJSONProtocol.TJSONProtocolFactory()
+
+
+class AcceleratedFramedTest(unittest.TestCase):
+ def testSplit(self):
+ """Test FramedTransport and BinaryProtocolAccelerated
+
+ Tests that TBinaryProtocolAccelerated and TFramedTransport
+ play nicely together when a read spans a frame"""
+
+ protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory()
+ bigstring = "".join(chr(byte) for byte in range(ord("a"), ord("z") + 1))
+
+ databuf = TTransport.TMemoryBuffer()
+ prot = protocol_factory.getProtocol(databuf)
+ prot.writeI32(42)
+ prot.writeString(bigstring)
+ prot.writeI16(24)
+ data = databuf.getvalue()
+ cutpoint = len(data) // 2
+ parts = [data[:cutpoint], data[cutpoint:]]
+
+ framed_buffer = TTransport.TMemoryBuffer()
+ framed_writer = TTransport.TFramedTransport(framed_buffer)
+ for part in parts:
+ framed_writer.write(part)
+ framed_writer.flush()
+ self.assertEquals(len(framed_buffer.getvalue()), len(data) + 8)
+
+ # Recreate framed_buffer so we can read from it.
+ framed_buffer = TTransport.TMemoryBuffer(framed_buffer.getvalue())
+ framed_reader = TTransport.TFramedTransport(framed_buffer)
+ prot = protocol_factory.getProtocol(framed_reader)
+ self.assertEqual(prot.readI32(), 42)
+ self.assertEqual(prot.readString(), bigstring)
+ self.assertEqual(prot.readI16(), 24)
+
+
+class SerializersTest(unittest.TestCase):
+
+ def testSerializeThenDeserialize(self):
+ obj = Xtruct2(i32_thing=1,
+ struct_thing=Xtruct(string_thing="foo"))
+
+ s1 = serialize(obj)
+ for i in range(10):
+ self.assertEquals(s1, serialize(obj))
+ objcopy = Xtruct2()
+ deserialize(objcopy, serialize(obj))
+ self.assertEquals(obj, objcopy)
+
+ obj = Xtruct(string_thing="bar")
+ objcopy = Xtruct()
+ deserialize(objcopy, serialize(obj))
+ self.assertEquals(obj, objcopy)
+
+ # test booleans
+ obj = Bools(im_true=True, im_false=False)
+ objcopy = Bools()
+ deserialize(objcopy, serialize(obj))
+ self.assertEquals(obj, objcopy)
+
+ # test enums
+ for num, name in Numberz._VALUES_TO_NAMES.items():
+ obj = Bonk(message='enum Numberz value %d is string %s' % (num, name), type=num)
+ objcopy = Bonk()
+ deserialize(objcopy, serialize(obj))
+ self.assertEquals(obj, objcopy)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+
+ suite.addTest(loader.loadTestsFromTestCase(NormalBinaryTest))
+ suite.addTest(loader.loadTestsFromTestCase(AcceleratedBinaryTest))
+ suite.addTest(loader.loadTestsFromTestCase(AcceleratedCompactTest))
+ suite.addTest(loader.loadTestsFromTestCase(CompactProtocolTest))
+ suite.addTest(loader.loadTestsFromTestCase(JSONProtocolTest))
+ suite.addTest(loader.loadTestsFromTestCase(AcceleratedFramedTest))
+ suite.addTest(loader.loadTestsFromTestCase(SerializersTest))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/src/jaegertracing/thrift/test/py/TSimpleJSONProtocolTest.py b/src/jaegertracing/thrift/test/py/TSimpleJSONProtocolTest.py
new file mode 100644
index 000000000..72987602b
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TSimpleJSONProtocolTest.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import Bonk, VersioningTestV1, VersioningTestV2
+from thrift.protocol import TJSONProtocol
+from thrift.transport import TTransport
+
+import json
+import unittest
+
+
+class SimpleJSONProtocolTest(unittest.TestCase):
+ protocol_factory = TJSONProtocol.TSimpleJSONProtocolFactory()
+
+ def _assertDictEqual(self, a, b, msg=None):
+ if hasattr(self, 'assertDictEqual'):
+ # assertDictEqual only in Python 2.7. Depends on your machine.
+ self.assertDictEqual(a, b, msg)
+ return
+
+ # Substitute implementation not as good as unittest library's
+ self.assertEquals(len(a), len(b), msg)
+ for k, v in a.iteritems():
+ self.assertTrue(k in b, msg)
+ self.assertEquals(b.get(k), v, msg)
+
+ def _serialize(self, obj):
+ trans = TTransport.TMemoryBuffer()
+ prot = self.protocol_factory.getProtocol(trans)
+ obj.write(prot)
+ return trans.getvalue()
+
+ def _deserialize(self, objtype, data):
+ prot = self.protocol_factory.getProtocol(TTransport.TMemoryBuffer(data))
+ ret = objtype()
+ ret.read(prot)
+ return ret
+
+ def testWriteOnly(self):
+ self.assertRaises(NotImplementedError,
+ self._deserialize, VersioningTestV1, b'{}')
+
+ def testSimpleMessage(self):
+ v1obj = VersioningTestV1(
+ begin_in_both=12345,
+ old_string='aaa',
+ end_in_both=54321)
+ expected = dict(begin_in_both=v1obj.begin_in_both,
+ old_string=v1obj.old_string,
+ end_in_both=v1obj.end_in_both)
+ actual = json.loads(self._serialize(v1obj).decode('ascii'))
+
+ self._assertDictEqual(expected, actual)
+
+ def testComplicated(self):
+ v2obj = VersioningTestV2(
+ begin_in_both=12345,
+ newint=1,
+ newbyte=2,
+ newshort=3,
+ newlong=4,
+ newdouble=5.0,
+ newstruct=Bonk(message="Hello!", type=123),
+ newlist=[7, 8, 9],
+ newset=set([42, 1, 8]),
+ newmap={1: 2, 2: 3},
+ newstring="Hola!",
+ end_in_both=54321)
+ expected = dict(begin_in_both=v2obj.begin_in_both,
+ newint=v2obj.newint,
+ newbyte=v2obj.newbyte,
+ newshort=v2obj.newshort,
+ newlong=v2obj.newlong,
+ newdouble=v2obj.newdouble,
+ newstruct=dict(message=v2obj.newstruct.message,
+ type=v2obj.newstruct.type),
+ newlist=v2obj.newlist,
+ newset=list(v2obj.newset),
+ newmap=v2obj.newmap,
+ newstring=v2obj.newstring,
+ end_in_both=v2obj.end_in_both)
+
+ # Need to load/dump because map keys get escaped.
+ expected = json.loads(json.dumps(expected))
+ actual = json.loads(self._serialize(v2obj).decode('ascii'))
+ self._assertDictEqual(expected, actual)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/jaegertracing/thrift/test/py/TestClient.py b/src/jaegertracing/thrift/test/py/TestClient.py
new file mode 100755
index 000000000..e7a9a1a0e
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestClient.py
@@ -0,0 +1,500 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+import sys
+import time
+import unittest
+
+from optparse import OptionParser
+from util import local_libpath
+sys.path.insert(0, local_libpath())
+from thrift.protocol import TProtocol, TProtocolDecorator
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+class AbstractTest(unittest.TestCase):
+ def setUp(self):
+ if options.trans == 'http':
+ uri = '{0}://{1}:{2}{3}'.format(('https' if options.ssl else 'http'),
+ options.host,
+ options.port,
+ (options.http_path if options.http_path else '/'))
+ if options.ssl:
+ __cafile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "CA.pem")
+ __certfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "client.crt")
+ __keyfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "client.key")
+ self.transport = THttpClient.THttpClient(uri, cafile=__cafile, cert_file=__certfile, key_file=__keyfile)
+ else:
+ self.transport = THttpClient.THttpClient(uri)
+ else:
+ if options.ssl:
+ from thrift.transport import TSSLSocket
+ socket = TSSLSocket.TSSLSocket(options.host, options.port, validate=False)
+ else:
+ socket = TSocket.TSocket(options.host, options.port)
+ # frame or buffer depending upon args
+ self.transport = TTransport.TBufferedTransport(socket)
+ if options.trans == 'framed':
+ self.transport = TTransport.TFramedTransport(socket)
+ elif options.trans == 'buffered':
+ self.transport = TTransport.TBufferedTransport(socket)
+ elif options.trans == '':
+ raise AssertionError('Unknown --transport option: %s' % options.trans)
+ if options.zlib:
+ self.transport = TZlibTransport.TZlibTransport(self.transport, 9)
+ self.transport.open()
+ protocol = self.get_protocol(self.transport)
+ self.client = ThriftTest.Client(protocol)
+ # for multiplexed services:
+ protocol2 = self.get_protocol2(self.transport)
+ self.client2 = SecondService.Client(protocol2) if protocol2 is not None else None
+
+ def tearDown(self):
+ self.transport.close()
+
+ def testVoid(self):
+ print('testVoid')
+ self.client.testVoid()
+
+ def testString(self):
+ print('testString')
+ self.assertEqual(self.client.testString('Python' * 20), 'Python' * 20)
+ self.assertEqual(self.client.testString(''), '')
+ s1 = u'\b\t\n/\\\\\r{}:パイソン"'
+ s2 = u"""Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
+ Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška,
+ Беларуская, Беларуская (тарашкевіца), Български, Bamanankan,
+ বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн,
+ Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg,
+ Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English,
+ Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt,
+ Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego,
+ Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski,
+ Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia,
+ Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa,
+ ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар,
+ Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino,
+ Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa
+ Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa
+ Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪
+ Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad,
+ Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو,
+ Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română,
+ Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple
+ English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk,
+ Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog,
+ Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
+ Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
+ Bân-lâm-gú, 粵語"""
+ if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
+ s1 = s1.encode('utf8')
+ s2 = s2.encode('utf8')
+ self.assertEqual(self.client.testString(s1), s1)
+ self.assertEqual(self.client.testString(s2), s2)
+
+ def testMultiplexed(self):
+ if self.client2 is not None:
+ print('testMultiplexed')
+ self.assertEqual(self.client2.secondtestString('foobar'), 'testString("foobar")')
+
+ def testBool(self):
+ print('testBool')
+ self.assertEqual(self.client.testBool(True), True)
+ self.assertEqual(self.client.testBool(False), False)
+
+ def testByte(self):
+ print('testByte')
+ self.assertEqual(self.client.testByte(63), 63)
+ self.assertEqual(self.client.testByte(-127), -127)
+
+ def testI32(self):
+ print('testI32')
+ self.assertEqual(self.client.testI32(-1), -1)
+ self.assertEqual(self.client.testI32(0), 0)
+
+ def testI64(self):
+ print('testI64')
+ self.assertEqual(self.client.testI64(1), 1)
+ self.assertEqual(self.client.testI64(-34359738368), -34359738368)
+
+ def testDouble(self):
+ print('testDouble')
+ self.assertEqual(self.client.testDouble(-5.235098235), -5.235098235)
+ self.assertEqual(self.client.testDouble(0), 0)
+ self.assertEqual(self.client.testDouble(-1), -1)
+ self.assertEqual(self.client.testDouble(-0.000341012439638598279), -0.000341012439638598279)
+
+ def testBinary(self):
+ print('testBinary')
+ val = bytearray([i for i in range(0, 256)])
+ self.assertEqual(bytearray(self.client.testBinary(bytes(val))), val)
+
+ def testStruct(self):
+ print('testStruct')
+ x = Xtruct()
+ x.string_thing = "Zero"
+ x.byte_thing = 1
+ x.i32_thing = -3
+ x.i64_thing = -5
+ y = self.client.testStruct(x)
+ self.assertEqual(y, x)
+
+ def testNest(self):
+ print('testNest')
+ inner = Xtruct(string_thing="Zero", byte_thing=1, i32_thing=-3, i64_thing=-5)
+ x = Xtruct2(struct_thing=inner, byte_thing=0, i32_thing=0)
+ y = self.client.testNest(x)
+ self.assertEqual(y, x)
+
+ def testMap(self):
+ print('testMap')
+ x = {0: 1, 1: 2, 2: 3, 3: 4, -1: -2}
+ y = self.client.testMap(x)
+ self.assertEqual(y, x)
+
+ def testSet(self):
+ print('testSet')
+ x = set([8, 1, 42])
+ y = self.client.testSet(x)
+ self.assertEqual(y, x)
+
+ def testList(self):
+ print('testList')
+ x = [1, 4, 9, -42]
+ y = self.client.testList(x)
+ self.assertEqual(y, x)
+
+ def testEnum(self):
+ print('testEnum')
+ x = Numberz.FIVE
+ y = self.client.testEnum(x)
+ self.assertEqual(y, x)
+
+ def testTypedef(self):
+ print('testTypedef')
+ x = 0xffffffffffffff # 7 bytes of 0xff
+ y = self.client.testTypedef(x)
+ self.assertEqual(y, x)
+
+ def testMapMap(self):
+ print('testMapMap')
+ x = {
+ -4: {-4: -4, -3: -3, -2: -2, -1: -1},
+ 4: {4: 4, 3: 3, 2: 2, 1: 1},
+ }
+ y = self.client.testMapMap(42)
+ self.assertEqual(y, x)
+
+ def testMulti(self):
+ print('testMulti')
+ xpected = Xtruct(string_thing='Hello2', byte_thing=74, i32_thing=0xff00ff, i64_thing=0xffffffffd0d0)
+ y = self.client.testMulti(xpected.byte_thing,
+ xpected.i32_thing,
+ xpected.i64_thing,
+ {0: 'abc'},
+ Numberz.FIVE,
+ 0xf0f0f0)
+ self.assertEqual(y, xpected)
+
+ def testException(self):
+ print('testException')
+ self.client.testException('Safe')
+ try:
+ self.client.testException('Xception')
+ self.fail("should have gotten exception")
+ except Xception as x:
+ self.assertEqual(x.errorCode, 1001)
+ self.assertEqual(x.message, 'Xception')
+ # TODO ensure same behavior for repr within generated python variants
+ # ensure exception's repr method works
+ # x_repr = repr(x)
+ # self.assertEqual(x_repr, 'Xception(errorCode=1001, message=\'Xception\')')
+
+ try:
+ self.client.testException('TException')
+ self.fail("should have gotten exception")
+ except TException as x:
+ pass
+
+ # Should not throw
+ self.client.testException('success')
+
+ def testMultiException(self):
+ print('testMultiException')
+ try:
+ self.client.testMultiException('Xception', 'ignore')
+ except Xception as ex:
+ self.assertEqual(ex.errorCode, 1001)
+ self.assertEqual(ex.message, 'This is an Xception')
+
+ try:
+ self.client.testMultiException('Xception2', 'ignore')
+ except Xception2 as ex:
+ self.assertEqual(ex.errorCode, 2002)
+ self.assertEqual(ex.struct_thing.string_thing, 'This is an Xception2')
+
+ y = self.client.testMultiException('success', 'foobar')
+ self.assertEqual(y.string_thing, 'foobar')
+
+ def testOneway(self):
+ print('testOneway')
+ start = time.time()
+ self.client.testOneway(1) # type is int, not float
+ end = time.time()
+ self.assertTrue(end - start < 3,
+ "oneway sleep took %f sec" % (end - start))
+
+ def testOnewayThenNormal(self):
+ print('testOnewayThenNormal')
+ self.client.testOneway(1) # type is int, not float
+ self.assertEqual(self.client.testString('Python'), 'Python')
+
+
+# LAST_SEQID is a global because we have one transport and multiple protocols
+# running on it (when multiplexed)
+LAST_SEQID = None
+
+
+class TPedanticSequenceIdProtocolWrapper(TProtocolDecorator.TProtocolDecorator):
+ """
+ Wraps any protocol with sequence ID checking: looks for outbound
+ uniqueness as well as request/response alignment.
+ """
+ def __init__(self, protocol):
+ # TProtocolDecorator.__new__ does all the heavy lifting
+ pass
+
+ def writeMessageBegin(self, name, type, seqid):
+ global LAST_SEQID
+ if LAST_SEQID and LAST_SEQID == seqid:
+ raise TProtocol.TProtocolException(
+ TProtocol.TProtocolException.INVALID_DATA,
+ "Python client reused sequence ID {0}".format(seqid))
+ LAST_SEQID = seqid
+ super(TPedanticSequenceIdProtocolWrapper, self).writeMessageBegin(
+ name, type, seqid)
+
+ def readMessageBegin(self):
+ global LAST_SEQID
+ (name, type, seqid) =\
+ super(TPedanticSequenceIdProtocolWrapper, self).readMessageBegin()
+ if LAST_SEQID != seqid:
+ raise TProtocol.TProtocolException(
+ TProtocol.TProtocolException.INVALID_DATA,
+ "We sent seqid {0} and server returned seqid {1}".format(
+ self.last, seqid))
+ return (name, type, seqid)
+
+
+def make_pedantic(proto):
+ """ Wrap a protocol in the pedantic sequence ID wrapper. """
+ return TPedanticSequenceIdProtocolWrapper(proto)
+
+
+class MultiplexedOptionalTest(AbstractTest):
+ def get_protocol2(self, transport):
+ return None
+
+
+class BinaryTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ return make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport))
+
+
+class MultiplexedBinaryTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+class AcceleratedBinaryTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ return make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+
+
+class MultiplexedAcceleratedBinaryTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+class CompactTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ return make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport))
+
+
+class MultiplexedCompactTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+class AcceleratedCompactTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ return make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+
+
+class MultiplexedAcceleratedCompactTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+class JSONTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ return make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport))
+
+
+class MultiplexedJSONTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(TJSONProtocol.TJSONProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+class HeaderTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ factory = THeaderProtocol.THeaderProtocolFactory()
+ return make_pedantic(factory.getProtocol(transport))
+
+
+class MultiplexedHeaderTest(MultiplexedOptionalTest):
+ def get_protocol(self, transport):
+ wrapped_proto = make_pedantic(THeaderProtocol.THeaderProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "ThriftTest")
+
+ def get_protocol2(self, transport):
+ wrapped_proto = make_pedantic(THeaderProtocol.THeaderProtocolFactory().getProtocol(transport))
+ return TMultiplexedProtocol.TMultiplexedProtocol(wrapped_proto, "SecondService")
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ if options.proto == 'binary': # look for --proto on cmdline
+ suite.addTest(loader.loadTestsFromTestCase(BinaryTest))
+ elif options.proto == 'accel':
+ suite.addTest(loader.loadTestsFromTestCase(AcceleratedBinaryTest))
+ elif options.proto == 'accelc':
+ suite.addTest(loader.loadTestsFromTestCase(AcceleratedCompactTest))
+ elif options.proto == 'compact':
+ suite.addTest(loader.loadTestsFromTestCase(CompactTest))
+ elif options.proto == 'header':
+ suite.addTest(loader.loadTestsFromTestCase(HeaderTest))
+ elif options.proto == 'json':
+ suite.addTest(loader.loadTestsFromTestCase(JSONTest))
+ elif options.proto == 'multi':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedBinaryTest))
+ elif options.proto == 'multia':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedAcceleratedBinaryTest))
+ elif options.proto == 'multiac':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedAcceleratedCompactTest))
+ elif options.proto == 'multic':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedCompactTest))
+ elif options.proto == 'multih':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedHeaderTest))
+ elif options.proto == 'multij':
+ suite.addTest(loader.loadTestsFromTestCase(MultiplexedJSONTest))
+ else:
+ raise AssertionError('Unknown protocol given with --protocol: %s' % options.proto)
+ return suite
+
+
+class OwnArgsTestProgram(unittest.TestProgram):
+ def parseArgs(self, argv):
+ if args:
+ self.testNames = args
+ else:
+ self.testNames = ([self.defaultTest])
+ self.createTests()
+
+
+if __name__ == "__main__":
+ parser = OptionParser()
+ parser.add_option('--libpydir', type='string', dest='libpydir',
+ help='include this directory in sys.path for locating library code')
+ parser.add_option('--genpydir', type='string', dest='genpydir',
+ help='include this directory in sys.path for locating generated code')
+ parser.add_option("--port", type="int", dest="port",
+ help="connect to server at port")
+ parser.add_option("--host", type="string", dest="host",
+ help="connect to server")
+ parser.add_option("--zlib", action="store_true", dest="zlib",
+ help="use zlib wrapper for compressed transport")
+ parser.add_option("--ssl", action="store_true", dest="ssl",
+ help="use SSL for encrypted transport")
+ parser.add_option("--http", dest="http_path",
+ help="Use the HTTP transport with the specified path")
+ parser.add_option('-v', '--verbose', action="store_const",
+ dest="verbose", const=2,
+ help="verbose output")
+ parser.add_option('-q', '--quiet', action="store_const",
+ dest="verbose", const=0,
+ help="minimal output")
+ parser.add_option('--protocol', dest="proto", type="string",
+ help="protocol to use, one of: accel, accelc, binary, compact, header, json, multi, multia, multiac, multic, multih, multij")
+ parser.add_option('--transport', dest="trans", type="string",
+ help="transport to use, one of: buffered, framed, http")
+ parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', port=9090, proto='binary')
+ options, args = parser.parse_args()
+
+ if options.genpydir:
+ sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
+
+ if options.http_path:
+ options.trans = 'http'
+
+ from ThriftTest import SecondService
+ from ThriftTest import ThriftTest
+ from ThriftTest.ttypes import Xtruct, Xtruct2, Numberz, Xception, Xception2
+ from thrift.Thrift import TException
+ from thrift.transport import TTransport
+ from thrift.transport import TSocket
+ from thrift.transport import THttpClient
+ from thrift.transport import TZlibTransport
+ from thrift.protocol import TBinaryProtocol
+ from thrift.protocol import TCompactProtocol
+ from thrift.protocol import THeaderProtocol
+ from thrift.protocol import TJSONProtocol
+ from thrift.protocol import TMultiplexedProtocol
+
+ OwnArgsTestProgram(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=1))
diff --git a/src/jaegertracing/thrift/test/py/TestEof.py b/src/jaegertracing/thrift/test/py/TestEof.py
new file mode 100755
index 000000000..0b4a82960
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestEof.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import Xtruct
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+from thrift.protocol import TCompactProtocol
+import unittest
+
+
+class TestEof(unittest.TestCase):
+
+ def make_data(self, pfactory=None):
+ trans = TTransport.TMemoryBuffer()
+ if pfactory:
+ prot = pfactory.getProtocol(trans)
+ else:
+ prot = TBinaryProtocol.TBinaryProtocol(trans)
+
+ x = Xtruct()
+ x.string_thing = "Zero"
+ x.byte_thing = 0
+
+ x.write(prot)
+
+ x = Xtruct()
+ x.string_thing = "One"
+ x.byte_thing = 1
+
+ x.write(prot)
+
+ return trans.getvalue()
+
+ def testTransportReadAll(self):
+ """Test that readAll on any type of transport throws an EOFError"""
+ trans = TTransport.TMemoryBuffer(self.make_data())
+ trans.readAll(1)
+
+ try:
+ trans.readAll(10000)
+ except EOFError:
+ return
+
+ self.fail("Should have gotten EOFError")
+
+ def eofTestHelper(self, pfactory):
+ trans = TTransport.TMemoryBuffer(self.make_data(pfactory))
+ prot = pfactory.getProtocol(trans)
+
+ x = Xtruct()
+ x.read(prot)
+ self.assertEqual(x.string_thing, "Zero")
+ self.assertEqual(x.byte_thing, 0)
+
+ x = Xtruct()
+ x.read(prot)
+ self.assertEqual(x.string_thing, "One")
+ self.assertEqual(x.byte_thing, 1)
+
+ try:
+ x = Xtruct()
+ x.read(prot)
+ except EOFError:
+ return
+
+ self.fail("Should have gotten EOFError")
+
+ def eofTestHelperStress(self, pfactory):
+ """Test the ability of TBinaryProtocol to deal with the removal of every byte in the file"""
+ # TODO: we should make sure this covers more of the code paths
+
+ data = self.make_data(pfactory)
+ for i in range(0, len(data) + 1):
+ trans = TTransport.TMemoryBuffer(data[0:i])
+ prot = pfactory.getProtocol(trans)
+ try:
+ x = Xtruct()
+ x.read(prot)
+ x.read(prot)
+ x.read(prot)
+ except EOFError:
+ continue
+ self.fail("Should have gotten an EOFError")
+
+ def testBinaryProtocolEof(self):
+ """Test that TBinaryProtocol throws an EOFError when it reaches the end of the stream"""
+ self.eofTestHelper(TBinaryProtocol.TBinaryProtocolFactory())
+ self.eofTestHelperStress(TBinaryProtocol.TBinaryProtocolFactory())
+
+ def testBinaryProtocolAcceleratedBinaryEof(self):
+ """Test that TBinaryProtocolAccelerated throws an EOFError when it reaches the end of the stream"""
+ self.eofTestHelper(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False))
+ self.eofTestHelperStress(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False))
+
+ def testCompactProtocolEof(self):
+ """Test that TCompactProtocol throws an EOFError when it reaches the end of the stream"""
+ self.eofTestHelper(TCompactProtocol.TCompactProtocolFactory())
+ self.eofTestHelperStress(TCompactProtocol.TCompactProtocolFactory())
+
+ def testCompactProtocolAcceleratedCompactEof(self):
+ """Test that TCompactProtocolAccelerated throws an EOFError when it reaches the end of the stream"""
+ self.eofTestHelper(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False))
+ self.eofTestHelperStress(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ suite.addTest(loader.loadTestsFromTestCase(TestEof))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/src/jaegertracing/thrift/test/py/TestFrozen.py b/src/jaegertracing/thrift/test/py/TestFrozen.py
new file mode 100755
index 000000000..6d2595cf2
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestFrozen.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty, Wrapper
+from thrift.Thrift import TFrozenDict
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol, TCompactProtocol
+import collections
+import unittest
+
+
+class TestFrozenBase(unittest.TestCase):
+ def _roundtrip(self, src, dst):
+ otrans = TTransport.TMemoryBuffer()
+ optoro = self.protocol(otrans)
+ src.write(optoro)
+ itrans = TTransport.TMemoryBuffer(otrans.getvalue())
+ iproto = self.protocol(itrans)
+ return dst.read(iproto) or dst
+
+ def test_dict_is_hashable_only_after_frozen(self):
+ d0 = {}
+ self.assertFalse(isinstance(d0, collections.Hashable))
+ d1 = TFrozenDict(d0)
+ self.assertTrue(isinstance(d1, collections.Hashable))
+
+ def test_struct_with_collection_fields(self):
+ pass
+
+ def test_set(self):
+ """Test that annotated set field can be serialized and deserialized"""
+ x = CompactProtoTestStruct(set_byte_map={
+ frozenset([42, 100, -100]): 99,
+ frozenset([0]): 100,
+ frozenset([]): 0,
+ })
+ x2 = self._roundtrip(x, CompactProtoTestStruct())
+ self.assertEqual(x2.set_byte_map[frozenset([42, 100, -100])], 99)
+ self.assertEqual(x2.set_byte_map[frozenset([0])], 100)
+ self.assertEqual(x2.set_byte_map[frozenset([])], 0)
+
+ def test_map(self):
+ """Test that annotated map field can be serialized and deserialized"""
+ x = CompactProtoTestStruct(map_byte_map={
+ TFrozenDict({42: 42, 100: -100}): 99,
+ TFrozenDict({0: 0}): 100,
+ TFrozenDict({}): 0,
+ })
+ x2 = self._roundtrip(x, CompactProtoTestStruct())
+ self.assertEqual(x2.map_byte_map[TFrozenDict({42: 42, 100: -100})], 99)
+ self.assertEqual(x2.map_byte_map[TFrozenDict({0: 0})], 100)
+ self.assertEqual(x2.map_byte_map[TFrozenDict({})], 0)
+
+ def test_list(self):
+ """Test that annotated list field can be serialized and deserialized"""
+ x = CompactProtoTestStruct(list_byte_map={
+ (42, 100, -100): 99,
+ (0,): 100,
+ (): 0,
+ })
+ x2 = self._roundtrip(x, CompactProtoTestStruct())
+ self.assertEqual(x2.list_byte_map[(42, 100, -100)], 99)
+ self.assertEqual(x2.list_byte_map[(0,)], 100)
+ self.assertEqual(x2.list_byte_map[()], 0)
+
+ def test_empty_struct(self):
+ """Test that annotated empty struct can be serialized and deserialized"""
+ x = CompactProtoTestStruct(empty_struct_field=Empty())
+ x2 = self._roundtrip(x, CompactProtoTestStruct())
+ self.assertEqual(x2.empty_struct_field, Empty())
+
+ def test_struct(self):
+ """Test that annotated struct can be serialized and deserialized"""
+ x = Wrapper(foo=Empty())
+ self.assertEqual(x.foo, Empty())
+ x2 = self._roundtrip(x, Wrapper)
+ self.assertEqual(x2.foo, Empty())
+
+
+class TestFrozen(TestFrozenBase):
+ def protocol(self, trans):
+ return TBinaryProtocol.TBinaryProtocolFactory().getProtocol(trans)
+
+
+class TestFrozenAcceleratedBinary(TestFrozenBase):
+ def protocol(self, trans):
+ return TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(trans)
+
+
+class TestFrozenAcceleratedCompact(TestFrozenBase):
+ def protocol(self, trans):
+ return TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(trans)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ suite.addTest(loader.loadTestsFromTestCase(TestFrozen))
+ suite.addTest(loader.loadTestsFromTestCase(TestFrozenAcceleratedBinary))
+ suite.addTest(loader.loadTestsFromTestCase(TestFrozenAcceleratedCompact))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/src/jaegertracing/thrift/test/py/TestRenderedDoubleConstants.py b/src/jaegertracing/thrift/test/py/TestRenderedDoubleConstants.py
new file mode 100644
index 000000000..20903d81b
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestRenderedDoubleConstants.py
@@ -0,0 +1,177 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import unittest
+
+from DoubleConstantsTest import constants
+
+#
+# In order to run the test under Windows. We need to create symbolic link
+# name 'thrift' to '../src' folder by using:
+#
+# mklink /D thrift ..\src
+#
+
+
+class TestRenderedDoubleConstants(unittest.TestCase):
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST = \
+ "failed to verify a double constant generated by Thrift (expected = %f, got = %f)"
+ ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_LIST_TEST =\
+ "failed to verify a list item by Thrift (expected = %f, got = %f)"
+ ASSERTION_MESSAGE_FOR_TYPE_CHECKS = "the rendered variable with name %s is not of double type"
+
+ # to make sure the variables inside Thrift files are generated correctly
+ def test_rendered_double_constants(self):
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1.0
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100.0
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807.0
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807.0
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT,
+ places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT,
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT,
+ places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT,
+ constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT,
+ places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT,
+ constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS,
+ constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE,
+ places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST, EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST))
+ self.assertAlmostEqual(
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE, places=7,
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST % (
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE,
+ constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST))
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST")
+ self.assertTrue(
+ isinstance(constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST, float),
+ msg=TestRenderedDoubleConstants.ASSERTION_MESSAGE_FOR_TYPE_CHECKS %
+ "DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST")
+
+ # to make sure the variables inside Thrift files are generated correctly
+ def test_rendered_double_list(self):
+ EXPECTED_DOUBLE_LIST = [1.0, -100.0, 100.0, 9223372036854775807.0, -9223372036854775807.0, 3.14159265359,
+ 1000000.1, -1000000.1, 1.7e+308, -1.7e+308, 9223372036854775816.43,
+ -9223372036854775816.43]
+ self.assertEqual(len(constants.DOUBLE_LIST_TEST), len(EXPECTED_DOUBLE_LIST))
+ for i, expectedValue in enumerate(EXPECTED_DOUBLE_LIST):
+ self.assertAlmostEqual(constants.DOUBLE_LIST_TEST[i], expectedValue, places=7)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ suite.addTest(loader.loadTestsFromTestCase(TestRenderedDoubleConstants))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/src/jaegertracing/thrift/test/py/TestServer.py b/src/jaegertracing/thrift/test/py/TestServer.py
new file mode 100755
index 000000000..d0a13e5f7
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestServer.py
@@ -0,0 +1,412 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+from __future__ import division
+import logging
+import os
+import signal
+import sys
+import time
+from optparse import OptionParser
+
+from util import local_libpath
+sys.path.insert(0, local_libpath())
+from thrift.protocol import TProtocol, TProtocolDecorator
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestHandler(object):
+ def testVoid(self):
+ if options.verbose > 1:
+ logging.info('testVoid()')
+
+ def testString(self, str):
+ if options.verbose > 1:
+ logging.info('testString(%s)' % str)
+ return str
+
+ def testBool(self, boolean):
+ if options.verbose > 1:
+ logging.info('testBool(%s)' % str(boolean).lower())
+ return boolean
+
+ def testByte(self, byte):
+ if options.verbose > 1:
+ logging.info('testByte(%d)' % byte)
+ return byte
+
+ def testI16(self, i16):
+ if options.verbose > 1:
+ logging.info('testI16(%d)' % i16)
+ return i16
+
+ def testI32(self, i32):
+ if options.verbose > 1:
+ logging.info('testI32(%d)' % i32)
+ return i32
+
+ def testI64(self, i64):
+ if options.verbose > 1:
+ logging.info('testI64(%d)' % i64)
+ return i64
+
+ def testDouble(self, dub):
+ if options.verbose > 1:
+ logging.info('testDouble(%f)' % dub)
+ return dub
+
+ def testBinary(self, thing):
+ if options.verbose > 1:
+ logging.info('testBinary()') # TODO: hex output
+ return thing
+
+ def testStruct(self, thing):
+ if options.verbose > 1:
+ logging.info('testStruct({%s, %s, %s, %s})' % (thing.string_thing, thing.byte_thing, thing.i32_thing, thing.i64_thing))
+ return thing
+
+ def testException(self, arg):
+ # if options.verbose > 1:
+ logging.info('testException(%s)' % arg)
+ if arg == 'Xception':
+ raise Xception(errorCode=1001, message=arg)
+ elif arg == 'TException':
+ raise TException(message='This is a TException')
+
+ def testMultiException(self, arg0, arg1):
+ if options.verbose > 1:
+ logging.info('testMultiException(%s, %s)' % (arg0, arg1))
+ if arg0 == 'Xception':
+ raise Xception(errorCode=1001, message='This is an Xception')
+ elif arg0 == 'Xception2':
+ raise Xception2(
+ errorCode=2002,
+ struct_thing=Xtruct(string_thing='This is an Xception2'))
+ return Xtruct(string_thing=arg1)
+
+ def testOneway(self, seconds):
+ if options.verbose > 1:
+ logging.info('testOneway(%d) => sleeping...' % seconds)
+ time.sleep(seconds / 3) # be quick
+ if options.verbose > 1:
+ logging.info('done sleeping')
+
+ def testNest(self, thing):
+ if options.verbose > 1:
+ logging.info('testNest(%s)' % thing)
+ return thing
+
+ def testMap(self, thing):
+ if options.verbose > 1:
+ logging.info('testMap(%s)' % thing)
+ return thing
+
+ def testStringMap(self, thing):
+ if options.verbose > 1:
+ logging.info('testStringMap(%s)' % thing)
+ return thing
+
+ def testSet(self, thing):
+ if options.verbose > 1:
+ logging.info('testSet(%s)' % thing)
+ return thing
+
+ def testList(self, thing):
+ if options.verbose > 1:
+ logging.info('testList(%s)' % thing)
+ return thing
+
+ def testEnum(self, thing):
+ if options.verbose > 1:
+ logging.info('testEnum(%s)' % thing)
+ return thing
+
+ def testTypedef(self, thing):
+ if options.verbose > 1:
+ logging.info('testTypedef(%s)' % thing)
+ return thing
+
+ def testMapMap(self, thing):
+ if options.verbose > 1:
+ logging.info('testMapMap(%s)' % thing)
+ return {
+ -4: {
+ -4: -4,
+ -3: -3,
+ -2: -2,
+ -1: -1,
+ },
+ 4: {
+ 4: 4,
+ 3: 3,
+ 2: 2,
+ 1: 1,
+ },
+ }
+
+ def testInsanity(self, argument):
+ if options.verbose > 1:
+ logging.info('testInsanity(%s)' % argument)
+ return {
+ 1: {
+ 2: argument,
+ 3: argument,
+ },
+ 2: {6: Insanity()},
+ }
+
+ def testMulti(self, arg0, arg1, arg2, arg3, arg4, arg5):
+ if options.verbose > 1:
+ logging.info('testMulti(%s)' % [arg0, arg1, arg2, arg3, arg4, arg5])
+ return Xtruct(string_thing='Hello2',
+ byte_thing=arg0, i32_thing=arg1, i64_thing=arg2)
+
+
+class SecondHandler(object):
+ def secondtestString(self, argument):
+ return "testString(\"" + argument + "\")"
+
+
+# LAST_SEQID is a global because we have one transport and multiple protocols
+# running on it (when multiplexed)
+LAST_SEQID = None
+
+
+class TPedanticSequenceIdProtocolWrapper(TProtocolDecorator.TProtocolDecorator):
+ """
+ Wraps any protocol with sequence ID checking: looks for outbound
+ uniqueness as well as request/response alignment.
+ """
+ def __init__(self, protocol):
+ # TProtocolDecorator.__new__ does all the heavy lifting
+ pass
+
+ def readMessageBegin(self):
+ global LAST_SEQID
+ (name, type, seqid) =\
+ super(TPedanticSequenceIdProtocolWrapper, self).readMessageBegin()
+ if LAST_SEQID is not None and LAST_SEQID == seqid:
+ raise TProtocol.TProtocolException(
+ TProtocol.TProtocolException.INVALID_DATA,
+ "We received the same seqid {0} twice in a row".format(seqid))
+ LAST_SEQID = seqid
+ return (name, type, seqid)
+
+
+def make_pedantic(proto):
+ """ Wrap a protocol in the pedantic sequence ID wrapper. """
+ # NOTE: this is disabled for now as many clients send seqid
+ # of zero and that is okay, need a way to identify
+ # clients that MUST send seqid unique to function right
+ # or just force all implementations to send unique seqids (preferred)
+ return proto # TPedanticSequenceIdProtocolWrapper(proto)
+
+
+class TPedanticSequenceIdProtocolFactory(TProtocol.TProtocolFactory):
+ def __init__(self, encapsulated):
+ super(TPedanticSequenceIdProtocolFactory, self).__init__()
+ self.encapsulated = encapsulated
+
+ def getProtocol(self, trans):
+ return make_pedantic(self.encapsulated.getProtocol(trans))
+
+
+def main(options):
+ # common header allowed client types
+ allowed_client_types = [
+ THeaderTransport.THeaderClientType.HEADERS,
+ THeaderTransport.THeaderClientType.FRAMED_BINARY,
+ THeaderTransport.THeaderClientType.UNFRAMED_BINARY,
+ THeaderTransport.THeaderClientType.FRAMED_COMPACT,
+ THeaderTransport.THeaderClientType.UNFRAMED_COMPACT,
+ ]
+
+ # set up the protocol factory form the --protocol option
+ prot_factories = {
+ 'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory(),
+ 'multia': TBinaryProtocol.TBinaryProtocolAcceleratedFactory(),
+ 'accelc': TCompactProtocol.TCompactProtocolAcceleratedFactory(),
+ 'multiac': TCompactProtocol.TCompactProtocolAcceleratedFactory(),
+ 'binary': TPedanticSequenceIdProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory()),
+ 'multi': TPedanticSequenceIdProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory()),
+ 'compact': TCompactProtocol.TCompactProtocolFactory(),
+ 'multic': TCompactProtocol.TCompactProtocolFactory(),
+ 'header': THeaderProtocol.THeaderProtocolFactory(allowed_client_types),
+ 'multih': THeaderProtocol.THeaderProtocolFactory(allowed_client_types),
+ 'json': TJSONProtocol.TJSONProtocolFactory(),
+ 'multij': TJSONProtocol.TJSONProtocolFactory(),
+ }
+ pfactory = prot_factories.get(options.proto, None)
+ if pfactory is None:
+ raise AssertionError('Unknown --protocol option: %s' % options.proto)
+ try:
+ pfactory.string_length_limit = options.string_limit
+ pfactory.container_length_limit = options.container_limit
+ except Exception:
+ # Ignore errors for those protocols that does not support length limit
+ pass
+
+ # get the server type (TSimpleServer, TNonblockingServer, etc...)
+ if len(args) > 1:
+ raise AssertionError('Only one server type may be specified, not multiple types.')
+ server_type = args[0]
+ if options.trans == 'http':
+ server_type = 'THttpServer'
+
+ # Set up the handler and processor objects
+ handler = TestHandler()
+ processor = ThriftTest.Processor(handler)
+
+ if options.proto.startswith('multi'):
+ secondHandler = SecondHandler()
+ secondProcessor = SecondService.Processor(secondHandler)
+
+ multiplexedProcessor = TMultiplexedProcessor()
+ multiplexedProcessor.registerDefault(processor)
+ multiplexedProcessor.registerProcessor('ThriftTest', processor)
+ multiplexedProcessor.registerProcessor('SecondService', secondProcessor)
+ processor = multiplexedProcessor
+
+ global server
+
+ # Handle THttpServer as a special case
+ if server_type == 'THttpServer':
+ if options.ssl:
+ __certfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "server.crt")
+ __keyfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "server.key")
+ server = THttpServer.THttpServer(processor, ('', options.port), pfactory, cert_file=__certfile, key_file=__keyfile)
+ else:
+ server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
+ server.serve()
+ sys.exit(0)
+
+ # set up server transport and transport factory
+
+ abs_key_path = os.path.join(os.path.dirname(SCRIPT_DIR), 'keys', 'server.pem')
+
+ host = None
+ if options.ssl:
+ from thrift.transport import TSSLSocket
+ transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path)
+ else:
+ transport = TSocket.TServerSocket(host, options.port)
+ tfactory = TTransport.TBufferedTransportFactory()
+ if options.trans == 'buffered':
+ tfactory = TTransport.TBufferedTransportFactory()
+ elif options.trans == 'framed':
+ tfactory = TTransport.TFramedTransportFactory()
+ elif options.trans == '':
+ raise AssertionError('Unknown --transport option: %s' % options.trans)
+ else:
+ tfactory = TTransport.TBufferedTransportFactory()
+ # if --zlib, then wrap server transport, and use a different transport factory
+ if options.zlib:
+ transport = TZlibTransport.TZlibTransport(transport) # wrap with zlib
+ tfactory = TZlibTransport.TZlibTransportFactory()
+
+ # do server-specific setup here:
+ if server_type == "TNonblockingServer":
+ server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
+ elif server_type == "TProcessPoolServer":
+ import signal
+ from thrift.server import TProcessPoolServer
+ server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
+ server.setNumWorkers(5)
+
+ def set_alarm():
+ def clean_shutdown(signum, frame):
+ for worker in server.workers:
+ if options.verbose > 0:
+ logging.info('Terminating worker: %s' % worker)
+ worker.terminate()
+ if options.verbose > 0:
+ logging.info('Requesting server to stop()')
+ try:
+ server.stop()
+ except Exception:
+ pass
+ signal.signal(signal.SIGALRM, clean_shutdown)
+ signal.alarm(4)
+ set_alarm()
+ else:
+ # look up server class dynamically to instantiate server
+ ServerClass = getattr(TServer, server_type)
+ server = ServerClass(processor, transport, tfactory, pfactory)
+ # enter server main loop
+ server.serve()
+
+
+def exit_gracefully(signum, frame):
+ print("SIGINT received\n")
+ server.shutdown() # doesn't work properly, yet
+ sys.exit(0)
+
+
+if __name__ == '__main__':
+ signal.signal(signal.SIGINT, exit_gracefully)
+
+ parser = OptionParser()
+ parser.add_option('--libpydir', type='string', dest='libpydir',
+ help='include this directory to sys.path for locating library code')
+ parser.add_option('--genpydir', type='string', dest='genpydir',
+ default='gen-py',
+ help='include this directory to sys.path for locating generated code')
+ parser.add_option("--port", type="int", dest="port",
+ help="port number for server to listen on")
+ parser.add_option("--zlib", action="store_true", dest="zlib",
+ help="use zlib wrapper for compressed transport")
+ parser.add_option("--ssl", action="store_true", dest="ssl",
+ help="use SSL for encrypted transport")
+ parser.add_option('-v', '--verbose', action="store_const",
+ dest="verbose", const=2,
+ help="verbose output")
+ parser.add_option('-q', '--quiet', action="store_const",
+ dest="verbose", const=0,
+ help="minimal output")
+ parser.add_option('--protocol', dest="proto", type="string",
+ help="protocol to use, one of: accel, accelc, binary, compact, json, multi, multia, multiac, multic, multih, multij")
+ parser.add_option('--transport', dest="trans", type="string",
+ help="transport to use, one of: buffered, framed, http")
+ parser.add_option('--container-limit', dest='container_limit', type='int', default=None)
+ parser.add_option('--string-limit', dest='string_limit', type='int', default=None)
+ parser.set_defaults(port=9090, verbose=1, proto='binary', transport='buffered')
+ options, args = parser.parse_args()
+
+ # Print TServer log to stdout so that the test-runner can redirect it to log files
+ logging.basicConfig(level=options.verbose)
+
+ sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
+
+ from ThriftTest import ThriftTest, SecondService
+ from ThriftTest.ttypes import Xtruct, Xception, Xception2, Insanity
+ from thrift.Thrift import TException
+ from thrift.TMultiplexedProcessor import TMultiplexedProcessor
+ from thrift.transport import THeaderTransport
+ from thrift.transport import TTransport
+ from thrift.transport import TSocket
+ from thrift.transport import TZlibTransport
+ from thrift.protocol import TBinaryProtocol
+ from thrift.protocol import TCompactProtocol
+ from thrift.protocol import THeaderProtocol
+ from thrift.protocol import TJSONProtocol
+ from thrift.server import TServer, TNonblockingServer, THttpServer
+
+ sys.exit(main(options))
diff --git a/src/jaegertracing/thrift/test/py/TestSocket.py b/src/jaegertracing/thrift/test/py/TestSocket.py
new file mode 100755
index 000000000..619eb1002
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestSocket.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.transport import TSocket
+import unittest
+import time
+import socket
+import random
+
+
+class TimeoutTest(unittest.TestCase):
+ def setUp(self):
+ for i in range(50):
+ try:
+ # find a port we can use
+ self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.port = random.randint(10000, 30000)
+ self.listen_sock.bind(('localhost', self.port))
+ self.listen_sock.listen(5)
+ break
+ except Exception:
+ if i == 49:
+ raise
+
+ def testConnectTimeout(self):
+ starttime = time.time()
+
+ try:
+ leaky = []
+ for i in range(100):
+ socket = TSocket.TSocket('localhost', self.port)
+ socket.setTimeout(10)
+ socket.open()
+ leaky.append(socket)
+ except Exception:
+ self.assert_(time.time() - starttime < 5.0)
+
+ def testWriteTimeout(self):
+ starttime = time.time()
+
+ try:
+ socket = TSocket.TSocket('localhost', self.port)
+ socket.setTimeout(10)
+ socket.open()
+ lsock = self.listen_sock.accept()
+ while True:
+ lsock.write("hi" * 100)
+
+ except Exception:
+ self.assert_(time.time() - starttime < 5.0)
+
+
+if __name__ == '__main__':
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+
+ suite.addTest(loader.loadTestsFromTestCase(TimeoutTest))
+
+ testRunner = unittest.TextTestRunner(verbosity=2)
+ testRunner.run(suite)
diff --git a/src/jaegertracing/thrift/test/py/TestSyntax.py b/src/jaegertracing/thrift/test/py/TestSyntax.py
new file mode 100755
index 000000000..dbe7975e2
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/TestSyntax.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Just import these generated files to make sure they are syntactically valid
+from DebugProtoTest import EmptyService # noqa
+from DebugProtoTest import Inherited # noqa
diff --git a/src/jaegertracing/thrift/test/py/explicit_module/runtest.sh b/src/jaegertracing/thrift/test/py/explicit_module/runtest.sh
new file mode 100755
index 000000000..6d7346283
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/explicit_module/runtest.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+rm -rf gen-py
+../../../compiler/cpp/thrift --gen py test1.thrift || exit 1
+../../../compiler/cpp/thrift --gen py test2.thrift || exit 1
+../../../compiler/cpp/thrift --gen py test3.thrift && exit 1 # Fail since test3.thrift has python keywords
+PYTHONPATH=./gen-py python -c 'import foo.bar.baz' || exit 1
+PYTHONPATH=./gen-py python -c 'import test2' || exit 1
+PYTHONPATH=./gen-py python -c 'import test1' &>/dev/null && exit 1 # Should fail.
+cp -r gen-py simple
+../../../compiler/cpp/thrift -r --gen py test2.thrift || exit 1
+PYTHONPATH=./gen-py python -c 'import test2' || exit 1
+diff -ur simple gen-py > thediffs
+file thediffs | grep -s -q empty || exit 1
+rm -rf simple thediffs
+echo 'All tests pass!'
diff --git a/src/jaegertracing/thrift/test/py/explicit_module/test1.thrift b/src/jaegertracing/thrift/test/py/explicit_module/test1.thrift
new file mode 100644
index 000000000..ec600d7d4
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/explicit_module/test1.thrift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace py foo.bar.baz
+
+struct astruct {
+ 1: i32 how_unoriginal;
+}
diff --git a/src/jaegertracing/thrift/test/py/explicit_module/test2.thrift b/src/jaegertracing/thrift/test/py/explicit_module/test2.thrift
new file mode 100644
index 000000000..68f9da4dd
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/explicit_module/test2.thrift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+include "test1.thrift"
+
+struct another {
+ 1: test1.astruct something;
+}
diff --git a/src/jaegertracing/thrift/test/py/explicit_module/test3.thrift b/src/jaegertracing/thrift/test/py/explicit_module/test3.thrift
new file mode 100644
index 000000000..154786bf8
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/explicit_module/test3.thrift
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace py validations
+
+struct from {
+ 1: i32 def;
+}
+
diff --git a/src/jaegertracing/thrift/test/py/generate.cmake b/src/jaegertracing/thrift/test/py/generate.cmake
new file mode 100644
index 000000000..4ed14cc52
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/generate.cmake
@@ -0,0 +1,36 @@
+macro(GENERATE FILENAME GENERATOR OUTPUTDIR)
+ file(MAKE_DIRECTORY ${MY_CURRENT_BINARY_DIR}/${OUTPUTDIR})
+ execute_process(COMMAND ${THRIFTCOMPILER} --gen ${GENERATOR} -out ${MY_CURRENT_BINARY_DIR}/${OUTPUTDIR} ${FILENAME}
+ RESULT_VARIABLE CMD_RESULT)
+ if(CMD_RESULT)
+ message(FATAL_ERROR "Error generating ${FILENAME} with generator ${GENERATOR}")
+ endif()
+endmacro(GENERATE)
+
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:dynamic,slots gen-py-dynamicslots)
+
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:dynamic,slots gen-py-dynamicslots)
+
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/DoubleConstantsTest.thrift py:dynamic,slots gen-py-dynamicslots)
+
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/Recursive.thrift py:dynamic,slots gen-py-dynamicslots)
diff --git a/src/jaegertracing/thrift/test/py/setup.cfg b/src/jaegertracing/thrift/test/py/setup.cfg
new file mode 100644
index 000000000..7da1f9608
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/src/jaegertracing/thrift/test/py/util.py b/src/jaegertracing/thrift/test/py/util.py
new file mode 100644
index 000000000..c2b3f5cba
--- /dev/null
+++ b/src/jaegertracing/thrift/test/py/util.py
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import glob
+import os
+import sys
+
+_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+_ROOT_DIR = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
+
+
+def local_libpath():
+ globdir = os.path.join(_ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
+ for libpath in glob.glob(globdir):
+ if libpath.endswith('-%d.%d' % (sys.version_info[0], sys.version_info[1])):
+ return libpath