aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src
diff options
context:
space:
mode:
authorPeter Andersson <[email protected]>2010-02-17 15:59:05 +0000
committerErlang/OTP <[email protected]>2010-02-17 15:59:05 +0000
commit332591f03f7bc4585c8c108c192ab3bba6fec12c (patch)
treec3d06e8750d53d0f157d61cd4c5f991959a05a51 /lib/common_test/src
parentf29538e8002cf0e37fa4f988fbf5484c46513bf4 (diff)
downloadotp-332591f03f7bc4585c8c108c192ab3bba6fec12c.tar.gz
otp-332591f03f7bc4585c8c108c192ab3bba6fec12c.tar.bz2
otp-332591f03f7bc4585c8c108c192ab3bba6fec12c.zip
OTP-8311: Various updates and fixes in Common Test and Test Server
Diffstat (limited to 'lib/common_test/src')
-rw-r--r--lib/common_test/src/ct_framework.erl36
-rw-r--r--lib/common_test/src/ct_run.erl12
-rw-r--r--lib/common_test/src/ct_telnet.erl59
-rw-r--r--lib/common_test/src/ct_telnet_client.erl179
-rw-r--r--lib/common_test/src/ct_testspec.erl76
-rw-r--r--lib/common_test/src/ct_util.hrl13
-rw-r--r--lib/common_test/src/unix_telnet.erl39
7 files changed, 261 insertions, 153 deletions
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 8456251b29..ed8b564921 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. 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%
%%
@@ -27,7 +27,7 @@
-export([init_tc/3, end_tc/3, get_suite/2, report/2, warn/1]).
-export([error_notification/4]).
--export([error_in_suite/1]).
+-export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]).
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -751,8 +751,15 @@ find_group(Mod, Name, Defs) ->
end.
make_conf(Mod, Name, Props, TestSpec) ->
- {conf,[{name,Name}|Props],
- {Mod,init_per_group},TestSpec,{Mod,end_per_group}}.
+ {InitConf,EndConf} =
+ case erlang:function_exported(Mod,init_per_group,2) of
+ true ->
+ {{Mod,init_per_group},{Mod,end_per_group}};
+ false ->
+ {{?MODULE,ct_init_per_group},
+ {?MODULE,ct_end_per_group}}
+ end,
+ {conf,[{name,Name}|Props],InitConf,TestSpec,EndConf}.
get_all(Mod, ConfTests) ->
@@ -916,6 +923,19 @@ check_multiple(Mod,Seq,TCs) ->
error_in_suite(Config) ->
Reason = test_server:lookup_config(error,Config),
exit(Reason).
+
+%% if the group config functions are missing in the suite,
+%% use these instead
+ct_init_per_group(GroupName, Config) ->
+ ct_logs:log("WARNING", "init_per_group/2 for ~w missing in suite, using default.",
+ [GroupName]),
+ Config.
+
+ct_end_per_group(GroupName, _) ->
+ ct_logs:log("WARNING", "end_per_group/2 for ~w missing in suite, using default.",
+ [GroupName]),
+ ok.
+
%%%-----------------------------------------------------------------
%%% @spec report(What,Data) -> ok
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index a1e2358578..6b1063f74c 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. 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%
%%
@@ -1515,7 +1515,7 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
debug_info],
Result =
if Mod == all ; Targets == helpmods ->
- case (catch ct_make:all([noexec])) of
+ case (catch ct_make:all([noexec|ErlFlags])) of
{'EXIT',_} = Failure ->
Failure;
MakeInfo ->
diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl
index c19d312f01..c6f5fd7df4 100644
--- a/lib/common_test/src/ct_telnet.erl
+++ b/lib/common_test/src/ct_telnet.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. 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%
%%
@@ -21,27 +21,28 @@
%%%
%%% <p>Use this module to set up telnet connections, send commands and
%%% perform string matching on the result.
-%%% (See the <code>unix_telnet</code> manual page for information
-%%% about how ct_telnet may be used specifically with unix hosts.)</p>
+%%% See the <c>unix_telnet</c> manual page for information about how to use
+%%% ct_telnet, and configure connections, specifically for unix hosts.</p>
%%% <p>The following default values are defined in ct_telnet:</p>
%%% <pre>
%%% Connection timeout = 10 sec (time to wait for connection)
%%% 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)
-%%% </pre>
+%%% Keep alive = true (will send NOP to the server every 10 sec if connection is idle)</pre>
%%% <p>These parameters can be altered by the user with the following
%%% configuration term:</p>
%%% <pre>
%%% {telnet_settings, [{connect_timeout,Millisec},
%%% {command_timeout,Millisec},
%%% {reconnection_attempts,N},
-%%% {reconnection_interval,Millisec}]}.
-%%% </pre>
+%%% {reconnection_interval,Millisec},
+%%% {keep_alive,Bool}]}.</pre>
%%% <p><code>Millisec = integer(), N = integer()</code></p>
%%% <p>Enter the <code>telnet_settings</code> term in a configuration
%%% file included in the test and ct_telnet will retrieve the information
-%%% automatically.</p>
+%%% automatically. Note that <c>keep_alive</c> may be specified per connection if
+%%% required. See <c>unix_telnet</c> for details.</p></doc>
%%% @type connection_type() = telnet | ts1 | ts2
@@ -88,7 +89,9 @@
buffer=[],
prompt=false,
name,
- target_mod,extra,
+ target_mod,
+ keep_alive,
+ extra,
conn_to=?DEFAULT_TIMEOUT,
com_to=?DEFAULT_TIMEOUT,
reconns=?RECONNS,
@@ -150,7 +153,7 @@ open(KeyOrName,ConnType,TargetMod) ->
%%% name can only be closed with the handle value.</p>
%%%
%%% <p><code>TargetMod</code> is a module which exports the functions
-%%% <code>connect(Ip,Port,Extra)</code> and <code>get_prompt_regexp()</code>
+%%% <code>connect(Ip,Port,KeepAlive,Extra)</code> and <code>get_prompt_regexp()</code>
%%% for the given <code>TargetType</code> (e.g. <code>unix_telnet</code>).</p>
open(KeyOrName,ConnType,TargetMod,Extra) ->
case ct:get_config({KeyOrName,ConnType}) of
@@ -169,9 +172,18 @@ open(KeyOrName,ConnType,TargetMod,Extra) ->
P -> {IP,P}
end
end,
+ KeepAlive =
+ case ct:get_config({KeyOrName,keep_alive}) of
+ undefined ->
+ case ct:get_config({telnet_settings,keep_alive}) of
+ undefined -> true;
+ Bool -> Bool
+ end;
+ Bool -> Bool
+ end,
log(heading(open,{KeyOrName,ConnType}),"Opening connection to: ~p",[Addr1]),
ct_gen_conn:start(KeyOrName,full_addr(Addr1,ConnType),
- {TargetMod,Extra},?MODULE)
+ {TargetMod,KeepAlive,Extra},?MODULE)
end.
%%%-----------------------------------------------------------------
@@ -373,14 +385,14 @@ expect(Connection,Patterns,Opts) ->
%%%=================================================================
%%% Callback functions
%% @hidden
-init(Name,{Ip,Port,Type},{TargetMod,Extra}) ->
+init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
S0 = case ct:get_config(telnet_settings) of
undefined ->
#state{};
Settings ->
set_telnet_defaults(Settings,#state{})
end,
- case catch TargetMod:connect(Ip,Port,S0#state.conn_to,Extra) of
+ case catch TargetMod:connect(Ip,Port,S0#state.conn_to,KeepAlive,Extra) of
{ok,TelnPid} ->
log(heading(init,{Name,Type}),
"Opened telnet connection\n"
@@ -389,13 +401,15 @@ init(Name,{Ip,Port,Type},{TargetMod,Extra}) ->
"Command timeout: ~p\n"
"Reconnection attempts: ~p\n"
"Reconnection interval: ~p\n"
- "Connection timeout: ~p",
+ "Connection timeout: ~p\n"
+ "Keep alive: ~w",
[Ip,Port,S0#state.com_to,S0#state.reconns,
- S0#state.reconn_int,S0#state.conn_to]),
+ S0#state.reconn_int,S0#state.conn_to,KeepAlive]),
{ok,TelnPid,S0#state{teln_pid=TelnPid,
type=type(Type),
name={Name,Type},
target_mod=TargetMod,
+ keep_alive=KeepAlive,
extra=Extra,
prx=TargetMod:get_prompt_regexp()}};
{'EXIT',Reason} ->
@@ -415,6 +429,12 @@ set_telnet_defaults([{reconnection_attempts,Rs}|Ss],S) ->
set_telnet_defaults(Ss,S#state{reconns=Rs});
set_telnet_defaults([{reconnection_interval,RInt}|Ss],S) ->
set_telnet_defaults(Ss,S#state{reconn_int=RInt});
+set_telnet_defaults([{keep_alive,_}|Ss],S) ->
+ set_telnet_defaults(Ss,S);
+set_telnet_defaults([Unknown|Ss],S) ->
+ log(heading(set_telnet_defaults,{telnet_settings,Unknown}),
+ "Bad element in telnet_settings: ~p",[Unknown]),
+ set_telnet_defaults(Ss,S);
set_telnet_defaults([],S) ->
S.
@@ -527,10 +547,11 @@ handle_msg({expect,Pattern,Opts},State) ->
reconnect({Ip,Port,_Type},State) ->
reconnect(Ip,Port,State#state.reconns,State).
reconnect(Ip,Port,N,State=#state{target_mod=TargetMod,
+ keep_alive=KeepAlive,
extra=Extra,
conn_to=ConnTo,
reconn_int=ReconnInt}) ->
- case TargetMod:connect(Ip,Port,ConnTo,Extra) of
+ case TargetMod:connect(Ip,Port,ConnTo,KeepAlive,Extra) of
{ok, NewPid} ->
{ok, NewPid, State#state{teln_pid=NewPid}};
Error when N==0 ->
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index e460a50eac..1a12c5e343 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. 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%
%%
@@ -32,13 +32,14 @@
-module(ct_telnet_client).
--export([open/1, open/2, open/3, close/1]).
+-export([open/1, open/2, open/3, open/4, close/1]).
-export([send_data/2, get_data/1]).
-define(DBG, false).
-define(TELNET_PORT, 23).
-define(OPEN_TIMEOUT,10000).
+-define(IDLE_TIMEOUT,10000).
%% telnet control characters
-define(SE, 240).
@@ -65,17 +66,20 @@
-define(TERMINAL_TYPE, 24).
-define(WINDOW_SIZE, 31).
--record(state,{get_data}).
+-record(state,{get_data, keep_alive=true}).
open(Server) ->
- open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT).
+ open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true).
open(Server, Port) ->
- open(Server, Port, ?OPEN_TIMEOUT).
+ open(Server, Port, ?OPEN_TIMEOUT, true).
open(Server, Port, Timeout) ->
+ open(Server, Port, Timeout, true).
+
+open(Server, Port, Timeout, KeepAlive) ->
Self = self(),
- Pid = spawn(fun() -> init(Self, Server, Port, Timeout) end),
+ Pid = spawn(fun() -> init(Self, Server, Port, Timeout, KeepAlive) end),
receive
{open,Pid} ->
{ok,Pid};
@@ -100,20 +104,18 @@ get_data(Pid) ->
%%%-----------------------------------------------------------------
%%% Internal functions
-init(Parent, Server, Port, Timeout) ->
+init(Parent, Server, Port, Timeout, KeepAlive) ->
case gen_tcp:connect(Server, Port, [list,{packet,0}], Timeout) of
{ok,Sock} ->
- dbg("Connected to: ~p\n", [Server]),
+ dbg("Connected to: ~p (port: ~w, keep_alive: ~w)\n", [Server,Port,KeepAlive]),
send([?IAC,?DO,?SUPPRESS_GO_AHEAD], Sock),
Parent ! {open,self()},
- loop(#state{get_data=10}, Sock, []),
+ loop(#state{get_data=10, keep_alive=KeepAlive}, Sock, []),
gen_tcp:close(Sock);
Error ->
Parent ! {Error,self()}
end.
-
-
loop(State, Sock, Acc) ->
receive
{tcp_closed,_} ->
@@ -137,21 +139,27 @@ loop(State, Sock, Acc) ->
[] ->
dbg("get_data nodata\n",[]),
erlang:send_after(100,self(),{get_data_delayed,Pid}),
- State#state{get_data=State#state.get_data - 1};
+ if State#state.keep_alive == true ->
+ State#state{get_data=State#state.get_data - 1};
+ State#state.keep_alive == false ->
+ State
+ end;
_ ->
Pid ! {data,lists:reverse(lists:append(Acc))},
State
end,
loop(NewState, Sock, []);
{get_data_delayed,Pid} ->
- NewState = case State#state.get_data of
- 0 ->
- send([?IAC,?DO,?NOP], Sock),
- dbg("delayed after 1000\n",[]),
- State#state{get_data=10};
- _ ->
- State
- end,
+ NewState =
+ case State of
+ #state{keep_alive = true, get_data = 0} ->
+ if Acc == [] -> send([?IAC,?NOP], Sock);
+ true -> ok
+ end,
+ State#state{get_data=10};
+ _ ->
+ State
+ end,
NewAcc =
case erlang:is_process_alive(Pid) of
true ->
@@ -160,29 +168,38 @@ loop(State, Sock, Acc) ->
false ->
Acc
end,
- loop(NewState, Sock, NewAcc);
-
+ loop(NewState, Sock, NewAcc);
close ->
dbg("Closing connection\n", []),
gen_tcp:close(Sock),
ok
- after 1000 ->
- case Acc of
- [] -> % no data buffered
- send([?IAC,?DO,?NOP], Sock),
- dbg("after 1000\n",[]);
- _ ->
- true
+ after wait(State#state.keep_alive,?IDLE_TIMEOUT) ->
+ if
+ Acc == [] -> send([?IAC,?NOP], Sock);
+ true -> ok
end,
loop(State, Sock, Acc)
end.
+wait(true, Time) -> Time;
+wait(false, _) -> infinity.
+
send(Data, Sock) ->
- dbg("Sending: ~p\n", [Data]),
+ case Data of
+ [?IAC|_] = Cmd ->
+ cmd_dbg(Cmd);
+ _ ->
+ dbg("Sending: ~p\n", [Data])
+ end,
gen_tcp:send(Sock, Data),
ok.
-check_msg(Sock,[?IAC | Cs], Acc) ->
+%% [IAC,IAC] = buffer data value 255
+check_msg(Sock, [?IAC,?IAC | T], Acc) ->
+ check_msg(Sock, T, [?IAC|Acc]);
+
+%% respond to a command
+check_msg(Sock, [?IAC | Cs], Acc) ->
case get_cmd(Cs) of
{Cmd,Cs1} ->
dbg("Got ", []),
@@ -192,11 +209,15 @@ check_msg(Sock,[?IAC | Cs], Acc) ->
error ->
Acc
end;
-check_msg(Sock,[H|T],Acc) ->
- check_msg(Sock,T,[H|Acc]);
-check_msg(_Sock,[],Acc) ->
+
+%% buffer a data value
+check_msg(Sock, [H|T], Acc) ->
+ check_msg(Sock, T, [H|Acc]);
+
+check_msg(_Sock, [], Acc) ->
Acc.
+
%% Positive responses (WILL and DO).
respond_cmd([?WILL,?ECHO], Sock) ->
@@ -234,6 +255,11 @@ respond_cmd([?DO | Opt], Sock) ->
cmd_dbg(R),
gen_tcp:send(Sock, R);
+%% Commands without options (which we ignore)
+
+respond_cmd(?NOP, _Sock) ->
+ ok;
+
%% Unexpected messages.
respond_cmd([Cmd | Opt], _Sock) when Cmd >= 240, Cmd =< 255 ->
@@ -246,7 +272,10 @@ respond_cmd([Cmd | Opt], _Sock) ->
get_cmd([Cmd | Rest]) when Cmd == ?SB ->
get_subcmd(Rest, []);
-get_cmd([Cmd,Opt | Rest]) ->
+get_cmd([Cmd | Rest]) when Cmd >= 240, Cmd =< 249 ->
+ {?NOP, Rest};
+
+get_cmd([Cmd,Opt | Rest]) when Cmd >= 251, Cmd =< 254 ->
{[Cmd,Opt], Rest};
get_cmd(_Other) ->
@@ -259,46 +288,34 @@ get_subcmd([Opt | Rest], Acc) ->
get_subcmd(Rest, [Opt | Acc]).
-dbg(_Str,_Args) -> ok.
-% if ?DBG -> io:format(_Str,_Args);
-% true -> ok
-% end.
-
-cmd_dbg(_Cmd) -> ok.
-% if ?DBG ->
-% case _Cmd of
-% [?IAC|Cmd1] ->
-% cmd_dbg(Cmd1);
-% [Ctrl|Opts] ->
-% CtrlStr =
-% case Ctrl of
-% ?DO -> "DO";
-% ?DONT -> "DONT";
-% ?WILL -> "WILL";
-% ?WONT -> "WONT";
-% _ -> "CMD"
-% end,
-% Opts1 =
-% case Opts of
-% [Opt] -> Opt;
-% _ -> Opts
-% end,
-% io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
-% Any ->
-% io:format("Unexpected in cmd_dbg:~n~w~n",[Any])
-% end;
-% true -> ok
-% end.
-
-
-
-
-
-
-
-
-
-
-
-
+dbg(_Str,_Args) ->
+ if ?DBG -> io:format(_Str,_Args);
+ true -> ok
+ end.
+cmd_dbg(_Cmd) ->
+ if ?DBG ->
+ case _Cmd of
+ [?IAC|Cmd1] ->
+ cmd_dbg(Cmd1);
+ [Ctrl|Opts] ->
+ CtrlStr =
+ case Ctrl of
+ ?DO -> "DO";
+ ?DONT -> "DONT";
+ ?WILL -> "WILL";
+ ?WONT -> "WONT";
+ ?NOP -> "NOP";
+ _ -> "CMD"
+ end,
+ Opts1 =
+ case Opts of
+ [Opt] -> Opt;
+ _ -> Opts
+ end,
+ io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
+ Any ->
+ io:format("Unexpected in cmd_dbg:~n~w~n",[Any])
+ end;
+ true -> ok
+ end.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 21a2f82a54..4378ec5a52 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. 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%
%%
@@ -231,9 +231,11 @@ collect_tests_from_file(Specs,Nodes,Relaxed) when is_list(Nodes) ->
catch collect_tests_from_file1(Specs,#testspec{nodes=NodeRefs},Relaxed).
collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) ->
+ SpecDir = filename:dirname(filename:absname(Spec)),
case file:consult(Spec) of
{ok,Terms} ->
- TestSpec1 = collect_tests(Terms,TestSpec,Relaxed),
+ TestSpec1 = collect_tests(Terms,TestSpec#testspec{spec_dir=SpecDir},
+ Relaxed),
collect_tests_from_file1(Specs,TestSpec1,Relaxed);
{error,Reason} ->
throw({error,{Spec,Reason}})
@@ -249,8 +251,11 @@ collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,[node()],Relaxed).
collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
+ {ok,Cwd} = file:get_cwd(),
NodeRefs = lists:map(fun(N) -> {undefined,N} end, Nodes),
- case catch collect_tests(Terms,#testspec{nodes=NodeRefs},Relaxed) of
+ case catch collect_tests(Terms,#testspec{nodes=NodeRefs,
+ spec_dir=Cwd},
+ Relaxed) of
E = {error,_} ->
E;
TS ->
@@ -265,17 +270,51 @@ collect_tests(Terms,TestSpec,Relaxed) ->
put(relaxed,Relaxed),
TestSpec1 = get_global(Terms,TestSpec),
TestSpec2 = get_all_nodes(Terms,TestSpec1),
+ case catch evaluate(Terms,TestSpec2) of
+ {error,{Node,{M,F,A},Reason}} ->
+ io:format("Error! Common Test failed to evaluate ~w:~w/~w on ~w. "
+ "Reason: ~p~n~n", [M,F,A,Node,Reason]);
+ _ -> ok
+ end,
add_tests(Terms,TestSpec2).
+evaluate([{eval,NodeRef,{M,F,Args}}|Ts],Spec) ->
+ Node = ref2node(NodeRef,Spec#testspec.nodes),
+ case rpc:call(Node,M,F,Args) of
+ {badrpc,Reason} ->
+ throw({error,{Node,{M,F,length(Args)},Reason}});
+ _ ->
+ ok
+ end,
+ evaluate(Ts,Spec);
+evaluate([{eval,{M,F,Args}}|Ts],Spec) ->
+ case catch apply(M,F,Args) of
+ {'EXIT',Reason} ->
+ throw({error,{node(),{M,F,length(Args)},Reason}});
+ _ ->
+ ok
+ end,
+ evaluate(Ts,Spec);
+evaluate([],_Spec) ->
+ ok.
+
get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) ->
- get_global(Ts,Spec#testspec{alias=[{Ref,get_absname(Dir)}|Refs]});
+ get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]});
get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) ->
get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]});
get_global([_|Ts],Spec) -> get_global(Ts,Spec);
get_global([],Spec) -> Spec.
-get_absname(TestDir) ->
- AbsName = filename:absname(TestDir),
+get_absfile(FullName,#testspec{spec_dir=SpecDir}) ->
+ File = filename:basename(FullName),
+ Dir = get_absname(filename:dirname(FullName),SpecDir),
+ filename:join(Dir,File).
+
+get_absdir(Dir,#testspec{spec_dir=SpecDir}) ->
+ get_absname(Dir,SpecDir).
+
+get_absname(TestDir,SpecDir) ->
+ AbsName = filename:absname(TestDir,SpecDir),
TestDirName = filename:basename(AbsName),
Path = filename:dirname(AbsName),
TopDir = filename:basename(Path),
@@ -345,16 +384,17 @@ list_nodes(#testspec{nodes=NodeRefs}) ->
%% --- logdir ---
add_tests([{logdir,all_nodes,Dir}|Ts],Spec) ->
Dirs = Spec#testspec.logdir,
- Tests = [{logdir,N,Dir} || N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Dirs) == false],
+ Tests = [{logdir,N,get_absdir(Dir,Spec)} ||
+ N <- list_nodes(Spec),
+ lists:keymember(ref2node(N,Spec#testspec.nodes),
+ 1,Dirs) == false],
add_tests(Tests++Ts,Spec);
add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) ->
Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{logdir,Node,Dir}|Ts],Spec) ->
Dirs = Spec#testspec.logdir,
- Dirs1 = [{ref2node(Node,Spec#testspec.nodes),Dir} |
+ Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} |
lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)],
add_tests(Ts,Spec#testspec{logdir=Dirs1});
add_tests([{logdir,Dir}|Ts],Spec) ->
@@ -369,7 +409,7 @@ add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) ->
add_tests(Ts1,Spec);
add_tests([{cover,Node,File}|Ts],Spec) ->
CoverFs = Spec#testspec.cover,
- CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),File} |
+ CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} |
lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)],
add_tests(Ts,Spec#testspec{cover=CoverFs1});
add_tests([{cover,File}|Ts],Spec) ->
@@ -385,7 +425,8 @@ add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) ->
add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) ->
Cfgs = Spec#testspec.config,
Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{config,Node,Fs}|Ts],Spec#testspec{config=[{Node1,F}|Cfgs]});
+ add_tests([{config,Node,Fs}|Ts],
+ Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]});
add_tests([{config,_Node,[]}|Ts],Spec) ->
add_tests(Ts,Spec);
add_tests([{config,Node,F}|Ts],Spec) ->
@@ -451,7 +492,8 @@ add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) ->
add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) ->
Dirs = Spec#testspec.include,
Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{include,Node,Ds}|Ts],Spec#testspec{include=[{Node1,D}|Dirs]});
+ add_tests([{include,Node,Ds}|Ts],
+ Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]});
add_tests([{include,_Node,[]}|Ts],Spec) ->
add_tests(Ts,Spec);
add_tests([{include,Node,D}|Ts],Spec) ->
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 94ae2625cf..c1dc14f943 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. 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%
%%
@@ -27,7 +27,8 @@
address,
callback}).
--record(testspec, {nodes=[],
+-record(testspec, {spec_dir,
+ nodes=[],
logdir=["."],
cover=[],
config=[],
diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl
index 14a70e9d22..25b9d4d5d2 100644
--- a/lib/common_test/src/unix_telnet.erl
+++ b/lib/common_test/src/unix_telnet.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. 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%
%%
@@ -23,10 +23,10 @@
%%% <p>It requires the following entry in the config file:</p>
%%% <pre>
%%% {unix,[{telnet,HostNameOrIpAddress},
-%%% {port,PortNum},
+%%% {port,PortNum}, % optional
%%% {username,UserName},
-%%% {password,Password}]}.
-%%% </pre>
+%%% {password,Password},
+%%% {keep_alive,Bool}]}. % optional</pre>
%%%
%%% <p>To talk telnet to the host specified by
%%% <code>HostNameOrIpAddress</code>, use the interface functions in
@@ -38,8 +38,14 @@
%%% <p>or</p>
%%% <pre> ct:require(Name,{unix,[telnet,username,password]}).</pre>
%%%
+%%% <p>The "keep alive" activity (i.e. that Common Test sends NOP to the server
+%%% every 10 seconds if the connection is idle) may be enabled or disabled for one
+%%% particular connection as described here. It may be disabled for all connections
+%%% using <c>telnet_settings</c> (see <c>ct_telnet</c>).</p>
+%%%
%%% <p>Note that the <code>{port,PortNum}</code> tuple is optional and if
-%%% omitted, default telnet port 23 will be used.</p>
+%%% omitted, default telnet port 23 will be used. Also the <c>keep_alive</c> tuple
+%%% is optional, and the value defauls to true (enabled).</p>
%%%
%%% @see ct
%%% @see ct_telnet
@@ -48,7 +54,7 @@
-compile(export_all).
%% Callbacks for ct_telnet.erl
--export([connect/4,get_prompt_regexp/0]).
+-export([connect/5,get_prompt_regexp/0]).
-import(ct_telnet,[start_log/1,cont_log/2,end_log/0]).
-define(username,"login: ").
@@ -70,10 +76,11 @@ get_prompt_regexp() ->
%%%-----------------------------------------------------------------
%%% @hidden
-%%% @spec connect(Ip,Port,Timeout,Extra) -> {ok,Handle} | {error,Reason}
+%%% @spec connect(Ip,Port,Timeout,KeepAlive,Extra) -> {ok,Handle} | {error,Reason}
%%% Ip = string() | {integer(),integer(),integer(),integer()}
%%% Port = integer()
%%% Timeout = integer()
+%%% KeepAlive = bool()
%%% Extra = {Username,Password}
%%% Username = string()
%%% Password = string()
@@ -82,23 +89,23 @@ get_prompt_regexp() ->
%%% @doc Callback for ct_telnet.erl.
%%%
%%% <p>Setup telnet connection to a UNIX host.</p>
-connect(Ip,Port,Timeout,Extra) ->
+connect(Ip,Port,Timeout,KeepAlive,Extra) ->
case Extra of
{Username,Password} ->
- connect(Ip,Port,Timeout,Username,Password);
+ connect1(Ip,Port,Timeout,KeepAlive,Username,Password);
Name ->
case get_username_and_password(Name) of
{ok,{Username,Password}} ->
- connect(Ip,Port,Timeout,Username,Password);
+ connect1(Ip,Port,Timeout,KeepAlive,Username,Password);
Error ->
Error
end
end.
-connect(Ip,Port,Timeout,Username,Password) ->
+connect1(Ip,Port,Timeout,KeepAlive,Username,Password) ->
start_log("unix_telnet:connect"),
Result =
- case ct_telnet_client:open(Ip,Port,Timeout) of
+ case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive) of
{ok,Pid} ->
case ct_telnet:silent_teln_expect(Pid,[],[prompt],?prx,[]) of
{ok,{prompt,?username},_} ->