From ce4905a124ed2dd7bd08f994b83f803232d7e5f7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 13 May 2013 16:10:03 +0200 Subject: [common_test] Add telnet server to use for testing --- lib/common_test/test/Makefile | 1 + lib/common_test/test/telnet_server.erl | 217 +++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 lib/common_test/test/telnet_server.erl (limited to 'lib') diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 94569fa87f..31ab28c41d 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -28,6 +28,7 @@ MODULES= \ ct_test_support \ ct_test_support_eh \ ct_userconfig_callback \ + telnet_server \ ct_smoke_test_SUITE \ ct_priv_dir_SUITE \ ct_event_handler_SUITE \ diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl new file mode 100644 index 0000000000..5843155eee --- /dev/null +++ b/lib/common_test/test/telnet_server.erl @@ -0,0 +1,217 @@ +-module(telnet_server). +-compile(export_all). + +%% telnet control characters +-define(SE, 240). +-define(NOP, 241). +-define(DM, 242). +-define(BRK, 243). +-define(IP, 244). +-define(AO, 245). +-define(AYT, 246). +-define(EC, 247). +-define(EL, 248). +-define(GA, 249). +-define(SB, 250). +-define(WILL, 251). +-define(WONT, 252). +-define(DO, 253). +-define(DONT, 254). +-define(IAC, 255). + +%% telnet options +-define(BINARY, 0). +-define(ECHO, 1). +-define(SUPPRESS_GO_AHEAD, 3). +-define(TERMINAL_TYPE, 24). + +-record(state, + {listen, + client, + users, + authorized=false, + suppress_go_ahead=false, + buffer=[]}). + +-type options() :: [{port,pos_integer()} | {users,users()}]. +-type users() :: [{user(),password()}]. +-type user() :: string(). +-type password() :: string(). + +-spec start(Opts::options()) -> Pid::pid(). +start(Opts) when is_list(Opts) -> + spawn_link(fun() -> init(Opts) end). + +-spec stop(Pid::pid()) -> ok. +stop(Pid) -> + Ref = erlang:monitor(process,Pid), + Pid ! stop, + receive {'DOWN',Ref,_,_,_} -> ok end. + +init(Opts) -> + Port = proplists:get_value(port,Opts), + Users = proplists:get_value(users,Opts,[]), + {ok, LSock} = gen_tcp:listen(Port, [list, {packet, 0}, + {active, true}]), + State = #state{listen=LSock,users=Users}, + accept(State), + ok = gen_tcp:close(LSock). + +accept(#state{listen=LSock}=State) -> + Server = self(), + Acceptor = spawn_link(fun() -> do_accept(LSock,Server) end), + receive + {Acceptor,Sock} when is_port(Sock) -> + case init_client(State#state{client=Sock}) of + stopped -> + io:format("telnet_server stopped\n"), + ok; + R -> + io:format("connection to client closed with reason ~p~n",[R]), + accept(State) + end; + {Acceptor,closed} -> + io:format("listen socket closed unexpectedly, " + "terminating telnet_server\n"), + ok; + stop -> + io:format("telnet_server stopped\n"), + ok + end. + +do_accept(LSock,Server) -> + case gen_tcp:accept(LSock) of + {ok, Sock} -> + ok = gen_tcp:controlling_process(Sock,Server), + Server ! {self(),Sock}; + {error,closed} -> + %% This will happen when stop/1 is called, since listen + %% socket is closed. Then the server is probably already + %% dead by now, but to be 100% sure not to hang, we send a + %% message to say what happened. + Server ! {self(),closed} + end. + +init_client(#state{client=Sock}=State) -> + R = case gen_tcp:send(Sock,"login: ") of + ok -> + loop(State); + Error -> + Error + end, + _ = gen_tcp:close(Sock), + R. + +loop(State) -> + receive + {tcp,_,Data} -> + try handle_data(Data,State) of + {ok,State1} -> + loop(State1) + catch + throw:Error -> + Error + end; + {tcp_closed, _} -> + closed; + {tcp_error,_,Error} -> + {error,tcp,Error}; + stop -> + stopped + end. + +handle_data([?IAC|Cmd],State) -> + dbg("Server got cmd: ~p~n",[Cmd]), + handle_cmd(Cmd,State); +handle_data(Data,State) -> + dbg("Server got data: ~p~n",[Data]), + case get_line(Data,[]) of + {Line,Rest} -> + WholeLine = lists:flatten(lists:reverse(State#state.buffer,Line)), + {ok,State1} = do_handle_data(WholeLine,State), + case Rest of + [] -> {ok,State1}; + _ -> handle_data(Rest,State1) + end; + false -> + {ok,State#state{buffer=[Data|State#state.buffer]}} + end. + +%% Add function clause below to handle new telnet commands (sent with +%% ?IAC from client - this is not possible to do from ct_telnet API, +%% but ct_telnet sends DONT SUPPRESS_GO_AHEAD) +handle_cmd([?DO,?SUPPRESS_GO_AHEAD|T],State) -> + send([?IAC,?WILL,?SUPPRESS_GO_AHEAD],State), + handle_cmd(T,State#state{suppress_go_ahead=true}); +handle_cmd([?DONT,?SUPPRESS_GO_AHEAD|T],State) -> + send([?IAC,?WONT,?SUPPRESS_GO_AHEAD],State), + handle_cmd(T,State#state{suppress_go_ahead=false}); +handle_cmd([?IAC|T],State) -> + %% Multiple commands in one packet + handle_cmd(T,State); +handle_cmd([_H|T],State) -> + %% Not responding to this command + handle_cmd(T,State); +handle_cmd([],State) -> + {ok,State}. + +%% Add function clause below to handle new text command (text entered +%% from the telnet prompt) +do_handle_data(Data,#state{authorized=false}=State) -> + check_user(Data,State); +do_handle_data(Data,#state{authorized={user,_}}=State) -> + check_pwd(Data,State); +do_handle_data("echo "++ Data,State) -> + send(Data++"\r\n> ",State), + {ok,State}; +do_handle_data("repeat "++ Data,State) -> + send(Data++"\r\n"++Data++"\r\n> ",State), + {ok,State}; +do_handle_data([],State) -> + send("> ",State), + {ok,State}; +do_handle_data(_Data,State) -> + send("error: unknown command\r\n> ",State), + {ok,State}. + +check_user(User,State) -> + case lists:keyfind(User,1,State#state.users) of + {User,Pwd} -> + dbg("user ok\n"), + send("Password: ",State), + {ok,State#state{authorized={user,Pwd}}}; + false -> + throw({error,unknown_user}) + end. + +check_pwd(Pwd,#state{authorized={user,Pwd}}=State) -> + dbg("password ok\n"), + send("Welcome to the ultimate telnet server!\r\n> ",State), + {ok,State#state{authorized=true}}; +check_pwd(_,_State) -> + throw({error,authentication}). + +send(Data,State) -> + dbg("Server sending: ~p~n",[Data]), + case gen_tcp:send(State#state.client,Data) of + ok -> + ok; + {error,Error} -> + throw({error,send,Error}) + end. + +get_line([$\r,$\n|Rest],Acc) -> + {lists:reverse(Acc),Rest}; +get_line([$\r,0|Rest],Acc) -> + {lists:reverse(Acc),Rest}; +get_line([$\n|Rest],Acc) -> + {lists:reverse(Acc),Rest}; +get_line([H|T],Acc) -> + get_line(T,[H|Acc]); +get_line([],_) -> + false. + +dbg(_F) -> + io:format(_F). +dbg(_F,_A) -> + io:format(_F,_A). -- cgit v1.2.3 From 4d086abb73f7b9af178c966d5fb66012c4ec6612 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 13 May 2013 16:12:10 +0200 Subject: [common_test] Add test for ct_telnet using own telnet server The new test suite ct_telnet_SUITE_data/ct_telnet_own_server_SUITE uses the very simple telnet_server.erl located in the common_test test directory. --- lib/common_test/test/ct_telnet_SUITE.erl | 44 ++++++++++++++++++--- .../ct_telnet_own_server_SUITE.erl | 46 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl (limited to 'lib') diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl index b4f24baa0c..c75f0f5843 100644 --- a/lib/common_test/test/ct_telnet_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE.erl @@ -33,6 +33,10 @@ -define(eh, ct_test_support_eh). +-define(erl_telnet_server_port,1234). +-define(erl_telnet_server_user,"telnuser"). +-define(erl_telnet_server_pwd,"telnpwd"). + %%-------------------------------------------------------------------- %% TEST SERVER CALLBACK FUNCTIONS %%-------------------------------------------------------------------- @@ -48,10 +52,18 @@ init_per_suite(Config) -> end_per_suite(Config) -> ct_test_support:end_per_suite(Config). +init_per_testcase(own_server=TestCase, Config) -> + TS = telnet_server:start([{port,1234},{users,[{?erl_telnet_server_user, + ?erl_telnet_server_pwd}]}]), + ct_test_support:init_per_testcase(TestCase, [{telnet_server,TS}|Config]); init_per_testcase(TestCase, Config) -> ct_test_support:init_per_testcase(TestCase, Config). end_per_testcase(TestCase, Config) -> + case ?config(telnet_server,Config) of + undefined -> ok; + TS -> telnet_server:stop(TS) + end, ct_test_support:end_per_testcase(TestCase, Config). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -71,11 +83,26 @@ default(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), Suite = filename:join(DataDir, "ct_telnet_basic_SUITE"), Cfg = {unix, ct:get_config(unix)}, - ok = file:write_file(filename:join(DataDir, "telnet.cfg"), io_lib:write(Cfg) ++ "."), CfgFile = filename:join(DataDir, "telnet.cfg"), + ok = file:write_file(CfgFile, io_lib:write(Cfg) ++ "."), {Opts,ERPid} = setup([{suite,Suite},{label,default}, {config, CfgFile}], Config), ok = execute(default, Opts, ERPid, Config). +own_server(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "ct_telnet_own_server_SUITE"), + Cfg = {unix,[{telnet,"localhost"}, + {port, 1234}, + {username,?erl_telnet_server_user}, + {password,?erl_telnet_server_pwd}, + {wait_for_linebreak, false}, +% {not_require_user_and_pass, true}, + {keep_alive,true}]}, + CfgFile = filename:join(DataDir, "telnet2.cfg"), + ok = file:write_file(CfgFile, io_lib:write(Cfg) ++ "."), + {Opts,ERPid} = setup([{suite,Suite},{label,own_server}, {config, CfgFile}], Config), + ok = execute(own_server, Opts, ERPid, Config). + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- @@ -107,15 +134,20 @@ reformat(Events, EH) -> %%% TEST EVENTS %%%----------------------------------------------------------------- events_to_check(default,Config) -> + all_cases(ct_telnet_basic_SUITE,Config); +events_to_check(own_server,Config) -> + all_cases(ct_telnet_own_server_SUITE,Config). + +all_cases(Suite,Config) -> {module,_} = code:load_abs(filename:join(?config(data_dir,Config), - ct_telnet_basic_SUITE)), - TCs = ct_telnet_basic_SUITE:all(), - code:purge(ct_telnet_basic_SUITE), - code:delete(ct_telnet_basic_SUITE), + Suite)), + TCs = Suite:all(), + code:purge(Suite), + code:delete(Suite), OneTest = [{?eh,start_logging,{'DEF','RUNDIR'}}] ++ - [{?eh,tc_done,{ct_telnet_basic_SUITE,TC,ok}} || TC <- TCs] ++ + [{?eh,tc_done,{Suite,TC,ok}} || TC <- TCs] ++ [{?eh,stop_logging,[]}], %% 2 tests (ct:run_test + script_start) is default diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl new file mode 100644 index 0000000000..2173c68c11 --- /dev/null +++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl @@ -0,0 +1,46 @@ +-module(ct_telnet_own_server_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + + +suite() -> [{require,erl_telnet_server,{unix,[telnet]}}]. + +all() -> + [expect, + expect_repeat]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +expect(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo ayt"), + {ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]), + ok = ct_telnet:close(Handle), + ok. + +expect_repeat(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "repeat xy"), + {ok,[["xy"],["xy"]],done} = ct_telnet:expect(Handle, ["xy"], + [{repeat,2}]), + ok = ct_telnet:close(Handle), + ok. -- cgit v1.2.3 From e27cf4ac6a9d71fa38ea8c3af2287a817110aedd Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 14 May 2013 11:15:32 +0200 Subject: [common_test] Backed out wait_for_linebreak option from ct_telnet --- lib/common_test/src/ct_telnet.erl | 46 ++++++++++--------------------------- lib/common_test/src/unix_telnet.erl | 36 ++++------------------------- 2 files changed, 17 insertions(+), 65 deletions(-) (limited to 'lib') diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index bd74991859..b748c17f30 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -29,9 +29,7 @@ %%% Command timeout = 10 sec (time to wait for a command to return) %%% Max no of reconnection attempts = 3 %%% Reconnection interval = 5 sek (time to wait in between reconnection attempts) -%%% Keep alive = true (will send NOP to the server every 10 sec if connection is idle) -%%% Wait for linebreak = true (Will expect answer from server to end with linebreak when -%%% using ct_telnet:expect) +%%% Keep alive = true (will send NOP to the server every 10 sec if connection is idle) %%%

