THRIFT-3400 Add Erlang to cross test
Client: Test, Erlang
Patch: Nobuaki Sukegawa
diff --git a/.gitignore b/.gitignore
index 44e630a..c1cc1a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -245,8 +245,8 @@
 /test/dart/**/pubspec.lock
 /test/log/
 /test/test.log
-/test/erl/.eunit/
 /test/erl/.generated
+/test/erl/ebin
 /test/go/bin/
 /test/go/ThriftTest.thrift
 /test/go/gopath
diff --git a/.travis.yml b/.travis.yml
index 5be7c34..4d2ecdb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -67,7 +67,7 @@
     # Put it here because it's most time consuming
     - TEST_NAME="make cross (automake)"
       THRIFT_CROSSTEST_CONCURRENCY=6
-      CONFIG="--enable-tutorial=no --without-erlang --without-lua --without-haxe --without-d"
+      CONFIG="--enable-tutorial=no --without-lua --without-haxe --without-d"
       ALL_DEPS="yes"
       MAKE_TARGET="cross"
       ERROR_LOG="test/log/unexpected_failures.log"
diff --git a/Makefile.am b/Makefile.am
index eeeb4f2..73bc48f 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,7 @@
 space := $(empty) $(empty)
 comma := ,
 
-CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@
+CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_JAVA@ @MAYBE_CSHARP@ @MAYBE_PYTHON@ @MAYBE_RUBY@ @MAYBE_HASKELL@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@
 CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS))
 
 cross: precross
diff --git a/build/docker/centos/Dockerfile b/build/docker/centos/Dockerfile
index c4c273c..8d5596a 100644
--- a/build/docker/centos/Dockerfile
+++ b/build/docker/centos/Dockerfile
@@ -67,7 +67,7 @@
 
 # Erlang Dependencies
 RUN curl -sSL http://packages.erlang-solutions.com/rpm/centos/erlang_solutions.repo -o /etc/yum.repos.d/erlang_solutions.repo && \
-    yum install -y erlang-kernel erlang-erts erlang-stdlib erlang-eunit erlang-rebar
+    yum install -y erlang-kernel erlang-erts erlang-stdlib erlang-eunit erlang-rebar erlang-tools
 
 # Go Dependencies
 RUN curl -sSL https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar -C /usr/lib/ -xz && \
diff --git a/build/docker/ubuntu/Dockerfile b/build/docker/ubuntu/Dockerfile
index 0148006..4ad94e1 100644
--- a/build/docker/ubuntu/Dockerfile
+++ b/build/docker/ubuntu/Dockerfile
@@ -56,7 +56,7 @@
 RUN echo 'deb http://packages.erlang-solutions.com/debian trusty contrib' > /etc/apt/sources.list.d/erlang_solutions.list && \
     curl -sSL http://packages.erlang-solutions.com/debian/erlang_solutions.asc | sudo apt-key add - && \
     apt-get update && \
-    apt-get install -y erlang-base erlang-eunit erlang-dev
+    apt-get install -y erlang-base erlang-eunit erlang-dev erlang-tools
 
 # GO dependencies
 RUN curl -sSL https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar -C /usr/lib/ -xz && \
diff --git a/configure.ac b/configure.ac
index 701fdc9..3d1b15f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -796,6 +796,8 @@
 AC_SUBST([MAYBE_GO])
 if test "$have_nodejs" = "yes" ; then MAYBE_NODEJS="nodejs" ; else MAYBE_NODEJS="" ; fi
 AC_SUBST([MAYBE_NODEJS])
+if test "$have_erlang" = "yes" ; then MAYBE_ERLANG="erl" ; else MAYBE_ERLANG="" ; fi
+AC_SUBST([MAYBE_ERLANG])
 
 AC_OUTPUT
 
