aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/test/diameter_tcp_test.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/test/diameter_tcp_test.erl')
-rw-r--r--lib/diameter/test/diameter_tcp_test.erl482
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.
+
+