These parameters can be altered by the user with the following %%% configuration term:

%%%
@@ -39,8 +37,7 @@
 %%%                    {command_timeout,Millisec},
 %%%                    {reconnection_attempts,N},
 %%%                    {reconnection_interval,Millisec},
-%%%                    {keep_alive,Bool},
-%%                     {wait_for_linebreak, Bool}]}.
+%%% {keep_alive,Bool}]}. %%%

Millisec = integer(), N = integer()

%%%

Enter the telnet_settings term in a configuration %%% file included in the test and ct_telnet will retrieve the information @@ -731,8 +728,7 @@ teln_get_all_data(Pid,Prx,Data,Acc,LastLine) -> haltpatterns=[], seq=false, repeat=false, - found_prompt=false, - wait_for_linebreak=true}). + found_prompt=false}). %% @hidden %% @doc Externally the silent_teln_expect function shall only be used @@ -758,25 +754,20 @@ silent_teln_expect(Pid,Data,Pattern,Prx,Opts) -> %% condition is fullfilled. %% 3b) Repeat (sequence): 2) is repeated either N times or until a %% halt condition is fullfilled. -teln_expect(Pid,Data,Pattern0,Prx,Opts) -> - HaltPatterns = case get_ignore_prompt(Opts) of - true -> - get_haltpatterns(Opts); - false -> - [prompt | get_haltpatterns(Opts)] - end, - WaitForLineBreak = get_line_break_opt(Opts), +teln_expect(Pid,Data,Pattern0,Prx,Opts) -> HaltPatterns = case + get_ignore_prompt(Opts) of true -> get_haltpatterns(Opts); false + -> [prompt | get_haltpatterns(Opts)] end, + Seq = get_seq(Opts), Pattern = convert_pattern(Pattern0,Seq), - + Timeout = get_timeout(Opts), - + EO = #eo{teln_pid=Pid, prx=Prx, timeout=Timeout, seq=Seq, - haltpatterns=HaltPatterns, - wait_for_linebreak=WaitForLineBreak}, + haltpatterns=HaltPatterns}, case get_repeat(Opts) of false -> @@ -817,11 +808,6 @@ get_timeout(Opts) -> {value,{timeout,T}} -> T; false -> ?DEFAULT_TIMEOUT end. -get_line_break_opt(Opts) -> - case lists:keysearch(wait_for_linebreak,1,Opts) of - {value,{wait_for_linebreak,false}} -> false; - _ -> true - end. get_repeat(Opts) -> case lists:keysearch(repeat,1,Opts) of {value,{repeat,N}} when is_integer(N) -> @@ -1018,9 +1004,8 @@ seq_expect1(Data,[],Acc,Rest,_EO) -> %% Split prompt-chunk at lines match_lines(Data,Patterns,EO) -> FoundPrompt = EO#eo.found_prompt, - NeedLineBreak = EO#eo.wait_for_linebreak, case one_line(Data,[]) of - {noline,Rest} when FoundPrompt=/=false, NeedLineBreak =:= true -> + {noline,Rest} when FoundPrompt=/=false -> %% This is the line including the prompt case match_line(Rest,Patterns,FoundPrompt,EO) of nomatch -> @@ -1028,14 +1013,7 @@ match_lines(Data,Patterns,EO) -> {Tag,Match} -> {Tag,Match,[]} end; - {noline,Rest} when NeedLineBreak =:= false -> - case match_line(Rest,Patterns,FoundPrompt,EO) of - nomatch -> - {nomatch,prompt}; - {Tag,Match} -> - {Tag,Match,[]} - end; - {noline, Rest} -> + {noline,Rest} -> {nomatch,Rest}; {Line,Rest} -> case match_line(Line,Patterns,false,EO) of diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl index 71df2ab44e..88199b07d0 100644 --- a/lib/common_test/src/unix_telnet.erl +++ b/lib/common_test/src/unix_telnet.erl @@ -94,16 +94,11 @@ connect(Ip,Port,Timeout,KeepAlive,Extra) -> {Username,Password} -> connect1(Ip,Port,Timeout,KeepAlive,Username,Password); Name -> - case not_require_user_and_pass(Name) of - true -> - connect_without_username_and_pass(Ip,Port,Timeout,KeepAlive); - _ -> - case get_username_and_password(Name) of - {ok,{Username,Password}} -> - connect1(Ip,Port,Timeout,KeepAlive,Username,Password); - Error -> - Error - end + case get_username_and_password(Name) of + {ok,{Username,Password}} -> + connect1(Ip,Port,Timeout,KeepAlive,Username,Password); + Error -> + Error end end. @@ -149,27 +144,6 @@ connect1(Ip,Port,Timeout,KeepAlive,Username,Password) -> end_log(), Result. -connect_without_username_and_pass(Ip,Port,Timeout,KeepAlive) -> - start_log("unix_telnet:connect"), - Result = - case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive) of - {ok,Pid} -> - {ok, Pid}; - Error -> - cont_log("Could not open telnet connection\n~p\n",[Error]), - Error - end, - end_log(), - Result. - -not_require_user_and_pass(Name) -> - case ct:get_config({Name, not_require_user_and_pass}) of - undefined -> - false; - _ -> - true - end. - get_username_and_password(Name) -> case ct:get_config({Name,username}) of undefined -> -- cgit v1.2.3 From 61641a3e1d86014ce6e6a27ab27faac7ec461d70 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 15 May 2013 11:36:41 +0200 Subject: [common_test] Add debug printouts in ct_telnet_client --- lib/common_test/src/ct_telnet_client.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl index 7329498ed6..2cbcba9c77 100644 --- a/lib/common_test/src/ct_telnet_client.erl +++ b/lib/common_test/src/ct_telnet_client.erl @@ -143,7 +143,9 @@ loop(State, Sock, Acc) -> State end; _ -> - Pid ! {data,lists:reverse(lists:append(Acc))}, + Data = lists:reverse(lists:append(Acc)), + dbg("get_data ~p\n",[Data]), + Pid ! {data,Data}, State end, loop(NewState, Sock, []); @@ -161,7 +163,9 @@ loop(State, Sock, Acc) -> NewAcc = case erlang:is_process_alive(Pid) of true -> - Pid ! {data,lists:reverse(lists:append(Acc))}, + Data = lists:reverse(lists:append(Acc)), + dbg("get_data_delayed ~p\n",[Data]), + Pid ! {data,Data}, []; false -> Acc -- cgit v1.2.3 From 55d975b3a1266d50b5a55004a004d0f244d5a17b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 15 May 2013 12:09:36 +0200 Subject: [common_test] Add new option 'no_prompt_check' to ct_telnet:expect/3. If this option is used, ct_telnet will not search for a prompt before attempting to match the given pattern. This is useful if, for instance, the Pattern itself matches the prompt or if the telnet session starts interactive programs which do not display the normal prompt. --- lib/common_test/src/ct_telnet.erl | 56 ++++++-- lib/common_test/test/ct_telnet_SUITE.erl | 7 +- .../ct_telnet_own_server_SUITE.erl | 146 ++++++++++++++++++++- lib/common_test/test/telnet_server.erl | 15 ++- 4 files changed, 205 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index b748c17f30..4755d939e0 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -309,7 +309,7 @@ expect(Connection,Patterns) -> %%% Tag = term() %%% Opts = [Opt] %%% Opt = {timeout,Timeout} | repeat | {repeat,N} | sequence | -%%% {halt,HaltPatterns} | ignore_prompt +%%% {halt,HaltPatterns} | ignore_prompt | no_prompt_check %%% Timeout = integer() %%% N = integer() %%% HaltPatterns = Patterns @@ -336,14 +336,28 @@ expect(Connection,Patterns) -> %%% will also include the matched Tag. Else, only %%% RxMatch is returned.

