THRIFT-847 Test Framework harmonization across all languages THRIFT-2946 Enhance usability of cross test framework
Patch: Nobuaki Sukegawa This closes: #358 Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/41ad4342 Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/41ad4342 Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/41ad4342 Branch: refs/heads/master Commit: 41ad4342c5a0389ab2cf2dbf098086413ac01204 Parents: a2d12b6 Author: Roger Meier <[email protected]> Authored: Tue Mar 24 22:30:40 2015 +0100 Committer: Roger Meier <[email protected]> Committed: Tue Mar 24 22:30:40 2015 +0100 ---------------------------------------------------------------------- .gitignore | 1 + .travis.yml | 14 +- Makefile.am | 10 +- configure.ac | 1 + lib/Makefile.am | 4 + lib/csharp/Makefile.am | 5 + lib/csharp/test/ThriftTest/Makefile.am | 32 ++ lib/csharp/test/ThriftTest/Program.cs | 9 +- lib/csharp/test/ThriftTest/TestClient.cs | 11 +- lib/csharp/test/ThriftTest/TestServer.cs | 11 +- lib/go/Makefile.am | 3 +- lib/go/test/Makefile.am | 4 +- lib/haxe/test/Makefile.am | 2 +- lib/java/Makefile.am | 5 + lib/nodejs/Makefile.am | 10 +- test/Makefile.am | 4 + test/c_glib/Makefile.am | 4 +- test/cpp/Makefile.am | 2 + test/crossrunner/__init__.py | 25 + test/crossrunner/collect.py | 136 ++++++ test/crossrunner/prepare.py | 55 +++ test/crossrunner/report.py | 395 +++++++++++++++ test/crossrunner/run.py | 317 ++++++++++++ test/crossrunner/test.py | 136 ++++++ test/crossrunner/util.py | 31 ++ test/erl/Makefile.am | 2 +- test/go/Makefile.am | 6 +- test/go/src/bin/testserver/main.go | 3 +- test/go/src/common/clientserver_test.go | 2 +- test/go/src/common/server.go | 1 + test/haxe/Makefile.am | 2 +- test/hs/Makefile.am | 4 +- test/known_failures_Linux.json | 664 ++++++++++++++++++++++++++ test/perl/Makefile.am | 4 +- test/perl/TestClient.pl | 5 + test/php/Makefile.am | 4 +- test/php/TestClient.php | 8 + test/py.twisted/Makefile.am | 2 +- test/py/Makefile.am | 18 +- test/rb/Makefile.am | 6 +- test/result.html | 88 +++- test/test.py | 360 ++++---------- test/tests.json | 622 +++++++++++++----------- 43 files changed, 2416 insertions(+), 612 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index 228c6ac..a3062d8 100644 --- a/.gitignore +++ b/.gitignore @@ -220,6 +220,7 @@ test-driver /node_modules/ /stamp-h1 /test/status.html +/test/results.json /test/c_glib/test_client /test/c_glib/test_server /test/cpp/StressTest http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml index a5c728e..bb45961 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,11 +55,17 @@ env: matrix: # Put it here because it's most time consuming - - TEST_NAME="make cross (gcc, automake)" + - TEST_NAME="make cross (automake)" CONFIG="--without-python" ALL_DEPS="yes" MAKE_TARGET="cross" + - TEST_NAME="make cross-py (automake)" + THRIFT_CROSSTEST_CONCURRENCY=6 + CONFIG="--enable-tutorial=no --without-erlang --without-lua --without-haxe --without-d" + ALL_DEPS="yes" + MAKE_TARGET="cross2" + # CMake builds - TEST_NAME="compiler (CMake + CPack)" CMAKE_CONFIG="-DBUILD_COMPILER=ON -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF" @@ -86,7 +92,7 @@ env: - TEST_NAME="Small Set (automake)" CONFIG="--without-erlang --without-haskell --without-python --without-go --without-lua --without-d --without-ruby --without-nodejs --without-java" ALL_DEPS="yes" - - TEST_NAME="dist (gcc, automake)" + - TEST_NAME="dist (automake)" CONFIG="" ALL_DEPS="yes" MAKE_TARGET="dist" @@ -99,13 +105,13 @@ matrix: exclude: # This one takes very long - compiler: gcc - env: TEST_NAME="make cross (gcc, automake)" CONFIG="--without-python" ALL_DEPS="yes" MAKE_TARGET="cross" + env: TEST_NAME="make cross (automake)" CONFIG="--without-python" ALL_DEPS="yes" MAKE_TARGET="cross" # Does not use native compiler, no need to do it twice - compiler: gcc env: TEST_NAME="compiler (mingw32-gcc, CMake + CPack)" CMAKE_CONFIG="-DCMAKE_TOOLCHAIN_FILE=../contrib/mingw32-toolchain.cmake -DBUILD_COMPILER=ON -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF" - compiler: gcc - env: TEST_NAME="dist (gcc, automake)" CONFIG="" ALL_DEPS="yes" MAKE_TARGET="dist" + env: TEST_NAME="dist (automake)" CONFIG="" ALL_DEPS="yes" MAKE_TARGET="dist" include: - env: http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/Makefile.am ---------------------------------------------------------------------- diff --git a/Makefile.am b/Makefile.am index 0a9e431..bbe6f80 100755 --- a/Makefile.am +++ b/Makefile.am @@ -37,10 +37,18 @@ dist-hook: print-version: @echo $(VERSION) +.PHONY: precross cross cross2 +precross-%: all + $(MAKE) -C $* precross +precross: all precross-test precross-lib -cross: check +cross: precross sh test/test.sh +# TODO: generate --server and --client switches from "--with(out)-..." build flags +cross2: precross + python test/test.py -s + codespell_skip_files = \ *.jar \ *.class \ http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index 6b26de5..1eaa359 100755 --- a/configure.ac +++ b/configure.ac @@ -692,6 +692,7 @@ AC_CONFIG_FILES([ lib/c_glib/thrift_c_glib.pc lib/c_glib/test/Makefile lib/csharp/Makefile + lib/csharp/test/ThriftTest/Makefile lib/d/Makefile lib/d/test/Makefile lib/erl/Makefile http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/Makefile.am b/lib/Makefile.am index 7b235d0..aa8b159 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -91,3 +91,7 @@ EXTRA_DIST = \ ocaml \ st \ ts + +precross-%: + $(MAKE) -C $* precross +precross: precross-nodejs precross-csharp precross-java http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/csharp/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/csharp/Makefile.am b/lib/csharp/Makefile.am index 1c75aa1..5ce4276 100644 --- a/lib/csharp/Makefile.am +++ b/lib/csharp/Makefile.am @@ -17,6 +17,8 @@ # under the License. # +SUBDIRS = . test/ThriftTest + THRIFTCODE= \ src/Collections/THashSet.cs \ src/Collections/TCollections.cs \ @@ -81,6 +83,9 @@ Thrift.dll: $(THRIFTCODE) clean-local: $(RM) Thrift.dll +precross: + $(MAKE) -C test/ThriftTest precross + # run csharp tests? # check: # cd test/ThriftTest && ./maketest.sh http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/csharp/test/ThriftTest/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/csharp/test/ThriftTest/Makefile.am b/lib/csharp/test/ThriftTest/Makefile.am new file mode 100644 index 0000000..fcde6fc --- /dev/null +++ b/lib/csharp/test/ThriftTest/Makefile.am @@ -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. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift +CSC=gmcs + +stubs: $(top_srcdir)/test/ThriftTest.thrift + $(THRIFT) --gen csharp -o . $(top_srcdir)/test/ThriftTest.thrift + +precross: TestClientServer.exe + +ThriftImpl.dll: stubs + $(CSC) /t:library /out:./ThriftImpl.dll /recurse:./gen-csharp/* /reference:../../Thrift.dll + +TestClientServer.exe: TestClient.cs TestServer.cs Program.cs ThriftImpl.dll + $(CSC) /out:TestClientServer.exe /reference:../../Thrift.dll /reference:ThriftImpl.dll TestClient.cs TestServer.cs Program.cs http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/csharp/test/ThriftTest/Program.cs ---------------------------------------------------------------------- diff --git a/lib/csharp/test/ThriftTest/Program.cs b/lib/csharp/test/ThriftTest/Program.cs index 3bf6796..5a4245b 100644 --- a/lib/csharp/test/ThriftTest/Program.cs +++ b/lib/csharp/test/ThriftTest/Program.cs @@ -31,12 +31,12 @@ namespace Test { class Program { - static void Main(string[] args) + static int Main(string[] args) { if (args.Length == 0) { Console.WriteLine("must provide 'server' or 'client' arg"); - return; + return -1; } string[] subArgs = new string[args.Length - 1]; @@ -46,16 +46,17 @@ namespace Test } if (args[0] == "client") { - TestClient.Execute(subArgs); + return TestClient.Execute(subArgs) ? 0 : 1; } else if (args[0] == "server") { - TestServer.Execute(subArgs); + return TestServer.Execute(subArgs) ? 0 : 1; } else { Console.WriteLine("first argument must be 'server' or 'client'"); } + return 0; } } } http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/csharp/test/ThriftTest/TestClient.cs ---------------------------------------------------------------------- diff --git a/lib/csharp/test/ThriftTest/TestClient.cs b/lib/csharp/test/ThriftTest/TestClient.cs index a0ceb15..ec0696a 100644 --- a/lib/csharp/test/ThriftTest/TestClient.cs +++ b/lib/csharp/test/ThriftTest/TestClient.cs @@ -32,7 +32,7 @@ namespace Test private static int numIterations = 1; private static string protocol = ""; - public static void Execute(string[] args) + public static bool Execute(string[] args) { try { @@ -41,6 +41,7 @@ namespace Test string url = null, pipe = null; int numThreads = 1; bool buffered = false, framed = false, encrypted = false; + string certPath = "../../../../../keys/server.pem"; try { @@ -96,6 +97,10 @@ namespace Test encrypted = true; Console.WriteLine("Using encrypted transport"); } + else if (args[i].StartsWith("--cert=")) + { + certPath = args[i].Substring("--cert=".Length); + } } } catch (Exception e) @@ -119,7 +124,7 @@ namespace Test else { if (encrypted) - trans = new TTLSSocket(host, port, "../../../../../keys/client.pem"); + trans = new TTLSSocket(host, port, certPath); else trans = new TSocket(host, port); } @@ -151,10 +156,12 @@ namespace Test catch (Exception outerEx) { Console.WriteLine(outerEx.Message + " ST: " + outerEx.StackTrace); + return false; } Console.WriteLine(); Console.WriteLine(); + return true; } public static void ClientThread(object obj) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/csharp/test/ThriftTest/TestServer.cs ---------------------------------------------------------------------- diff --git a/lib/csharp/test/ThriftTest/TestServer.cs b/lib/csharp/test/ThriftTest/TestServer.cs index 2096cf8..0e9fe05 100644 --- a/lib/csharp/test/ThriftTest/TestServer.cs +++ b/lib/csharp/test/ThriftTest/TestServer.cs @@ -358,13 +358,14 @@ namespace Test } // class TestHandler - public static void Execute(string[] args) + public static bool Execute(string[] args) { try { bool useBufferedSockets = false, useFramed = false, useEncryption = false, compact = false, json = false; int port = 9090; string pipe = null; + string certPath = "../../../../../keys/server.pem"; for (int i = 0; i < args.Length; i++) { if (args[i] == "-pipe") // -pipe name @@ -395,6 +396,10 @@ namespace Test { useEncryption = true; } + else if (args[i].StartsWith("--cert=")) + { + certPath = args[i].Substring("--cert=".Length); + } } // Processor @@ -411,7 +416,7 @@ namespace Test { if (useEncryption) { - trans = new TTLSServerSocket(port, 0, useBufferedSockets, new X509Certificate2("../../../../../keys/server.pem")); + trans = new TTLSServerSocket(port, 0, useBufferedSockets, new X509Certificate2(certPath)); } else { @@ -461,8 +466,10 @@ namespace Test catch (Exception x) { Console.Error.Write(x); + return false; } Console.WriteLine("done."); + return true; } } } http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/go/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/go/Makefile.am b/lib/go/Makefile.am index be2a2e5..bf173ba 100644 --- a/lib/go/Makefile.am +++ b/lib/go/Makefile.am @@ -33,7 +33,8 @@ install: check-local: $(GO) test ./thrift -all-local: check-local +all-local: + $(GO) build ./thrift EXTRA_DIST = \ thrift \ http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/go/test/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/go/test/Makefile.am b/lib/go/test/Makefile.am index ef61249..38a0968 100644 --- a/lib/go/test/Makefile.am +++ b/lib/go/test/Makefile.am @@ -17,11 +17,11 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift -out gopath/src/ --gen go:thrift_import=thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift # Thrift for GO has problems with complex map keys: THRIFT-2063 -gopath: $(top_srcdir)/compiler/cpp/thrift $(THRIFTTEST) \ +gopath: $(THRIFT) $(THRIFTTEST) \ IncludesTest.thrift \ NamespacedTest.thrift \ MultiplexedProtocolTest.thrift \ http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/haxe/test/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/haxe/test/Makefile.am b/lib/haxe/test/Makefile.am index 13b4266..91cfc90 100644 --- a/lib/haxe/test/Makefile.am +++ b/lib/haxe/test/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift THRIFTCMD = $(THRIFT) --gen haxe -r THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift AGGR = $(top_srcdir)/contrib/async-test/aggr.thrift http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/java/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/java/Makefile.am b/lib/java/Makefile.am index cbec7af..63d40a6 100644 --- a/lib/java/Makefile.am +++ b/lib/java/Makefile.am @@ -19,6 +19,8 @@ export CLASSPATH +THRIFT = $(top_builddir)/compiler/cpp/thrift + all-local: $(ANT) $(ANT_FLAGS) @@ -31,6 +33,9 @@ clean-local: ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ $$ANT $(ANT_FLAGS) clean +precross: $(THRIFT) + $(ANT) $(ANT_FLAGS) compile-test + check-local: all $(ANT) $(ANT_FLAGS) test http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/lib/nodejs/Makefile.am ---------------------------------------------------------------------- diff --git a/lib/nodejs/Makefile.am b/lib/nodejs/Makefile.am index 9998348..b8e441b 100755 --- a/lib/nodejs/Makefile.am +++ b/lib/nodejs/Makefile.am @@ -16,14 +16,18 @@ # under the License. -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift -#stubs: $(top_srcdir)/test/ThriftTest.thrift -# $(THRIFT) --gen js:node -o test/ $(top_srcdir)/test/ThriftTest.thrift +stubs: $(top_srcdir)/test/ThriftTest.thrift + $(THRIFT) --gen js:node -o test/ $(top_srcdir)/test/ThriftTest.thrift deps: $(top_srcdir)/package.json $(NPM) install --no-bin-links $(top_srcdir)/ +all-local: deps + +precross: deps stubs + check: deps cd $(top_srcdir) && $(NPM) test && cd lib/nodejs http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/Makefile.am ---------------------------------------------------------------------- diff --git a/test/Makefile.am b/test/Makefile.am index 23ec498..5bd2d0e 100755 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -104,3 +104,7 @@ EXTRA_DIST = \ ThriftTest.thrift \ FastbinaryTest.py \ README.md + +precross-%: + $(MAKE) -C $* precross +precross: precross-py precross-rb precross-c_glib precross-cpp precross-perl precross-php precross-go http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/c_glib/Makefile.am ---------------------------------------------------------------------- diff --git a/test/c_glib/Makefile.am b/test/c_glib/Makefile.am index 252edb9..9412415 100755 --- a/test/c_glib/Makefile.am +++ b/test/c_glib/Makefile.am @@ -29,6 +29,8 @@ nodist_libtestcglib_la_SOURCES = \ libtestcglib_la_LIBADD = $(top_builddir)/lib/c_glib/libthrift_c_glib.la +precross: test_client test_server + check_PROGRAMS = \ test_client \ test_server @@ -54,7 +56,7 @@ test_server_LDADD = \ # THRIFT = $(top_builddir)/compiler/cpp/thrift -gen-c_glib/t_test_second_service.c gen-c_glib/t_test_second_service.h gen-c_glib/t_test_thrift_test.c gen-c_glib/t_test_thrift_test.h gen-c_glib/t_test_thrift_test_types.c gen-c_glib/t_test_thrift_test_types.h: $(top_srcdir)/test/ThriftTest.thrift +gen-c_glib/t_test_second_service.c gen-c_glib/t_test_second_service.h gen-c_glib/t_test_thrift_test.c gen-c_glib/t_test_thrift_test.h gen-c_glib/t_test_thrift_test_types.c gen-c_glib/t_test_thrift_test_types.h: $(top_srcdir)/test/ThriftTest.thrift $(THRIFT) $(THRIFT) --gen c_glib -r $< AM_CFLAGS = -g -Wall -Wextra $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/cpp/Makefile.am ---------------------------------------------------------------------- diff --git a/test/cpp/Makefile.am b/test/cpp/Makefile.am index 7d57f5c..89fed8f 100755 --- a/test/cpp/Makefile.am +++ b/test/cpp/Makefile.am @@ -49,6 +49,8 @@ nodist_libstresstestgencpp_la_SOURCES = \ libstresstestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la +precross: TestServer TestClient + check_PROGRAMS = \ TestServer \ TestClient \ http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/__init__.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/__init__.py b/test/crossrunner/__init__.py new file mode 100644 index 0000000..06de2d0 --- /dev/null +++ b/test/crossrunner/__init__.py @@ -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. +# + +from crossrunner.test import test_name +from crossrunner.collect import collect_tests +from crossrunner.run import TestDispatcher +from crossrunner.report import generate_known_failures +from crossrunner.report import load_known_failures +from crossrunner.prepare import prepare http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/collect.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/collect.py b/test/crossrunner/collect.py new file mode 100644 index 0000000..80a82e7 --- /dev/null +++ b/test/crossrunner/collect.py @@ -0,0 +1,136 @@ +# +# 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 platform +from itertools import product + +from crossrunner.util import merge_dict + +# Those keys are passed to execution as is. +# Note that there are keys other than these, namely: +# delay: After server is started, client start is delayed for the value +# (seconds). +# timeout: Test timeout after client is started (seconds). +# platforms: Supported platforms. Should match platform.system() value. +# protocols: list of supported protocols +# transports: list of supported transports +# sockets: list of supported sockets +VALID_JSON_KEYS = [ + 'name', # name of the library, typically a language name + 'workdir', # work directory where command is executed + 'command', # test command + 'extra_args', # args appended to command after other args are appended + 'join_args', # whether args should be passed as single concatenated string + 'env', # additional environmental variable +] + +DEFAULT_DELAY = 1 +DEFAULT_TIMEOUT = 5 + + +def collect_testlibs(config, server_match, client_match): + """Collects server/client configurations from library configurations""" + def expand_libs(config): + for lib in config: + sv = lib.pop('server', None) + cl = lib.pop('client', None) + yield lib, sv, cl + + def yield_testlibs(base_configs, configs, match): + for base, conf in zip(base_configs, configs): + if conf: + if not match or base['name'] in match: + platforms = conf.get('platforms') or base.get('platforms') + if not platforms or platform.system() in platforms: + yield merge_dict(base, conf) + + libs, svs, cls = zip(*expand_libs(config)) + servers = list(yield_testlibs(libs, svs, server_match)) + clients = list(yield_testlibs(libs, cls, client_match)) + return servers, clients + + +def do_collect_tests(servers, clients): + def intersection(key, o1, o2): + """intersection of two collections. + collections are replaced with sets the first time""" + def cached_set(o, key): + v = o[key] + if not isinstance(v, set): + v = set(v) + o[key] = v + return v + return cached_set(o1, key) & cached_set(o2, key) + + # each entry can be spec:impl (e.g. binary:accel) + def intersect_with_spec(key, o1, o2): + # store as set of (spec, impl) tuple + def cached_set(o): + def to_spec_impl_tuples(values): + for v in values: + spec, _, impl = v.partition(':') + yield spec, impl or spec + v = o[key] + if not isinstance(v, set): + v = set(to_spec_impl_tuples(set(v))) + o[key] = v + return v + for spec1, impl1 in cached_set(o1): + for spec2, impl2 in cached_set(o2): + if spec1 == spec2: + name = impl1 if impl1 == impl2 else '%s-%s' % (impl1, impl2) + yield name, impl1, impl2 + + def maybe_max(key, o1, o2, default): + """maximum of two if present, otherwise defult value""" + v1 = o1.get(key) + v2 = o2.get(key) + return max(v1, v2) if v1 and v2 else v1 or v2 or default + + def filter_with_validkeys(o): + ret = {} + for key in VALID_JSON_KEYS: + if key in o: + ret[key] = o[key] + return ret + + def merge_metadata(o, **ret): + for key in VALID_JSON_KEYS: + if key in o: + ret[key] = o[key] + return ret + + for sv, cl in product(servers, clients): + for proto, proto1, proto2 in intersect_with_spec('protocols', sv, cl): + for trans, trans1, trans2 in intersect_with_spec('transports', sv, cl): + for sock in intersection('sockets', sv, cl): + yield { + 'server': merge_metadata(sv, **{'protocol': proto1, 'transport': trans1}), + 'client': merge_metadata(cl, **{'protocol': proto2, 'transport': trans2}), + 'delay': maybe_max('delay', sv, cl, DEFAULT_DELAY), + 'timeout': maybe_max('timeout', sv, cl, DEFAULT_TIMEOUT), + 'protocol': proto, + 'transport': trans, + 'socket': sock + } + + +def collect_tests(tests_dict, server_match, client_match): + sv, cl = collect_testlibs(tests_dict, server_match, client_match) + return list(do_collect_tests(sv, cl)) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/prepare.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/prepare.py b/test/crossrunner/prepare.py new file mode 100644 index 0000000..6e4f6ee --- /dev/null +++ b/test/crossrunner/prepare.py @@ -0,0 +1,55 @@ +# +# 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 subprocess + +from crossrunner.collect import collect_testlibs + + +def prepare(config_dict, testdir, server_match, client_match): + libs, libs2 = collect_testlibs(config_dict, server_match, client_match) + libs.extend(libs2) + + def prepares(): + for lib in libs: + pre = lib.get('prepare') + if pre: + yield pre, lib['workdir'] + + def files(): + for lib in libs: + workdir = os.path.join(testdir, lib['workdir']) + for c in lib['command']: + if not c.startswith('-'): + p = os.path.join(workdir, c) + if not os.path.exists(p): + yield os.path.split(p) + + def make(p): + d, f = p + with open(os.devnull, 'w') as devnull: + return subprocess.Popen(['make', f], cwd=d, stderr=devnull) + + for pre, d in prepares(): + subprocess.Popen(pre, cwd=d).wait() + + for p in list(map(make, set(files()))): + p.wait() + return True http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/report.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/report.py b/test/crossrunner/report.py new file mode 100644 index 0000000..da478fa --- /dev/null +++ b/test/crossrunner/report.py @@ -0,0 +1,395 @@ +# +# 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 datetime +import json +import multiprocessing +import os +import platform +import re +import subprocess +import sys +import time +import traceback + +from crossrunner.test import TestEntry + +LOG_DIR = 'log' +RESULT_HTML = 'result.html' +RESULT_JSON = 'results.json' +FAIL_JSON = 'known_failures_%s.json' + + +def generate_known_failures(testdir, overwrite, save, out): + def collect_failures(results): + success_index = 5 + for r in results: + if not r[success_index]: + yield TestEntry.get_name(*r) + try: + with open(os.path.join(testdir, RESULT_JSON), 'r') as fp: + results = json.load(fp) + except IOError: + sys.stderr.write('Unable to load last result. Did you run tests ?\n') + return False + fails = collect_failures(results['results']) + if not overwrite: + known = load_known_failures(testdir) + known.extend(fails) + fails = known + fails_json = json.dumps(sorted(set(fails)), indent=2) + if save: + with open(os.path.join(testdir, FAIL_JSON % platform.system()), 'w+') as fp: + fp.write(fails_json) + sys.stdout.write('Successfully updated known failures.\n') + if out: + sys.stdout.write(fails_json) + sys.stdout.write('\n') + return True + + +def load_known_failures(testdir): + try: + with open(os.path.join(testdir, FAIL_JSON % platform.system()), 'r') as fp: + return json.load(fp) + except IOError: + return [] + + +class TestReporter(object): + # Unfortunately, standard library doesn't handle timezone well + # DATETIME_FORMAT = '%a %b %d %H:%M:%S %Z %Y' + DATETIME_FORMAT = '%a %b %d %H:%M:%S %Y' + + def __init__(self): + self._log = multiprocessing.get_logger() + self._lock = multiprocessing.Lock() + + @classmethod + def test_logfile(cls, dir, test_name, prog_kind): + return os.path.realpath(os.path.join( + dir, 'log', '%s_%s.log' % (test_name, prog_kind))) + + def _start(self): + self._start_time = time.time() + + @property + def _elapsed(self): + return time.time() - self._start_time + + @classmethod + def _format_date(cls): + return '%s' % datetime.datetime.now().strftime(cls.DATETIME_FORMAT) + + def _print_date(self): + self.out.write('%s\n' % self._format_date()) + + def _print_bar(self, out=None): + (out or self.out).write( + '======================================================================\n') + + def _print_exec_time(self): + self.out.write('Test execution took {:.1f} seconds.\n'.format(self._elapsed)) + + +class ExecReporter(TestReporter): + def __init__(self, testdir, test, prog): + super(ExecReporter, self).__init__() + self._test = test + self._prog = prog + self.logpath = self.test_logfile(testdir, test.name, prog.kind) + self.out = None + + def begin(self): + self._start() + self._open() + if self.out and not self.out.closed: + self._print_header() + else: + self._log.debug('Output stream is not available.') + + def end(self, returncode): + self._lock.acquire() + try: + if self.out and not self.out.closed: + self._print_footer(returncode) + self._close() + self.out = None + else: + self._log.debug('Output stream is not available.') + finally: + self._lock.release() + + def killed(self): + self._lock.acquire() + try: + if self.out and not self.out.closed: + self._print_footer() + self._close() + self.out = None + else: + self._log.debug('Output stream is not available.') + finally: + self._lock.release() + + _init_failure_exprs = { + 'server': list(map(re.compile, [ + '[Aa]ddress already in use', + 'Could not bind', + 'EADDRINUSE', + ])), + 'client': list(map(re.compile, [ + '[Cc]onnection refused', + 'Could not connect to localhost', + 'ECONNREFUSED', + 'No such file or directory', # domain socket + ])), + } + + def maybe_false_positive(self): + """Searches through log file for socket bind error. + Returns True if suspicious expression is found, otherwise False""" + def match(line): + for expr in exprs: + if expr.search(line): + return True + try: + if self.out and not self.out.closed: + self.out.flush() + exprs = list(map(re.compile, self._init_failure_exprs[self._prog.kind])) + + server_logfile = self.logpath + # need to handle unicode errors on Python 3 + kwargs = {} if sys.version_info[0] < 3 else {'errors': 'replace'} + with open(server_logfile, 'r', **kwargs) as fp: + if any(map(match, fp)): + return True + except (KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + self._log.warn('[%s]: Error while detecting false positive: %s' % (self._test.name, str(ex))) + self._log.info(traceback.print_exc()) + return False + + def _open(self): + self.out = open(self.logpath, 'w+') + + def _close(self): + self.out.close() + + def _print_header(self): + self._print_date() + self.out.write('Executing: %s\n' % ' '.join(self._prog.command)) + self.out.write('Directory: %s\n' % self._prog.workdir) + self.out.write('config:delay: %s\n' % self._test.delay) + self.out.write('config:timeout: %s\n' % self._test.timeout) + self._print_bar() + self.out.flush() + + def _print_footer(self, returncode=None): + self._print_bar() + if returncode is not None: + self.out.write('Return code: %d\n' % returncode) + else: + self.out.write('Process is killed.\n') + self._print_exec_time() + self._print_date() + + +class SummaryReporter(TestReporter): + def __init__(self, testdir, concurrent=True): + super(SummaryReporter, self).__init__() + self.testdir = testdir + self.logdir = os.path.join(testdir, LOG_DIR) + self.out_path = os.path.join(testdir, RESULT_JSON) + self.concurrent = concurrent + self.out = sys.stdout + self._platform = platform.system() + self._revision = self._get_revision() + self._tests = [] + if not os.path.exists(self.logdir): + os.mkdir(self.logdir) + self._known_failures = load_known_failures(testdir) + self._unexpected_success = [] + self._unexpected_failure = [] + self._expected_failure = [] + self._print_header() + + def _get_revision(self): + p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], + cwd=self.testdir, stdout=subprocess.PIPE) + out, _ = p.communicate() + return out.strip() + + def _format_test(self, test, with_result=True): + name = '%s-%s' % (test.server.name, test.client.name) + trans = '%s-%s' % (test.transport, test.socket) + if not with_result: + return '{:19s}{:13s}{:25s}'.format(name[:18], test.protocol[:12], trans[:24]) + else: + result = 'success' if test.success else ( + 'timeout' if test.expired else 'failure') + result_string = '%s(%d)' % (result, test.returncode) + return '{:19s}{:13s}{:25s}{:s}\n'.format(name[:18], test.protocol[:12], trans[:24], result_string) + + def _print_test_header(self): + self._print_bar() + self.out.write( + '{:19s}{:13s}{:25s}{:s}\n'.format('server-client:', 'protocol:', 'transport:', 'result:')) + + def _print_header(self): + self._start() + self.out.writelines([ + 'Apache Thrift - Integration Test Suite\n', + ]) + self._print_date() + self._print_test_header() + + def _print_unexpected_failure(self): + if len(self._unexpected_failure) > 0: + self.out.writelines([ + '*** Following %d failures were unexpected ***:\n' % len(self._unexpected_failure), + 'If it is introduced by you, please fix it before submitting the code.\n', + # 'If not, please report at https://issues.apache.org/jira/browse/THRIFT\n', + ]) + self._print_test_header() + for i in self._unexpected_failure: + self.out.write(self._format_test(self._tests[i])) + self._print_bar() + else: + self.out.write('No unexpected failures.\n') + + def _print_unexpected_success(self): + if len(self._unexpected_success) > 0: + self.out.write( + 'Following %d tests were known to fail but succeeded (it\'s normal):\n' % len(self._unexpected_success)) + self._print_test_header() + for i in self._unexpected_success: + self.out.write(self._format_test(self._tests[i])) + self._print_bar() + + def _print_footer(self): + fail_count = len(self._expected_failure) + len(self._unexpected_failure) + self._print_bar() + self._print_unexpected_success() + self._print_unexpected_failure() + self._write_html_data() + self._assemble_log('unexpected failures', self._unexpected_failure) + self._assemble_log('known failures', self._expected_failure) + self.out.writelines([ + 'You can browse results at:\n', + '\tfile://%s/%s\n' % (self.testdir, RESULT_HTML), + 'Full log for each test is here:\n', + '\ttest/log/client_server_protocol_transport_client.log\n', + '\ttest/log/client_server_protocol_transport_server.log\n', + '%d failed of %d tests in total.\n' % (fail_count, len(self._tests)), + ]) + self._print_exec_time() + self._print_date() + + def _render_result(self, test): + return [ + test.server.name, + test.client.name, + test.protocol, + test.transport, + test.socket, + test.success, + test.as_expected, + test.returncode, + { + 'server': self.test_logfile(test.testdir, test.name, test.server.kind), + 'client': self.test_logfile(test.testdir, test.name, test.client.kind), + }, + ] + + def _write_html_data(self): + """Writes JSON data to be read by result html""" + results = [self._render_result(r) for r in self._tests] + with open(self.out_path, 'w+') as fp: + fp.write(json.dumps({ + 'date': self._format_date(), + 'revision': str(self._revision), + 'platform': self._platform, + 'duration': '{:.1f}'.format(self._elapsed), + 'results': results, + }, indent=2)) + + def _assemble_log(self, title, indexes): + if len(indexes) > 0: + def add_prog_log(fp, test, prog_kind): + fp.write('*************************** %s message ***************************\n' + % prog_kind) + path = self.test_logfile(self.testdir, test.name, prog_kind) + kwargs = {} if sys.version_info[0] < 3 else {'errors': 'replace'} + with open(path, 'r', **kwargs) as prog_fp: + fp.write(prog_fp.read()) + filename = title.replace(' ', '_') + '.log' + with open(os.path.join(self.logdir, filename), 'w+') as fp: + for test in map(self._tests.__getitem__, indexes): + fp.write('TEST: [%s]\n' % test.name) + add_prog_log(fp, test, test.server.kind) + add_prog_log(fp, test, test.client.kind) + fp.write('**********************************************************************\n\n') + self.out.write('%s are logged to test/%s/%s\n' % (title.capitalize(), LOG_DIR, filename)) + + def end(self): + self._print_footer() + return len(self._unexpected_failure) == 0 + + def add_test(self, test_dict): + test = TestEntry(self.testdir, **test_dict) + self._lock.acquire() + try: + if not self.concurrent: + self.out.write(self._format_test(test, False)) + self.out.flush() + self._tests.append(test) + return len(self._tests) - 1 + finally: + self._lock.release() + + def add_result(self, index, returncode, expired): + self._lock.acquire() + try: + failed = returncode is None or returncode != 0 + test = self._tests[index] + known = test.name in self._known_failures + if failed: + if known: + self._log.debug('%s failed as expected' % test.name) + self._expected_failure.append(index) + else: + self._log.info('unexpected failure: %s' % test.name) + self._unexpected_failure.append(index) + elif known: + self._log.info('unexpected success: %s' % test.name) + self._unexpected_success.append(index) + test.success = not failed + test.returncode = returncode + test.expired = expired + test.as_expected = known == failed + if not self.concurrent: + result = 'success' if not failed else 'failure' + result_string = '%s(%d)' % (result, returncode) + self.out.write(result_string + '\n') + else: + self.out.write(self._format_test(test)) + finally: + self._lock.release() http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/run.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/run.py b/test/crossrunner/run.py new file mode 100644 index 0000000..e3300ba --- /dev/null +++ b/test/crossrunner/run.py @@ -0,0 +1,317 @@ +# +# 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 contextlib +import multiprocessing +import multiprocessing.managers +import os +import platform +import random +import socket +import signal +import subprocess +import threading +import time +import traceback + +from crossrunner.test import TestEntry, domain_socket_path +from crossrunner.report import ExecReporter, SummaryReporter + +RESULT_TIMEOUT = 128 +RESULT_ERROR = 64 + + +class ExecutionContext(object): + def __init__(self, cmd, cwd, env, report): + self._log = multiprocessing.get_logger() + self.report = report + self.cmd = cmd + self.cwd = cwd + self.env = env + self.timer = None + self.expired = False + + def _expire(self): + self._log.info('Timeout') + self.expired = True + self.kill() + + def kill(self): + self._log.debug('Killing process : %d' % self.proc.pid) + if platform.system() != 'Windows': + try: + os.killpg(self.proc.pid, signal.SIGKILL) + except Exception as err: + self._log.info('Failed to kill process group : %s' % str(err)) + try: + self.proc.kill() + except Exception as err: + self._log.info('Failed to kill process : %s' % str(err)) + self.report.killed() + + def _popen_args(self): + args = { + 'cwd': self.cwd, + 'env': self.env, + 'stdout': self.report.out, + 'stderr': subprocess.STDOUT, + } + # make sure child processes doesn't remain after killing + if platform.system() == 'Windows': + DETACHED_PROCESS = 0x00000008 + args.update(creationflags=DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP) + else: + args.update(preexec_fn=os.setsid) + return args + + def start(self, timeout=0): + self._log.debug('COMMAND: %s', ' '.join(self.cmd)) + self._log.debug('WORKDIR: %s', self.cwd) + self._log.debug('LOGFILE: %s', self.report.logpath) + self.report.begin() + self.proc = subprocess.Popen(self.cmd, **self._popen_args()) + if timeout > 0: + self.timer = threading.Timer(timeout, self._expire) + self.timer.start() + return self._scoped() + + @contextlib.contextmanager + def _scoped(self): + yield self + self._log.debug('Killing scoped process') + self.kill() + + def wait(self): + self.proc.communicate() + if self.timer: + self.timer.cancel() + self.report.end(self.returncode) + + @property + def returncode(self): + return self.proc.returncode if self.proc else None + + +def exec_context(port, testdir, test, prog): + report = ExecReporter(testdir, test, prog) + prog.build_command(port) + return ExecutionContext(prog.command, prog.workdir, prog.env, report) + + +def run_test(testdir, test_dict, async=True, max_retry=3): + try: + logger = multiprocessing.get_logger() + retry_count = 0 + test = TestEntry(testdir, **test_dict) + while True: + if stop.is_set(): + logger.debug('Skipping because shutting down') + return None + logger.debug('Start') + with PortAllocator.alloc_port_scoped(ports, test.socket) as port: + logger.debug('Start with port %d' % port) + sv = exec_context(port, testdir, test, test.server) + cl = exec_context(port, testdir, test, test.client) + + logger.debug('Starting server') + with sv.start(): + if test.delay > 0: + logger.debug('Delaying client for %.2f seconds' % test.delay) + time.sleep(test.delay) + cl_retry_count = 0 + cl_max_retry = 10 + cl_retry_wait = 0.5 + while True: + logger.debug('Starting client') + cl.start(test.timeout) + logger.debug('Waiting client') + cl.wait() + if not cl.report.maybe_false_positive() or cl_retry_count >= cl_max_retry: + if cl_retry_count > 0 and cl_retry_count < cl_max_retry: + logger.warn('[%s]: Connected after %d retry (%.2f sec each)' % (test.server.name, cl_retry_count, cl_retry_wait)) + break + logger.debug('Server may not be ready, waiting %.2f second...' % cl_retry_wait) + time.sleep(cl_retry_wait) + cl_retry_count += 1 + + if not sv.report.maybe_false_positive() or retry_count >= max_retry: + logger.debug('Finish') + return RESULT_TIMEOUT if cl.expired else cl.proc.returncode + logger.warn('[%s]: Detected socket bind failure, retrying...' % test.server.name) + retry_count += 1 + except (KeyboardInterrupt, SystemExit): + logger.info('Interrupted execution') + if not async: + raise + stop.set() + return None + except Exception as ex: + logger.warn('Error while executing test : %s' % str(ex)) + if not async: + raise + logger.info(traceback.print_exc()) + return RESULT_ERROR + + +class PortAllocator(object): + def __init__(self): + self._log = multiprocessing.get_logger() + self._lock = multiprocessing.Lock() + self._ports = set() + self._dom_ports = set() + self._last_alloc = 0 + + def _get_tcp_port(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', 0)) + port = sock.getsockname()[1] + self._lock.acquire() + try: + ok = port not in self._ports + if ok: + self._ports.add(port) + self._last_alloc = time.time() + finally: + self._lock.release() + sock.close() + return port if ok else self._get_tcp_port() + + def _get_domain_port(self): + port = random.randint(1024, 65536) + self._lock.acquire() + try: + ok = port not in self._dom_ports + if ok: + self._dom_ports.add(port) + finally: + self._lock.release() + return port if ok else self._get_domain_port() + + def alloc_port(self, socket_type): + if socket_type == 'domain': + return self._get_domain_port() + else: + return self._get_tcp_port() + + # static method for inter-process invokation + @staticmethod + @contextlib.contextmanager + def alloc_port_scoped(allocator, socket_type): + port = allocator.alloc_port(socket_type) + yield port + allocator.free_port(socket_type, port) + + def free_port(self, socket_type, port): + self._log.debug('free_port') + self._lock.acquire() + try: + if socket_type == 'domain': + self._dom_ports.remove(port) + path = domain_socket_path(port) + if os.path.exists(path): + os.remove(path) + else: + self._ports.remove(port) + except IOError as err: + self._log.info('Error while freeing port : %s' % str(err)) + finally: + self._lock.release() + + +class NonAsyncResult(object): + def __init__(self, value): + self._value = value + + def get(self, timeout=None): + return self._value + + def wait(self, timeout=None): + pass + + def ready(self): + return True + + def successful(self): + return self._value == 0 + + +class TestDispatcher(object): + def __init__(self, testdir, concurrency): + self._log = multiprocessing.get_logger() + self.testdir = testdir + # seems needed for python 2.x to handle keyboard interrupt + self._stop = multiprocessing.Event() + self._async = concurrency > 1 + if not self._async: + self._pool = None + global stop + global ports + stop = self._stop + ports = PortAllocator() + else: + self._m = multiprocessing.managers.BaseManager() + self._m.register('ports', PortAllocator) + self._m.start() + self._pool = multiprocessing.Pool(concurrency, self._pool_init, (self._m.address,)) + self._report = SummaryReporter(testdir, concurrency > 1) + self._log.debug( + 'TestDispatcher started with %d concurrent jobs' % concurrency) + + def _pool_init(self, address): + global stop + global m + global ports + stop = self._stop + m = multiprocessing.managers.BaseManager(address) + m.connect() + ports = m.ports() + + def _dispatch_sync(self, test, cont): + r = run_test(self.testdir, test, False) + cont(r) + return NonAsyncResult(r) + + def _dispatch_async(self, test, cont): + return self._pool.apply_async(func=run_test, args=(self.testdir, test,), callback=cont) + + def dispatch(self, test): + index = self._report.add_test(test) + + def cont(r): + if not self._stop.is_set(): + self._log.debug('freeing port') + self._log.debug('adding result') + self._report.add_result(index, r, r == RESULT_TIMEOUT) + self._log.debug('finish continuation') + fn = self._dispatch_async if self._async else self._dispatch_sync + return fn(test, cont) + + def wait(self): + if self._async: + self._pool.close() + self._pool.join() + self._m.shutdown() + return self._report.end() + + def terminate(self): + self._stop.set() + if self._async: + self._pool.terminate() + self._pool.join() + self._m.shutdown() http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/test.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/test.py b/test/crossrunner/test.py new file mode 100644 index 0000000..512e664 --- /dev/null +++ b/test/crossrunner/test.py @@ -0,0 +1,136 @@ +# +# 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 copy +import multiprocessing +import os +import sys + +from crossrunner.util import merge_dict + + +def domain_socket_path(port): + return '/tmp/ThriftTest.thrift.%d' % port + + +class TestProgram(object): + def __init__(self, kind, name, protocol, transport, socket, workdir, command, env=None, + extra_args=[], join_args=False, **kwargs): + self.kind = kind + self.name = name + self.protocol = protocol + self.transport = transport + self.socket = socket + self.workdir = workdir + self.command = None + self._base_command = self._fix_cmd_path(command) + if env: + self.env = copy.copy(os.environ) + self.env.update(env) + else: + self.env = os.environ + self._extra_args = extra_args + self._join_args = join_args + + def _fix_cmd_path(self, cmd): + # if the arg is a file in the current directory, make it path + def abs_if_exists(arg): + p = os.path.join(self.workdir, arg) + return p if os.path.exists(p) else arg + + if cmd[0] == 'python': + cmd[0] = sys.executable + else: + cmd[0] = abs_if_exists(cmd[0]) + return cmd + + def _socket_arg(self, socket, port): + return { + 'ip-ssl': '--ssl', + 'domain': '--domain-socket=%s' % domain_socket_path(port), + }.get(socket, None) + + def build_command(self, port): + cmd = copy.copy(self._base_command) + args = [] + args.append('--protocol=' + self.protocol) + args.append('--transport=' + self.transport) + socket_arg = self._socket_arg(self.socket, port) + if socket_arg: + args.append(socket_arg) + args.append('--port=%d' % port) + if self._join_args: + cmd.append('%s' % " ".join(args)) + else: + cmd.extend(args) + if self._extra_args: + cmd.extend(self._extra_args) + self.command = cmd + return self.command + + +class TestEntry(object): + def __init__(self, testdir, server, client, delay, timeout, **kwargs): + self.testdir = testdir + self._log = multiprocessing.get_logger() + self._config = kwargs + self.protocol = kwargs['protocol'] + self.transport = kwargs['transport'] + self.socket = kwargs['socket'] + self.server = TestProgram('server', **self._fix_workdir(merge_dict(self._config, server))) + self.client = TestProgram('client', **self._fix_workdir(merge_dict(self._config, client))) + self.delay = delay + self.timeout = timeout + self._name = None + # results + self.success = None + self.as_expected = None + self.returncode = None + self.expired = False + + def _fix_workdir(self, config): + key = 'workdir' + path = config.get(key, None) + if not path: + path = self.testdir + if os.path.isabs(path): + path = os.path.realpath(path) + else: + path = os.path.realpath(os.path.join(self.testdir, path)) + config.update({key: path}) + return config + + @classmethod + def get_name(cls, server, client, proto, trans, sock, *args): + return '%s-%s_%s_%s-%s' % (server, client, proto, trans, sock) + + @property + def name(self): + if not self._name: + self._name = self.get_name( + self.server.name, self.client.name, self.protocol, self.transport, self.socket) + return self._name + + @property + def transport_name(self): + return '%s-%s' % (self.transport, self.socket) + + +def test_name(server, client, protocol, transport, socket, **kwargs): + return TestEntry.get_name(server['name'], client['name'], protocol, transport, socket) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/crossrunner/util.py ---------------------------------------------------------------------- diff --git a/test/crossrunner/util.py b/test/crossrunner/util.py new file mode 100644 index 0000000..750ed47 --- /dev/null +++ b/test/crossrunner/util.py @@ -0,0 +1,31 @@ +# +# 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 copy + + +def merge_dict(base, update): + """Update dict concatenating list values""" + res = copy.deepcopy(base) + for k, v in list(update.items()): + if k in list(res.keys()) and isinstance(v, list): + res[k].extend(v) + else: + res[k] = v + return res http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/erl/Makefile.am ---------------------------------------------------------------------- diff --git a/test/erl/Makefile.am b/test/erl/Makefile.am index a54e217..1940ce3 100644 --- a/test/erl/Makefile.am +++ b/test/erl/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift REBAR = $(top_srcdir)/lib/erl/rebar THRIFT_FILES = $(wildcard ../*.thrift) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/go/Makefile.am ---------------------------------------------------------------------- diff --git a/test/go/Makefile.am b/test/go/Makefile.am index 66f81ad..7357f50 100644 --- a/test/go/Makefile.am +++ b/test/go/Makefile.am @@ -17,11 +17,11 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift THRIFTCMD = $(THRIFT) -out src/gen --gen go:thrift_import=thrift THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift -all: bin/testclient bin/testserver bin/stress +precross: bin/testclient bin/testserver ThriftTest.thrift: $(THRIFTTEST) grep -v list.*map.*list.*map $(THRIFTTEST) > ThriftTest.thrift @@ -47,6 +47,8 @@ bin/stress: gopath clean-local: $(RM) -r src/gen src/code.google.com src/thrift bin pkg gopath ThriftTest.thrift +check_PROGRAMS: bin/testclient bin/testserver bin/stress + check: gopath GOPATH=`pwd` $(GO) test -v common/... http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/go/src/bin/testserver/main.go ---------------------------------------------------------------------- diff --git a/test/go/src/bin/testserver/main.go b/test/go/src/bin/testserver/main.go index cd32f92..ebcd8e5 100644 --- a/test/go/src/bin/testserver/main.go +++ b/test/go/src/bin/testserver/main.go @@ -31,10 +31,11 @@ var domain_socket = flag.String("domain-socket", "", "Domain Socket (e.g. /tmp/T var transport = flag.String("transport", "buffered", "Transport: buffered, framed, http") var protocol = flag.String("protocol", "binary", "Protocol: binary, compact, json") var ssl = flag.Bool("ssl", false, "Encrypted Transport using SSL") +var certPath = flag.String("certPath", "keys", "Directory that contains SSL certificates") func main() { flag.Parse() - server, err := common.StartServer(*host, *port, *domain_socket, *transport, *protocol, *ssl, common.PrintingHandler) + server, err := common.StartServer(*host, *port, *domain_socket, *transport, *protocol, *ssl, *certPath, common.PrintingHandler) if err != nil { log.Fatalf("Unable to start server: ", err) } http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/go/src/common/clientserver_test.go ---------------------------------------------------------------------- diff --git a/test/go/src/common/clientserver_test.go b/test/go/src/common/clientserver_test.go index 3b512ad..1b833c9 100644 --- a/test/go/src/common/clientserver_test.go +++ b/test/go/src/common/clientserver_test.go @@ -56,7 +56,7 @@ func doUnit(t *testing.T, unit *test_unit) { ctrl := gomock.NewController(t) defer ctrl.Finish() handler := NewMockThriftTest(ctrl) - server, err := StartServer(unit.host, unit.port, unit.domain_socket, unit.transport, unit.protocol, unit.ssl, handler) + server, err := StartServer(unit.host, unit.port, unit.domain_socket, unit.transport, unit.protocol, unit.ssl, "../../../keys", handler) if err != nil { t.Errorf("Unable to start server", err) t.FailNow() http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/go/src/common/server.go ---------------------------------------------------------------------- diff --git a/test/go/src/common/server.go b/test/go/src/common/server.go index e77cd37..d354b32 100644 --- a/test/go/src/common/server.go +++ b/test/go/src/common/server.go @@ -43,6 +43,7 @@ func StartServer( transport string, protocol string, ssl bool, + certPath string, handler thrifttest.ThriftTest) (srv *thrift.TSimpleServer, err error) { hostPort := fmt.Sprintf("%s:%d", host, port) http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/haxe/Makefile.am ---------------------------------------------------------------------- diff --git a/test/haxe/Makefile.am b/test/haxe/Makefile.am index 9b7548b..1e537d3 100644 --- a/test/haxe/Makefile.am +++ b/test/haxe/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift THRIFTCMD = $(THRIFT) --gen haxe -r THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/hs/Makefile.am ---------------------------------------------------------------------- diff --git a/test/hs/Makefile.am b/test/hs/Makefile.am index b974ed8..e171248 100644 --- a/test/hs/Makefile.am +++ b/test/hs/Makefile.am @@ -17,7 +17,7 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift stubs: ../ConstantsDemo.thrift ../DebugProtoTest.thrift ../ThriftTest.thrift ../Include.thrift $(THRIFT) --gen hs ../ConstantsDemo.thrift @@ -36,6 +36,6 @@ clean-local: $(RM) *.hi $(RM) *.o -all: check +all-local: stubs ghc -igen-hs TestServer.hs ghc -igen-hs TestClient.hs http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/known_failures_Linux.json ---------------------------------------------------------------------- diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json new file mode 100644 index 0000000..a83c111 --- /dev/null +++ b/test/known_failures_Linux.json @@ -0,0 +1,664 @@ +[ + "c_glib-c_glib_binary_buffered-ip", + "c_glib-c_glib_binary_framed-ip", + "c_glib-cpp_binary_buffered-ip", + "c_glib-cpp_binary_framed-ip", + "c_glib-csharp_binary_buffered-ip", + "c_glib-csharp_binary_framed-ip", + "c_glib-go_binary_buffered-ip", + "c_glib-go_binary_framed-ip", + "c_glib-hs_binary_buffered-ip", + "c_glib-hs_binary_framed-ip", + "c_glib-java_binary_buffered-ip", + "c_glib-java_binary_framed-fastframed-ip", + "c_glib-java_binary_framed-ip", + "c_glib-nodejs_binary_buffered-ip", + "c_glib-nodejs_binary_framed-ip", + "c_glib-perl_binary_buffered-ip", + "c_glib-php_binary_buffered-ip", + "c_glib-php_binary_framed-ip", + "c_glib-py_binary-accel_buffered-ip", + "c_glib-py_binary-accel_framed-ip", + "c_glib-py_binary_buffered-ip", + "c_glib-py_binary_framed-ip", + "c_glib-rb_binary-accel_buffered-ip", + "c_glib-rb_binary-accel_framed-ip", + "c_glib-rb_binary_buffered-ip", + "c_glib-rb_binary_framed-ip", + "cpp-cpp_binary_http-domain", + "cpp-cpp_binary_http-ip", + "cpp-cpp_compact_http-domain", + "cpp-cpp_compact_http-ip", + "cpp-cpp_json_http-domain", + "cpp-cpp_json_http-ip", + "cpp-csharp_binary_buffered-ip-ssl", + "cpp-csharp_binary_framed-ip-ssl", + "cpp-csharp_compact_buffered-ip-ssl", + "cpp-csharp_compact_framed-ip-ssl", + "cpp-csharp_json_buffered-ip-ssl", + "cpp-csharp_json_framed-ip-ssl", + "cpp-go_binary_buffered-ip", + "cpp-go_binary_buffered-ip-ssl", + "cpp-go_binary_framed-ip", + "cpp-go_binary_framed-ip-ssl", + "cpp-go_compact_buffered-ip", + "cpp-go_compact_buffered-ip-ssl", + "cpp-go_compact_framed-ip", + "cpp-go_compact_framed-ip-ssl", + "cpp-go_json_buffered-ip", + "cpp-go_json_buffered-ip-ssl", + "cpp-go_json_framed-ip", + "cpp-go_json_framed-ip-ssl", + "cpp-hs_binary_buffered-ip-ssl", + "cpp-hs_binary_framed-ip", + "cpp-hs_binary_framed-ip-ssl", + "cpp-hs_binary_http-evhttp-ip", + "cpp-hs_binary_http-evhttp-ip-ssl", + "cpp-hs_binary_http-ip", + "cpp-hs_binary_http-ip-ssl", + "cpp-hs_compact_buffered-ip-ssl", + "cpp-hs_compact_framed-ip", + "cpp-hs_compact_framed-ip-ssl", + "cpp-hs_compact_http-evhttp-ip", + "cpp-hs_compact_http-evhttp-ip-ssl", + "cpp-hs_compact_http-ip", + "cpp-hs_compact_http-ip-ssl", + "cpp-hs_json_buffered-ip-ssl", + "cpp-hs_json_framed-ip", + "cpp-hs_json_framed-ip-ssl", + "cpp-hs_json_http-evhttp-ip", + "cpp-hs_json_http-evhttp-ip-ssl", + "cpp-hs_json_http-ip", + "cpp-hs_json_http-ip-ssl", + "cpp-java_binary_http-ip", + "cpp-java_binary_http-ip-ssl", + "cpp-java_compact_http-ip", + "cpp-java_compact_http-ip-ssl", + "cpp-java_json_http-ip", + "cpp-java_json_http-ip-ssl", + "cpp-nodejs_json_buffered-ip", + "cpp-nodejs_json_buffered-ip-ssl", + "cpp-php_binary_framed-ip", + "cpp-rb_binary-accel_buffered-ip", + "cpp-rb_binary-accel_framed-ip", + "cpp-rb_binary_buffered-ip", + "cpp-rb_binary_framed-ip", + "cpp-rb_compact_buffered-ip", + "cpp-rb_compact_framed-ip", + "cpp-rb_json_buffered-ip", + "cpp-rb_json_framed-ip", + "csharp-c_glib_binary_buffered-ip", + "csharp-c_glib_binary_framed-ip", + "csharp-cpp_binary_buffered-ip", + "csharp-cpp_binary_buffered-ip-ssl", + "csharp-cpp_binary_framed-ip", + "csharp-cpp_binary_framed-ip-ssl", + "csharp-cpp_compact_buffered-ip", + "csharp-cpp_compact_buffered-ip-ssl", + "csharp-cpp_compact_framed-ip", + "csharp-cpp_compact_framed-ip-ssl", + "csharp-cpp_json_buffered-ip", + "csharp-cpp_json_buffered-ip-ssl", + "csharp-cpp_json_framed-ip", + "csharp-cpp_json_framed-ip-ssl", + "csharp-csharp_binary_buffered-ip-ssl", + "csharp-csharp_binary_framed-ip-ssl", + "csharp-csharp_compact_buffered-ip-ssl", + "csharp-csharp_compact_framed-ip-ssl", + "csharp-csharp_json_buffered-ip-ssl", + "csharp-csharp_json_framed-ip-ssl", + "csharp-go_binary_buffered-ip", + "csharp-go_binary_buffered-ip-ssl", + "csharp-go_binary_framed-ip", + "csharp-go_binary_framed-ip-ssl", + "csharp-go_compact_buffered-ip", + "csharp-go_compact_buffered-ip-ssl", + "csharp-go_compact_framed-ip", + "csharp-go_compact_framed-ip-ssl", + "csharp-go_json_buffered-ip", + "csharp-go_json_buffered-ip-ssl", + "csharp-go_json_framed-ip", + "csharp-go_json_framed-ip-ssl", + "csharp-hs_binary_buffered-ip", + "csharp-hs_binary_buffered-ip-ssl", + "csharp-hs_binary_framed-ip", + "csharp-hs_binary_framed-ip-ssl", + "csharp-hs_compact_buffered-ip", + "csharp-hs_compact_buffered-ip-ssl", + "csharp-hs_compact_framed-ip", + "csharp-hs_compact_framed-ip-ssl", + "csharp-hs_json_buffered-ip", + "csharp-hs_json_buffered-ip-ssl", + "csharp-hs_json_framed-ip", + "csharp-hs_json_framed-ip-ssl", + "csharp-java_binary_buffered-ip", + "csharp-java_binary_buffered-ip-ssl", + "csharp-java_binary_framed-fastframed-ip", + "csharp-java_binary_framed-fastframed-ip-ssl", + "csharp-java_binary_framed-ip", + "csharp-java_binary_framed-ip-ssl", + "csharp-java_compact_buffered-ip", + "csharp-java_compact_buffered-ip-ssl", + "csharp-java_compact_framed-fastframed-ip", + "csharp-java_compact_framed-fastframed-ip-ssl", + "csharp-java_compact_framed-ip", + "csharp-java_compact_framed-ip-ssl", + "csharp-java_json_buffered-ip", + "csharp-java_json_buffered-ip-ssl", + "csharp-java_json_framed-fastframed-ip", + "csharp-java_json_framed-fastframed-ip-ssl", + "csharp-java_json_framed-ip", + "csharp-java_json_framed-ip-ssl", + "csharp-nodejs_binary_buffered-ip", + "csharp-nodejs_binary_buffered-ip-ssl", + "csharp-nodejs_binary_framed-ip", + "csharp-nodejs_binary_framed-ip-ssl", + "csharp-nodejs_compact_buffered-ip", + "csharp-nodejs_compact_buffered-ip-ssl", + "csharp-nodejs_compact_framed-ip", + "csharp-nodejs_compact_framed-ip-ssl", + "csharp-nodejs_json_buffered-ip", + "csharp-nodejs_json_buffered-ip-ssl", + "csharp-nodejs_json_framed-ip", + "csharp-nodejs_json_framed-ip-ssl", + "csharp-php_binary_framed-ip", + "csharp-py_binary-accel_buffered-ip-ssl", + "csharp-py_binary-accel_framed-ip-ssl", + "csharp-py_binary_buffered-ip-ssl", + "csharp-py_binary_framed-ip-ssl", + "csharp-py_compact_buffered-ip-ssl", + "csharp-py_compact_framed-ip-ssl", + "csharp-py_json_buffered-ip-ssl", + "csharp-py_json_framed-ip-ssl", + "csharp-rb_binary-accel_buffered-ip", + "csharp-rb_binary-accel_framed-ip", + "csharp-rb_binary_buffered-ip", + "csharp-rb_binary_framed-ip", + "csharp-rb_compact_buffered-ip", + "csharp-rb_compact_framed-ip", + "csharp-rb_json_buffered-ip", + "csharp-rb_json_framed-ip", + "go-c_glib_binary_buffered-ip", + "go-c_glib_binary_framed-ip", + "go-cpp_binary_buffered-ip", + "go-cpp_binary_buffered-ip-ssl", + "go-cpp_binary_framed-ip", + "go-cpp_binary_framed-ip-ssl", + "go-cpp_compact_buffered-ip", + "go-cpp_compact_buffered-ip-ssl", + "go-cpp_compact_framed-ip", + "go-cpp_compact_framed-ip-ssl", + "go-cpp_json_buffered-ip", + "go-cpp_json_buffered-ip-ssl", + "go-cpp_json_framed-ip", + "go-cpp_json_framed-ip-ssl", + "go-csharp_binary_buffered-ip", + "go-csharp_binary_buffered-ip-ssl", + "go-csharp_binary_framed-ip", + "go-csharp_binary_framed-ip-ssl", + "go-csharp_compact_buffered-ip", + "go-csharp_compact_buffered-ip-ssl", + "go-csharp_compact_framed-ip", + "go-csharp_compact_framed-ip-ssl", + "go-csharp_json_buffered-ip", + "go-csharp_json_buffered-ip-ssl", + "go-csharp_json_framed-ip", + "go-csharp_json_framed-ip-ssl", + "go-hs_binary_buffered-ip-ssl", + "go-hs_binary_framed-ip", + "go-hs_binary_framed-ip-ssl", + "go-hs_compact_buffered-ip-ssl", + "go-hs_compact_framed-ip", + "go-hs_compact_framed-ip-ssl", + "go-hs_json_buffered-ip-ssl", + "go-hs_json_framed-ip", + "go-hs_json_framed-ip-ssl", + "go-java_binary_buffered-ip", + "go-java_binary_buffered-ip-ssl", + "go-java_binary_framed-fastframed-ip", + "go-java_binary_framed-fastframed-ip-ssl", + "go-java_binary_framed-ip", + "go-java_binary_framed-ip-ssl", + "go-java_compact_buffered-ip", + "go-java_compact_buffered-ip-ssl", + "go-java_compact_framed-fastframed-ip", + "go-java_compact_framed-fastframed-ip-ssl", + "go-java_compact_framed-ip", + "go-java_compact_framed-ip-ssl", + "go-java_json_buffered-ip", + "go-java_json_buffered-ip-ssl", + "go-java_json_framed-fastframed-ip", + "go-java_json_framed-fastframed-ip-ssl", + "go-java_json_framed-ip", + "go-java_json_framed-ip-ssl", + "go-nodejs_binary_buffered-ip", + "go-nodejs_binary_buffered-ip-ssl", + "go-nodejs_binary_framed-ip", + "go-nodejs_binary_framed-ip-ssl", + "go-nodejs_compact_buffered-ip", + "go-nodejs_compact_buffered-ip-ssl", + "go-nodejs_compact_framed-ip", + "go-nodejs_compact_framed-ip-ssl", + "go-nodejs_json_buffered-ip", + "go-nodejs_json_buffered-ip-ssl", + "go-nodejs_json_framed-ip", + "go-nodejs_json_framed-ip-ssl", + "go-perl_binary_buffered-ip", + "go-php_binary_buffered-ip", + "go-php_binary_framed-ip", + "go-py_json_buffered-ip", + "go-py_json_buffered-ip-ssl", + "go-py_json_framed-ip", + "go-py_json_framed-ip-ssl", + "go-rb_binary-accel_buffered-ip", + "go-rb_binary-accel_framed-ip", + "go-rb_binary_buffered-ip", + "go-rb_binary_framed-ip", + "go-rb_compact_buffered-ip", + "go-rb_compact_framed-ip", + "go-rb_json_buffered-ip", + "go-rb_json_framed-ip", + "hs-c_glib_binary_buffered-ip", + "hs-c_glib_binary_framed-ip", + "hs-cpp_binary_buffered-ip-ssl", + "hs-cpp_binary_evhttp-http-ip", + "hs-cpp_binary_evhttp-http-ip-ssl", + "hs-cpp_binary_framed-ip", + "hs-cpp_binary_framed-ip-ssl", + "hs-cpp_binary_http-ip", + "hs-cpp_binary_http-ip-ssl", + "hs-cpp_compact_buffered-ip-ssl", + "hs-cpp_compact_evhttp-http-ip", + "hs-cpp_compact_evhttp-http-ip-ssl", + "hs-cpp_compact_framed-ip", + "hs-cpp_compact_framed-ip-ssl", + "hs-cpp_compact_http-ip", + "hs-cpp_compact_http-ip-ssl", + "hs-cpp_json_buffered-ip-ssl", + "hs-cpp_json_evhttp-http-ip", + "hs-cpp_json_evhttp-http-ip-ssl", + "hs-cpp_json_framed-ip", + "hs-cpp_json_framed-ip-ssl", + "hs-cpp_json_http-ip", + "hs-cpp_json_http-ip-ssl", + "hs-csharp_binary_buffered-ip", + "hs-csharp_binary_buffered-ip-ssl", + "hs-csharp_binary_framed-ip", + "hs-csharp_binary_framed-ip-ssl", + "hs-csharp_compact_buffered-ip", + "hs-csharp_compact_buffered-ip-ssl", + "hs-csharp_compact_framed-ip", + "hs-csharp_compact_framed-ip-ssl", + "hs-csharp_json_buffered-ip", + "hs-csharp_json_buffered-ip-ssl", + "hs-csharp_json_framed-ip", + "hs-csharp_json_framed-ip-ssl", + "hs-go_binary_buffered-ip", + "hs-go_binary_buffered-ip-ssl", + "hs-go_binary_framed-ip", + "hs-go_binary_framed-ip-ssl", + "hs-go_compact_buffered-ip", + "hs-go_compact_buffered-ip-ssl", + "hs-go_compact_framed-ip", + "hs-go_compact_framed-ip-ssl", + "hs-go_json_buffered-ip", + "hs-go_json_buffered-ip-ssl", + "hs-go_json_framed-ip", + "hs-go_json_framed-ip-ssl", + "hs-java_binary_buffered-ip-ssl", + "hs-java_binary_evhttp-http-ip", + "hs-java_binary_evhttp-http-ip-ssl", + "hs-java_binary_framed-fastframed-ip", + "hs-java_binary_framed-fastframed-ip-ssl", + "hs-java_binary_framed-ip", + "hs-java_binary_framed-ip-ssl", + "hs-java_binary_http-ip", + "hs-java_binary_http-ip-ssl", + "hs-java_compact_buffered-ip-ssl", + "hs-java_compact_evhttp-http-ip", + "hs-java_compact_evhttp-http-ip-ssl", + "hs-java_compact_framed-fastframed-ip", + "hs-java_compact_framed-fastframed-ip-ssl", + "hs-java_compact_framed-ip", + "hs-java_compact_framed-ip-ssl", + "hs-java_compact_http-ip", + "hs-java_compact_http-ip-ssl", + "hs-java_json_buffered-ip-ssl", + "hs-java_json_evhttp-http-ip", + "hs-java_json_evhttp-http-ip-ssl", + "hs-java_json_framed-fastframed-ip", + "hs-java_json_framed-fastframed-ip-ssl", + "hs-java_json_framed-ip", + "hs-java_json_framed-ip-ssl", + "hs-java_json_http-ip", + "hs-java_json_http-ip-ssl", + "hs-nodejs_binary_buffered-ip", + "hs-nodejs_binary_buffered-ip-ssl", + "hs-nodejs_binary_framed-ip", + "hs-nodejs_binary_framed-ip-ssl", + "hs-nodejs_compact_buffered-ip", + "hs-nodejs_compact_buffered-ip-ssl", + "hs-nodejs_compact_framed-ip", + "hs-nodejs_compact_framed-ip-ssl", + "hs-nodejs_json_buffered-ip", + "hs-nodejs_json_buffered-ip-ssl", + "hs-nodejs_json_framed-ip", + "hs-nodejs_json_framed-ip-ssl", + "hs-py_binary-accel_buffered-ip-ssl", + "hs-py_binary-accel_framed-ip", + "hs-py_binary-accel_framed-ip-ssl", + "hs-py_binary_buffered-ip-ssl", + "hs-py_binary_framed-ip", + "hs-py_binary_framed-ip-ssl", + "hs-py_compact_buffered-ip-ssl", + "hs-py_compact_framed-ip", + "hs-py_compact_framed-ip-ssl", + "hs-py_json_buffered-ip-ssl", + "hs-py_json_framed-ip", + "hs-py_json_framed-ip-ssl", + "hs-rb_binary-accel_buffered-ip", + "hs-rb_binary-accel_framed-ip", + "hs-rb_binary_buffered-ip", + "hs-rb_binary_framed-ip", + "hs-rb_compact_buffered-ip", + "hs-rb_compact_framed-ip", + "hs-rb_json_buffered-ip", + "hs-rb_json_framed-ip", + "java-csharp_binary_buffered-ip-ssl", + "java-csharp_binary_fastframed-framed-ip-ssl", + "java-csharp_binary_framed-ip-ssl", + "java-csharp_compact_buffered-ip-ssl", + "java-csharp_compact_fastframed-framed-ip-ssl", + "java-csharp_compact_framed-ip-ssl", + "java-csharp_json_buffered-ip-ssl", + "java-csharp_json_fastframed-framed-ip-ssl", + "java-csharp_json_framed-ip-ssl", + "java-hs_binary_buffered-ip-ssl", + "java-hs_binary_fastframed-framed-ip", + "java-hs_binary_fastframed-framed-ip-ssl", + "java-hs_binary_framed-ip", + "java-hs_binary_framed-ip-ssl", + "java-hs_compact_buffered-ip-ssl", + "java-hs_compact_fastframed-framed-ip", + "java-hs_compact_fastframed-framed-ip-ssl", + "java-hs_compact_framed-ip", + "java-hs_compact_framed-ip-ssl", + "java-hs_json_buffered-ip-ssl", + "java-hs_json_fastframed-framed-ip", + "java-hs_json_fastframed-framed-ip-ssl", + "java-hs_json_framed-ip", + "java-hs_json_framed-ip-ssl", + "java-nodejs_json_buffered-ip", + "java-nodejs_json_buffered-ip-ssl", + "java-php_binary_fastframed-framed-ip", + "java-php_binary_framed-ip", + "java-rb_binary-accel_buffered-ip", + "java-rb_binary-accel_fastframed-framed-ip", + "java-rb_binary-accel_framed-ip", + "java-rb_binary_buffered-ip", + "java-rb_binary_fastframed-framed-ip", + "java-rb_binary_framed-ip", + "java-rb_compact_buffered-ip", + "java-rb_compact_fastframed-framed-ip", + "java-rb_compact_framed-ip", + "java-rb_json_buffered-ip", + "java-rb_json_fastframed-framed-ip", + "java-rb_json_framed-ip", + "nodejs-csharp_binary_buffered-ip", + "nodejs-csharp_binary_buffered-ip-ssl", + "nodejs-csharp_binary_framed-ip", + "nodejs-csharp_binary_framed-ip-ssl", + "nodejs-csharp_compact_buffered-ip", + "nodejs-csharp_compact_buffered-ip-ssl", + "nodejs-csharp_compact_framed-ip", + "nodejs-csharp_compact_framed-ip-ssl", + "nodejs-csharp_json_buffered-ip", + "nodejs-csharp_json_buffered-ip-ssl", + "nodejs-csharp_json_framed-ip", + "nodejs-csharp_json_framed-ip-ssl", + "nodejs-go_binary_buffered-ip", + "nodejs-go_binary_buffered-ip-ssl", + "nodejs-go_binary_framed-ip", + "nodejs-go_binary_framed-ip-ssl", + "nodejs-go_compact_buffered-ip", + "nodejs-go_compact_buffered-ip-ssl", + "nodejs-go_compact_framed-ip", + "nodejs-go_compact_framed-ip-ssl", + "nodejs-go_json_buffered-ip", + "nodejs-go_json_buffered-ip-ssl", + "nodejs-go_json_framed-ip", + "nodejs-go_json_framed-ip-ssl", + "nodejs-hs_binary_buffered-ip-ssl", + "nodejs-hs_binary_framed-ip", + "nodejs-hs_binary_framed-ip-ssl", + "nodejs-hs_compact_buffered-ip-ssl", + "nodejs-hs_compact_framed-ip", + "nodejs-hs_compact_framed-ip-ssl", + "nodejs-hs_json_buffered-ip", + "nodejs-hs_json_buffered-ip-ssl", + "nodejs-hs_json_framed-ip", + "nodejs-hs_json_framed-ip-ssl", + "nodejs-java_json_buffered-ip-ssl", + "nodejs-nodejs_json_buffered-ip-ssl", + "nodejs-php_binary_framed-ip", + "nodejs-py_compact_buffered-ip", + "nodejs-py_compact_buffered-ip-ssl", + "nodejs-py_compact_framed-ip", + "nodejs-py_compact_framed-ip-ssl", + "nodejs-py_json_buffered-ip", + "nodejs-py_json_buffered-ip-ssl", + "nodejs-py_json_framed-ip", + "nodejs-py_json_framed-ip-ssl", + "nodejs-rb_binary-accel_buffered-ip", + "nodejs-rb_binary-accel_framed-ip", + "nodejs-rb_binary_buffered-ip", + "nodejs-rb_binary_framed-ip", + "nodejs-rb_compact_buffered-ip", + "nodejs-rb_compact_framed-ip", + "nodejs-rb_json_buffered-ip", + "nodejs-rb_json_framed-ip", + "py-c_glib_accel-binary_buffered-ip", + "py-c_glib_accel-binary_framed-ip", + "py-c_glib_binary_buffered-ip", + "py-c_glib_binary_framed-ip", + "py-cpp_accel-binary_buffered-ip", + "py-cpp_accel-binary_buffered-ip-ssl", + "py-cpp_accel-binary_framed-ip", + "py-cpp_accel-binary_framed-ip-ssl", + "py-cpp_binary_buffered-ip", + "py-cpp_binary_buffered-ip-ssl", + "py-cpp_binary_framed-ip", + "py-cpp_binary_framed-ip-ssl", + "py-cpp_compact_buffered-ip", + "py-cpp_compact_buffered-ip-ssl", + "py-cpp_compact_framed-ip", + "py-cpp_compact_framed-ip-ssl", + "py-cpp_json_buffered-ip", + "py-cpp_json_buffered-ip-ssl", + "py-cpp_json_framed-ip", + "py-cpp_json_framed-ip-ssl", + "py-csharp_accel-binary_buffered-ip", + "py-csharp_accel-binary_buffered-ip-ssl", + "py-csharp_accel-binary_framed-ip", + "py-csharp_accel-binary_framed-ip-ssl", + "py-csharp_binary_buffered-ip", + "py-csharp_binary_buffered-ip-ssl", + "py-csharp_binary_framed-ip", + "py-csharp_binary_framed-ip-ssl", + "py-csharp_compact_buffered-ip", + "py-csharp_compact_buffered-ip-ssl", + "py-csharp_compact_framed-ip", + "py-csharp_compact_framed-ip-ssl", + "py-csharp_json_buffered-ip", + "py-csharp_json_buffered-ip-ssl", + "py-csharp_json_framed-ip", + "py-csharp_json_framed-ip-ssl", + "py-go_accel-binary_buffered-ip", + "py-go_accel-binary_buffered-ip-ssl", + "py-go_accel-binary_framed-ip", + "py-go_accel-binary_framed-ip-ssl", + "py-go_binary_buffered-ip", + "py-go_binary_buffered-ip-ssl", + "py-go_binary_framed-ip", + "py-go_binary_framed-ip-ssl", + "py-go_compact_buffered-ip", + "py-go_compact_buffered-ip-ssl", + "py-go_compact_framed-ip", + "py-go_compact_framed-ip-ssl", + "py-go_json_buffered-ip", + "py-go_json_buffered-ip-ssl", + "py-go_json_framed-ip", + "py-go_json_framed-ip-ssl", + "py-hs_accel-binary_buffered-ip", + "py-hs_accel-binary_buffered-ip-ssl", + "py-hs_accel-binary_framed-ip", + "py-hs_accel-binary_framed-ip-ssl", + "py-hs_binary_buffered-ip", + "py-hs_binary_buffered-ip-ssl", + "py-hs_binary_framed-ip", + "py-hs_binary_framed-ip-ssl", + "py-hs_compact_buffered-ip", + "py-hs_compact_buffered-ip-ssl", + "py-hs_compact_framed-ip", + "py-hs_compact_framed-ip-ssl", + "py-hs_json_buffered-ip", + "py-hs_json_buffered-ip-ssl", + "py-hs_json_framed-ip", + "py-hs_json_framed-ip-ssl", + "py-java_accel-binary_buffered-ip", + "py-java_accel-binary_buffered-ip-ssl", + "py-java_accel-binary_framed-fastframed-ip", + "py-java_accel-binary_framed-fastframed-ip-ssl", + "py-java_accel-binary_framed-ip", + "py-java_accel-binary_framed-ip-ssl", + "py-java_binary_buffered-ip", + "py-java_binary_buffered-ip-ssl", + "py-java_binary_framed-fastframed-ip", + "py-java_binary_framed-fastframed-ip-ssl", + "py-java_binary_framed-ip", + "py-java_binary_framed-ip-ssl", + "py-java_compact_buffered-ip", + "py-java_compact_buffered-ip-ssl", + "py-java_compact_framed-fastframed-ip", + "py-java_compact_framed-fastframed-ip-ssl", + "py-java_compact_framed-ip", + "py-java_compact_framed-ip-ssl", + "py-java_json_buffered-ip", + "py-java_json_buffered-ip-ssl", + "py-java_json_framed-fastframed-ip", + "py-java_json_framed-fastframed-ip-ssl", + "py-java_json_framed-ip", + "py-java_json_framed-ip-ssl", + "py-nodejs_accel-binary_buffered-ip", + "py-nodejs_accel-binary_buffered-ip-ssl", + "py-nodejs_accel-binary_framed-ip", + "py-nodejs_accel-binary_framed-ip-ssl", + "py-nodejs_binary_buffered-ip", + "py-nodejs_binary_buffered-ip-ssl", + "py-nodejs_binary_framed-ip", + "py-nodejs_binary_framed-ip-ssl", + "py-nodejs_compact_buffered-ip", + "py-nodejs_compact_buffered-ip-ssl", + "py-nodejs_compact_framed-ip", + "py-nodejs_compact_framed-ip-ssl", + "py-nodejs_json_buffered-ip", + "py-nodejs_json_buffered-ip-ssl", + "py-nodejs_json_framed-ip", + "py-nodejs_json_framed-ip-ssl", + "py-php_accel-binary_framed-ip", + "py-php_binary_framed-ip", + "py-rb_accel-binary_buffered-ip", + "py-rb_accel-binary_framed-ip", + "py-rb_accel_buffered-ip", + "py-rb_accel_framed-ip", + "py-rb_binary-accel_buffered-ip", + "py-rb_binary-accel_framed-ip", + "py-rb_binary_buffered-ip", + "py-rb_binary_framed-ip", + "py-rb_compact_buffered-ip", + "py-rb_compact_framed-ip", + "py-rb_json_buffered-ip", + "py-rb_json_framed-ip", + "rb-c_glib_accel-binary_buffered-ip", + "rb-c_glib_accel-binary_framed-ip", + "rb-c_glib_binary_buffered-ip", + "rb-c_glib_binary_framed-ip", + "rb-cpp_accel-binary_buffered-ip", + "rb-cpp_accel-binary_framed-ip", + "rb-cpp_binary_buffered-ip", + "rb-cpp_binary_framed-ip", + "rb-cpp_compact_buffered-ip", + "rb-cpp_compact_framed-ip", + "rb-cpp_json_buffered-ip", + "rb-cpp_json_framed-ip", + "rb-csharp_accel-binary_buffered-ip", + "rb-csharp_accel-binary_framed-ip", + "rb-csharp_binary_buffered-ip", + "rb-csharp_binary_framed-ip", + "rb-csharp_compact_buffered-ip", + "rb-csharp_compact_framed-ip", + "rb-csharp_json_buffered-ip", + "rb-csharp_json_framed-ip", + "rb-go_accel-binary_buffered-ip", + "rb-go_accel-binary_framed-ip", + "rb-go_binary_buffered-ip", + "rb-go_binary_framed-ip", + "rb-go_compact_buffered-ip", + "rb-go_compact_framed-ip", + "rb-go_json_buffered-ip", + "rb-go_json_framed-ip", + "rb-hs_accel-binary_buffered-ip", + "rb-hs_accel-binary_framed-ip", + "rb-hs_binary_buffered-ip", + "rb-hs_binary_framed-ip", + "rb-hs_compact_buffered-ip", + "rb-hs_compact_framed-ip", + "rb-hs_json_buffered-ip", + "rb-hs_json_framed-ip", + "rb-java_accel-binary_buffered-ip", + "rb-java_accel-binary_framed-fastframed-ip", + "rb-java_accel-binary_framed-ip", + "rb-java_binary_buffered-ip", + "rb-java_binary_framed-fastframed-ip", + "rb-java_binary_framed-ip", + "rb-java_compact_buffered-ip", + "rb-java_compact_framed-fastframed-ip", + "rb-java_compact_framed-ip", + "rb-java_json_buffered-ip", + "rb-java_json_framed-fastframed-ip", + "rb-java_json_framed-ip", + "rb-nodejs_accel-binary_buffered-ip", + "rb-nodejs_accel-binary_framed-ip", + "rb-nodejs_binary_buffered-ip", + "rb-nodejs_binary_framed-ip", + "rb-nodejs_compact_buffered-ip", + "rb-nodejs_compact_framed-ip", + "rb-nodejs_json_buffered-ip", + "rb-nodejs_json_framed-ip", + "rb-php_accel-binary_framed-ip", + "rb-php_binary_framed-ip", + "rb-py_accel-binary_buffered-ip", + "rb-py_accel-binary_framed-ip", + "rb-py_accel_buffered-ip", + "rb-py_accel_framed-ip", + "rb-py_binary-accel_buffered-ip", + "rb-py_binary-accel_framed-ip", + "rb-py_binary_buffered-ip", + "rb-py_binary_framed-ip", + "rb-py_compact_buffered-ip", + "rb-py_compact_framed-ip", + "rb-py_json_buffered-ip", + "rb-py_json_framed-ip", + "rb-rb_accel-binary_buffered-ip", + "rb-rb_accel-binary_framed-ip", + "rb-rb_accel_buffered-ip", + "rb-rb_accel_framed-ip", + "rb-rb_binary-accel_buffered-ip", + "rb-rb_binary-accel_framed-ip", + "rb-rb_binary_buffered-ip", + "rb-rb_binary_framed-ip", + "rb-rb_compact_buffered-ip", + "rb-rb_compact_framed-ip", + "rb-rb_json_buffered-ip", + "rb-rb_json_framed-ip" +] \ No newline at end of file http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/perl/Makefile.am ---------------------------------------------------------------------- diff --git a/test/perl/Makefile.am b/test/perl/Makefile.am index 291106b..d975f69 100644 --- a/test/perl/Makefile.am +++ b/test/perl/Makefile.am @@ -17,11 +17,13 @@ # under the License. # -THRIFT = $(top_srcdir)/compiler/cpp/thrift +THRIFT = $(top_builddir)/compiler/cpp/thrift stubs: ../ThriftTest.thrift $(THRIFT) --gen perl ../ThriftTest.thrift +precross: stubs + check: stubs clean-local: http://git-wip-us.apache.org/repos/asf/thrift/blob/41ad4342/test/perl/TestClient.pl ---------------------------------------------------------------------- diff --git a/test/perl/TestClient.pl b/test/perl/TestClient.pl index ca1d47e..5a9a6f1 100644 --- a/test/perl/TestClient.pl +++ b/test/perl/TestClient.pl @@ -41,6 +41,11 @@ $|++; my $host = 'localhost'; my $port = 9090; +foreach my $arg (@ARGV) { + if($arg =~ /^--port=([0-9]+)/) { + $port = $1; + } +} my $socket = new Thrift::Socket($host, $port);
