aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/test/megaco_udp_test.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/megaco/test/megaco_udp_test.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/megaco/test/megaco_udp_test.erl')
-rw-r--r--lib/megaco/test/megaco_udp_test.erl1251
1 files changed, 1251 insertions, 0 deletions
diff --git a/lib/megaco/test/megaco_udp_test.erl b/lib/megaco/test/megaco_udp_test.erl
new file mode 100644
index 0000000000..2e2f5465dd
--- /dev/null
+++ b/lib/megaco/test/megaco_udp_test.erl
@@ -0,0 +1,1251 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. 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:
+%%----------------------------------------------------------------------
+-module(megaco_udp_test).
+
+%%----------------------------------------------------------------------
+%% Include files
+%%----------------------------------------------------------------------
+-include_lib("megaco/src/udp/megaco_udp.hrl").
+-include("megaco_test_lib.hrl").
+
+
+%%----------------------------------------------------------------------
+%% External exports
+%%----------------------------------------------------------------------
+-export([
+ all/1,
+
+ start/1,
+ start_normal/1,
+ start_invalid_opt/1,
+ start_and_stop/1,
+
+ sending/1,
+ sendreceive/1,
+ block_unblock/1,
+
+ errors/1,
+ socket_failure/1,
+
+ init_per_testcase/2, fin_per_testcase/2,
+
+ t/0, t/1
+ ]).
+
+%%----------------------------------------------------------------------
+%% Internal exports
+%%----------------------------------------------------------------------
+-export([
+ receive_message/4,
+ process_received_message/4
+ ]).
+
+
+%%----------------------------------------------------------------------
+%% Macros
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Records
+%%----------------------------------------------------------------------
+
+-record(command, {id, desc, cmd}).
+-record(server, {parent, transport_ref, control_pid, handle, remote}).
+-record(client, {parent, transport_ref, control_pid, handle, remote}).
+
+
+%%======================================================================
+%% External functions
+%%======================================================================
+%%----------------------------------------------------------------------
+%% Function: t/0
+%% Description: Run all test cases
+%%----------------------------------------------------------------------
+t() -> megaco_test_lib:t(?MODULE).
+
+
+%%----------------------------------------------------------------------
+%% Function: t/1
+%% Description: Run the specified test cases
+%%----------------------------------------------------------------------
+t(Case) -> megaco_test_lib:t({?MODULE, Case}).
+
+
+%%======================================================================
+%% Test server callbacks
+%%======================================================================
+%%----------------------------------------------------------------------
+%% Function: init_per_testcase/2
+%% Description:
+%%----------------------------------------------------------------------
+init_per_testcase(Case, Config) ->
+ megaco_test_lib:init_per_testcase(Case, Config).
+
+
+%%----------------------------------------------------------------------
+%% Function: fin_per_testcase/2
+%% Description:
+%%----------------------------------------------------------------------
+fin_per_testcase(Case, Config) ->
+ megaco_test_lib:fin_per_testcase(Case, Config).
+
+
+%%======================================================================
+%% Test case definitions
+%%======================================================================
+
+all(suite) ->
+ [
+ start,
+ sending,
+ errors
+ ].
+
+start(suite) ->
+ [
+ start_normal,
+ start_invalid_opt,
+ start_and_stop
+ ].
+
+sending(suite) ->
+ [
+ sendreceive,
+ block_unblock
+
+ ].
+
+errors(suite) ->
+ [
+ socket_failure
+ ].
+
+
+%% =================================================
+%%
+%% ------------------ start ------------------------
+%%
+%% =================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_normal(suite) ->
+ [];
+start_normal(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ Opts = [{port, 0}, {receive_handle, apa}],
+ {ok, Pid} = start_case(Opts, ok),
+ megaco_udp:stop_transport(Pid),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_invalid_opt(suite) ->
+ [];
+start_invalid_opt(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ Opts = [{port, 0}, {receivehandle, apa}],
+ start_case(Opts, error).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_and_stop(suite) ->
+ [];
+start_and_stop(doc) ->
+ ["This test case sets up a connection and then cloises it. "
+ "No data is sent. "];
+start_and_stop(Config) when is_list(Config) ->
+ put(sname, "start_and_stop"),
+ p("BEGIN TEST-CASE"),
+
+ process_flag(trap_exit, true),
+
+ p("create nodes"),
+ ServerNode = make_node_name(server),
+ ClientNode = make_node_name(client),
+ Nodes = [ServerNode, ClientNode],
+ ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE),
+
+ %% Create command sequences
+ p("create command sequences"),
+ ServerPort = 2944,
+ ServerCmds = start_and_stop_server_commands(ServerPort),
+ {ok, ServerHost} = inet:gethostname(),
+ ClientCmds = start_and_stop_client_commands(ServerPort, ServerHost),
+
+ %% Start the test procs used in the test-case, one for each node
+ p("start command handlers"),
+ Server = server_start_command_handler(ServerNode, ServerCmds),
+ p("server command handler started: ~p", [Server]),
+ Client = client_start_command_handler(ClientNode, ClientCmds),
+ p("client command handler started: ~p", [Client]),
+
+ ok =
+ receive
+ {operational, Server} ->
+ p("received listening message from server [~p] => "
+ "send continue to client [~p]~n", [Server, Client]),
+ Client ! {continue, self()},
+ ok;
+ {'EXIT', Server, {skip, Reason}} ->
+ ?SKIP(Reason);
+ {'EXIT', Client, {skip, Reason}} ->
+ ?SKIP(Reason)
+ after 5000 ->
+ {error, server_timeout}
+ end,
+
+ ok = await_command_handler_completion([Server, Client], timer:seconds(20)),
+ p("done"),
+ ok.
+
+
+start_and_stop_server_commands(Port) ->
+ Opts = [{port, Port}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#server{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ server_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ server_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Notify operational",
+ cmd = fun(State) ->
+ server_notify_operational(State)
+ end},
+
+ #command{id = 5,
+ desc = "Await nothing",
+ cmd = fun(State) ->
+ server_await_nothing(State, 5000)
+ end},
+
+ #command{id = 6,
+ desc = "Close",
+ cmd = fun(State) ->
+ server_close(State)
+ end},
+
+ #command{id = 7,
+ desc = "Stop",
+ cmd = fun(State) ->
+ server_stop_transport(State)
+ end}
+
+ ].
+
+start_and_stop_client_commands(ServerPort, _ServerHost) ->
+ Opts = [{port, ServerPort}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#client{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ client_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ client_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Await continue",
+ cmd = fun(State) ->
+ client_await_continue_signal(State, 5000)
+ end},
+
+ #command{id = 5,
+ desc = "Await nothing",
+ cmd = fun(State) ->
+ client_await_nothing(State, 5000)
+ end},
+
+ #command{id = 6,
+ desc = "Close",
+ cmd = fun(State) ->
+ client_close(State)
+ end},
+
+ #command{id = 7,
+ desc = "Stop transport",
+ cmd = fun(State) ->
+ client_stop_transport(State)
+ end}
+ ].
+
+
+
+%% =================================================
+%%
+%% ------------------ sending ------------------------
+%%
+%% =================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+sendreceive(suite) ->
+ [];
+sendreceive(doc) ->
+ ["Test send and receive with the UDP transport. "];
+sendreceive(Config) when is_list(Config) ->
+ put(sname, "sendreceive"),
+ p("BEGIN TEST-CASE"),
+
+ process_flag(trap_exit, true),
+
+ p("create nodes"),
+ ServerNode = make_node_name(server),
+ ClientNode = make_node_name(client),
+ Nodes = [ServerNode, ClientNode],
+ ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE),
+
+ %% Create command sequences
+ p("create command sequences"),
+ ServerPort = 2944,
+ ServerCmds = sendreceive_server_commands(ServerPort),
+ {ok, ServerHost} = inet:gethostname(),
+ ClientCmds = sendreceive_client_commands(ServerPort, ServerHost),
+
+ %% Start the test procs used in the test-case, one for each node
+ p("start command handlers"),
+ Server = server_start_command_handler(ServerNode, ServerCmds),
+ p("server command handler started: ~p", [Server]),
+ Client = client_start_command_handler(ClientNode, ClientCmds),
+ p("client command handler started: ~p", [Client]),
+
+ ok =
+ receive
+ {operational, Server} ->
+ p("received operational message from server [~p] => "
+ "send continue to client [~p]~n", [Server, Client]),
+ Client ! {continue, self()},
+ ok;
+ {'EXIT', Server, {skip, Reason}} ->
+ ?SKIP(Reason);
+ {'EXIT', Client, {skip, Reason}} ->
+ ?SKIP(Reason)
+ after 5000 ->
+ {error, server_timeout}
+ end,
+
+ ok = await_command_handler_completion([Server, Client], timer:seconds(20)),
+ p("done"),
+ ok.
+
+
+sendreceive_server_commands(Port) ->
+ Opts = [{port, Port}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#server{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ server_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ server_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Notify operational",
+ cmd = fun(State) ->
+ server_notify_operational(State)
+ end},
+
+ #command{id = 5,
+ desc = "Await initial message (ping)",
+ cmd = fun(State) ->
+ server_await_initial_message(State, "ping", 5000)
+ end},
+
+ #command{id = 6,
+ desc = "Send reply (pong) to initial message",
+ cmd = fun(State) ->
+ server_send_message(State, "pong")
+ end},
+
+ #command{id = 7,
+ desc = "Await nothing before sending a message (hejsan)",
+ cmd = fun(State) ->
+ server_await_nothing(State, 1000)
+ end},
+
+ #command{id = 8,
+ desc = "Send message (hejsan)",
+ cmd = fun(State) ->
+ server_send_message(State, "hejsan")
+ end},
+
+ #command{id = 9,
+ desc = "Await reply (hoppsan) to message",
+ cmd = fun(State) ->
+ server_await_message(State, "hoppsan", 1000)
+ end},
+
+ #command{id = 10,
+ desc = "Await nothing before closing",
+ cmd = fun(State) ->
+ server_await_nothing(State, 1000)
+ end},
+
+ #command{id = 11,
+ desc = "Close",
+ cmd = fun(State) ->
+ server_close(State)
+ end},
+
+ #command{id = 12,
+ desc = "Await nothing before stopping transport",
+ cmd = fun(State) ->
+ server_await_nothing(State, 1000)
+ end},
+
+ #command{id = 13,
+ desc = "Stop",
+ cmd = fun(State) ->
+ server_stop_transport(State)
+ end}
+
+ ].
+
+sendreceive_client_commands(ServerPort, ServerHost) ->
+ OwnPort = ServerPort+1,
+ Opts = [{port, OwnPort}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#client{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ client_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ client_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Await continue",
+ cmd = fun(State) ->
+ client_await_continue_signal(State, 5000)
+ end},
+
+ #command{id = 5,
+ desc = "Connect",
+ cmd = fun(State) ->
+ client_connect(State, ServerHost, ServerPort)
+ end},
+
+ #command{id = 6,
+ desc = "Send initial message (ping)",
+ cmd = fun(State) ->
+ client_send_message(State, "ping")
+ end},
+
+ #command{id = 7,
+ desc = "Await reply (pong) to initial message",
+ cmd = fun(State) ->
+ client_await_message(State, "pong", 1000)
+ end},
+
+ #command{id = 8,
+ desc = "Await message (hejsan)",
+ cmd = fun(State) ->
+ client_await_message(State, "hejsan", 5000)
+ end},
+
+ #command{id = 9,
+ desc = "Send reply (hoppsan) to message",
+ cmd = fun(State) ->
+ client_send_message(State, "hoppsan")
+ end},
+
+ #command{id = 10,
+ desc = "Await nothing before closing",
+ cmd = fun(State) ->
+ client_await_nothing(State, 1000)
+ end},
+
+ #command{id = 11,
+ desc = "Close",
+ cmd = fun(State) ->
+ client_close(State)
+ end},
+
+ #command{id = 12,
+ desc = "Await nothing before stopping transport",
+ cmd = fun(State) ->
+ client_await_nothing(State, 1000)
+ end},
+
+ #command{id = 13,
+ desc = "Stop transport",
+ cmd = fun(State) ->
+ client_stop_transport(State)
+ end}
+ ].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+block_unblock(suite) ->
+ [];
+block_unblock(doc) ->
+ ["Test the block/unblock functions of the UDP transport. "];
+block_unblock(Config) when is_list(Config) ->
+ put(sname, "block_unblock"),
+ p("BEGIN TEST-CASE"),
+
+ process_flag(trap_exit, true),
+
+ p("create nodes"),
+ ServerNode = make_node_name(server),
+ ClientNode = make_node_name(client),
+ Nodes = [ServerNode, ClientNode],
+ ok = megaco_test_lib:start_nodes(Nodes, ?FILE, ?LINE),
+
+ %% Create command sequences
+ p("create command sequences"),
+ ServerPort = 2944,
+ ServerCmds = block_unblock_server_commands(ServerPort),
+ {ok, ServerHost} = inet:gethostname(),
+ ClientCmds = block_unblock_client_commands(ServerPort, ServerHost),
+
+ %% Start the test procs used in the test-case, one for each node
+ p("start command handlers"),
+ Server = server_start_command_handler(ServerNode, ServerCmds),
+ p("server command handler started: ~p", [Server]),
+ Client = client_start_command_handler(ClientNode, ClientCmds),
+ p("client command handler started: ~p", [Client]),
+
+ %% Wait for the server to become ready for operation
+ %% and then tell the client to continue
+ ok =
+ receive
+ {operational, Server} ->
+ p("received operational message from server [~p] => "
+ "send continue to client [~p]~n", [Server, Client]),
+ Client ! {continue, self()},
+ ok;
+ {'EXIT', Server, {skip, Reason1}} ->
+ ?SKIP(Reason1);
+ {'EXIT', Client, {skip, Reason2}} ->
+ ?SKIP(Reason2)
+ after 5000 ->
+ {error, server_timeout}
+ end,
+
+ %% Wait for the client to become blocked
+ %% and then tell the server to continue
+ ok =
+ receive
+ {blocked, Client} ->
+ p("received blocked message from client [~p] => "
+ "send continue to server [~p]~n", [Client, Server]),
+ Server ! {continue, self()},
+ ok;
+ {'EXIT', Server, {skip, Reason3}} ->
+ ?SKIP(Reason3);
+ {'EXIT', Client, {skip, Reason4}} ->
+ ?SKIP(Reason4)
+ after 5000 ->
+ {error, timeout}
+ end,
+
+ ok = await_command_handler_completion([Server, Client], timer:seconds(20)),
+ p("done"),
+ ok.
+
+
+block_unblock_server_commands(Port) ->
+ Opts = [{port, Port}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#server{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ server_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ server_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Notify operational",
+ cmd = fun(State) ->
+ server_notify_operational(State)
+ end},
+
+ #command{id = 5,
+ desc = "Await initial message (ping)",
+ cmd = fun(State) ->
+ server_await_initial_message(State, "ping", 5000)
+ end},
+
+ #command{id = 6,
+ desc = "Send reply (pong) to initial message",
+ cmd = fun(State) ->
+ server_send_message(State, "pong")
+ end},
+
+ #command{id = 7,
+ desc = "Await continue",
+ cmd = fun(State) ->
+ server_await_continue_signal(State, 5000)
+ end},
+
+ #command{id = 8,
+ desc = "Send message (hejsan)",
+ cmd = fun(State) ->
+ server_send_message(State, "hejsan")
+ end},
+
+ #command{id = 9,
+ desc = "Await nothing before receiving (hoppsan) reply",
+ cmd = fun(State) ->
+ server_await_nothing(State, 4000)
+ end},
+
+ #command{id = 10,
+ desc = "Await reply (hoppsan) to message",
+ cmd = fun(State) ->
+ server_await_message(State, "hoppsan", 2000)
+ end},
+
+ #command{id = 11,
+ desc = "Await nothing before closing",
+ cmd = fun(State) ->
+ server_await_nothing(State, 1000)
+ end},
+
+ #command{id = 12,
+ desc = "Close",
+ cmd = fun(State) ->
+ server_close(State)
+ end},
+
+ #command{id = 13,
+ desc = "Await nothing before stopping transport",
+ cmd = fun(State) ->
+ server_await_nothing(State, 1000)
+ end},
+
+ #command{id = 14,
+ desc = "Stop",
+ cmd = fun(State) ->
+ server_stop_transport(State)
+ end}
+
+ ].
+
+block_unblock_client_commands(ServerPort, ServerHost) ->
+ OwnPort = ServerPort+1,
+ Opts = [{port, OwnPort}],
+ Self = self(),
+ [
+ #command{id = 1,
+ desc = "Command sequence init",
+ cmd = fun(State) ->
+ {ok, State#client{parent = Self}}
+ end},
+
+ #command{id = 2,
+ desc = "Start transport",
+ cmd = fun(State) ->
+ client_start_transport(State)
+ end},
+
+ #command{id = 3,
+ desc = "Open",
+ cmd = fun(State) ->
+ client_open(State, Opts)
+ end},
+
+ #command{id = 4,
+ desc = "Await continue",
+ cmd = fun(State) ->
+ client_await_continue_signal(State, 5000)
+ end},
+
+ #command{id = 5,
+ desc = "[pseudo] Connect",
+ cmd = fun(State) ->
+ client_connect(State, ServerHost, ServerPort)
+ end},
+
+ #command{id = 6,
+ desc = "Send initial message (ping)",
+ cmd = fun(State) ->
+ client_send_message(State, "ping")
+ end},
+
+ #command{id = 7,
+ desc = "Await reply (pong) to initial message",
+ cmd = fun(State) ->
+ client_await_message(State, "pong", 1000)
+ end},
+
+ #command{id = 8,
+ desc = "Block",
+ cmd = fun(State) ->
+ client_block(State)
+ end},
+
+ #command{id = 9,
+ desc = "Notify blocked",
+ cmd = fun(State) ->
+ client_notify_blocked(State)
+ end},
+
+ #command{id = 10,
+ desc = "Await nothing before unblocking",
+ cmd = fun(State) ->
+ client_await_nothing(State, 5000)
+ end},
+
+ #command{id = 11,
+ desc = "Unblock",
+ cmd = fun(State) ->
+ client_unblock(State)
+ end},
+
+ #command{id = 8,
+ desc = "Await message (hejsan)",
+ cmd = fun(State) ->
+ client_await_message(State, "hejsan", 5000)
+ end},
+
+ #command{id = 9,
+ desc = "Send reply (hoppsan) to message",
+ cmd = fun(State) ->
+ client_send_message(State, "hoppsan")
+ end},
+
+ #command{id = 10,
+ desc = "Await nothing before closing",
+ cmd = fun(State) ->
+ client_await_nothing(State, 1000)
+ end},
+
+ #command{id = 11,
+ desc = "Close",
+ cmd = fun(State) ->
+ client_close(State)
+ end},
+
+ #command{id = 12,
+ desc = "Await nothing before stopping transport",
+ cmd = fun(State) ->
+ client_await_nothing(State, 1000)
+ end},
+
+ #command{id = 13,
+ desc = "Stop transport",
+ cmd = fun(State) ->
+ client_stop_transport(State)
+ end}
+ ].
+
+
+%% =================================================
+%%
+%% ------------------ errors ------------------------
+%%
+%% =================================================
+
+socket_failure(suite) ->
+ [];
+socket_failure(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ failing_socket().
+
+
+%%======================================================================
+%% Test functions
+%%======================================================================
+
+start_case(Opts, Expect) ->
+ case (catch megaco_udp:start_transport()) of
+ {ok, Pid} ->
+ case (catch megaco_udp:open(Pid, Opts)) of
+ {ok, _Handle, _CtrlPid} when Expect =:= ok ->
+ {ok, Pid};
+ {ok, Handle, CtrlPid} ->
+ megaco_udp:stop_transport(Pid),
+ ?ERROR({unexpected_start_sucesss, Handle, CtrlPid});
+ {error, _Reason} when Expect =:= error ->
+ megaco_udp:stop_transport(Pid),
+ ok;
+ {error, Reason} ->
+ megaco_udp:stop_transport(Pid),
+ ?ERROR({unexpected_start_failure, Reason});
+ Error ->
+ ?ERROR({unexpected_result, Error})
+ end;
+ {error, Reason} ->
+ ?ERROR({failed_starting_transport, Reason})
+ end.
+
+
+failing_socket() ->
+ ?SKIP(not_yet_implemented).
+
+
+
+%%----------------------------------------------------------------------
+%% Message Callback functions
+%%----------------------------------------------------------------------
+
+receive_message(ReceiveHandle, ControlPid, SendHandle, BinMsg)
+ when is_pid(ReceiveHandle) andalso is_binary(BinMsg) ->
+ Msg = binary_to_list(BinMsg),
+ ReceiveHandle ! {receive_message, {ControlPid, SendHandle, Msg}},
+ ok.
+
+process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg)
+ when is_pid(ReceiveHandle) andalso is_binary(BinMsg) ->
+ Msg = binary_to_list(BinMsg),
+ ReceiveHandle ! {process_received_message, {ControlPid, SendHandle, Msg}},
+ ok.
+
+
+%%======================================================================
+%% Internal functions
+%%======================================================================
+
+%% ------- Server command handler and utility functions ----------
+
+server_start_command_handler(Node, Commands) ->
+ start_command_handler(Node, Commands, #server{}, "server").
+
+server_start_transport(State) when is_record(State, server) ->
+ case (catch megaco_udp:start_transport()) of
+ {ok, Ref} ->
+ {ok, State#server{transport_ref = Ref}};
+ Error ->
+ Error
+ end.
+
+server_open(#server{transport_ref = Ref} = State, Options)
+ when is_record(State, server) andalso is_list(Options) ->
+ Opts = [{receive_handle, self()}, {module, ?MODULE} | Options],
+ case (catch megaco_udp:open(Ref, Opts)) of
+ {ok, Socket, ControlPid} ->
+ {ok, State#server{handle = {socket, Socket}, % Temporary
+ control_pid = ControlPid}};
+ {error, {could_not_open_udp_port, eaddrinuse}} ->
+ {skip, {server, eaddrinuse}};
+ Error ->
+ Error
+ end.
+
+server_notify_operational(#server{parent = Parent} = State)
+ when is_record(State, server) ->
+ Parent ! {operational, self()},
+ {ok, State}.
+
+server_await_continue_signal(#server{parent = Parent} = State, Timeout) ->
+ receive
+ {continue, Parent} ->
+ {ok, State}
+ after Timeout ->
+ {error, timeout}
+ end.
+
+server_await_initial_message(State, InitialMessage, Timeout)
+ when is_record(State, server) ->
+ receive
+ {receive_message, {ControlPid, Handle, InitialMessage}} ->
+ p("received expected event with: "
+ "~n ControlPid: ~p"
+ "~n Handle: ~p", [ControlPid, Handle]),
+ NewState = State#server{handle = Handle},
+ {ok, NewState};
+
+ Any ->
+ p("received unexpected event: ~p", [Any]),
+ {error, {unexpected_event, Any}}
+
+ after Timeout ->
+ {error, timeout}
+ end.
+
+server_send_message(#server{handle = Handle} = State, Message) ->
+ Bin = if
+ is_list(Message) ->
+ list_to_binary(Message);
+ true ->
+ Message
+ end,
+ megaco_udp:send_message(Handle, Bin),
+ {ok, State}.
+
+server_await_nothing(State, Timeout)
+ when is_record(State, server) ->
+ receive
+ Any ->
+ p("received unexpected event: ~p", [Any]),
+ {error, {unexpected_event, Any}}
+
+ after Timeout ->
+ {ok, State}
+ end.
+
+
+server_await_message(State, ExpectMessage, Timeout)
+ when is_record(State, server) ->
+ receive
+ {receive_message, {_, _, ExpectMessage}} ->
+ p("received expected message [~p]", [ExpectMessage]),
+ {ok, State};
+
+ Any ->
+ p("received unexpected event: ~p", [Any]),
+ {error, {unexpected_event, Any}}
+
+ after Timeout ->
+ {error, timeout}
+ end.
+
+server_close(#server{handle = {socket, Socket}} = State) ->
+ megaco_udp:close(Socket),
+ {ok, State#server{handle = undefined, control_pid = undefined}};
+server_close(#server{handle = Handle} = State)
+ when (Handle =/= undefined) ->
+ megaco_udp:close(Handle),
+ {ok, State#server{handle = undefined, control_pid = undefined}}.
+
+%% server_block(#server{handle = Handle} = State)
+%% when (Handle =/= undefined) ->
+%% megaco_udp:block(Handle),
+%% {ok, State}.
+
+%% server_unblock(#server{handle = Handle} = State)
+%% when (Handle =/= undefined) ->
+%% megaco_udp:unblock(Handle),
+%% {ok, State}.
+
+server_stop_transport(#server{transport_ref = Ref} = State)
+ when (Ref =/= undefined) ->
+ megaco_udp:stop_transport(Ref),
+ {ok, State#server{transport_ref = undefined}}.
+
+
+%% ------- Client command handler and utility functions ----------
+
+client_start_command_handler(Node, Commands) ->
+ start_command_handler(Node, Commands, #client{}, "client").
+
+client_start_transport(State) when is_record(State, client) ->
+ case (catch megaco_udp:start_transport()) of
+ {ok, Ref} ->
+ {ok, State#client{transport_ref = Ref}};
+ Error ->
+ Error
+ end.
+
+client_open(#client{transport_ref = Ref} = State, Options)
+ when is_record(State, client) andalso is_list(Options) ->
+ Opts = [{receive_handle, self()}, {module, ?MODULE} | Options],
+ case (catch megaco_udp:open(Ref, Opts)) of
+ {ok, Socket, ControlPid} ->
+ {ok, State#client{handle = {socket, Socket},
+ control_pid = ControlPid}};
+ {error, {could_not_open_udp_port, eaddrinuse}} ->
+ {skip, {client, eaddrinuse}};
+ Error ->
+ Error
+ end.
+
+client_await_continue_signal(#client{parent = Parent} = State, Timeout) ->
+ receive
+ {continue, Parent} ->
+ {ok, State}
+ after Timeout ->
+ {error, timeout}
+ end.
+
+client_notify_blocked(#client{parent = Parent} = State) ->
+ Parent ! {blocked, self()},
+ {ok, State}.
+
+client_await_nothing(State, Timeout)
+ when is_record(State, client) ->
+ receive
+ Any ->
+ p("received unexpected event: ~p", [Any]),
+ {error, {unexpected_event, Any}}
+ after Timeout ->
+ {ok, State}
+ end.
+
+client_connect(#client{handle = {socket, Socket}} = State, Host, Port) ->
+ Handle = megaco_udp:create_send_handle(Socket, Host, Port),
+ {ok, State#client{handle = Handle}}.
+
+client_send_message(#client{handle = Handle} = State, Message) ->
+ Bin = if
+ is_list(Message) ->
+ list_to_binary(Message);
+ true ->
+ Message
+ end,
+ megaco_udp:send_message(Handle, Bin),
+ {ok, State}.
+
+client_await_message(State, ExpectMessage, Timeout)
+ when is_record(State, client) ->
+ receive
+ {receive_message, {_, _, ExpectMessage}} ->
+ {ok, State};
+
+ Any ->
+ p("received unexpected event: ~p", [Any]),
+ {error, {unexpected_event, Any}}
+
+ after Timeout ->
+ {error, timeout}
+ end.
+
+client_block(#client{handle = Handle} = State)
+ when (Handle =/= undefined) ->
+ megaco_udp:block(Handle),
+ {ok, State}.
+
+client_unblock(#client{handle = Handle} = State)
+ when (Handle =/= undefined) ->
+ megaco_udp:unblock(Handle),
+ {ok, State}.
+
+client_close(#client{handle = {socket, Socket}} = State) ->
+ megaco_udp:close(Socket),
+ {ok, State#client{handle = undefined, control_pid = undefined}};
+client_close(#client{handle = Handle} = State)
+ when (Handle =/= undefined) ->
+ megaco_udp:close(Handle),
+ {ok, State#client{handle = undefined, control_pid = undefined}}.
+
+client_stop_transport(#client{transport_ref = Ref} = State)
+ when (Ref =/= undefined) ->
+ megaco_udp:stop_transport(Ref),
+ {ok, State#client{transport_ref = undefined}}.
+
+
+%% -------- Command handler ---------
+
+start_command_handler(Node, Commands, State, ShortName) ->
+ Fun = fun() ->
+ put(sname, ShortName),
+ process_flag(trap_exit, true),
+ Result = (catch command_handler(Commands, State)),
+ p("command handler terminated with: "
+ "~n Result: ~p", [Result]),
+ exit(Result)
+ end,
+ erlang:spawn_link(Node, Fun).
+
+command_handler([], State) ->
+ p("command_handler -> entry when done with"
+ "~n State: ~p", [State]),
+ {ok, State};
+command_handler([#command{id = Id,
+ desc = Desc,
+ cmd = Cmd}|Commands], State) ->
+ p("command_handler -> entry with"
+ "~n Id: ~p"
+ "~n Desc: ~p", [Id, Desc]),
+ case (catch Cmd(State)) of
+ {ok, NewState} ->
+ p("command_handler -> cmd ~w ok", [Id]),
+ command_handler(Commands, NewState);
+ {skip, _} = SKIP ->
+ p("command_handler -> cmd ~w skip", [Id]),
+ SKIP;
+ {error, Reason} ->
+ p("command_handler -> cmd ~w error: "
+ "~n Reason: ~p", [Id, Reason]),
+ {error, {cmd_error, Reason}};
+ {'EXIT', Reason} ->
+ p("command_handler -> cmv ~w exit: "
+ "~n Reason: ~p", [Id, Reason]),
+ {error, {cmd_exit, Reason}};
+ Error ->
+ p("command_handler -> cmd ~w failure: "
+ "~n Error: ~p", [Id, Error]),
+ {error, {cmd_failure, Error}}
+ end.
+
+
+await_command_handler_completion(Pids, Timeout) ->
+ await_command_handler_completion(Pids, [], [], Timeout).
+
+await_command_handler_completion([], [], _Good, _Timeout) ->
+ p("await_command_handler_completion -> entry when done"),
+ ok;
+await_command_handler_completion([], Bad, Good, _Timeout) ->
+ p("await_command_handler_completion -> entry when done with bad result: "
+ "~n Bad: ~p"
+ "~n Good: ~p", [Bad, Good]),
+ {error, Bad, Good};
+await_command_handler_completion(Pids, Bad, Good, Timeout) ->
+ p("await_command_handler_completion -> entry when waiting for"
+ "~n Pids: ~p"
+ "~n Bad: ~p"
+ "~n Good: ~p"
+ "~n Timeout: ~p", [Pids, Bad, Good, Timeout]),
+ Begin = ms(),
+ receive
+ {'EXIT', Pid, {ok, FinalState}} ->
+ p("await_command_handler_completion -> "
+ "received ok EXIT signal from ~p", [Pid]),
+ case lists:delete(Pid, Pids) of
+ Pids ->
+ await_command_handler_completion(Pids, Bad, Good,
+ Timeout - (ms() - Begin));
+ Pids2 ->
+ p("await_command_handler_completion -> ~p done", [Pid]),
+ await_command_handler_completion(Pids2,
+ Bad,
+ [{Pid, FinalState}|Good],
+ Timeout - (ms() - Begin))
+ end;
+
+ {'EXIT', Pid, {error, Reason}} ->
+ p("await_command_handler_completion -> "
+ "received error EXIT signal from ~p", [Pid]),
+ case lists:delete(Pid, Pids) of
+ Pids ->
+ await_command_handler_completion(Pids, Bad, Good,
+ Timeout - (ms() - Begin));
+ Pids2 ->
+ p("await_command_handler_completion -> ~p done with"
+ "~n ~p", [Pid, Reason]),
+ await_command_handler_completion(Pids2,
+ [{Pid, Reason}|Bad],
+ Good,
+ Timeout - (ms() - Begin))
+ end;
+
+ {'EXIT', Pid, {skip, Reason}} ->
+ p("await_command_handler_completion -> "
+ "received skip EXIT signal from ~p with"
+ "~p", [Pid, Reason]),
+ ?SKIP(Reason)
+
+ after Timeout ->
+ p("await_command_handler_completion -> timeout"),
+ exit({timeout, Pids})
+ end.
+
+
+
+%% ------- Misc functions --------
+
+make_node_name(Name) ->
+ case string:tokens(atom_to_list(node()), [$@]) of
+ [_,Host] ->
+ list_to_atom(lists:concat([atom_to_list(Name) ++ "@" ++ Host]));
+ _ ->
+ exit("Test node must be started with '-sname'")
+ end.
+
+
+p(F) ->
+ p(F, []).
+
+p(F, A) ->
+ p(get(sname), F, A).
+
+p(S, F, A) when is_list(S) ->
+ io:format("*** [~s] ~p ~s ***"
+ "~n " ++ F ++ "~n",
+ [format_timestamp(now()), self(), S | A]);
+p(_S, F, A) ->
+ io:format("*** [~s] ~p ~s *** "
+ "~n " ++ F ++ "~n",
+ [format_timestamp(now()), self(), "undefined" | A]).
+
+
+ms() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
+
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).