diff options
Diffstat (limited to 'lib/diameter/test/diameter_tcp_test.erl')
-rw-r--r-- | lib/diameter/test/diameter_tcp_test.erl | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/lib/diameter/test/diameter_tcp_test.erl b/lib/diameter/test/diameter_tcp_test.erl new file mode 100644 index 0000000000..01b5dc5293 --- /dev/null +++ b/lib/diameter/test/diameter_tcp_test.erl @@ -0,0 +1,482 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Verify the tcp transport component of the Diameter application +%%---------------------------------------------------------------------- +%% +-module(diameter_tcp_test). + +-export([ + init_per_testcase/2, fin_per_testcase/2, + + all/0, + groups/0, + init_per_suite/1, end_per_suite/1, + suite_init/1, suite_fin/1, + init_per_group/2, end_per_group/2, + + start_and_stop_transport_plain/1, + start_and_listen/1, + simple_connect/1, + simple_send_and_recv/1 + + ]). + +-export([t/0, t/1]). + +%% diameter_peer (internal) callback API +-export([up/1, up/3, recv/2]). + +-include("diameter_test_lib.hrl"). +-include_lib("diameter/include/diameter.hrl"). +%% -include_lib("diameter/src/tcp/diameter_tcp.hrl"). + + +t() -> diameter_test_server:t(?MODULE). +t(Case) -> diameter_test_server:t({?MODULE, Case}). + + +%% Test server callbacks +init_per_testcase(Case, Config) -> + diameter_test_server:init_per_testcase(Case, Config). + +fin_per_testcase(Case, Config) -> + diameter_test_server:fin_per_testcase(Case, Config). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> + [ + {group, start}, + {group, simple} + ]. + +groups() -> + [ + {start, [], [start_and_stop_transport_plain, start_and_listen]}, + {simple, [], [simple_connect, simple_send_and_recv]} + ]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite_init(X) -> init_per_suite(X). + +init_per_suite(suite) -> []; +init_per_suite(doc) -> []; +init_per_suite(Config) when is_list(Config) -> + Config. + + +suite_fin(X) -> end_per_suite(X). + +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config) when is_list(Config) -> + Config. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Test case(s) +%% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Plain start and stop of TCP transport +%% + +start_and_stop_transport_plain(suite) -> + []; +start_and_stop_transport_plain(doc) -> + []; +start_and_stop_transport_plain(Config) when is_list(Config) -> + + ?SKIP(not_yet_implemented), + + %% This has been changed *a lot* since it was written... + + process_flag(trap_exit, true), + Transport = ensure_transport_started(), + TcpTransport = ensure_tcp_transport_started(), + ensure_tcp_transport_stopped(TcpTransport), + ensure_transport_stopped(Transport), + i("done"), + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Start TCP transport and then create a listen socket +%% + +start_and_listen(suite) -> + []; +start_and_listen(doc) -> + []; +start_and_listen(Config) when is_list(Config) -> + + ?SKIP(not_yet_implemented), + + %% This has been changed *a lot* since it was written... + + process_flag(trap_exit, true), + Transport = ensure_transport_started(), + TcpTransport = ensure_tcp_transport_started(), + + case listen([{port, 0}]) of + {ok, Acceptor} when is_pid(Acceptor) -> + Ref = erlang:monitor(process, Acceptor), + [{Acceptor, Info}] = diameter_tcp:which_listeners(), + case lists:keysearch(socket, 1, Info) of + {value, {_, Listen}} -> + i("Listen socket: ~p" + "~n Opts: ~p" + "~n Stats: ~p" + "~n Name: ~p", + [Listen, + ok(inet:getopts(Listen, [keepalive, delay_send])), + ok(inet:getstat(Listen)), + ok(inet:sockname(Listen)) + ]), + ok; + _ -> + ?FAIL({bad_listener_info, Acceptor, Info}) + end, + Crash = simulate_crash, + exit(Acceptor, Crash), + receive + {'DOWN', Ref, process, Acceptor, Crash} -> + ?SLEEP(1000), + case diameter_tcp:which_listeners() of + [{NewAcceptor, _NewInfo}] -> + diameter_tcp_accept:stop(NewAcceptor), + ?SLEEP(1000), + case diameter_tcp:which_listeners() of + [] -> + ok; + UnexpectedListeners -> + ?FAIL({unexpected_listeners, empty, UnexpectedListeners}) + end; + UnexpectedListeners -> + ?FAIL({unexpected_listeners, non_empty, UnexpectedListeners}) + end + after 5000 -> + ?FAIL({failed_killing, Acceptor}) + end; + Error -> + ?FAIL({failed_creating_acceptor, Error}) + end, + ensure_tcp_transport_stopped(TcpTransport), + ensure_transport_stopped(Transport), + i("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% TCP transport connecting +%% + +simple_connect(suite) -> + []; +simple_connect(doc) -> + []; +simple_connect(Config) when is_list(Config) -> + + ?SKIP(not_yet_implemented), + + %% This has been changed *a lot* since it was written... + + process_flag(trap_exit, true), + Transport = ensure_transport_started(), + TcpTransport = ensure_tcp_transport_started(), + {_Acceptor, Port} = ensure_tcp_listener(), + + {ok, Hostname} = inet:gethostname(), + + i("try connect"), + Opts = [{host, Hostname}, {port, Port}, {module, ?MODULE}], + Conn = case connect(Opts) of + {ok, C} -> + C; + Error -> + ?FAIL({failed_connecting, Error}) + end, + i("connected: ~p", [Conn]), + + %% Up for connect + receive + {diameter, {up, Host, Port}} -> + i("Received expected connect up (~p:~p)", [Host, Port]), + ok + after 5000 -> + ?FAIL(connect_up_confirmation_timeout) + end, + + %% Up for accept + receive + {diameter, {up, _ConnPid}} -> + i("Received expected accept up"), + ok + after 5000 -> + ?FAIL(acceptor_up_confirmation_timeout) + end, + + i("try disconnect"), + diameter_tcp:disconnect(Conn), + ensure_tcp_transport_stopped(TcpTransport), + ensure_transport_stopped(Transport), + i("done"), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Plain start and stop of TCP transport +%% + +simple_send_and_recv(suite) -> + []; +simple_send_and_recv(doc) -> + []; +simple_send_and_recv(Config) when is_list(Config) -> + + ?SKIP(not_yet_implemented), + + %% This has been changed *a lot* since it was written... + + process_flag(trap_exit, true), + %% -------------------------------------------------- + %% Start the TCP transport sub-system + %% + + Transport = ensure_transport_started(), + TcpTransport = ensure_tcp_transport_started(), + + {_Acceptor, Port} = ensure_tcp_listener(), + + {ok, Hostname} = inet:gethostname(), + + i("try connect"), + Opts = [{host, Hostname}, {port, Port}, {module, ?MODULE}], + Conn = case connect(Opts) of + {ok, C1} -> + C1; + Error -> + ?FAIL({failed_connecting, Error}) + end, + i("connected: ~p", [Conn]), + + %% Up for connect + receive + {diameter, {up, Host, Port}} -> + i("Received expected connect up (~p:~p)", [Host, Port]), + ok + after 5000 -> + ?FAIL(connect_up_confirmation_timeout) + end, + + %% Up for accept + APid = + receive + {diameter, {up, C2}} -> + i("Received expected accept up"), + C2 + after 5000 -> + ?FAIL(acceptor_up_confirmation_timeout) + end, + + %% -------------------------------------------------- + %% Start some stuff needed for the codec to run + %% + + i("start persistent table"), + {ok, _Pers} = diameter_persistent_table:start_link(), + + i("start session"), + {ok, _Session} = diameter_session:start_link(), + + i("try decode a (DWR) message"), + Base = diameter_gen_base_rfc3588, + DWR = ['DWR', + {'Origin-Host', Hostname}, + {'Origin-Realm', "whatever-realm"}, + {'Origin-State-Id', [10]}], + + #diameter_packet{msg = Msg} = diameter_codec:encode(Base, DWR), + + + %% -------------------------------------------------- + %% Now try to send the message + %% + %% This is not the codec-test suite, so we dont really care what we + %% send, as long as it encoded/decodes correctly in the transport + %% + + i("try send from connect side"), + ok = diameter_tcp:send_message(Conn, Msg), + + %% Wait for data on Accept side + APkt = + receive + {diameter, {recv, A}} -> + i("[accept] Received expected data message: ~p", [A]), + A + after 5000 -> + ?FAIL(acceptor_up_confirmation_timeout) + end, + + %% Send the same message back, just to have something to send... + i("try send (\"reply\") from accept side"), + ok = diameter_tcp:send_message(APid, APkt), + + %% Wait for data on Connect side + receive + {diameter, {recv, B}} -> + i("[connect] Received expected data message: ~p", [B]), + ok + after 5000 -> + ?FAIL(acceptor_up_confirmation_timeout) + end, + + i("we are done - now close shop"), + diameter_session:stop(), + diameter_persistent_table:stop(), + + ensure_tcp_transport_stopped(TcpTransport), + ensure_transport_stopped(Transport), + i("done"), + ok. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ensure_transport_started() -> +%% i("start diameter transport (top) supervisor"), + case diameter_transport_sup:start_link() of + {ok, TransportSup} -> + TransportSup; + Error -> + ?FAIL({failed_starting_transport_sup, Error}) + end. + +ensure_transport_stopped(Pid) when is_pid(Pid) -> +%% i("stop diameter transport (top) supervisor"), + Stop = fun(P) -> exit(P, kill) end, + ensure_stopped(Pid, Stop, failed_stopping_transport_sup). + +ensure_tcp_transport_started() -> +%% i("start diameter TCP transport"), + case diameter_tcp:start_transport() of + {ok, TcpTransport} when is_pid(TcpTransport) -> + TcpTransport; + Error -> + ?FAIL({failed_starting_transport, Error}) + end. + +ensure_tcp_transport_stopped(Pid) when is_pid(Pid) -> +%% i("stop diameter TCP transport supervisor"), + Stop = fun(P) -> diameter_tcp:stop_transport(P) end, + ensure_stopped(Pid, Stop, failed_stopping_tcp_transport). + + +ensure_tcp_listener() -> +%% i("create diameter TCP transport listen socket"), + case listen([{port, 0}]) of + {ok, Acceptor} -> + [{Acceptor, Info}] = diameter_tcp:which_listeners(), + case lists:keysearch(socket, 1, Info) of + {value, {_, Listen}} -> + {ok, Port} = inet:port(Listen), + {Acceptor, Port}; + _ -> + ?FAIL({failed_retrieving_listen_socket, Info}) + end; + Error -> + ?FAIL({failed_creating_listen_socket, Error}) + end. + + +ensure_stopped(Pid, Stop, ReasonTag) when is_pid(Pid) -> +%% i("ensure_stopped -> create monitor to ~p", [Pid]), + Ref = erlang:monitor(process, Pid), +%% i("ensure_stopped -> try stop"), + Stop(Pid), +%% i("ensure_stopped -> await DOWN message"), + receive + {'DOWN', Ref, process, Pid, _} -> +%% i("ensure_stopped -> received DOWN message"), + ok + after 5000 -> +%% i("ensure_stopped -> timeout"), + ?FAIL({ReasonTag, Pid}) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +listen(Opts) -> + diameter_tcp:listen([{module, ?MODULE} | Opts]). + +connect(Opts) -> + diameter_tcp:connect([{module, ?MODULE} | Opts]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +up(Pid, Host, Port) -> + Pid ! {diameter, {up, Host, Port}}, + ok. + +up(Pid) -> + Pid ! {diameter, {up, self()}}, + ok. + +recv(Pid, Pkt) -> + Pid ! {diameter, {recv, Pkt}}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +i(F) -> + i(F, []). + +i(F, A) -> + io:format(F ++ "~n", A). + + +ok({ok, Whatever}) -> + Whatever; +ok(Crap) -> + Crap. + + |