THRIFT-2110 Erlang: Support for Multiplexing Services on any Transport, Protocol and Server Client: Erlang Patch: David Robakowski rebased by Nobuaki Sukegawa
Modification: Return value fix in thrift_client uncovered by added tests Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/ae971ce9 Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/ae971ce9 Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/ae971ce9 Branch: refs/heads/master Commit: ae971ce917bf9b60ee8ae83b834dad1eb149a82f Parents: ca93936 Author: David Robakowski <[email protected]> Authored: Fri Aug 2 12:16:00 2013 +0200 Committer: Nobuaki Sukegawa <[email protected]> Committed: Mon Nov 23 17:07:10 2015 +0900 ---------------------------------------------------------------------- .gitignore | 4 + lib/erl/include/thrift_constants.hrl | 3 + lib/erl/src/thrift_client.erl | 3 +- lib/erl/src/thrift_client_util.erl | 34 +++++++++ lib/erl/src/thrift_multiplexed_map_wrapper.erl | 57 ++++++++++++++ lib/erl/src/thrift_multiplexed_protocol.erl | 83 +++++++++++++++++++++ lib/erl/src/thrift_processor.erl | 56 ++++++++------ lib/erl/src/thrift_socket_server.erl | 55 ++++++++++++-- lib/erl/test/multiplexing.thrift | 7 ++ lib/erl/test/multiplexing_test.erl | 57 ++++++++++++++ lib/erl/test/thrift_socket_server_test.erl | 49 ++++++++++++ 11 files changed, 379 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index c1cc1a9..040c71e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ cmake-* node_modules compile test-driver +erl_crash.dump .sonar .DS_Store @@ -182,6 +183,9 @@ test-driver /lib/erl/.eunit /lib/erl/ebin /lib/erl/deps/ +/lib/erl/src/thrift.app.src +/lib/erl/test/*.hrl +/lib/erl/test/*.beam /lib/haxe/test/bin /lib/hs/dist /lib/java/build http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/include/thrift_constants.hrl ---------------------------------------------------------------------- diff --git a/lib/erl/include/thrift_constants.hrl b/lib/erl/include/thrift_constants.hrl index 7b724a0..7cb29eb 100644 --- a/lib/erl/include/thrift_constants.hrl +++ b/lib/erl/include/thrift_constants.hrl @@ -57,3 +57,6 @@ -define(TApplicationException_INVALID_TRANSFORM, 8). -define(TApplicationException_INVALID_PROTOCOL, 9). -define(TApplicationException_UNSUPPORTED_CLIENT_TYPE, 10). + +-define (MULTIPLEXED_SERVICE_SEPARATOR, ":"). +-define (MULTIPLEXED_ERROR_HANDLER_KEY, "error_handler"). http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_client.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_client.erl b/lib/erl/src/thrift_client.erl index 209bd4c..7bf50a5 100644 --- a/lib/erl/src/thrift_client.erl +++ b/lib/erl/src/thrift_client.erl @@ -39,6 +39,7 @@ call(Client = #tclient{}, Function, Args) when is_atom(Function), is_list(Args) -> case send_function_call(Client, Function, Args) of {ok, Client1} -> receive_function_result(Client1, Function); + {{error, X}, Client1} -> {Client1, {error, X}}; Else -> Else end. @@ -66,7 +67,7 @@ close(#tclient{protocol=Protocol}) -> send_function_call(Client = #tclient{service = Service}, Function, Args) -> {Params, Reply} = try {Service:function_info(Function, params_type), Service:function_info(Function, reply_type)} - catch error:function_clause -> no_function + catch error:function_clause -> {no_function, 0} end, MsgType = case Reply of oneway_void -> ?tMessageType_ONEWAY; http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_client_util.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_client_util.erl b/lib/erl/src/thrift_client_util.erl index 265c308..1dbe51e 100644 --- a/lib/erl/src/thrift_client_util.erl +++ b/lib/erl/src/thrift_client_util.erl @@ -20,6 +20,11 @@ -module(thrift_client_util). -export([new/4]). +-export([new_multiplexed/3, new_multiplexed/4]). + +-type service_name() :: nonempty_string(). +-type service_module() :: atom(). +-type multiplexed_service_map() :: [{ServiceName::service_name(), ServiceModule::service_module()}]. %% %% Splits client options into client, protocol, and transport options @@ -76,3 +81,32 @@ new(Host, Port, Service, Options) {error, Error} -> {error, Error} end. + +-spec new_multiplexed(Host, Port, Services, Options) -> {ok, ServiceThriftClientList} when + Host :: nonempty_string(), + Port :: non_neg_integer(), + Services :: multiplexed_service_map(), + Options :: list(), + ServiceThriftClientList :: [{ServiceName::list(), ThriftClient::term()}]. +new_multiplexed(Host, Port, Services, Options) when is_integer(Port), + is_list(Services), + is_list(Options) -> + new_multiplexed(thrift_socket_transport:new_transport_factory(Host, Port, Options), Services, Options). + +-spec new_multiplexed(TransportFactoryTuple, Services, Options) -> {ok, ServiceThriftClientList} when + TransportFactoryTuple :: {ok, TransportFactory::term()}, + Services :: multiplexed_service_map(), + Options :: list(), + ServiceThriftClientList :: [{ServiceName::service_name(), ThriftClient::term()}]. +new_multiplexed(TransportFactoryTuple, Services, Options) when is_list(Services), + is_list(Options), + is_tuple(TransportFactoryTuple) -> + {ProtoOpts, _} = split_options(Options), + + {ok, TransportFactory} = TransportFactoryTuple, + + {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory(TransportFactory, ProtoOpts), + + {ok, Protocol} = ProtocolFactory(), + + {ok, [{ServiceName, element(2, thrift_client:new(element(2, thrift_multiplexed_protocol:new(Protocol, ServiceName)), Service))} || {ServiceName, Service} <- Services]}. http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_multiplexed_map_wrapper.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_multiplexed_map_wrapper.erl b/lib/erl/src/thrift_multiplexed_map_wrapper.erl new file mode 100644 index 0000000..34c5e95 --- /dev/null +++ b/lib/erl/src/thrift_multiplexed_map_wrapper.erl @@ -0,0 +1,57 @@ +%% +%% 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. +%% + +-module(thrift_multiplexed_map_wrapper). + +-export([ + new/0 + ,store/3 + ,find/2 + ,fetch/2 + ]). + +-type service_handler() :: nonempty_string(). +-type module_() :: atom(). +-type service_handler_map() :: [{ServiceHandler::service_handler(), Module::module_()}]. + +-spec new() -> service_handler_map(). +new() -> + orddict:new(). + +-spec store(ServiceHandler, Module, Map) -> NewMap when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(), + NewMap :: service_handler_map(). +store(ServiceHandler, Module, Map) -> + orddict:store(ServiceHandler, Module, Map). + +-spec find(ServiceHandler, Map) -> {ok, Module} | error when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(). +find(ServiceHandler, Map) -> + orddict:find(ServiceHandler, Map). + +-spec fetch(ServiceHandler, Map) -> Module when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(). +fetch(ServiceHandler, Map) -> + orddict:fetch(ServiceHandler, Map). http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_multiplexed_protocol.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_multiplexed_protocol.erl b/lib/erl/src/thrift_multiplexed_protocol.erl new file mode 100644 index 0000000..5f7b70c --- /dev/null +++ b/lib/erl/src/thrift_multiplexed_protocol.erl @@ -0,0 +1,83 @@ +%% +%% 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. +%% + +-module(thrift_multiplexed_protocol). + +-behaviour(thrift_protocol). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-include("thrift_protocol_behaviour.hrl"). + +-export([new/2, + read/2, + write/2, + flush_transport/1, + close_transport/1 + ]). + +-record(protocol, {module, data}). +-type protocol() :: #protocol{}. + +-record (multiplexed_protocol, {protocol_module_to_decorate::atom(), + protocol_data_to_decorate::term(), + service_name::nonempty_string()}). + +-type state() :: #multiplexed_protocol{}. + +-spec new(ProtocolToDecorate::protocol(), ServiceName::nonempty_string()) -> {ok, Protocol::protocol()}. +new(ProtocolToDecorate, ServiceName) when is_record(ProtocolToDecorate, protocol), + is_list(ServiceName) -> + State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolToDecorate#protocol.module, + protocol_data_to_decorate = ProtocolToDecorate#protocol.data, + service_name = ServiceName}, + thrift_protocol:new(?MODULE, State). + +flush_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}) -> + {State1, ok} = ProtocolModuleToDecorate:flush_transport(State0), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +close_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}) -> + {State1, ok} = ProtocolModuleToDecorate:close_transport(State0), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0, + service_name = ServiceName}, + Message = #protocol_message_begin{name = Name}) -> + {State1, ok} = ProtocolModuleToDecorate:write(State0, + Message#protocol_message_begin{name=ServiceName ++ + ?MULTIPLEXED_SERVICE_SEPARATOR ++ + Name}), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}; + +write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}, + Message) -> + {State1, ok} = ProtocolModuleToDecorate:write(State0, Message), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +read(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}, + Message) -> + {State1, Result} = ProtocolModuleToDecorate:read(State0, Message), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, Result}. http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_processor.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_processor.erl b/lib/erl/src/thrift_processor.erl index d474294..5c9f26f 100644 --- a/lib/erl/src/thrift_processor.erl +++ b/lib/erl/src/thrift_processor.erl @@ -33,41 +33,53 @@ init({_Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) -> handler = Handler}). loop(State0 = #thrift_processor{protocol = Proto0, - handler = Handler}) -> + handler = Handler, + service = Service}) -> + {Proto1, MessageBegin} = thrift_protocol:read(Proto0, message_begin), State1 = State0#thrift_processor{protocol = Proto1}, + + ErrorHandler = fun + (HandlerModules) when is_list(HandlerModules) -> thrift_multiplexed_map_wrapper:fetch(?MULTIPLEXED_ERROR_HANDLER_KEY, HandlerModules); + (HandlerModule) -> HandlerModule + end, + case MessageBegin of + #protocol_message_begin{name = Function, - type = ?tMessageType_CALL, - seqid = Seqid} -> - case handle_function(State1, list_to_atom(Function), Seqid) of - {State2, ok} -> loop(State2); - {_State2, {error, Reason}} -> - Handler:handle_error(list_to_atom(Function), Reason), - thrift_protocol:close_transport(Proto1), - ok - end; - #protocol_message_begin{name = Function, - type = ?tMessageType_ONEWAY, - seqid = Seqid} -> - case handle_function(State1, list_to_atom(Function), Seqid) of - {State2, ok} -> loop(State2); - {_State2, {error, Reason}} -> - Handler:handle_error(list_to_atom(Function), Reason), - thrift_protocol:close_transport(Proto1), - ok + type = Type, + seqid = Seqid} when Type =:= ?tMessageType_CALL; Type =:= ?tMessageType_ONEWAY -> + case string:tokens(Function, ?MULTIPLEXED_SERVICE_SEPARATOR) of + [ServiceName, FunctionName] -> + ServiceModule = thrift_multiplexed_map_wrapper:fetch(ServiceName, Service), + ServiceHandler = thrift_multiplexed_map_wrapper:fetch(ServiceName, Handler), + case handle_function(State1#thrift_processor{service=ServiceModule, handler=ServiceHandler}, list_to_atom(FunctionName), Seqid) of + {State2, ok} -> loop(State2#thrift_processor{service=Service, handler=Handler}); + {_State2, {error, Reason}} -> + apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]), + thrift_protocol:close_transport(Proto1), + ok + end; + _ -> + case handle_function(State1, list_to_atom(Function), Seqid) of + {State2, ok} -> loop(State2); + {_State2, {error, Reason}} -> + apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]), + thrift_protocol:close_transport(Proto1), + ok + end end; {error, timeout = Reason} -> - Handler:handle_error(undefined, Reason), + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), thrift_protocol:close_transport(Proto1), ok; {error, closed = Reason} -> %% error_logger:info_msg("Client disconnected~n"), - Handler:handle_error(undefined, Reason), + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), thrift_protocol:close_transport(Proto1), exit(shutdown); {error, Reason} -> - Handler:handle_error(undefined, Reason), + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), thrift_protocol:close_transport(Proto1), exit(shutdown) end. http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/src/thrift_socket_server.erl ---------------------------------------------------------------------- diff --git a/lib/erl/src/thrift_socket_server.erl b/lib/erl/src/thrift_socket_server.erl index e9ad6f4..4e3c052 100644 --- a/lib/erl/src/thrift_socket_server.erl +++ b/lib/erl/src/thrift_socket_server.erl @@ -21,12 +21,19 @@ -behaviour(gen_server). --export([start/1, stop/1]). +-include ("thrift_constants.hrl"). --export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, - handle_info/2]). +-ifdef(TEST). + -compile(export_all). + -export_records([thrift_socket_server]). +-else. + -export([start/1, stop/1]). --export([acceptor_loop/1]). + -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, + handle_info/2]). + + -export([acceptor_loop/1]). +-endif. -record(thrift_socket_server, {port, @@ -94,10 +101,46 @@ parse_options([{ip, Ip} | Rest], State) -> parse_options(Rest, State#thrift_socket_server{ip=ParsedIp}); parse_options([{socket_opts, L} | Rest], State) when is_list(L), length(L) > 0 -> parse_options(Rest, State#thrift_socket_server{socket_opts=L}); -parse_options([{handler, Handler} | Rest], State) -> + +parse_options([{handler, []} | _Rest], _State) -> + throw("At least an error handler must be defined."); +parse_options([{handler, ServiceHandlerPropertyList} | Rest], State) when is_list(ServiceHandlerPropertyList) -> + ServiceHandlerMap = + case State#thrift_socket_server.handler of + undefined -> + lists:foldl( + fun ({ServiceName, ServiceHandler}, Acc) when is_list(ServiceName), is_atom(ServiceHandler) -> + thrift_multiplexed_map_wrapper:store(ServiceName, ServiceHandler, Acc); + (_, _Acc) -> + throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]") + end, thrift_multiplexed_map_wrapper:new(), ServiceHandlerPropertyList); + _ -> throw("Error while parsing the handler option.") + end, + case thrift_multiplexed_map_wrapper:find(?MULTIPLEXED_ERROR_HANDLER_KEY, ServiceHandlerMap) of + {ok, _ErrorHandler} -> parse_options(Rest, State#thrift_socket_server{handler=ServiceHandlerMap}); + error -> throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]") + end; +parse_options([{handler, Handler} | Rest], State) when State#thrift_socket_server.handler == undefined, is_atom(Handler) -> parse_options(Rest, State#thrift_socket_server{handler=Handler}); -parse_options([{service, Service} | Rest], State) -> + +parse_options([{service, []} | _Rest], _State) -> + throw("At least one service module must be defined."); +parse_options([{service, ServiceModulePropertyList} | Rest], State) when is_list(ServiceModulePropertyList) -> + ServiceModuleMap = + case State#thrift_socket_server.service of + undefined -> + lists:foldl( + fun ({ServiceName, ServiceModule}, Acc) when is_list(ServiceName), is_atom(ServiceModule) -> + thrift_multiplexed_map_wrapper:store(ServiceName, ServiceModule, Acc); + (_, _Acc) -> + throw("The service option is not properly configured for multiplexed services. It should be a kind of [{SericeName::list(), ServiceModule::atom()}, ...]") + end, thrift_multiplexed_map_wrapper:new(), ServiceModulePropertyList); + _ -> throw("Error while parsing the service option.") + end, + parse_options(Rest, State#thrift_socket_server{service=ServiceModuleMap}); +parse_options([{service, Service} | Rest], State) when State#thrift_socket_server.service == undefined, is_atom(Service) -> parse_options(Rest, State#thrift_socket_server{service=Service}); + parse_options([{max, Max} | Rest], State) -> MaxInt = case Max of Max when is_list(Max) -> http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/test/multiplexing.thrift ---------------------------------------------------------------------- diff --git a/lib/erl/test/multiplexing.thrift b/lib/erl/test/multiplexing.thrift new file mode 100644 index 0000000..7c7994b --- /dev/null +++ b/lib/erl/test/multiplexing.thrift @@ -0,0 +1,7 @@ +service Multiplexing_Calculator { + i32 add(1: i32 x, 2: i32 y) +} + +service Multiplexing_WeatherReport { + double getTemperature() +} http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/test/multiplexing_test.erl ---------------------------------------------------------------------- diff --git a/lib/erl/test/multiplexing_test.erl b/lib/erl/test/multiplexing_test.erl new file mode 100644 index 0000000..0f2d616 --- /dev/null +++ b/lib/erl/test/multiplexing_test.erl @@ -0,0 +1,57 @@ +-module(multiplexing_test). + +-include_lib("eunit/include/eunit.hrl"). + +-export([ + handle_function/2 + ,handle_error/2 +]). + +start_multiplexed_server_test() -> + + Port = 9090, + Services = [ + {"Multiplexing_Calculator", multiplexing__calculator_thrift}, + {"Multiplexing_WeatherReport", multiplexing__weather_report_thrift} + ], + + {ok, Pid} = thrift_socket_server:start([ + {ip, "127.0.0.1"}, + {port, Port}, + {name, ?MODULE}, + {service, Services}, + {handler, [ + {"error_handler", ?MODULE}, + {"Multiplexing_Calculator", ?MODULE}, + {"Multiplexing_WeatherReport", ?MODULE} + ]} + ]), + + {ok, [{"Multiplexing_Calculator", CalculatorClient0}, + {"Multiplexing_WeatherReport", WeatherReportClient0}]} = thrift_client_util:new_multiplexed("127.0.0.1", Port, Services, []), + + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(WeatherReportClient0, getTemperature, [1])), + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1])), + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1,1,1])), + + ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(CalculatorClient0, getTemperature, [])), + ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(WeatherReportClient0, add, [41, 1])), + + ?assertMatch({_, {ok, 42}}, thrift_client:call(CalculatorClient0, add, [41, 1])), + ?assertMatch({_, {ok, 42.0}}, thrift_client:call(WeatherReportClient0, getTemperature, [])), + + thrift_socket_server:stop(Pid). + +%% HANDLE FUNCTIONS + +%% Calculator handles +handle_function(add, {X, Y}) -> + {reply, X + Y}; + +%% WeatherReport handles +handle_function(getTemperature, {}) -> + {reply, 42.0}. + +handle_error(_F, _Reason) -> +%% ?debugHere, ?debugVal({_F, _Reason}), + ok. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/thrift/blob/ae971ce9/lib/erl/test/thrift_socket_server_test.erl ---------------------------------------------------------------------- diff --git a/lib/erl/test/thrift_socket_server_test.erl b/lib/erl/test/thrift_socket_server_test.erl new file mode 100644 index 0000000..0818b84 --- /dev/null +++ b/lib/erl/test/thrift_socket_server_test.erl @@ -0,0 +1,49 @@ +-module (thrift_socket_server_test). + +-include_lib("eunit/include/eunit.hrl"). + +-include ("thrift_constants.hrl"). + +parse_handler_options_test_() -> + CorrectServiceHandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", ?MODULE}], + MissingErrorHandlerOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}], + WrongService2HandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", "Module"}], + WrongServiceKeyOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {'service1', ?MODULE}, {"Service2", ?MODULE}], + CorrectHandlerTestFunction = fun() -> + ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}])), + {thrift_socket_server,_,_, HandlerList,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}]), + lists:foreach(fun + ({ServiceName, HandlerModule}) -> + ?assertMatch({ok, HandlerModule} when is_atom(HandlerModule), thrift_multiplexed_map_wrapper:find(ServiceName, HandlerList)) + end, CorrectServiceHandlerOptionList) + end, + [ + {"Bad argument for the handler option", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, []}]))}, + {"Try to parse the handler option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, ?MODULE}, {handler, CorrectServiceHandlerOptionList}]))}, + {"Parse the handler option as a non multiplexed service handler", ?_assertMatch({thrift_socket_server,_,_,?MODULE,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, ?MODULE}]))}, + {"No error handler was defined", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, MissingErrorHandlerOptionList}]))}, + {"Bad handler module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongService2HandlerOptionList}]))}, + {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongServiceKeyOptionList}]))}, + {"Try to parse a correct handler option list", CorrectHandlerTestFunction} + ]. + +parse_service_options_test_() -> + CorrectServiceModuleOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}], + WrongService2ModuleOptionList = [{"Service1", ?MODULE}, {"Service2", "thrift_service_module"}], + WrongServiceKeyOptionList = [{'service1', ?MODULE}, {"Service2", ?MODULE}], + CorrectServiceModuleTestFunction = fun() -> + ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}])), + {thrift_socket_server,_, ServiceModuleList,_,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}]), + lists:foreach(fun + ({ServiceName, ServiceModule}) -> + ?assertMatch({ok, ServiceModule} when is_atom(ServiceModule), thrift_multiplexed_map_wrapper:find(ServiceName, ServiceModuleList)) + end, CorrectServiceModuleOptionList) + end, + [ + {"Bad argument for the service option", ?_assertThrow(_, thrift_socket_server:parse_options([{service, []}]))}, + {"Try to parse the service option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{service, ?MODULE}, {service, CorrectServiceModuleOptionList}]))}, + {"Parse a service module for a non multiplexed service", ?_assertMatch({thrift_socket_server,_,?MODULE,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, ?MODULE}]))}, + {"Bad service module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongService2ModuleOptionList}]))}, + {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongServiceKeyOptionList}]))}, + {"Try to parse a correct service option list", CorrectServiceModuleTestFunction} + ].
