From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/megaco/test/megaco_test_tcp_generator.erl | 504 ++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 lib/megaco/test/megaco_test_tcp_generator.erl (limited to 'lib/megaco/test/megaco_test_tcp_generator.erl') diff --git a/lib/megaco/test/megaco_test_tcp_generator.erl b/lib/megaco/test/megaco_test_tcp_generator.erl new file mode 100644 index 0000000000..e4f27f32f5 --- /dev/null +++ b/lib/megaco/test/megaco_test_tcp_generator.erl @@ -0,0 +1,504 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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: megaco_tcp sequence generator for the megaco test suite +%%---------------------------------------------------------------------- + +-module(megaco_test_tcp_generator). + +-behaviour(megaco_test_generator). + +%% API +-export([ + start_link/1, start_link/2, + stop/1, + exec/2, exec/3 + ]). + +%% genarator behaviour callback exports +-export([ + init/1, + handle_parse/2, + handle_exec/2, + terminate/2 + ]). + + +-record(state, + { + listen, % Listen socket + connection, % Connection socket + encode, % Encode fun + decode, % Decode fun + result = [] % Accumulated results from exec + }). + + +%%---------------------------------------------------------------------- +%% API +%%---------------------------------------------------------------------- + +start_link(Name) -> + megaco_test_generator:start_link(?MODULE, [], Name). + +start_link(Name, Node) -> + megaco_test_generator:start_link(?MODULE, [], Name, Node). + +stop(Server) -> + megaco_test_generator:stop(Server). + +exec(Server, Instructions) when is_list(Instructions) -> + megaco_test_generator:exec(Server, Instructions). + +exec(Server, Instructions, Timeout) when is_list(Instructions) -> + megaco_test_generator:exec(Server, Instructions, Timeout). + + +%%---------------------------------------------------------------------- +%% generator callback functions +%%---------------------------------------------------------------------- + +init([]) -> + {ok, #state{}}. + + +%% ----- instruction parser ----- + +handle_parse({debug, Debug} = Instruction, State) + when (Debug == true) orelse (Debug == false) -> + {ok, Instruction, State}; + +handle_parse({decode, Decode} = Instruction, State) + when is_function(Decode) -> + {ok, Instruction, State}; + +handle_parse({encode, Encode} = Instruction, State) + when is_function(Encode) -> + {ok, Instruction, State}; + +handle_parse(disconnect = Instruction, State) -> + {ok, Instruction, State}; + +handle_parse({listen, Port} = Instruction, State) + when is_integer(Port) andalso (Port >= 0) -> + {ok, Instruction, State}; + +handle_parse({expect_accept, any} = _Instruction, State) -> + {ok, {expect_accept, {any, infinity}}, State}; + +handle_parse({expect_accept, {any, To}} = Instruction, State) + when (is_integer(To) andalso (To >= 0)) orelse (To == infinity) -> + {ok, Instruction, State}; + +handle_parse({expect_accept, {Host, To}} = _Instruction, State) + when (is_integer(To) andalso (To >= 0)) orelse (To == infinity) -> + case inet:getaddr(Host, inet) of + {ok, Addr} -> + Instruction = {expect_accept, {Addr, To}}, + {ok, Instruction, State}; + {error, Reason} -> + {error, {bad_host, Host, Reason}} + end; + +handle_parse({expect_accept, Host} = _Instruction, State) -> + case inet:getaddr(Host, inet) of + {ok, Addr} -> + Instruction = {expect_accept, {Addr, infinity}}, + {ok, Instruction, State}; + {error, Reason} -> + {error, {bad_host, Host, Reason}} + end; + +handle_parse({active, NewState} = Instruction, State) + when (NewState == true) orelse + (NewState == false) orelse + (NewState == once) -> + {ok, Instruction, State}; + +handle_parse({connect, Port} = _Instruction, State) + when is_integer(Port) andalso (Port >= 0) -> + Host = + case inet:gethostname() of + {ok, Hostname} -> + Hostname; + {error, Reason1} -> + error({failed_geting_own_hostname, Reason1}) + end, + case inet:getaddr(Host, inet) of + {ok, Address} -> + Instruction = {connect, {Address, Port, infinity}}, + {ok, Instruction, State}; + {error, Reason2} -> + {error, {bad_host, Host, Reason2}} + end; + +handle_parse({connect, {Port, To}} = _Instruction, State) + when (is_integer(Port) andalso + (Port >= 0)) andalso ((is_integer(To) andalso (To >= 0)) orelse + (To == infinity)) -> + Host = + case inet:gethostname() of + {ok, Hostname} -> + Hostname; + {error, Reason1} -> + error({failed_geting_own_hostname, Reason1}) + end, + case inet:getaddr(Host, inet) of + {ok, Address} -> + Instruction = {connect, {Address, Port, To}}, + {ok, Instruction, State}; + {error, Reason2} -> + {error, {bad_host, Host, Reason2}} + end; + +handle_parse({connect, {Host, Port}} = _Instruction, State) + when (is_integer(Port) andalso (Port >= 0)) -> + case inet:getaddr(Host, inet) of + {ok, Address} -> + Instruction = {connect, {Address, Port, infinity}}, + {ok, Instruction, State}; + {error, Reason} -> + {error, {bad_host, Host, Reason}} + end; + +handle_parse({connect, {Host, Port, To}} = _Instruction, State) + when (is_integer(Port) andalso + (Port >= 0)) andalso ((is_integer(To) andalso (To >= 0)) orelse + (To == infinity)) -> + case inet:getaddr(Host, inet) of + {ok, Address} -> + Instruction = {connect, {Address, Port, To}}, + {ok, Instruction, State}; + {error, Reason} -> + {error, {bad_host, Host, Reason}} + end; + +handle_parse({sleep, To} = Instruction, State) + when is_integer(To) andalso (To > 0) -> + {ok, Instruction, State}; + +handle_parse({expect_nothing, To} = Instruction, State) + when is_integer(To) andalso (To > 0) -> + {ok, Instruction, State}; + +handle_parse({expect_closed, To} = Instruction, State) + when is_integer(To) andalso (To > 0) -> + {ok, Instruction, State}; + +handle_parse({expect_receive, Desc, Verify} = _Instruction, State) + when is_list(Desc) andalso is_function(Verify) -> + Instruction = {expect_receive, Desc, {Verify, infinity}}, + {ok, Instruction, State}; + +handle_parse({expect_receive, Desc, {Verify, To}} = Instruction, State) + when is_list(Desc) andalso + is_function(Verify) andalso + ((is_integer(To) andalso (To >= 0)) orelse (To == infinity)) -> + {ok, Instruction, State}; + +handle_parse({send, Desc, Msg} = Instruction, State) + when is_list(Desc) andalso (is_tuple(Msg) orelse is_binary(Msg)) -> + {ok, Instruction, State}; + +handle_parse({trigger, Desc, Trigger} = Instruction, State) + when is_list(Desc) andalso is_function(Trigger) -> + {ok, Instruction, State}; + +handle_parse(Instruction, _State) -> + {error, {unknown_instruction, Instruction}}. + + +%% ----- instruction exececutor ----- + +handle_exec({debug, Debug}, + State) -> + p("debug: ~p", [Debug]), + put(debug, Debug), + {ok, State}; + +handle_exec({encode, Encode}, + State) -> + p("encode: ~p", [Encode]), + {ok, State#state{encode = Encode}}; + +handle_exec({decode, Decode}, + State) -> + p("Decode: ~p", [Decode]), + {ok, State#state{decode = Decode}}; + +handle_exec(disconnect, + #state{listen = Listen, + connection = Sock, + result = Res} = State) -> + p("disconnect"), + (catch gen_tcp:close(Sock)), + (catch gen_tcp:close(Listen)), + {ok, State#state{listen = undefined, + connection = undefined, + result = [disconnected|Res]}}; + +handle_exec({listen, Port}, #state{result = Res} = State) -> + p("listen to ~p", [Port]), + Opts = [binary, + {packet, tpkt}, + {active, false}, + {reuseaddr, true}, + {nodelay, true}], + case (catch gen_tcp:listen(Port, Opts)) of + {ok, Listen} -> + d("listen -> listen socket created"), + {ok, State#state{listen = Listen, result = [listening | Res]}}; + {error, Reason} -> + e("failed creating listen socket: ~p", [Reason]), + {error, {failed_creating_listen_socket, Reason, Res}} + end; + +handle_exec({expect_accept, {Addr, To}}, + #state{listen = Listen, + result = Res} = State) -> + p("expect_accept from ~p (~p)", [Addr, To]), + case (catch gen_tcp:accept(Listen, To)) of + {ok, Sock} -> + d("expect_accept -> connection accepted"), + case (catch inet:peername(Sock)) of + {ok, {Addr, _Port}} -> + d("expect_accept -> valid address"), + NewState = + State#state{connection = Sock, + result = [{connected, Addr}|Res]}, + {ok, NewState}; + {ok, {OtherAddr, _Port}} when Addr == any -> + d("expect_accept -> valid (~p)", [OtherAddr]), + NewState = + State#state{connection = Sock, + result = [{connected, OtherAddr}|Res]}, + {ok, NewState}; + {ok, AddrAndPort} -> + {error, {invalid_connect, AddrAndPort, Res}}; + {error, Reason} -> + e("failed getting peername for socket: ~p", [Reason]), + (catch gen_tcp:close(Sock)), + {error, {failed_getting_peername, Sock, Reason}} + end; + {error, Reason} -> + e("failed accepting connection: ~p", [Reason]), + (catch gen_tcp:close(Listen)), + {error, {failed_accepting_conection, Reason, Listen}} + end; + +handle_exec({active, NewState}, + #state{connection = Sock, + result = Res} = State) -> + p("active to ~p", [NewState]), + case inet:setopts(Sock, [{active, NewState}]) of + ok -> + d("active -> state changed"), + {ok, State#state{result = [{active, NewState}|Res]}}; + {error, Reason} -> + e("failed changing active state to ~w: ~p", [NewState, Reason]), + {error, {failed_setting_active, Reason}} + end; + +handle_exec({connect, {Addr, Port, To}}, + #state{result = Res} = State) -> + p("connect to ~p, ~p", [Addr, Port]), + Opts = [binary, {packet, tpkt}, {active, once}, {nodelay, true}], + case (catch gen_tcp:connect(Addr, Port, Opts, To)) of + {ok, Sock} -> + d("connect -> connected"), + {ok, State#state{connection = Sock, + result = [{connected, Addr, Port}|Res]}}; + {error, Reason} -> + e("failed connecting: ~p", [Reason]), + {error, {failed_connect, Addr, Port, Reason}} + end; + +%% Already encoded +handle_exec({send, Desc, Bin}, + #state{connection = Sock, + result = Res} = State) + when is_binary(Bin) -> + p("send ~s message", [Desc]), + NewBin = add_tpkt_header(Bin), + d("send -> tpkt header added [~w], now send", [sz(NewBin)]), + case (catch gen_tcp:send(Sock, NewBin)) of + ok -> + d("send -> message sent"), + {ok, State#state{result = [{sent, Desc}|Res]}}; + {error, Reason} -> + e("send -> send failed: ~n~p",[Reason]), + {error, {failed_send, Reason}} + end; + +handle_exec({send, Desc, Msg}, + #state{connection = Sock, + encode = Encode, + result = Res} = State) -> + p("send ~s message", [Desc]), + case (catch Encode(Msg)) of + {ok, Bin} -> + d("send -> message encoded [~w], now add tpkt header: ~n~s", + [sz(Bin), binary_to_list(Bin)]), + NewBin = add_tpkt_header(Bin), + d("send -> tpkt header added [~w], now send", [sz(NewBin)]), + case (catch gen_tcp:send(Sock, NewBin)) of + ok -> + d("send -> message sent"), + {ok, State#state{result = [{sent, Desc}|Res]}}; + {error, Reason} -> + e("send -> send failed: ~n~p", [Reason]), + {error, {failed_send, Reason}} + end; + Error -> + e("send -> encode failed: ~n~p", [Error]), + {error, {encode_failed, Error}} + end; + +handle_exec({expect_receive, Desc, {Verify, To}}, + #state{connection = Sock, + decode = Decode, + result = Acc} = State) -> + p("expect_receive ~s message", [Desc]), + inet:setopts(Sock, [{active, once}]), + receive + {tcp, Sock, <<3:8, _X:8, Length:16, Msg/binary>>} -> + d("expect_receive -> received message: Length = ~p", [Length]), + case (catch Decode(Msg)) of + {ok, MegaMsg} when is_tuple(MegaMsg) -> + d("expect_receive -> decode successfull, now verify"), + case (catch Verify(MegaMsg)) of + {ok, Res} -> + d("expect_receive -> verify successfull"), + {ok, State#state{result = [Res|Acc]}}; + Else -> + e("failed to verify message: ~n~p~n~p", + [Else, MegaMsg]), + {error, {expect_receive, {verify_failed, Else}}} + end; + Error -> + e("failed decoding message: ~p", [Error]), + {error, {expect_receive, Error}} + end; + Else -> + e("received unknown message: ~p", [Else]), + {error, {expect_receive, {unexpected_message, Else}}} + after To -> + {error, {expect_receive, timeout}} + end; + +handle_exec({expect_closed, To}, + #state{connection = Sock, + result = Acc} = State) -> + p("expect_closed ~w", [To]), + inet:setopts(Sock, [{active, once}]), + p("expect_closed - await closed", []), + receive + {tcp_closed, Sock} -> + p("expect_closed - received closed"), + {ok, State#state{connection = undefined, + result = [closed|Acc]}} + after To -> + e("expect_closed timeout after ~w", [To]), + {error, {expect_closed, timeout}} + end; + +handle_exec({expect_nothing, To}, + #state{connection = Sock, + result = Acc} = State) -> + p("expect_nothing ~w", [To]), + inet:setopts(Sock, [{active, once}]), + p("expect_nothing - await anything", []), + receive + Any -> + e("expect_nothing - received: ~p", [Any]), + {error, {expect_nothing, Any}} + after To -> + p("expect_nothing timeout after ~w", [To]), + {ok, State#state{result = [{nothing, To}|Acc]}} + end; + +handle_exec({trigger, Desc, Trigger}, + #state{result = Acc} = State) when is_function(Trigger) -> + p("trigger: ~s", [Desc]), + Trigger(), + {ok, State#state{result = [triggered|Acc]}}; + +handle_exec({sleep, To}, + #state{result = Acc} = State) -> + p("sleep ~p", [To]), + sleep(To), + {ok, State#state{result = [{slept, To}|Acc]}}; + +handle_exec(Instruction, _State) -> + {error, {unknown_instruction, Instruction}}. + + +%% ----- terminate ----- + +terminate(normal, #state{listen = Listen, + connection = Sock, + result = Result}) -> + (catch gen_tcp:close(Sock)), + (catch gen_tcp:close(Listen)), + {ok, Result}; +terminate(Reason, #state{listen = Listen, + connection = Sock, + result = Result}) -> + (catch gen_tcp:close(Sock)), + (catch gen_tcp:close(Listen)), + {error, {Reason, Result}}. + + +%%---------------------------------------------------------------------- +%% internal utility functions +%%---------------------------------------------------------------------- + +error(Reason) -> + throw({error, Reason}). + + +%%% ---------------------------------------------------------------- + +add_tpkt_header(Bin) when is_binary(Bin) -> + L = size(Bin) + 4, + SZ1 = ((L) bsr 8) band 16#ff, + SZ2 = (L) band 16#ff, + <<3, 0, SZ1, SZ2, Bin/binary>>; +add_tpkt_header(IOList) when is_list(IOList) -> + add_tpkt_header(list_to_binary(IOList)). + +sleep(X) -> megaco_test_generator:sleep(X). + +sz(X) -> megaco_test_generator:sz(X). + + +%%% ---------------------------------------------------------------- + +d(F) -> megaco_test_generator:debug(F). +d(F, A) -> megaco_test_generator:debug(F, A). + +e(F, A) -> megaco_test_generator:error(F, A). + +p(F ) -> p("", F, []). +p(F, A) -> p("", F, A). +p(P, F, A) -> megaco_test_generator:print(P, F, A). + + -- cgit v1.2.3