aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src/ct_telnet.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/ct_telnet.erl')
-rw-r--r--lib/common_test/src/ct_telnet.erl470
1 files changed, 290 insertions, 180 deletions
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index b4d82a53cf..babe73e575 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2014. 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
@@ -46,100 +46,80 @@
%%
%% == Logging ==
%%
-%% `ct_telnet' can be configured to uses the `error_logger' for logging telnet
-%% traffic. A special purpose error handler is implemented in
-%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log'
-%% hook in your test suite, e.g:
-%%
+%% The default logging behaviour of `ct_telnet' is to print information
+%% to the test case HTML log about performed operations and commands
+%% and their corresponding results. What won't be printed to the HTML log
+%% are text strings sent from the telnet server that are not explicitly
+%% received by means of a `ct_telnet' function such as `expect/3'.
+%% `ct_telnet' may however be configured to use a special purpose event handler,
+%% implemented in `ct_conn_log_h', for logging <b>all</b> telnet traffic.
+%% To use this handler, you need to install a Common Test hook named
+%% `cth_conn_log'. Example (using the test suite info function):
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
-%%'''
+%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
+%% '''
%%
%% `conn_mod()' is the name of the common_test module implementing
%% the connection protocol, i.e. `ct_telnet'.
%%
-%% The hook option `log_type' specifies the type of logging:
-%%
-%% <dl>
-%% <dt>`raw'</dt>
-%% <dd>The sent and received telnet data is logged to a separate
-%% text file as is, without any formatting. A link to the file is
-%% added to the test case HTML log.</dd>
-%%
-%% <dt>`html (default)'</dt>
-%% <dd>The sent and received telnet traffic is pretty printed
-%% directly in the test case HTML log.</dd>
+%% The `cth_conn_log' hook performs unformatted logging of telnet data to
+%% a separate text file. All telnet communication is captured and printed,
+%% including arbitrary data sent from the server. The link to this text file
+%% can be found on the top of the test case HTML log.
%%
-%% <dt>`silent'</dt>
-%% <dd>Telnet traffic is not logged.</dd>
-%% </dl>
-%%
-%% By default, all telnet traffic is logged in one single log
-%% file. However, it is possible to have different connections logged
-%% in separate files. To do this, use the hook option `hosts' and
-%% list the names of the servers/connections that will be used in the
-%% suite. Note that the connections must be named for this to work
+%% By default, data for all telnet connections is logged in one common
+%% file (named `default'), which might get messy e.g. if multiple telnet
+%% sessions are running in parallel. It is therefore possible to create a
+%% separate log file for each connection. To configure this, use the hook
+%% option `hosts' and list the names of the servers/connections that will be
+%% used in the suite. Note that the connections must be named for this to work
%% (see the `open' function below).
%%
-%% The `hosts' option has no effect if `log_type' is set to `html' or
-%% `silent'.
-%%
-%% The hook options can also be specified in a configuration file with
-%% the configuration variable `ct_conn_log':
-%%
-%% ```
-%% {ct_conn_log,[{conn_mod(),hook_options()}]}.
-%% '''
+%% The hook option named `log_type' may be used to change the `cth_conn_log'
+%% behaviour. The default value of this option is `raw', which results in the
+%% behaviour described above. If the value is set to `html', all telnet
+%% communication is printed to the test case HTML log instead.
%%
-%% For example:
+%% All `cth_conn_log' hook options described above can also be specified in
+%% a configuration file with the configuration variable `ct_conn_log'. Example:
%%
%% ```
-%% {ct_conn_log,[{ct_telnet,[{log_type,raw},
-%% {hosts,[key_or_name()]}]}]}
+%% {ct_conn_log, [{ct_telnet,[{log_type,raw},
+%% {hosts,[key_or_name()]}]}]}
%% '''
%%
%% <b>Note</b> that hook options specified in a configuration file
-%% will overwrite any hardcoded hook options in the test suite.
+%% will overwrite any hardcoded hook options in the test suite!
%%
-%% === Logging example 1 ===
+%% === Logging example ===
%%
-%% The following `ct_hooks' statement will cause raw printing of
-%% telnet traffic to separate logs for the connections named
-%% `server1' and `server2'. Any other connections will be logged
-%% to default telnet log.
+%% The following `ct_hooks' statement will cause printing of telnet traffic
+%% to separate logs for the connections named `server1' and `server2'.
+%% Traffic for any other connections will be logged in the default telnet log.
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, [{ct_telnet,[{log_type,raw}},
-%% {hosts,[server1,server2]}]}
-%% ]}]}].
+%% [{ct_hooks,
+%% [{cth_conn_log, [{ct_telnet,[{hosts,[server1,server2]}]}]}]}].
%%'''
%%
-%% === Logging example 2 ===
-%%
-%% The following configuration file will cause raw logging of all
-%% telnet traffic into one single text file.
+%% As previously explained, the above specification could also be provided
+%% by means of an entry like this in a configuration file:
%%
%% ```
-%% {ct_conn_log,[{ct_telnet,[{log_type,raw}]}]}.
+%% {ct_conn_log, [{ct_telnet,[{hosts,[server1,server2]}]}]}.
%% '''
%%
-%% The `ct_hooks' statement must look like this:
+%% in which case the `ct_hooks' statement in the test suite may simply look
+%% like this:
%%
%% ```
%% suite() ->
-%% [{ct_hooks, [{cth_conn_log, []}]}].
+%% [{ct_hooks, [{cth_conn_log, []}]}].
%% '''
%%
-%% The same `ct_hooks' statement without the configuration file would
-%% cause HTML logging of all telnet connections into the test case
-%% HTML log.
-%%
-%% <b>Note</b> that if the `cth_conn_log' hook is not added, telnet
-%% traffic is still logged in the test case HTML log file (on the legacy
-%% `ct_telnet' format).
%% @end
%% @type connection_type() = telnet | ts1 | ts2
@@ -161,7 +141,8 @@
-export([open/1, open/2, open/3, open/4, close/1]).
-export([cmd/2, cmd/3, cmdf/3, cmdf/4, get_data/1,
- send/2, sendf/3, expect/2, expect/3]).
+ send/2, send/3, sendf/3, sendf/4,
+ expect/2, expect/3]).
%% Callbacks
-export([init/3,handle_msg/2,reconnect/2,terminate/2]).
@@ -205,6 +186,7 @@ open(Name) ->
%%% Name = target_name()
%%% ConnType = ct_telnet:connection_type()
%%% Handle = ct_telnet:handle()
+%%% Reason = term()
%%%
%%% @doc Open a telnet connection to the specified target host.
open(Name,ConnType) ->
@@ -234,6 +216,7 @@ open(KeyOrName,ConnType,TargetMod) ->
%%% TargetMod = atom()
%%% Extra = term()
%%% Handle = handle()
+%%% Reason = term()
%%%
%%% @doc Open a telnet connection to the specified target host.
%%%
@@ -281,13 +264,22 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
end,
log(undefined,open,"Connecting to ~p(~p)",
[KeyOrName,Addr1]),
- ct_gen_conn:start(KeyOrName,full_addr(Addr1,ConnType),
- {TargetMod,KeepAlive,Extra},?MODULE)
+ Reconnect =
+ case ct:get_config({telnet_settings,reconnection_attempts}) of
+ 0 -> false;
+ _ -> true
+ end,
+ ct_gen_conn:start(full_addr(Addr1,ConnType),
+ {TargetMod,KeepAlive,Extra},
+ ?MODULE, [{name,KeyOrName},
+ {reconnect,Reconnect},
+ {old,true}])
end.
%%%-----------------------------------------------------------------
%%% @spec close(Connection) -> ok | {error,Reason}
-%%% Connection = ct_telnet:connection()
+%%% Connection = ct_telnet:connection()
+%%% Reason = term()
%%%
%%% @doc Close the telnet connection and stop the process managing it.
%%%
@@ -300,7 +292,7 @@ close(Connection) ->
{ok,Pid} ->
log(undefined,close,"Connection closed, handle: ~w",[Pid]),
case ct_gen_conn:stop(Pid) of
- {error,{process_down,Pid,noproc}} ->
+ {error,{process_down,Pid,_}} ->
{error,already_closed};
Result ->
Result
@@ -313,45 +305,80 @@ close(Connection) ->
%%% Test suite interface
%%%-----------------------------------------------------------------
%%% @spec cmd(Connection,Cmd) -> {ok,Data} | {error,Reason}
-%%% @equiv cmd(Connection,Cmd,DefaultTimeout)
+%%% @equiv cmd(Connection,Cmd,[])
cmd(Connection,Cmd) ->
- cmd(Connection,Cmd,default).
+ cmd(Connection,Cmd,[]).
%%%-----------------------------------------------------------------
-%%% @spec cmd(Connection,Cmd,Timeout) -> {ok,Data} | {error,Reason}
+%%% @spec cmd(Connection,Cmd,Opts) -> {ok,Data} | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% Cmd = string()
-%%% Timeout = integer()
+%%% Opts = [Opt]
+%%% Opt = {timeout,timeout()} | {newline,boolean()}
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Send a command via telnet and wait for prompt.
-cmd(Connection,Cmd,Timeout) ->
- case get_handle(Connection) of
- {ok,Pid} ->
- call(Pid,{cmd,Cmd,Timeout});
+%%%
+%%% This function will by default add a newline to the end of the
+%%% given command. If this is not desired, the option
+%%% `{newline,false}' can be used. This is necessary, for example,
+%%% when sending telnet command sequences (prefixed with the
+%%% Interprete As Command, IAC, character).
+%%%
+%%% The option `timeout' specifies how long the client shall wait for
+%%% prompt. If the time expires, the function returns
+%%% `{error,timeout}'. See the module description for information
+%%% about the default value for the command timeout.
+cmd(Connection,Cmd,Opts) when is_list(Opts) ->
+ case check_cmd_opts(Opts) of
+ ok ->
+ case get_handle(Connection) of
+ {ok,Pid} ->
+ call(Pid,{cmd,Cmd,Opts});
+ Error ->
+ Error
+ end;
Error ->
Error
- end.
+ end;
+cmd(Connection,Cmd,Timeout) when is_integer(Timeout); Timeout==default ->
+ %% This clause is kept for backwards compatibility only
+ cmd(Connection,Cmd,[{timeout,Timeout}]).
+
+check_cmd_opts([{timeout,Timeout}|Opts]) when is_integer(Timeout);
+ Timeout==default ->
+ check_cmd_opts(Opts);
+check_cmd_opts([]) ->
+ ok;
+check_cmd_opts(Opts) ->
+ check_send_opts(Opts).
+
%%%-----------------------------------------------------------------
%%% @spec cmdf(Connection,CmdFormat,Args) -> {ok,Data} | {error,Reason}
-%%% @equiv cmdf(Connection,CmdFormat,Args,DefaultTimeout)
+%%% @equiv cmdf(Connection,CmdFormat,Args,[])
cmdf(Connection,CmdFormat,Args) ->
- cmdf(Connection,CmdFormat,Args,default).
+ cmdf(Connection,CmdFormat,Args,[]).
%%%-----------------------------------------------------------------
-%%% @spec cmdf(Connection,CmdFormat,Args,Timeout) -> {ok,Data} | {error,Reason}
+%%% @spec cmdf(Connection,CmdFormat,Args,Opts) -> {ok,Data} | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% CmdFormat = string()
%%% Args = list()
-%%% Timeout = integer()
+%%% Opts = [Opt]
+%%% Opt = {timeout,timeout()} | {newline,boolean()}
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Send a telnet command and wait for prompt
%%% (uses a format string and list of arguments to build the command).
-cmdf(Connection,CmdFormat,Args,Timeout) when is_list(Args) ->
+%%%
+%%% See {@link cmd/3} further description.
+cmdf(Connection,CmdFormat,Args,Opts) when is_list(Args) ->
Cmd = lists:flatten(io_lib:format(CmdFormat,Args)),
- cmd(Connection,Cmd,Timeout).
+ cmd(Connection,Cmd,Opts).
%%%-----------------------------------------------------------------
%%% @spec get_data(Connection) -> {ok,Data} | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% Data = [string()]
+%%% Reason = term()
%%% @doc Get all data which has been received by the telnet client
%%% since last command was sent.
get_data(Connection) ->
@@ -364,30 +391,67 @@ get_data(Connection) ->
%%%-----------------------------------------------------------------
%%% @spec send(Connection,Cmd) -> ok | {error,Reason}
+%%% @equiv send(Connection,Cmd,[])
+send(Connection,Cmd) ->
+ send(Connection,Cmd,[]).
+
+%%%-----------------------------------------------------------------
+%%% @spec send(Connection,Cmd,Opts) -> ok | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% Cmd = string()
+%%% Opts = [Opt]
+%%% Opt = {newline,boolean()}
+%%% Reason = term()
%%% @doc Send a telnet command and return immediately.
%%%
+%%% This function will by default add a newline to the end of the
+%%% given command. If this is not desired, the option
+%%% `{newline,false}' can be used. This is necessary, for example,
+%%% when sending telnet command sequences (prefixed with the
+%%% Interprete As Command, IAC, character).
+%%%
%%% <p>The resulting output from the command can be read with
%%% <code>get_data/1</code> or <code>expect/2/3</code>.</p>
-send(Connection,Cmd) ->
- case get_handle(Connection) of
- {ok,Pid} ->
- call(Pid,{send,Cmd});
+send(Connection,Cmd,Opts) ->
+ case check_send_opts(Opts) of
+ ok ->
+ case get_handle(Connection) of
+ {ok,Pid} ->
+ call(Pid,{send,Cmd,Opts});
+ Error ->
+ Error
+ end;
Error ->
Error
end.
+check_send_opts([{newline,Bool}|Opts]) when is_boolean(Bool) ->
+ check_send_opts(Opts);
+check_send_opts([Invalid|_]) ->
+ {error,{invalid_option,Invalid}};
+check_send_opts([]) ->
+ ok.
+
+
%%%-----------------------------------------------------------------
%%% @spec sendf(Connection,CmdFormat,Args) -> ok | {error,Reason}
+%%% @equiv sendf(Connection,CmdFormat,Args,[])
+sendf(Connection,CmdFormat,Args) when is_list(Args) ->
+ sendf(Connection,CmdFormat,Args,[]).
+
+%%%-----------------------------------------------------------------
+%%% @spec sendf(Connection,CmdFormat,Args,Opts) -> ok | {error,Reason}
%%% Connection = ct_telnet:connection()
%%% CmdFormat = string()
%%% Args = list()
+%%% Opts = [Opt]
+%%% Opt = {newline,boolean()}
+%%% Reason = term()
%%% @doc Send a telnet command and return immediately (uses a format
%%% string and a list of arguments to build the command).
-sendf(Connection,CmdFormat,Args) when is_list(Args) ->
+sendf(Connection,CmdFormat,Args,Opts) when is_list(Args) ->
Cmd = lists:flatten(io_lib:format(CmdFormat,Args)),
- send(Connection,Cmd).
+ send(Connection,Cmd,Opts).
%%%-----------------------------------------------------------------
%%% @spec expect(Connection,Patterns) -> term()
@@ -405,9 +469,11 @@ expect(Connection,Patterns) ->
%%% Prompt = string()
%%% Tag = term()
%%% Opts = [Opt]
-%%% Opt = {timeout,Timeout} | repeat | {repeat,N} | sequence |
-%%% {halt,HaltPatterns} | ignore_prompt | no_prompt_check
-%%% Timeout = integer()
+%%% Opt = {idle_timeout,IdleTimeout} | {total_timeout,TotalTimeout} |
+%%% repeat | {repeat,N} | sequence | {halt,HaltPatterns} |
+%%% ignore_prompt | no_prompt_check
+%%% IdleTimeout = infinity | integer()
+%%% TotalTimeout = infinity | integer()
%%% N = integer()
%%% HaltPatterns = Patterns
%%% MatchList = [Match]
@@ -433,11 +499,16 @@ expect(Connection,Patterns) ->
%%% will also include the matched <code>Tag</code>. Else, only
%%% <code>RxMatch</code> is returned.</p>
%%%
-%%% <p>The <code>timeout</code> option indicates that the function
+%%% <p>The <code>idle_timeout</code> option indicates that the function
%%% shall return if the telnet client is idle (i.e. if no data is
-%%% received) for more than <code>Timeout</code> milliseconds. Default
+%%% received) for more than <code>IdleTimeout</code> milliseconds. Default
%%% timeout is 10 seconds.</p>
%%%
+%%% <p>The <code>total_timeout</code> option sets a time limit for
+%%% the complete expect operation. After <code>TotalTimeout</code>
+%%% milliseconds, <code>{error,timeout}</code> is returned. The default
+%%% value is <code>infinity</code> (i.e. no time limit).</p>
+%%%
%%% <p>The function will always return when a prompt is found, unless
%%% any of the <code>ignore_prompt</code> or
%%% <code>no_prompt_check</code> options are used, in which case it
@@ -556,7 +627,7 @@ set_telnet_defaults([],S) ->
S.
%% @hidden
-handle_msg({cmd,Cmd,Timeout},State) ->
+handle_msg({cmd,Cmd,Opts},State) ->
start_gen_log(heading(cmd,State#state.name)),
log(State,cmd,"Cmd: ~p",[Cmd]),
@@ -570,22 +641,25 @@ handle_msg({cmd,Cmd,Timeout},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
- TO = if Timeout == default -> State#state.com_to;
- true -> Timeout
+ TO = case proplists:get_value(timeout,Opts,default) of
+ default -> State#state.com_to;
+ Timeout -> Timeout
end,
+ Newline = proplists:get_value(newline,Opts,true),
{Return,NewBuffer,Prompt} =
- case teln_cmd(State#state.teln_pid, Cmd, State#state.prx, TO) of
+ case teln_cmd(State#state.teln_pid, Cmd, State#state.prx,
+ Newline, TO) of
{ok,Data,_PromptType,Rest} ->
log(State,recv,"Return: ~p",[{ok,Data}]),
{{ok,Data},Rest,true};
@@ -594,18 +668,19 @@ handle_msg({cmd,Cmd,Timeout},State) ->
{State#state.name,
State#state.type},
State#state.teln_pid,
- {cmd,Cmd,TO}}},
+ {cmd,Cmd,Opts}}},
log(State,recv,"Return: ~p",[Error]),
{Retry,[],false}
end,
end_gen_log(),
{Return,State#state{buffer=NewBuffer,prompt=Prompt}};
-handle_msg({send,Cmd},State) ->
- log(State,send,"Cmd: ~p",[Cmd]),
-
+handle_msg({send,Cmd,Opts},State) ->
+ start_gen_log(heading(send,State#state.name)),
+ log(State,send,"Sending: ~p",[Cmd]),
+
debug_cont_gen_log("Throwing Buffer:",[]),
debug_log_lines(State#state.buffer),
-
+
case {State#state.type,State#state.prompt} of
{ts,_} ->
silent_teln_expect(State#state.name,
@@ -613,18 +688,20 @@ handle_msg({send,Cmd},State) ->
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,2000}]);
+ [{idle_timeout,2000}]);
{ip,false} ->
silent_teln_expect(State#state.name,
State#state.teln_pid,
State#state.buffer,
prompt,
State#state.prx,
- [{timeout,200}]);
+ [{idle_timeout,200}]);
{ip,true} ->
ok
end,
- ct_telnet_client:send_data(State#state.teln_pid,Cmd),
+ Newline = proplists:get_value(newline,Opts,true),
+ ct_telnet_client:send_data(State#state.teln_pid,Cmd,Newline),
+ end_gen_log(),
{ok,State#state{buffer=[],prompt=false}};
handle_msg(get_data,State) ->
start_gen_log(heading(get_data,State#state.name)),
@@ -752,8 +829,8 @@ check_if_prompt_was_reached(Data,_) when is_list(Data) ->
check_if_prompt_was_reached(_,_) ->
false.
-%%% @hidden
-%% Functions for logging ct_telnet reports and telnet data
+%%%-----------------------------------------------------------------
+%%% Functions for logging ct_telnet reports and telnet data
heading(Action,undefined) ->
io_lib:format("~w ~w",[?MODULE,Action]);
@@ -763,6 +840,8 @@ heading(Action,Name) ->
force_log(State,Action,String,Args) ->
log(State,Action,String,Args,true).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(State,Action,String,Args) when is_record(State, state) ->
log(State,Action,String,Args,false);
log(Name,Action,String,Args) when is_atom(Name) ->
@@ -770,6 +849,8 @@ log(Name,Action,String,Args) when is_atom(Name) ->
log(TelnPid,Action,String,Args) when is_pid(TelnPid) ->
log(#state{teln_pid=TelnPid},Action,String,Args,false).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(undefined,String,Args) ->
log(#state{},undefined,String,Args,false);
log(Name,String,Args) when is_atom(Name) ->
@@ -777,72 +858,73 @@ log(Name,String,Args) when is_atom(Name) ->
log(TelnPid,String,Args) when is_pid(TelnPid) ->
log(#state{teln_pid=TelnPid},undefined,String,Args).
+%%%-----------------------------------------------------------------
+%%% @hidden
log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
Action,String,Args,ForcePrint) ->
Name1 = if Name == undefined -> get({ct_telnet_pid2name,TelnPid});
true -> Name
end,
Silent = get(silent),
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- Result when Result /= undefined, Result /= silent, Silent /= true ->
- {PrintHeader,PreBR} = if Action==undefined ->
- {false,""};
- true ->
- {true,"\n"}
- end,
- error_logger:info_report(#conn_log{header=PrintHeader,
- client=self(),
- conn_pid=TelnPid,
- address={Host,Port},
- name=Name1,
- action=Action,
- module=?MODULE},
- {PreBR++String,Args});
- Result when Result /= undefined ->
- ok;
- _ when Action == open; Action == close; Action == reconnect;
- Action == info; Action == error ->
- ct_gen_conn:log(heading(Action,Name1),String,Args);
- _ when ForcePrint == false ->
- case ct_util:is_silenced(telnet) of
- true ->
- ok;
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ if Action == general_io ->
+ case ct_util:get_testdata({cth_conn_log,?MODULE}) of
+ HookMode when HookMode /= undefined, HookMode /= silent,
+ Silent /= true ->
+ error_logger:info_report(#conn_log{header=false,
+ client=self(),
+ conn_pid=TelnPid,
+ address={Host,Port},
+ name=Name1,
+ action=Action,
+ module=?MODULE},
+ {String,Args});
+ _ -> %% hook inactive or silence requested
+ ok
end;
- _ when ForcePrint == true ->
- case ct_util:is_silenced(telnet) of
- true ->
- %% call log/3 now instead of cont_log/2 since
- %% start_gen_log/1 will not have been previously called
+
+ true ->
+ if Action == open; Action == close; Action == reconnect;
+ Action == info; Action == error ->
ct_gen_conn:log(heading(Action,Name1),String,Args);
- false ->
- ct_gen_conn:cont_log(String,Args)
+
+ ForcePrint == false ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ ok;
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end;
+
+ ForcePrint == true ->
+ case ct_util:is_silenced(telnet) of
+ true ->
+ %% call log/3 now instead of cont_log/2 since
+ %% start_gen_log/1 will not have been previously
+ %% called
+ ct_gen_conn:log(heading(Action,Name1),String,Args);
+ false ->
+ ct_gen_conn:cont_log(String,Args)
+ end
end
end.
+%%%-----------------------------------------------------------------
+%%% @hidden
start_gen_log(Heading) ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:start_log(Heading)
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:start_log(Heading)
end.
+%%%-----------------------------------------------------------------
+%%% @hidden
end_gen_log() ->
- case ct_util:get_testdata({cth_conn_log,?MODULE}) of
- undefined ->
- %% check if output is suppressed
- case ct_util:is_silenced(telnet) of
- true -> ok;
- false -> ct_gen_conn:end_log()
- end;
- _ ->
- ok
+ %% check if output is suppressed
+ case ct_util:is_silenced(telnet) of
+ true -> ok;
+ false -> ct_gen_conn:end_log()
end.
%%% @hidden
@@ -858,19 +940,18 @@ format_data(_How,{String,Args}) ->
%%%=================================================================
%%% Abstraction layer on top of ct_telnet_client.erl
-teln_cmd(Pid,Cmd,Prx,Timeout) ->
- ct_telnet_client:send_data(Pid,Cmd),
+teln_cmd(Pid,Cmd,Prx,Newline,Timeout) ->
+ ct_telnet_client:send_data(Pid,Cmd,Newline),
teln_receive_until_prompt(Pid,Prx,Timeout).
teln_get_all_data(Pid,Prx,Data,Acc,LastLine) ->
- case check_for_prompt(Prx,lists:reverse(LastLine) ++ Data) of
+ case check_for_prompt(Prx,LastLine++Data) of
{prompt,Lines,_PromptType,Rest} ->
teln_get_all_data(Pid,Prx,Rest,[Lines|Acc],[]);
{noprompt,Lines,LastLine1} ->
case ct_telnet_client:get_data(Pid) of
{ok,[]} ->
- {ok,lists:reverse(lists:append([Lines|Acc])),
- lists:reverse(LastLine1)};
+ {ok,lists:reverse(lists:append([Lines|Acc])),LastLine1};
{ok,Data1} ->
teln_get_all_data(Pid,Prx,Data1,[Lines|Acc],LastLine1)
end
@@ -879,7 +960,8 @@ teln_get_all_data(Pid,Prx,Data,Acc,LastLine) ->
%% Expect options record
-record(eo,{teln_pid,
prx,
- timeout,
+ idle_timeout,
+ total_timeout,
haltpatterns=[],
seq=false,
repeat=false,
@@ -921,11 +1003,12 @@ teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) ->
Seq = get_seq(Opts),
Pattern = convert_pattern(Pattern0,Seq),
- Timeout = get_timeout(Opts),
+ {IdleTimeout,TotalTimeout} = get_timeouts(Opts),
EO = #eo{teln_pid=Pid,
prx=Prx,
- timeout=Timeout,
+ idle_timeout=IdleTimeout,
+ total_timeout=TotalTimeout,
seq=Seq,
haltpatterns=HaltPatterns,
prompt_check=PromptCheck},
@@ -964,11 +1047,22 @@ rm_dupl([P|Ps],Acc) ->
rm_dupl([],Acc) ->
lists:reverse(Acc).
-get_timeout(Opts) ->
- case lists:keysearch(timeout,1,Opts) of
- {value,{timeout,T}} -> T;
- false -> ?DEFAULT_TIMEOUT
- end.
+get_timeouts(Opts) ->
+ {case lists:keysearch(idle_timeout,1,Opts) of
+ {value,{_,T}} ->
+ T;
+ false ->
+ %% this check is for backwards compatibility (pre CT v1.8)
+ case lists:keysearch(timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> ?DEFAULT_TIMEOUT
+ end
+ end,
+ case lists:keysearch(total_timeout,1,Opts) of
+ {value,{_,T}} -> T;
+ false -> infinity
+ end}.
+
get_repeat(Opts) ->
case lists:keysearch(repeat,1,Opts) of
{value,{repeat,N}} when is_integer(N) ->
@@ -1010,7 +1104,8 @@ repeat_expect(Name,Pid,Data,Pattern,Acc,EO) ->
{error,Reason}
end.
-teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
+teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
+ total_timeout=TotalTO}) ->
ExpectFun = case EO#eo.seq of
true -> fun() ->
seq_expect(Name,Pid,Data,Pattern,Acc,EO)
@@ -1027,12 +1122,12 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
NotFinished ->
%% Get more data
Fun = fun() -> get_data1(EO#eo.teln_pid) end,
- case ct_gen_conn:do_within_time(Fun, EO#eo.timeout) of
- {error,Reason} ->
+ case timer:tc(ct_gen_conn, do_within_time, [Fun, IdleTO]) of
+ {_,{error,Reason}} ->
%% A timeout will occur when the telnet connection
- %% is idle for EO#eo.timeout milliseconds.
+ %% is idle for EO#eo.idle_timeout milliseconds.
{error,Reason};
- {ok,Data1} ->
+ {_,{ok,Data1}} when TotalTO == infinity ->
case NotFinished of
{nomatch,Rest} ->
%% One expect
@@ -1040,6 +1135,21 @@ teln_expect1(Name,Pid,Data,Pattern,Acc,EO) ->
{continue,Patterns1,Acc1,Rest} ->
%% Sequence
teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO)
+ end;
+ {Elapsed,{ok,Data1}} ->
+ TVal = trunc(TotalTO - (Elapsed/1000)),
+ if TVal =< 0 ->
+ {error,timeout};
+ true ->
+ EO1 = EO#eo{total_timeout = TVal},
+ case NotFinished of
+ {nomatch,Rest} ->
+ %% One expect
+ teln_expect1(Name,Pid,Rest++Data1,Pattern,[],EO1);
+ {continue,Patterns1,Acc1,Rest} ->
+ %% Sequence
+ teln_expect1(Name,Pid,Rest++Data1,Patterns1,Acc1,EO1)
+ end
end
end
end.
@@ -1299,7 +1409,7 @@ teln_receive_until_prompt(Pid,Prx,Timeout) ->
teln_receive_until_prompt(Pid,Prx,Acc,LastLine) ->
{ok,Data} = ct_telnet_client:get_data(Pid),
- case check_for_prompt(Prx,LastLine ++ Data) of
+ case check_for_prompt(Prx,LastLine++Data) of
{prompt,Lines,PromptType,Rest} ->
Return = lists:reverse(lists:append([Lines|Acc])),
{ok,Return,PromptType,Rest};