-module(ct_telnet_own_server_SUITE).
-compile(export_all).
-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).
-define(SHORT_TIME,2000).
%%--------------------------------------------------------------------
%% TEST SERVER CALLBACK FUNCTIONS
%%--------------------------------------------------------------------
suite() ->
[
{require,telnet_server_conn1,{unix,[telnet]}},
{require,ct_conn_log},
{ct_hooks, [{cth_conn_log,[]}]}
].
all() ->
[
expect,
expect_repeat,
expect_sequence,
expect_wait_until_prompt,
expect_error_prompt,
expect_error_timeout1,
expect_error_timeout2,
expect_error_timeout3,
total_timeout_less_than_idle,
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,
large_string,
server_speaks,
server_disconnects,
newline_ayt,
newline_break
].
groups() ->
[].
init_per_suite(Config) ->
ct:pal("Will use these log hook options: ~p",
[ct:get_config(ct_conn_log,[])]),
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
%% Simple expect
expect(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo ayt"),
{ok,["ayt"]} = ct_telnet:expect(Handle, "ayt"),
ok = ct_telnet:send(Handle, "echo ayt"),
{ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]),
ok = ct_telnet:close(Handle),
ok.
%% Expect with repeat option
expect_repeat(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
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(telnet_server_conn1),
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 can wait for delayed prompt
expect_wait_until_prompt(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
Timeouts = [{idle_timeout,5000},{total_timeout,7000}],
ok = ct_telnet:send(Handle, "echo_delayed_prompt 3000 xxx"),
{ok,["xxx"]} =
ct_telnet:expect(Handle, "xxx",
[wait_for_prompt|Timeouts]),
ok = ct_telnet:send(Handle, "echo_delayed_prompt 3000 yyy zzz"),
{ok,[["yyy"],["zzz"]]} =
ct_telnet:expect(Handle, ["yyy","zzz"],
[{wait_for_prompt,"> "}|Timeouts]),
ok.
%% Check that expect returns when a prompt is found, even if pattern
%% is not matched.
expect_error_prompt(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
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_timeout1(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{timeout,?SHORT_TIME}]),
ok = ct_telnet:close(Handle),
ok.
expect_error_timeout2(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["xxx"], [{idle_timeout,1000},
{total_timeout,infinity}]),
ok = ct_telnet:close(Handle),
ok.
%% Check that if server loops and pattern not matching, the operation
%% can be aborted
expect_error_timeout3(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_loop 5000 xxx"),
T0 = now(),
{error,timeout} = ct_telnet:expect(Handle, ["yyy"],
[{idle_timeout,infinity},
{total_timeout,2001}]),
Diff = trunc(timer:now_diff(now(),T0)/1000),
{_,true} = {Diff, (Diff >= 2000) and (Diff =< 4000)},
ok = ct_telnet:send(Handle, "echo ayt"),
{ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]),
ok = ct_telnet:close(Handle),
ok.
%% OTP-12335: If total_timeout < idle_timeout, expect will never timeout
%% until after idle_timeout, which is incorrect.
total_timeout_less_than_idle(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"),
T0 = now(),
{error,timeout} = ct_telnet:expect(Handle, ["yyy"],
[{idle_timeout,5000},
{total_timeout,2001}]),
Diff = trunc(timer:now_diff(now(),T0)/1000),
{_,true} = {Diff, (Diff >= 2000) and (Diff =< 4000)},
ok = ct_telnet:send(Handle, "echo ayt"),
{ok,["ayt"]} = ct_telnet:expect(Handle, ["ayt"]),
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(telnet_server_conn1),
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(telnet_server_conn1),
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(telnet_server_conn1),
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(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["yyy"], [ignore_prompt,
{timeout,?SHORT_TIME}]),
ok = ct_telnet:send(Handle, "echo xxx"), % sends prompt and newline
{ok,["xxx"]} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt,
{timeout,?SHORT_TIME}]),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx\n"), % no prompt, but newline
{ok,["xxx"]} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt,
{timeout,?SHORT_TIME}]),
ok = ct_telnet:send(Handle, "echo_no_prompt xxx"), % no prompt, no newline
{error,timeout} = ct_telnet:expect(Handle, ["xxx"], [ignore_prompt,
{timeout,?SHORT_TIME}]),
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(telnet_server_conn1),
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(telnet_server_conn1),
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(telnet_server_conn1),
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(telnet_server_conn1),
ok = ct_telnet:send(Handle, "echo xxx"),
{error,timeout} = ct_telnet:expect(Handle, ["yyy"], [no_prompt_check,
{timeout,?SHORT_TIME}]),
ok = ct_telnet:close(Handle),
ok.
%% Check that it's possible to receive multiple chunks of data sent from
%% the server with one get_data call
large_string(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
String = "abcd efgh ijkl mnop qrst uvwx yz ",
BigString = lists:flatmap(fun(S) -> S end,
[String || _ <- lists:seq(1,10)]),
VerifyStr = [C || C <- BigString, C/=$ ],
{ok,Data} = ct_telnet:cmd(Handle, "echo_sep "++BigString),
ct:log("[CMD] Received ~w chars: ~s", [length(lists:flatten(Data)),Data]),
VerifyStr = [C || C <- lists:flatten(Data), C/=$ , C/=$\r, C/=$\n, C/=$>],
%% Test #1: With a long sleep value, all data gets gets buffered and
%% ct_telnet can receive it with one single request to ct_telnet_client.
%% Test #2: With a short sleep value, ct_telnet needs multiple calls to
%% ct_telnet_client to collect the data. This iterative operation should
%% yield the same result as the single request case.
ok = ct_telnet:send(Handle, "echo_sep "++BigString),
ct:sleep(1000),
{ok,Data1} = ct_telnet:get_data(Handle),
ct:log("[GET DATA #1] Received ~w chars: ~s",
[length(lists:flatten(Data1)),Data1]),
VerifyStr = [C || C <- lists:flatten(Data1), C/=$ , C/=$\r, C/=$\n, C/=$>],
ok = ct_telnet:send(Handle, "echo_sep "++BigString),
ct:sleep(50),
{ok,Data2} = ct_telnet:get_data(Handle),
ct:log("[GET DATA #2] Received ~w chars: ~s", [length(lists:flatten(Data2)),Data2]),
VerifyStr = [C || C <- lists:flatten(Data2), C/=$ , C/=$\r, C/=$\n, C/=$>],
ok = ct_telnet:close(Handle),
ok.
%% The server says things. Manually check that it gets printed correctly
%% in the general IO log.
%%
%% In this test case we simulate data sent spontaneously from the
%% server. We use ct_telnet_client:send_data instead of ct_telnet:send
%% to avoid flushing of buffers in the client, and we use
%% echo_no_prompt since the server would normally not send a prompt in
%% this case.
server_speaks(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
Backdoor = ct_gen_conn:get_conn_pid(Handle),
ok = ct_telnet_client:send_data(Backdoor,
"echo_no_prompt This is the first message"),
ok = ct_telnet_client:send_data(Backdoor,
"echo_no_prompt This is the second message"),
%% Let ct_telnet_client get an idle timeout. This should print the
%% two messages to the log. Note that the buffers are not flushed here!
ct:sleep(15000),
ok = ct_telnet_client:send_data(Backdoor,
"echo_no_prompt This is the third message"),
{ok,_} = ct_telnet:expect(Handle, ["first.*second.*third"],
[no_prompt_check, sequence]),
{error,timeout} = ct_telnet:expect(Handle, ["the"], [no_prompt_check,
{timeout,?SHORT_TIME}]),
ok = ct_telnet_client:send_data(Backdoor,
"echo_no_prompt This is the fourth message"),
%% give the server time to respond
ct:sleep(2000),
%% closing the connection should print last message in log
ok = ct_telnet:close(Handle),
ok.
%% Let the server close the connection. Make sure buffered data gets printed
%% to the general IO log.
server_disconnects(_) ->
{ok, Handle} = ct_telnet:open(telnet_server_conn1),
ok = ct_telnet:send(Handle, "disconnect_after 1500"),
%% wait until the get_data operation (triggered by send/2) times out
%% before sending the msg
ct:sleep(500),
ok = ct_telnet:send(Handle, "echo_no_prompt This is the message"),
%% when the server closes the connection, the last message should be
%% printed in the log
ct: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.