%%% -%%%

The function will always return when a prompt is found, unless -%%% the ignore_prompt options is used.

-%%% %%%

The timeout option indicates that the function %%% shall return if the telnet client is idle (i.e. if no data is %%% received) for more than Timeout milliseconds. Default %%% timeout is 10 seconds.

%%% +%%%

The function will always return when a prompt is found, unless +%%% any of the ignore_prompt or +%%% no_prompt_check options are used, in which case it +%%% will return when a match is found or after a timeout.

+%%% +%%%

If the ignore_prompt option is used, +%%% ct_telnet will ignore any prompt found. This option +%%% is useful if data sent by the server could include a pattern that +%%% would match the prompt regexp (as returned by +%%% TargedMod:get_prompt_regexp/0), but which should not +%%% cause the function to return.

+%%% +%%%

If the no_prompt_check option is used, +%%% ct_telnet will not search for a prompt at all. This +%%% is useful if, for instance, the Pattern itself +%%% matches the prompt.

+%%% %%%

The repeat option indicates that the pattern(s) %%% shall be matched multiple times. If N is given, the %%% pattern(s) will be matched N times, and the function @@ -728,7 +742,8 @@ teln_get_all_data(Pid,Prx,Data,Acc,LastLine) -> haltpatterns=[], seq=false, repeat=false, - found_prompt=false}). + found_prompt=false, + prompt_check=true}). %% @hidden %% @doc Externally the silent_teln_expect function shall only be used @@ -754,10 +769,16 @@ silent_teln_expect(Pid,Data,Pattern,Prx,Opts) -> %% condition is fullfilled. %% 3b) Repeat (sequence): 2) is repeated either N times or until a %% halt condition is fullfilled. -teln_expect(Pid,Data,Pattern0,Prx,Opts) -> HaltPatterns = case - get_ignore_prompt(Opts) of true -> get_haltpatterns(Opts); false - -> [prompt | get_haltpatterns(Opts)] end, +teln_expect(Pid,Data,Pattern0,Prx,Opts) -> + HaltPatterns = + case get_ignore_prompt(Opts) of + true -> + get_haltpatterns(Opts); + false -> + [prompt | get_haltpatterns(Opts)] + end, + PromptCheck = get_prompt_check(Opts), Seq = get_seq(Opts), Pattern = convert_pattern(Pattern0,Seq), @@ -767,7 +788,8 @@ teln_expect(Pid,Data,Pattern0,Prx,Opts) -> HaltPatterns = case prx=Prx, timeout=Timeout, seq=Seq, - haltpatterns=HaltPatterns}, + haltpatterns=HaltPatterns, + prompt_check=PromptCheck}, case get_repeat(Opts) of false -> @@ -831,7 +853,9 @@ get_haltpatterns(Opts) -> end. get_ignore_prompt(Opts) -> lists:member(ignore_prompt,Opts). - +get_prompt_check(Opts) -> + not lists:member(no_prompt_check,Opts). + %% Repeat either single or sequence. All match results are accumulated %% and returned when a halt condition is fulllfilled. repeat_expect(Rest,_Pattern,Acc,#eo{repeat=0}) -> @@ -892,6 +916,9 @@ get_data1(Pid) -> %% lines and each line is matched against each pattern. %% one_expect: split data chunk at prompts +one_expect(Data,Pattern,EO) when EO#eo.prompt_check==false -> +% io:format("Raw Data ~p Pattern ~p EO ~p ",[Data,Pattern,EO]), + one_expect1(Data,Pattern,[],EO#eo{found_prompt=false}); one_expect(Data,Pattern,EO) -> case match_prompt(Data,EO#eo.prx) of {prompt,UptoPrompt,PromptType,Rest} -> @@ -950,6 +977,8 @@ seq_expect(Data,[],Acc,_EO) -> {match,lists:reverse(Acc),Data}; seq_expect([],Patterns,Acc,_EO) -> {continue,Patterns,lists:reverse(Acc),[]}; +seq_expect(Data,Patterns,Acc,EO) when EO#eo.prompt_check==false -> + seq_expect1(Data,Patterns,Acc,[],EO#eo{found_prompt=false}); seq_expect(Data,Patterns,Acc,EO) -> case match_prompt(Data,EO#eo.prx) of {prompt,UptoPrompt,PromptType,Rest} -> @@ -1013,6 +1042,13 @@ match_lines(Data,Patterns,EO) -> {Tag,Match} -> {Tag,Match,[]} end; + {noline,Rest} when EO#eo.prompt_check==false -> + case match_line(Rest,Patterns,false,EO) of + nomatch -> + {nomatch,Rest}; + {Tag,Match} -> + {Tag,Match,[]} + end; {noline,Rest} -> {nomatch,Rest}; {Line,Rest} -> diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl index c75f0f5843..a378c531f0 100644 --- a/lib/common_test/test/ct_telnet_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE.erl @@ -53,8 +53,9 @@ end_per_suite(Config) -> ct_test_support:end_per_suite(Config). init_per_testcase(own_server=TestCase, Config) -> - TS = telnet_server:start([{port,1234},{users,[{?erl_telnet_server_user, - ?erl_telnet_server_pwd}]}]), + TS = telnet_server:start([{port,?erl_telnet_server_port}, + {users,[{?erl_telnet_server_user, + ?erl_telnet_server_pwd}]}]), ct_test_support:init_per_testcase(TestCase, [{telnet_server,TS}|Config]); init_per_testcase(TestCase, Config) -> ct_test_support:init_per_testcase(TestCase, Config). @@ -92,7 +93,7 @@ own_server(Config) -> DataDir = ?config(data_dir, Config), Suite = filename:join(DataDir, "ct_telnet_own_server_SUITE"), Cfg = {unix,[{telnet,"localhost"}, - {port, 1234}, + {port, ?erl_telnet_server_port}, {username,?erl_telnet_server_user}, {password,?erl_telnet_server_pwd}, {wait_for_linebreak, false}, diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl index 2173c68c11..3f7c0d68bf 100644 --- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl @@ -19,7 +19,18 @@ suite() -> [{require,erl_telnet_server,{unix,[telnet]}}]. all() -> [expect, - expect_repeat]. + expect_repeat, + expect_sequence, + expect_error_prompt, + expect_error_timeout, + no_prompt_check, + no_prompt_check_repeat, + no_prompt_check_sequence, + no_prompt_check_timeout, + ignore_prompt, + ignore_prompt_repeat, + ignore_prompt_sequence, + ignore_prompt_timeout]. groups() -> []. @@ -30,6 +41,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +%% Simple expect expect(_) -> {ok, Handle} = ct_telnet:open(erl_telnet_server), ok = ct_telnet:send(Handle, "echo ayt"), @@ -37,10 +49,136 @@ expect(_) -> ok = ct_telnet:close(Handle), ok. +%% Expect with repeat option expect_repeat(_) -> {ok, Handle} = ct_telnet:open(erl_telnet_server), - ok = ct_telnet:send(Handle, "repeat xy"), - {ok,[["xy"],["xy"]],done} = ct_telnet:expect(Handle, ["xy"], - [{repeat,2}]), + ok = ct_telnet:send(Handle, "echo_ml xy xy"), + {ok,[["xy"],["xy"]],done} = ct_telnet:expect(Handle, ["xy"],[{repeat,2}]), + ok = ct_telnet:close(Handle), + ok. + +%% Expect with sequence option +expect_sequence(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_ml ab cd ef"), + {ok,[["ab"],["cd"],["ef"]]} = ct_telnet:expect(Handle, + [["ab"],["cd"],["ef"]], + [sequence]), + ok = ct_telnet:close(Handle), + ok. + +%% Check that expect returns when a prompt is found, even if pattern +%% is not matched. +expect_error_prompt(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo xxx> yyy"), + {error,{prompt,"> "}} = ct_telnet:expect(Handle, ["yyy"]), + ok = ct_telnet:close(Handle), + ok. + +%% Check that expect returns after idle timeout, and even if the +%% expected pattern is received - as long as not newline or prompt is +%% received it will not match. +expect_error_timeout(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_no_prompt xxx"), + {error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{timeout,1000}]), + ok = ct_telnet:close(Handle), + ok. + +%% expect with ignore_prompt option should not return even if a prompt +%% is found. The pattern after the prompt (here "> ") can be matched. +ignore_prompt(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo xxx> yyy"), + {ok,["yyy"]} = ct_telnet:expect(Handle, ["yyy"], [ignore_prompt]), + ok = ct_telnet:close(Handle), + ok. + +%% expect with ignore_prompt and repeat options. +ignore_prompt_repeat(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_ml yyy> yyy>"), + {ok,[["yyy"],["yyy"]],done} = ct_telnet:expect(Handle, ["yyy"], + [{repeat,2}, + ignore_prompt]), + ok = ct_telnet:close(Handle), + ok. + +%% expect with ignore_prompt and sequence options. +ignore_prompt_sequence(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_ml xxx> yyy> zzz> "), + {ok,[["xxx"],["yyy"],["zzz"]]} = ct_telnet:expect(Handle, + [["xxx"],["yyy"],["zzz"]], + [sequence, + ignore_prompt]), + ok = ct_telnet:close(Handle), + ok. + +%% Check that expect returns after idle timeout when ignore_prompt +%% option is used. +%% As for expect without the ignore_prompt option, it a newline or a +%% prompt is required in order for the pattern to match. +ignore_prompt_timeout(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo xxx"), + {error,timeout} = ct_telnet:expect(Handle, ["yyy"], [ignore_prompt, + {timeout,1000}]), + ok = ct_telnet:send(Handle, "echo xxx"), % sends prompt and newline + {ok,["xxx"]} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt, + {timeout,1000}]), + ok = ct_telnet:send(Handle, "echo_no_prompt xxx\n"), % no prompt, but newline + {ok,["xxx"]} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt, + {timeout,1000}]), + ok = ct_telnet:send(Handle, "echo_no_prompt xxx"), % no prompt, no newline + {error,timeout} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt, + {timeout,1000}]), + ok = ct_telnet:close(Handle), + ok. + +%% no_prompt_check option shall match pattern both when prompt is sent +%% and when it is not. +no_prompt_check(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo xxx"), + {ok,["xxx"]} = ct_telnet:expect(Handle, ["xxx"], [no_prompt_check]), + ok = ct_telnet:send(Handle, "echo_no_prompt yyy"), + {ok,["yyy"]} = ct_telnet:expect(Handle, ["yyy"], [no_prompt_check]), + ok = ct_telnet:close(Handle), + ok. + +%% no_prompt_check and repeat options +no_prompt_check_repeat(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_ml xxx xxx"), + {ok,[["xxx"],["xxx"]],done} = ct_telnet:expect(Handle,["xxx"], + [{repeat,2}, + no_prompt_check]), + ok = ct_telnet:send(Handle, "echo_ml_no_prompt yyy yyy"), + {ok,[["yyy"],["yyy"]],done} = ct_telnet:expect(Handle,["yyy"], + [{repeat,2}, + no_prompt_check]), + ok = ct_telnet:close(Handle), + ok. + +%% no_prompt_check and sequence options +no_prompt_check_sequence(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo_ml_no_prompt ab cd ef"), + {ok,[["ab"],["cd"],["ef"]]} = ct_telnet:expect(Handle, + [["ab"],["cd"],["ef"]], + [sequence, + no_prompt_check]), + ok = ct_telnet:close(Handle), + ok. + +%% Check that expect returns after idle timeout when no_prompt_check +%% option is used. +no_prompt_check_timeout(_) -> + {ok, Handle} = ct_telnet:open(erl_telnet_server), + ok = ct_telnet:send(Handle, "echo xxx"), + {error,timeout} = ct_telnet:expect(Handle, ["yyy"], [no_prompt_check, + {timeout,1000}]), ok = ct_telnet:close(Handle), ok. diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl index 5843155eee..31884aa182 100644 --- a/lib/common_test/test/telnet_server.erl +++ b/lib/common_test/test/telnet_server.erl @@ -93,6 +93,7 @@ do_accept(LSock,Server) -> end. init_client(#state{client=Sock}=State) -> + dbg("Server sending: ~p~n",["login: "]), R = case gen_tcp:send(Sock,"login: ") of ok -> loop(State); @@ -164,8 +165,18 @@ do_handle_data(Data,#state{authorized={user,_}}=State) -> do_handle_data("echo "++ Data,State) -> send(Data++"\r\n> ",State), {ok,State}; -do_handle_data("repeat "++ Data,State) -> - send(Data++"\r\n"++Data++"\r\n> ",State), +do_handle_data("echo_no_prompt "++ Data,State) -> + send(Data,State), + {ok,State}; +do_handle_data("echo_ml "++ Data,State) -> + Lines = string:tokens(Data," "), + ReturnData = string:join(Lines,"\n"), + send(ReturnData++"\r\n> ",State), + {ok,State}; +do_handle_data("echo_ml_no_prompt "++ Data,State) -> + Lines = string:tokens(Data," "), + ReturnData = string:join(Lines,"\n"), + send(ReturnData,State), {ok,State}; do_handle_data([],State) -> send("> ",State), -- cgit v1.2.3 From cae4bc87c2b3009a505f91a1c2d38a484cc9a1e4 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 15 May 2013 16:49:17 +0200 Subject: [common_test] Refactor ct_telnet_SUITE.erl --- lib/common_test/test/ct_telnet_SUITE.erl | 54 ++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/common_test/test/ct_telnet_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE.erl index a378c531f0..17617f59eb 100644 --- a/lib/common_test/test/ct_telnet_SUITE.erl +++ b/lib/common_test/test/ct_telnet_SUITE.erl @@ -52,7 +52,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> ct_test_support:end_per_suite(Config). -init_per_testcase(own_server=TestCase, Config) -> +init_per_testcase(TestCase, Config) when TestCase=/=unix_telnet-> TS = telnet_server:start([{port,?erl_telnet_server_port}, {users,[{?erl_telnet_server_user, ?erl_telnet_server_pwd}]}]), @@ -71,7 +71,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ - default + unix_telnet, + own_server ]. %%-------------------------------------------------------------------- @@ -80,34 +81,29 @@ all() -> %%%----------------------------------------------------------------- %%% -default(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - Suite = filename:join(DataDir, "ct_telnet_basic_SUITE"), - Cfg = {unix, ct:get_config(unix)}, - CfgFile = filename:join(DataDir, "telnet.cfg"), - ok = file:write_file(CfgFile, io_lib:write(Cfg) ++ "."), - {Opts,ERPid} = setup([{suite,Suite},{label,default}, {config, CfgFile}], Config), - ok = execute(default, Opts, ERPid, Config). +unix_telnet(Config) when is_list(Config) -> + all_tests_in_suite(unix_telnet,"ct_telnet_basic_SUITE","telnet.cfg",Config). own_server(Config) -> - DataDir = ?config(data_dir, Config), - Suite = filename:join(DataDir, "ct_telnet_own_server_SUITE"), - Cfg = {unix,[{telnet,"localhost"}, - {port, ?erl_telnet_server_port}, - {username,?erl_telnet_server_user}, - {password,?erl_telnet_server_pwd}, - {wait_for_linebreak, false}, -% {not_require_user_and_pass, true}, - {keep_alive,true}]}, - CfgFile = filename:join(DataDir, "telnet2.cfg"), - ok = file:write_file(CfgFile, io_lib:write(Cfg) ++ "."), - {Opts,ERPid} = setup([{suite,Suite},{label,own_server}, {config, CfgFile}], Config), - ok = execute(own_server, Opts, ERPid, Config). + all_tests_in_suite(own_server,"ct_telnet_own_server_SUITE", + "telnet2.cfg",Config). %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- +all_tests_in_suite(TestCase, SuiteName, CfgFileName, Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, SuiteName), + CfgFile = filename:join(DataDir, CfgFileName), + Cfg = telnet_config(TestCase), + ok = file:write_file(CfgFile, io_lib:write(Cfg) ++ "."), + {Opts,ERPid} = setup([{suite,Suite}, + {label,TestCase}, + {config,CfgFile}], + Config), + ok = execute(TestCase, Opts, ERPid, Config). + setup(Test, Config) -> Opts0 = ct_test_support:get_opts(Config), Level = ?config(trace_level, Config), @@ -131,10 +127,20 @@ execute(Name, Opts, ERPid, Config) -> reformat(Events, EH) -> ct_test_support:reformat(Events, EH). + +telnet_config(unix_telnet) -> + {unix, ct:get_config(unix)}; +telnet_config(_) -> + {unix,[{telnet,"localhost"}, + {port, ?erl_telnet_server_port}, + {username,?erl_telnet_server_user}, + {password,?erl_telnet_server_pwd}, + {keep_alive,true}]}. + %%%----------------------------------------------------------------- %%% TEST EVENTS %%%----------------------------------------------------------------- -events_to_check(default,Config) -> +events_to_check(unix_telnet,Config) -> all_cases(ct_telnet_basic_SUITE,Config); events_to_check(own_server,Config) -> all_cases(ct_telnet_own_server_SUITE,Config). -- cgit v1.2.3