diff --git a/lib/erl/Makefile.am b/lib/erl/Makefile.am
index f577a63..21d21bf 100644
--- a/lib/erl/Makefile.am
+++ b/lib/erl/Makefile.am
@@ -19,21 +19,26 @@
 
 THRIFT = ../../compiler/cpp/thrift
 THRIFT_FILES = $(wildcard test/*.thrift) \
+		  ../../test/NameConflictTest.thrift \
 		  ../../test/ThriftTest.thrift
 
 if ERLANG_OTP16
-.generated: $(THRIFT) $(THRIFT_FILES)
-	for f in $(THRIFT_FILES) ; do \
-		$(THRIFT) --gen erl:otp16 -o test $$f ; \
-	done ;
+ERL_FLAG = erl:otp16
+ERL_FLAG_LEGACY = erl:otp16,legacynames
+# otp16 + maps does not make sense. We need to generate it anyway to avoid include error.
+ERL_FLAG_MAPS = erl:otp16
 else
+ERL_FLAG = erl
+ERL_FLAG_LEGACY = erl:legacynames
+ERL_FLAG_MAPS = erl:maps
+endif
 .generated: $(THRIFT) $(THRIFT_FILES)
 	for f in $(THRIFT_FILES) ; do \
-		$(THRIFT) --gen erl -o test $$f ; \
-	done ; \
-	$(THRIFT) --gen erl:maps -o test test/Thrift3214.thrift ; \
+		$(THRIFT) --gen $(ERL_FLAG) -o test $$f ; \
+	done
+	$(THRIFT) --gen $(ERL_FLAG_LEGACY) -o test test/flags/LegacyNames.thrift
+	$(THRIFT) --gen $(ERL_FLAG_MAPS) -o test test/flags/Thrift3214.thrift
 	touch .generated
-endif
 
 all: .generated
 	./rebar get-deps
diff --git a/test/erl/LegacyNames.thrift b/lib/erl/test/flags/LegacyNames.thrift
similarity index 100%
rename from test/erl/LegacyNames.thrift
rename to lib/erl/test/flags/LegacyNames.thrift
diff --git a/lib/erl/test/Thrift3214.thrift b/lib/erl/test/flags/Thrift3214.thrift
similarity index 100%
rename from lib/erl/test/Thrift3214.thrift
rename to lib/erl/test/flags/Thrift3214.thrift
diff --git a/test/erl/src/legacy_names_test.erl b/lib/erl/test/legacy_names_test.erl
similarity index 97%
rename from test/erl/src/legacy_names_test.erl
rename to lib/erl/test/legacy_names_test.erl
index 2ace7d0..c16aa3e 100644
--- a/test/erl/src/legacy_names_test.erl
+++ b/lib/erl/test/legacy_names_test.erl
@@ -22,7 +22,7 @@
 
 -include_lib("eunit/include/eunit.hrl").
 
--include("legacyNames_constants.hrl").
+-include("gen-erl/legacyNames_constants.hrl").
 
 record_generation_test_() ->
   [
@@ -66,4 +66,4 @@
       {struct, [{1, {struct, {'legacyNames_types', 'xception'}}}]},
       legacyNames_thrift:function_info(names, exceptions)
     )}
-  ].
\ No newline at end of file
+  ].
diff --git a/test/erl/src/name_conflict_test.erl b/lib/erl/test/name_conflict_test.erl
similarity index 98%
rename from test/erl/src/name_conflict_test.erl
rename to lib/erl/test/name_conflict_test.erl
index 5576ffa..b01df57 100644
--- a/test/erl/src/name_conflict_test.erl
+++ b/lib/erl/test/name_conflict_test.erl
@@ -22,7 +22,7 @@
 
 -include_lib("eunit/include/eunit.hrl").
 
--include("name_conflict_test_constants.hrl").
+-include("gen-erl/name_conflict_test_constants.hrl").
 
 record_generation_test_() ->
   [
@@ -296,4 +296,4 @@
       {struct, [{1, {struct, {name_conflict_test_types, 'Problem_'}}}]},
       extern_thrift:function_info('Foo', exceptions)
     )}
-  ].
\ No newline at end of file
+  ].
diff --git a/test/erl/src/thrift_test_test.erl b/lib/erl/test/thrift_test_test.erl
similarity index 99%
rename from test/erl/src/thrift_test_test.erl
rename to lib/erl/test/thrift_test_test.erl
index 07dfe1c..e506437 100644
--- a/test/erl/src/thrift_test_test.erl
+++ b/lib/erl/test/thrift_test_test.erl
@@ -23,7 +23,7 @@
 
 -include_lib("eunit/include/eunit.hrl").
 
--include("thrift_test_constants.hrl").
+-include("gen-erl/thrift_test_constants.hrl").
 
 constant_test_() ->
   [
@@ -652,4 +652,4 @@
       {struct, []},
       second_service_thrift:function_info(secondtestString, exceptions)
     )}
-  ].
\ No newline at end of file
+  ].
diff --git a/test/Makefile.am b/test/Makefile.am
index 7590921..593b1c4 100755
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -72,6 +72,7 @@
 
 if WITH_ERLANG
 SUBDIRS += erl
+PRECROSS_TARGET += precross-erl
 endif
 
 #
diff --git a/test/erl/Makefile.am b/test/erl/Makefile.am
index 1940ce3..b489d8e 100644
--- a/test/erl/Makefile.am
+++ b/test/erl/Makefile.am
@@ -22,19 +22,23 @@
 
 THRIFT_FILES = $(wildcard ../*.thrift)
 
+if ERLANG_OTP16
+ERL_FLAG = erl:otp16
+else
+ERL_FLAG = erl
+endif
 # make sure ThriftTest.thrift is generated last to prevent conflicts with other *.thrift files
 .generated: $(THRIFT_FILES)
 	for f in $(THRIFT_FILES) ; do \
-	  $(THRIFT) --gen erl $$f ; \
+	  $(THRIFT) --gen $(ERL_FLAG) -o src $$f ; \
 	done ; \
-	$(THRIFT) --gen erl:legacynames LegacyNames.thrift
-	$(THRIFT) --gen erl ../ThriftTest.thrift
+	$(THRIFT) --gen $(ERL_FLAG) -o src ../ThriftTest.thrift
 	touch .generated
 
-check: .generated
-	$(REBAR) eunit
+precross: .generated
+	$(REBAR) compile
 
 clean:
 	rm -f .generated
-	rm -rf gen-erl
+	rm -rf src/gen-erl
 	$(REBAR) clean
diff --git a/test/erl/rebar.config b/test/erl/rebar.config
index 6035849..59a0788 100644
--- a/test/erl/rebar.config
+++ b/test/erl/rebar.config
@@ -1,5 +1,6 @@
+{sub_dirs, ["../../lib/erl"]}.
+
 {erl_opts, [
   debug_info,
-  {i, ["gen-erl"]},
-  {src_dirs, ["gen-erl"]}
+  {i, "../../lib/erl/include"}
 ]}.
diff --git a/lib/erl/test/test_client.erl b/test/erl/src/test_client.erl
similarity index 81%
rename from lib/erl/test/test_client.erl
rename to test/erl/src/test_client.erl
index 4e85c47..fad0988 100644
--- a/lib/erl/test/test_client.erl
+++ b/test/erl/src/test_client.erl
@@ -27,26 +27,35 @@
                   client_opts = []}).
 
 parse_args(Args) -> parse_args(Args, #options{}).
-parse_args([], Opts) -> Opts;
+parse_args([], Opts) ->
+  Opts;
 parse_args([Head | Rest], Opts) ->
     NewOpts =
-        case catch list_to_integer(Head) of
-            Port when is_integer(Port) ->
-                Opts#options{port = Port};
-            _Else ->
-                case Head of
+        case Head of
+            "--port=" ++ Port ->
+                case string:to_integer(Port) of
+                  {IntPort,_} when is_integer(IntPort) ->
+                    Opts#options{port = IntPort};
+                  _Else ->
+                    erlang:error({bad_arg, Head})
+                end;
+            "--transport=" ++ Trans ->
+                % TODO: Enable Buffered and HTTP transport
+                case Trans of
                     "framed" ->
                         Opts#options{client_opts = [{framed, true} | Opts#options.client_opts]};
-                    "" ->
-                        Opts;
                     _Else ->
-                        erlang:error({bad_arg, Head})
-                end
+                        Opts
+                end;
+            "--protocol=binary" ->
+                % TODO: Enable JSON protocol
+                Opts;
+            _Else ->
+                erlang:error({bad_arg, Head})
         end,
     parse_args(Rest, NewOpts).
 
-
-start() -> start([]).
+start() -> start(init:get_plain_arguments()).
 start(Args) ->
   #options{port = Port, client_opts = ClientOpts} = parse_args(Args),
   {ok, Client0} = thrift_client_util:new(
@@ -70,9 +79,9 @@
   DemoDict = dict:from_list([ {Key, Key-10} || Key <- lists:seq(0,10) ]),
   DemoSet = sets:from_list([ Key || Key <- lists:seq(-3,3) ]),
 
-  %DemoInsane = #insanity{
-  %  userMap = dict:from_list([{?thriftTest_FIVE, 5000}]),
-  %  xtructs = [#xtruct{ string_thing = <<"Truck">>, byte_thing = 8, i32_thing = 8, i64_thing = 8}]},
+  DemoInsane = #'Insanity'{
+    userMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_FIVE, 5000}]),
+    xtructs = [#'Xtruct'{ string_thing = <<"Truck">>, byte_thing = 8, i32_thing = 8, i64_thing = 8}]},
 
   {Client01, {ok, ok}} = thrift_client:call(Client0, testVoid, []),
 
@@ -91,11 +100,8 @@
   {Client13, {ok, [-1,2,3]}}        = thrift_client:call(Client12, testList, [[-1,2,3]]),
   {Client14, {ok, 1}}               = thrift_client:call(Client13, testEnum, [?THRIFT_TEST_NUMBERZ_ONE]),
   {Client15, {ok, 309858235082523}} = thrift_client:call(Client14, testTypedef, [309858235082523]),
-
-  % No python implementation, but works with C++ and Erlang.
-  %{Client16, {ok, InsaneResult}}    = thrift_client:call(Client15, testInsanity, [DemoInsane]),
-  %io:format("~p~n", [InsaneResult]),
-  Client16 = Client15,
+  {Client16, {ok, InsaneResult}}    = thrift_client:call(Client15, testInsanity, [DemoInsane]),
+  io:format("~p~n", [InsaneResult]),
 
   {Client17, {ok, #'Xtruct'{string_thing = <<"Message">>}}} =
     thrift_client:call(Client16, testMultiException, ["Safe", "Message"]),
diff --git a/lib/erl/test/test_thrift_server.erl b/test/erl/src/test_thrift_server.erl
similarity index 82%
rename from lib/erl/test/test_thrift_server.erl
rename to test/erl/src/test_thrift_server.erl
index 1f43b72..6969465 100644
--- a/lib/erl/test/test_thrift_server.erl
+++ b/test/erl/src/test_thrift_server.erl
@@ -19,34 +19,42 @@
 
 -module(test_thrift_server).
 
--export([go/0, go/1, start_link/2, handle_function/2]).
+-export([start/0, start/1, start_link/2, handle_function/2]).
 
+-include("thrift_constants.hrl").
 -include("gen-erl/thrift_test_types.hrl").
 
 -record(options, {port = 9090,
                   server_opts = []}).
 
 parse_args(Args) -> parse_args(Args, #options{}).
-parse_args([], Opts) -> Opts;
+parse_args([], Opts) ->
+  Opts;
 parse_args([Head | Rest], Opts) ->
     NewOpts =
-        case catch list_to_integer(Head) of
-            Port when is_integer(Port) ->
-                Opts#options{port = Port};
-            _Else ->
-                case Head of
+        case Head of
+            "--port=" ++ Port ->
+                case string:to_integer(Port) of
+                  {IntPort,_} when is_integer(IntPort) ->
+                    Opts#options{port = IntPort};
+                  _Else ->
+                    erlang:error({bad_arg, Head})
+                end;
+            "--transport=" ++ Trans ->
+                case Trans of
                     "framed" ->
                         Opts#options{server_opts = [{framed, true} | Opts#options.server_opts]};
-                    "" ->
-                        Opts;
                     _Else ->
-                        erlang:error({bad_arg, Head})
-                end
+                        Opts
+                end;
+            "--protocol=" ++ _ -> Opts;
+            _Else ->
+                erlang:error({bad_arg, Head})
         end,
     parse_args(Rest, NewOpts).
 
-go() -> go([]).
-go(Args) ->
+start() -> start(init:get_plain_arguments()).
+start(Args) ->
     #options{port = Port, server_opts = ServerOpts} = parse_args(Args),
     spawn(fun() -> start_link(Port, ServerOpts), receive after infinity -> ok end end).
 
@@ -65,6 +73,10 @@
     io:format("testString: ~p~n", [S]),
     {reply, S};
 
+handle_function(testBool, {B}) when is_boolean(B) ->
+    io:format("testBool: ~p~n", [B]),
+    {reply, B};
+
 handle_function(testByte, {I8}) when is_integer(I8) ->
     io:format("testByte: ~p~n", [I8]),
     {reply, I8};
@@ -107,6 +119,10 @@
     io:format("testMap: ~p~n", [dict:to_list(Map)]),
     {reply, Map};
 
+handle_function(testStringMap, {Map}) ->
+    io:format("testStringMap: ~p~n", [dict:to_list(Map)]),
+    {reply, Map};
+
 handle_function(testSet, {Set}) ->
     true = sets:is_set(Set),
     io:format("testSet: ~p~n", [sets:to_list(Set)]),
@@ -127,8 +143,8 @@
 handle_function(testMapMap, {Hello}) ->
     io:format("testMapMap: ~p~n", [Hello]),
 
-    PosList = [{I, I}   || I <- lists:seq(1, 5)],
-    NegList = [{-I, -I} || I <- lists:seq(1, 5)],
+    PosList = [{I, I}   || I <- lists:seq(1, 4)],
+    NegList = [{-I, -I} || I <- lists:seq(1, 4)],
 
     MapMap = dict:from_list([{4,  dict:from_list(PosList)},
                              {-4, dict:from_list(NegList)}]),
@@ -149,13 +165,10 @@
       xtructs = [Goodbye]
       },
 
-    Looney = #'Insanity'{
-      userMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_FIVE, 5}]),
-      xtructs = [Hello]
-      },
+    Looney = #'Insanity'{},
 
-    FirstMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_TWO, Crazy},
-                               {?THRIFT_TEST_NUMBERZ_THREE, Crazy}]),
+    FirstMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_TWO, Insanity},
+                               {?THRIFT_TEST_NUMBERZ_THREE, Insanity}]),
 
     SecondMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_SIX, Looney}]),
 
@@ -185,6 +198,8 @@
         <<"Xception">> ->
             throw(#'Xception'{errorCode = 1001,
                             message = String});
+        <<"TException">> ->
+            throw({?TApplicationException_Structure});
         _ ->
             ok
     end;
diff --git a/test/erl/src/thrift_test.app.src b/test/erl/src/thrift_test.app.src
index 4dcd377..7896a95 100644
--- a/test/erl/src/thrift_test.app.src
+++ b/test/erl/src/thrift_test.app.src
@@ -19,13 +19,16 @@
 %%% -*- mode:erlang -*-
 {application, thrift_test, [
   % A quick description of the application.
-  {description, "tests for thrift erlang compiler backend"},
+  {description, "Thrift cross language test"},
 
   % The version of the applicaton
   {vsn, "1.0.0-dev"},
 
   % All modules used by the application.
-  {modules, [legacy_names_test, name_conflict_test, thrift_test_test]},
+  {modules, [
+    test_client,
+    test_thrift_server
+  ]},
 
   % All of the registered names the application uses. This can be ignored.
   {registered, []},
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index dbc97cc..801b1dc 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -44,6 +44,11 @@
   "csharp-nodejs_json_buffered-ip-ssl",
   "csharp-nodejs_json_framed-ip",
   "csharp-nodejs_json_framed-ip-ssl",
+  "erl-nodejs_binary_buffered-ip",
+  "erl-rb_binary-accel_buffered-ip",
+  "erl-rb_binary-accel_framed-ip",
+  "erl-rb_binary_buffered-ip",
+  "erl-rb_binary_framed-ip",
   "go-dart_binary_framed-ip",
   "go-dart_json_framed-ip",
   "go-hs_json_buffered-ip",
diff --git a/test/tests.json b/test/tests.json
index 8c152fc..3d8ddbe 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -376,5 +376,52 @@
       ]
     },
     "workdir": "dart"
+  },
+  {
+    "name": "erl",
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "binary"
+    ],
+    "client": {
+      "command": [
+        "erl",
+        "+K",
+        "true",
+        "-noshell",
+        "-pa",
+        "../../lib/erl/ebin/",
+        "-pa",
+        "./ebin",
+        "-s",
+        "test_client",
+        "-s",
+        "init",
+        "stop",
+        "-extra"
+      ]
+    },
+    "server": {
+      "command": [
+        "erl",
+        "+K",
+        "true",
+        "-noshell",
+        "-pa",
+        "../../lib/erl/ebin/",
+        "-pa",
+        "./ebin",
+        "-s",
+        "test_thrift_server",
+        "-extra"
+      ]
+    },
+    "workdir": "erl"
   }
 ]
diff --git a/tutorial/erl/json_client.erl b/tutorial/erl/json_client.erl
index 524e9ae..312b01e 100644
--- a/tutorial/erl/json_client.erl
+++ b/tutorial/erl/json_client.erl
@@ -27,8 +27,8 @@
 
 -export([t/0]).
 
-%% Client constructor for the common-case of socket transports
-%% with the binary protocol
+%% Client constructor for the http transports
+%% with the json protocol
 new_client(Host, Path, Service, _Options) ->
     {ProtoOpts, TransOpts} = {[],[]},
     TransportFactory = fun() -> thrift_http_transport:new(Host, Path, TransOpts) end,