%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% %% Licensed 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. %% %% %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/0,groups/0,init_per_group/2,end_per_group/2, start_normal/1, start_invalid_opt/1, start_and_stop/1, sendreceive/1, block_unblock/1, socket_failure/1, init_per_testcase/2, end_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: end_per_testcase/2 %% Description: %%---------------------------------------------------------------------- end_per_testcase(Case, Config) -> megaco_test_lib:end_per_testcase(Case, Config). %%====================================================================== %% Test case definitions %%====================================================================== all() -> [{group, start}, {group, sending}, {group, errors}]. groups() -> [{start, [], [start_normal, start_invalid_opt, start_and_stop]}, {sending, [], [sendreceive, block_unblock]}, {errors, [], [socket_failure]}]. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. %% ================================================= %% %% ------------------ 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).