aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2014-11-11 14:35:09 +0100
committerSiri Hansen <[email protected]>2014-11-11 14:35:09 +0100
commitb3741f2b8e2ed9f370abb029160d1817497436e1 (patch)
tree128289e4f6c65b4edebdb17fcd3ff2d36be39fae
parent8a120f6b7c97c64917e11fac3f6c21507ad374c3 (diff)
parent9a5b4e9b2a2a622e67fb55ab4262eb858dd46e54 (diff)
downloadotp-b3741f2b8e2ed9f370abb029160d1817497436e1.tar.gz
otp-b3741f2b8e2ed9f370abb029160d1817497436e1.tar.bz2
otp-b3741f2b8e2ed9f370abb029160d1817497436e1.zip
Merge branch 'siri/ct_telnet/no-newline/OTP-12252' into maint
* siri/ct_telnet/no-newline/OTP-12252: [ct] Add 'newline' option to send functions in ct_telnet
-rw-r--r--lib/common_test/src/ct_telnet.erl136
-rw-r--r--lib/common_test/src/ct_telnet_client.erl10
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_basic_SUITE.erl12
-rw-r--r--lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_own_server_SUITE.erl41
-rw-r--r--lib/common_test/test/telnet_server.erl36
5 files changed, 189 insertions, 46 deletions
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index 3b2652d06c..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
@@ -141,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]).
@@ -304,42 +305,74 @@ 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}
@@ -358,32 +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()
@@ -559,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]),
@@ -584,11 +652,14 @@ handle_msg({cmd,Cmd,Timeout},State) ->
{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};
@@ -597,13 +668,13 @@ 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) ->
+handle_msg({send,Cmd,Opts},State) ->
start_gen_log(heading(send,State#state.name)),
log(State,send,"Sending: ~p",[Cmd]),
@@ -628,7 +699,8 @@ handle_msg({send,Cmd},State) ->
{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) ->
@@ -868,8 +940,8 @@ 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) ->
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index ce30dcb74b..3ae373e433 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.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
@@ -35,7 +35,7 @@
%% -define(debug, true).
-export([open/2, open/3, open/4, open/5, close/1]).
--export([send_data/2, get_data/1]).
+-export([send_data/2, send_data/3, get_data/1]).
-define(TELNET_PORT, 23).
-define(OPEN_TIMEOUT,10000).
@@ -97,7 +97,11 @@ close(Pid) ->
end.
send_data(Pid, Data) ->
- Pid ! {send_data, Data++"\n"},
+ send_data(Pid, Data, true).
+send_data(Pid, Data, true) ->
+ send_data(Pid, Data++"\n", false);
+send_data(Pid, Data, false) ->
+ Pid ! {send_data, Data},
ok.
get_data(Pid) ->
diff --git a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_basic_SUITE.erl b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_basic_SUITE.erl
index 80616af064..3885c1991d 100644
--- a/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_basic_SUITE.erl
+++ b/lib/common_test/test/ct_telnet_SUITE_data/ct_telnet_basic_SUITE.erl
@@ -20,7 +20,7 @@ suite() -> [
operations() ->
[start_stop, send_and_get, expect, already_closed,
- cmd, sendf, close_wrong_type].
+ cmd, sendf, no_newline, close_wrong_type].
mult_case(_Case, 0) ->
[];
@@ -129,6 +129,16 @@ sendf(Config) ->
ok = ct_telnet:close(Handle),
ok.
+no_newline(Config) ->
+ {ok, Handle} = ct_telnet:open(?conn_name(?get_n(Config))),
+ IAC = 255, % interprete as command
+ AYT = 246, % are you there
+ ok = ct_telnet:send(Handle, [IAC,AYT], [{newline,false}]),
+ {ok,_} = ct_telnet:expect(Handle,"yes",[no_prompt_check]),
+ {ok,_} = ct_telnet:cmd(Handle, ""), % send newline only to get back prompt
+ ok = ct_telnet:close(Handle),
+ ok.
+
close_wrong_type(_) ->
{error, _} = ct_telnet:close(whatever),
ok.
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 c0f79d0f10..9afe545b26 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
@@ -4,6 +4,24 @@
-include_lib("common_test/include/ct.hrl").
+%% 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).
+
%%--------------------------------------------------------------------
%% TEST SERVER CALLBACK FUNCTIONS
%%--------------------------------------------------------------------
@@ -34,7 +52,9 @@ all() ->
ignore_prompt_timeout,
large_string,
server_speaks,
- server_disconnects
+ server_disconnects,
+ newline_ayt,
+ newline_break
].
groups() ->
@@ -285,3 +305,22 @@ server_disconnects(_) ->
timer:sleep(3000),
_ = ct_telnet:close(Handle),
ok.
+
+%% Test option {newline,false} to send telnet command sequence.
+newline_ayt(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, [?IAC,?AYT], [{newline,false}]),
+ {ok,["yes"]} = ct_telnet:expect(Handle, ["yes"]),
+ ok = ct_telnet:close(Handle),
+ ok.
+
+%% Test option {newline,false} to send telnet command sequence.
+newline_break(_) ->
+ {ok, Handle} = ct_telnet:open(telnet_server_conn1),
+ ok = ct_telnet:send(Handle, [?IAC,?BRK], [{newline,false}]),
+ %% '#' is the prompt in break mode
+ {ok,["# "]} = ct_telnet:expect(Handle, ["# "], [no_prompt_check]),
+ {ok,R} = ct_telnet:cmd(Handle, "q", [{newline,false}]),
+ "> " = lists:flatten(R),
+ 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 1d341d6106..0a23a66324 100644
--- a/lib/common_test/test/telnet_server.erl
+++ b/lib/common_test/test/telnet_server.erl
@@ -31,7 +31,8 @@
users,
authorized=false,
suppress_go_ahead=false,
- buffer=[]}).
+ buffer=[],
+ break=false}).
-type options() :: [{port,pos_integer()} | {users,users()}].
-type users() :: [{user(),password()}].
@@ -148,6 +149,9 @@ loop(State, N) ->
stopped
end.
+handle_data(Cmd,#state{break=true}=State) ->
+ dbg("Server got data when in break mode: ~p~n",[Cmd]),
+ handle_break_cmd(Cmd,State);
handle_data([?IAC|Cmd],State) ->
dbg("Server got cmd: ~p~n",[Cmd]),
handle_cmd(Cmd,State);
@@ -171,24 +175,38 @@ handle_data(Data,State) ->
{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)
+%% Add function clause below to handle new telnet commands sent with
+%% ?IAC from client. This can be done from ct_telnet:send or
+%% ct_telnet:cmd if using the option {newline,false}. Also, 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_data(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_data(T,State#state{suppress_go_ahead=false});
+handle_cmd([?BRK|T],State) ->
+ %% Used when testing 'newline' option in ct_telnet:send and ct_telnet:cmd.
+ send("# ",State),
+ handle_data(T,State#state{break=true});
+handle_cmd([?AYT|T],State) ->
+ %% Used when testing 'newline' option in ct_telnet:send and ct_telnet:cmd.
+ send("yes\r\n> ",State),
+ handle_data(T,State);
handle_cmd([_H|T],State) ->
%% Not responding to this command
handle_cmd(T,State);
handle_cmd([],State) ->
{ok,State}.
+handle_break_cmd([$q|T],State) ->
+ %% Dummy cmd allowed in break mode - quit break mode
+ send("\r\n> ",State),
+ handle_data(T,State#state{break=false});
+handle_break_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) ->