From a71c6b976fa79fa3bcd0e61850a1a57071159b28 Mon Sep 17 00:00:00 2001
From: Lukas Larsson
[{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]},
- {lm_directory, "/test/loadmodules"}]
+ {lm_directory, "/test/loadmodules"}]
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index a5886b9687..2f796b91ab 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -205,7 +205,7 @@
{error,{RunTimeError,StackTrace}} |
{timetrap_timeout,integer()} |
{failed,{Suite,end_per_testcase,FailInfo}}, reason for failure.
Example: require the variable Example 1: require the variable In this case the config file must at least contain: Example: require the variable Example 2: require the key In this case the config file must at least contain: Example 3: require the key In this case the config file must at least contain: If the requested data is available, the main entry will be
+%%% If the requested data is available, the sub entry will be
%%% associated with
testcase2() ->
- [{require, unix_telnet, {unix, [telnet, username, password]}},
+ [{require, unix_telnet, unix},
+ {require, {unix, [telnet, username, password]}},
{default_config, unix, [{telnet, "my_telnet_host"},
{username, "aladdin"},
{password, "sesame"}]}}].
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 6373634812..922f794395 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -266,27 +266,34 @@ stop_interactive() ->
%%%-----------------------------------------------------------------
%%% @spec require(Required) -> ok | {error,Reason}
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKeys} | {Key,SubKey,SubKeys}
%%% Key = atom()
%%% SubKeys = SubKey | [SubKey]
%%% SubKey = atom()
%%%
-%%% @doc Check if the required configuration is available.
+%%% @doc Check if the required configuration is available. It is possible
+%%% to specify arbitrarily deep tuples as myvar
:
-%%% ok = ct:require(myvar)
myvar
:ok = ct:require(myvar).
%%%
%%%
-%%% {myvar,Value}.
+%%% {myvar,Value}.
%%%
-%%% myvar
with
-%%% subvariable sub1
:
-%%% ok = ct:require({myvar,sub1})
myvar
with
+%%% subkeys sub1
and sub2
:ok = ct:require({myvar,[sub1,sub2]}).
%%%
%%%
-%%% {myvar,[{sub1,Value}]}.
+%%% {myvar,[{sub1,Value},{sub2,Value}]}.
+%%%
+%%% myvar
with
+%%% subkey sub1
with subsub1
:ok = ct:require({myvar,sub1,sub2}).
+%%%
+%%% {myvar,[{sub1,[{sub2,Value}]}]}.
%%%
%%% @see require/2
%%% @see get_config/1
@@ -298,30 +305,36 @@ require(Required) ->
%%%-----------------------------------------------------------------
%%% @spec require(Name,Required) -> ok | {error,Reason}
%%% Name = atom()
-%%% Required = Key | {Key,SubKeys}
+%%% Required = Key | {Key,SubKey} | {Key,SubKey,SubKey}
+%%% SubKey = Key
%%% Key = atom()
-%%% SubKeys = SubKey | [SubKey]
-%%% SubKey = atom()
%%%
%%% @doc Check if the required configuration is available, and give it
-%%% a name.
+%%% a name. The semantics for Name
so that the value of the element
%%% can be read with get_config/1,2
provided
-%%% Name
instead of the Key
.Name
instead of the whole Required
term.
Example: Require one node with a telnet connection and an
-%%% ftp connection. Name the node a
:
ok =
-%%% ct:require(a,{node,[telnet,ftp]}).
All references
-%%% to this node may then use the node name. E.g. you can fetch a
-%%% file over ftp like this:
-%%% ok = ct:ftp_get(a,RemoteFile,LocalFile).
a
:
+%%% ok = ct:require(a,{machine,node}).+%%% All references to this node may then use the node name. +%%% E.g. you can fetch a file over ftp like this: +%%%
ok = ct:ftp_get(a,RemoteFile,LocalFile).%%% %%%
For this to work, the config file must at least contain:
-%%%-%%% {node,[{telnet,IpAddr}, -%%% {ftp,IpAddr}]}.+%%%
{machine,[{node,[{telnet,IpAddr},{ftp,IpAddr}]}]}.+%%% +%%%
Example, given the following config file:
%%%%%% {unix,[{telnet,IpAddr}, -%%% {username,Username}, -%%% {password,Password}]}.-%%%
get_config(unix,Default) ->
+%%% {user,[{username,Username},
+%%% {password,Password}]}]}.
+%%%
ct:get_config(unix,Default) ->
%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]
-%%% get_config({unix,telnet},Default) -> IpAddr
-%%% get_config({unix,ftp},Default) -> Default
-%%% get_config(unknownkey,Default) -> Default
+%%% ct:get_config({unix,telnet},Default) -> IpAddr
+%%% ct:get_config({unix,user,username},Default) -> Username
+%%% ct:get_config({unix,ftp},Default) -> Default
+%%% ct:get_config(unknownkey,Default) -> Default
If a config variable key has been associated with a name (by
%%% means of require/2
or a require statement), the name
%%% may be used instead of the key to read the value:
require(myhost,unix) -> ok
-%%% get_config(myhost,Default) ->
-%%% [{telnet,IpAddr},
-%%% {username,Username},
-%%% {password,Password}]
ct:require(myuser,{unix,user}) -> ok.
+%%% ct:get_config(myuser,Default) ->
+%%% [{username,Username},
+%%% {password,Password}]
If a config variable is defined in multiple files and you want to
%%% access all possible values, use the all
option. The
@@ -390,9 +403,7 @@ get_config(Required,Default) ->
%%%
%%%
If you want config elements (key-value tuples) returned as result
%%% instead of values, use the element
option.
-%%% The returned elements will then be on the form {KeyOrName,Value}
,
-%%% or (in case a subkey has been specified)
-%%% {{KeyOrName,SubKey},Value}
{Required,Value}
%%%
%%% @see get_config/1
%%% @see get_config/2
@@ -403,7 +414,7 @@ get_config(Required,Default,Opts) ->
%%%-----------------------------------------------------------------
%%% @spec reload_config(Required) -> ValueOrElement
-%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey}
%%% KeyOrName = atom()
%%% SubKey = atom()
%%% ValueOrElement = term()
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
index 9277af5bc1..5b7ed0f7fb 100644
--- a/lib/common_test/src/ct_config.erl
+++ b/lib/common_test/src/ct_config.erl
@@ -122,8 +122,8 @@ return({To,Ref},Result) ->
loop(StartDir) ->
receive
- {{require,Name,Tag,SubTags},From} ->
- Result = do_require(Name,Tag,SubTags),
+ {{require,Name,Key},From} ->
+ Result = do_require(Name,Key),
return(From,Result),
loop(StartDir);
{{set_default_config,{Config,Scope}},From} ->
@@ -168,16 +168,19 @@ reload_config(KeyOrName) ->
call({reload_config, KeyOrName}).
process_default_configs(Opts) ->
- case lists:keysearch(config, 1, Opts) of
- {value,{_,Files=[File|_]}} when is_list(File) ->
- Files;
- {value,{_,File=[C|_]}} when is_integer(C) ->
- [File];
- {value,{_,[]}} ->
- [];
- false ->
- []
- end.
+ lists:flatmap(fun({config,[_|_] = FileOrFiles}) ->
+ case {io_lib:printable_list(FileOrFiles),
+ io_lib:printable_list(hd(FileOrFiles))} of
+ {true,true} ->
+ FileOrFiles;
+ {true,false} ->
+ [FileOrFiles];
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end,Opts).
process_user_configs(Opts, Acc) ->
case lists:keytake(userconfig, 1, Opts) of
@@ -319,75 +322,58 @@ get_config(KeyOrName,Default) ->
get_config(KeyOrName,Default,[]).
get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- [{_Ref,Val}|_] = Vals ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)];
- {true,false} ->
- [V || {_R,V} <- lists:sort(Vals)];
- {false,true} ->
- {KeyOrName,Val};
- {false,false} ->
- Val
- end
+ case get_config({KeyOrName}, Default, Opts) of
+ %% If only an atom is given, then we need to unwrap the
+ %% key if it is returned
+ {{KeyOrName}, Val} ->
+ {KeyOrName, Val};
+ [{{KeyOrName}, _Val}|_] = Res ->
+ [{K, Val} || {{K},Val} <- Res, K == KeyOrName];
+ Else ->
+ Else
end;
-get_config({KeyOrName,SubKey},Default,Opts) ->
- case lookup_config(KeyOrName) of
+%% This useage of get_config is only used by internal ct functions
+%% and may change at any time
+get_config({DeepKey,SubKey}, Default, Opts) when is_tuple(DeepKey) ->
+ get_config(erlang:append_element(DeepKey, SubKey), Default, Opts);
+get_config(KeyOrName,Default,Opts) when is_tuple(KeyOrName) ->
+ case lookup_config(element(1,KeyOrName)) of
[] ->
- Default;
+ format_value([Default],KeyOrName,Opts);
Vals ->
- Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of
- Result=[L|_] when is_list(L) ->
- case L of
- [{_,_}|_] ->
- Result;
- _ ->
- []
- end;
- _ ->
- []
- end,
- case get_subconfig([SubKey],Vals1,[],Opts) of
- {ok,[{_,SubVal}|_]=SubVals} ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals];
- {true,false} ->
- [Val || {_SubKey,Val} <- SubVals];
- {false,true} ->
- {{KeyOrName,SubKey},SubVal};
- {false,false} ->
- SubVal
- end;
- _ ->
- Default
- end
+ NewVals =
+ lists:map(
+ fun({Val}) ->
+ get_config(tl(tuple_to_list(KeyOrName)),
+ Val,Default,Opts)
+ end,Vals),
+ format_value(NewVals,KeyOrName,Opts)
end.
-get_subconfig(SubKeys,Values) ->
- get_subconfig(SubKeys,Values,[],[]).
-
-get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,SubMapped} ->
- case lists:member(all,Opts) of
- true ->
- get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts);
- false ->
- {ok,SubMapped}
- end;
- _Error ->
- get_subconfig(SubKeys,Rest,Mapped,Opts)
+get_config([],Vals,_Default,_Opts) ->
+ Vals;
+get_config([[]],Vals,Default,Opts) ->
+ get_config([],Vals,Default,Opts);
+%% This case is used by {require,{unix,[port,host]}} functionality
+get_config([SubKeys], Vals, Default, _Opts) when is_list(SubKeys) ->
+ case do_get_config(SubKeys, Vals, []) of
+ {ok, SubVals} ->
+ [SubVal || {_,SubVal} <- SubVals];
+
+ _ ->
+ Default
end;
-get_subconfig(SubKeys,[],[],_) ->
- {error,{not_available,SubKeys}};
-get_subconfig(_SubKeys,[],Mapped,_) ->
- {ok,Mapped}.
+get_config([Key|Rest], Vals, Default, Opts) ->
+ case do_get_config([Key], Vals, []) of
+ {ok, [{Key,NewVals}]} ->
+ get_config(Rest, NewVals, Default, Opts);
+ _ ->
+ Default
+ end.
+do_get_config([Key|_], Available, _Mapped) when not is_list(Available) ->
+ {error,{not_available,Key}};
do_get_config([Key|Required],Available,Mapped) ->
case lists:keysearch(Key,1,Available) of
{value,{Key,Value}} ->
@@ -403,8 +389,7 @@ do_get_config([],_Available,Mapped) ->
get_all_config() ->
ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
default='$4',_='_'},
- [],
- [{{'$1','$2','$3','$4'}}]}]).
+ [],[{{'$1','$2','$3','$4'}}]}]).
lookup_config(KeyOrName) ->
case lookup_name(KeyOrName) of
@@ -415,13 +400,23 @@ lookup_config(KeyOrName) ->
end.
lookup_name(Name) ->
- ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{value='$1',name=Name,_='_'},
+ [],[{{'$1'}}]}]).
lookup_key(Key) ->
- ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
- [],
- [{{'$1','$2'}}]}]).
+ ets:select(?attr_table,[{#ct_conf{key=Key,value='$1',name='_UNDEF',_='_'},
+ [],[{{'$1'}}]}]).
+
+format_value([SubVal|_] = SubVals, KeyOrName, Opts) ->
+ case {lists:member(all,Opts),lists:member(element,Opts)} of
+ {true,true} ->
+ [{KeyOrName,Val} || Val <- SubVals];
+ {true,false} ->
+ [Val || Val <- SubVals];
+ {false,true} ->
+ {KeyOrName,SubVal};
+ {false,false} ->
+ SubVal
+ end.
lookup_handler_for_config({Key, _Subkey}) ->
lookup_handler_for_config(Key);
@@ -475,65 +470,78 @@ release_allocated([H|T]) ->
release_allocated([]) ->
ok.
-allocate(Name,Key,SubKeys) ->
- case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
- [] ->
+allocate(Name,Key) ->
+ Ref = make_ref(),
+ case get_config(Key,Ref,[all,element]) of
+ [{_,Ref}] ->
{error,{not_available,Key}};
- Available ->
- case allocate_subconfig(Name,SubKeys,Available,false) of
- ok ->
- ok;
- Error ->
- Error
- end
+ Configs ->
+ associate(Name,Key,Configs),
+ ok
end.
-allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,_SubMapped} ->
- ets:insert(?attr_table,C#ct_conf{name=Name}),
- allocate_subconfig(Name,SubKeys,Rest,true);
- _Error ->
- allocate_subconfig(Name,SubKeys,Rest,Found)
- end;
-allocate_subconfig(_Name,_SubKeys,[],true) ->
+
+associate('_UNDEF',_Key,_Configs) ->
ok;
-allocate_subconfig(_Name,SubKeys,[],false) ->
- {error,{not_available,SubKeys}}.
+associate(Name,{Key,SubKeys},Configs) when is_atom(Key), is_list(SubKeys) ->
+ associate_int(Name,Configs,"true");
+associate(Name,_Key,Configs) ->
+ associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")).
+
+associate_int(Name,Configs,"true") ->
+ lists:map(fun({K,_Config}) ->
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=element(1,K),
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name})
+ || C <- Cs]
+ end,Configs);
+associate_int(Name,Configs,_) ->
+ lists:map(fun({K,Config}) ->
+ Key = if is_tuple(K) -> element(1,K);
+ is_atom(K) -> K
+ end,
+
+ Cs = ets:match_object(
+ ?attr_table,
+ #ct_conf{key=Key,
+ name='_UNDEF',_='_'}),
+ [ets:insert(?attr_table,C#ct_conf{name=Name,
+ value=Config})
+ || C <- Cs]
+ end,Configs).
+
+
delete_config(Default) ->
ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}),
ok.
-require(Key) when is_atom(Key) ->
- require({Key,[]});
-require({Key,SubKeys}) when is_atom(Key) ->
- allocate('_UNDEF',Key,to_list(SubKeys));
+require(Key) when is_atom(Key); is_tuple(Key) ->
+ allocate('_UNDEF',Key);
require(Key) ->
{error,{invalid,Key}}.
-require(Name,Key) when is_atom(Key) ->
- require(Name,{Key,[]});
-require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) ->
- call({require,Name,Key,to_list(SubKeys)});
+require(Name,Key) when is_atom(Name),is_atom(Key) orelse is_tuple(Key) ->
+ call({require,Name,Key});
require(Name,Keys) ->
{error,{invalid,{Name,Keys}}}.
-to_list(X) when is_list(X) -> X;
-to_list(X) -> [X].
-
-do_require(Name,Key,SubKeys) when is_list(SubKeys) ->
+do_require(Name,Key) ->
case get_key_from_name(Name) of
{error,_} ->
- allocate(Name,Key,SubKeys);
+ allocate(Name,Key);
{ok,Key} ->
%% already allocated - check that it has all required subkeys
- Vals = [Val || {_Ref,Val} <- lookup_name(Name)],
- case get_subconfig(SubKeys,Vals) of
- {ok,_SubMapped} ->
- ok;
- Error ->
- Error
+ R = make_ref(),
+ case get_config(Key,R,[]) of
+ R ->
+ {error,{not_available,Key}};
+ {error,_} = Error ->
+ Error;
+ _Error ->
+ ok
end;
{ok,OtherKey} ->
{error,{name_in_use,Name,OtherKey}}
diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl
index 5db73066a3..d1d511f137 100644
--- a/lib/common_test/src/ct_ftp.erl
+++ b/lib/common_test/src/ct_ftp.erl
@@ -66,6 +66,7 @@
%%% {unix,[{ftp,IpAddr},
%%% {username,Username},
%%% {password,Password}]}.
+%%% @see ct:require/3
put(KeyOrName,LocalFile,RemoteFile) ->
Fun = fun(Ftp) -> send(Ftp,LocalFile,RemoteFile) end,
open_and_do(KeyOrName,Fun).
@@ -85,6 +86,7 @@ put(KeyOrName,LocalFile,RemoteFile) ->
%%%
%%% The config file must be as for put/3.
%%% @see put/3 +%%% @see ct:require/3 get(KeyOrName,RemoteFile,LocalFile) -> Fun = fun(Ftp) -> recv(Ftp,RemoteFile,LocalFile) end, open_and_do(KeyOrName,Fun). @@ -105,6 +107,10 @@ get(KeyOrName,RemoteFile,LocalFile) -> %%% simply useKey
, the configuration variable name, to
%%% specify the target. Note that a connection that has no associated target
%%% name can only be closed with the handle value.
+%%%
+%%% See
Name
. If Key
is
%%% used, the returned handle must be used for subsequent calls
%%% (multiple connections may be opened using the config
-%%% data specified by Key
).
+%%% data specified by Key
). See ConnType
will always override the type
%%% specified in the address tuple in the configuration data (and
@@ -152,6 +153,8 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% The extra options will override any existing options with the
%%% same key in the config data. For details on valid SSH
%%% options, see the documentation for the OTP ssh application.
TargetMod
is a module which exports the functions
%%% connect(Ip,Port,KeepAlive,Extra)
and get_prompt_regexp()
%%% for the given TargetType
(e.g. unix_telnet
).
The
With
The
This function is called as the last test case in the
suite. It is meant to be used for cleaning up after
-
This is the test case group info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of a test case group (i.e. its test cases
- and sub-groups). Properties set by
The
With
The
OPTIONAL
This function is called after the execution of a test case group is finished.
- It is meant to be used for cleaning up after
OPTIONAL
This function is called after each test case, and can be used
- to clean up after
This is the test case info function. It is supposed to
return a list of tagged tuples that specify various properties
related to the execution of this particular test case.
- Properties set by
The
If
With
Other tuples than the ones defined will simply be ignored.
@@ -550,7 +551,7 @@This is the implementation of a test case. Here you must
call the functions you want to test, and do whatever you
need to check the result. If something fails, make sure the
- function causes a runtime error, or call
Elements from the
To read the value of a config variable, use the function
-
Example:
@@ -118,7 +119,7 @@-%%% @see ct:require/3 +%%% @see ct:require/2 put(KeyOrName,LocalFile,RemoteFile) -> Fun = fun(Ftp) -> send(Ftp,LocalFile,RemoteFile) end, open_and_do(KeyOrName,Fun). @@ -86,7 +86,7 @@ put(KeyOrName,LocalFile,RemoteFile) -> %%% %%%diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 30486d3eec..c1f68becc3 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -242,12 +242,12 @@ Using configuration variables defined in multiple files If a configuration variable is defined in multiple files and you - want to access all possible values, you may use the
ct:get_config/3 + want to access all possible values, you may use thefunction and specify ct:get_config/3 all in the options list. The values will then be returned in a list and the order of the elements corresponds to the order that the config files were specified at startup. Please see @@ -130,7 +131,7 @@It is possible to encrypt configuration files containing sensitive data if these files must be stored in open and shared directories.
-Call
ct:encrypt_config_file/[2,3] to have Common Test encrypt a +Call
to have Common Test encrypt a specified file using the DES3 function in the OTP ct:encrypt_config_file/2/3 crypto application. The encrypted file can then be used as a regular configuration file, in combination with other encrypted files or normal text files. The key @@ -139,7 +140,7 @@decrypt_file flag/option, or a key file in a predefined location.Common Test also provides decryption functions, -
ct:decrypt_config_file/[2,3] , for recreating the original text +, for recreating the original text files. ct:decrypt_config_file/2/3 Please see the
ct reference manual for @@ -149,8 +150,8 @@Opening connections by using configuration data There are two different methods for opening a connection - by means of the support functions in e.g.
+ by means of the support functions in e.g.ct_ssh ,ct_ftp , - andct_telnet :, ct_ssh , + and ct_ftp : ct_telnet
- Using a configuration target name (an alias) as reference.
- Using the configuration variable as reference.
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml index b7162cb542..fc609ee137 100644 --- a/lib/common_test/doc/src/cover_chapter.xml +++ b/lib/common_test/doc/src/cover_chapter.xml @@ -100,7 +100,7 @@
$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec You may also pass the cover specification file name in a - call to
ct:run_test/1 , by adding a{cover,CoverSpec} + call to, by adding a ct:run_test/1 {cover,CoverSpec} tuple to theOpts argument. Also, you can of course enable code coverage in your test specifications (read more in the chapter about diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 014507c886..c938851e0e 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -192,12 +192,12 @@External configuration data and Logging It's possible in the CTH to read configuration data values - by calling
ct:get_config/1/2/3 (as explained in the + by calling(as explained in the ct:get_config/1/2/3 External configuration data chapter). The config variables in question must, as always, first have beenrequired by means of a suite-, group-, or test case info function, - or thect:require/1/2 function. Note that the latter can also be used + or thefunction. Note that the latter can also be used in CT hook functions. ct:require/1/2 The CT hook functions may call any of the logging functions available in the
ct interface to print information to the log files, or to diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml index 078b9b958c..434cb7da7c 100644 --- a/lib/common_test/doc/src/ct_run.xml +++ b/lib/common_test/doc/src/ct_run.xml @@ -46,7 +46,7 @@ particular mode.There is an interface function that corresponds to this program, - called
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml index 2f796b91ab..b95a18e47e 100644 --- a/lib/common_test/doc/src/event_handler_chapter.xml +++ b/lib/common_test/doc/src/event_handler_chapter.xml @@ -64,7 +64,7 @@ct:run_test/1 , for starting Common Test from the Erlang + called, for starting Common Test from the Erlang shell (or an Erlang program). Please see the ct:run_test/1 ct man page for details.Usage Event handlers may be installed by means of an
event_handler - start flag (ct_run ) or option (ct:run_test/1 ), where the + start flag (ct_run ) or option (), where the argument specifies the names of one or more event handler modules. Example: ct:run_test/1
$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1 @@ -78,7 +78,7 @@ example). An event_handler tuple in the argument
+ definition (see alsoOpts has the following - definition (see alsoct:run_test/1 in the reference manual):in the reference manual): ct:run_test/1 {event_handler,EventHandlers} @@ -308,7 +308,7 @@ manager can look like.To ensure that printouts to standard out (or printouts made with -
ct:log/2/3 orct:pal/2/3 ) get written to the test case log +or ct:log/2/3 ) get written to the test case log file, and not to the Common Test framework log, you can syncronize with the Common Test server by matching on the ct:pal/2/3 tc_start andtc_done events. In the period between these events, all IO gets directed to the diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml index 039578dd2e..891cbc49f3 100644 --- a/lib/common_test/doc/src/getting_started_chapter.xml +++ b/lib/common_test/doc/src/getting_started_chapter.xml @@ -90,7 +90,7 @@As you can understand from the illustration above, Common Test requires that a test case generates a runtime error to indicate failure (e.g. by causing a bad match error or by calling
exit/1 , preferrably - through thect:fail/1,2 help function). A succesful execution is + through thehelp function). A succesful execution is indicated by means of a normal return from the test case function. ct:fail/1,2 Common Test provides an Erlang API for running tests. The main (and most flexible) function for specifying and executing tests is called -
ct:run_test/1 . This function takes the same start parameters as +. This function takes the same start parameters as the ct:run_test/1 ct_run program described above, only the flags are instead given as options in a list of key-value tuples. E.g. a test specified withct_run like:-
$ ct_run -suite ./my_SUITE -logdir ./results is with
+ct:run_test/1 specified as:is with
specified as: ct:run_test/1
1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]). For detailed documentation, please see the
@@ -266,9 +266,9 @@ for trying out various operations during test suite development.ct manual page.To invoke the interactive shell mode, you can start an Erlang shell - manually and call
ct:install/1 to install any configuration + manually and callto install any configuration data you might need (use ct:install/1 [] as argument otherwise), then - callct:start_interactive/0 to start Common Test. If you use + callto start Common Test. If you use the ct:start_interactive/0 ct_run program, you may start the Erlang shell and Common Test in the same go by using the-shell and, optionally, the-config and/or-userconfig flag. Examples: @@ -287,7 +287,8 @@If any functions using "required config data" (e.g. ct_telnet or ct_ftp functions) are to be called from the erlang shell, config - data must first be required with
@@ -943,7 +944,7 @@ct:require/[1,2] . This is + data must first be required with. This is equivalent to a + ct:require/1/2 require statement in theTest Suite Info Function or in theIf you wish to exit the interactive mode (e.g. to start an - automated test run with
@@ -326,7 +327,7 @@ct:run_test/1 ), call the function -ct:stop_interactive/0 . This shuts down the + automated test run with), call the function + ct:run_test/1 . This shuts down the running ct:stop_interactive/0 ct application. Associations between configuration names and data created withrequire are - consequently deleted.ct:start_interactive/0 will get you + consequently deleted.will get you back into interactive mode, but the previous state is not restored. ct:start_interactive/0 Step by step execution of test cases with the Erlang Debugger By means of
@@ -586,7 +587,7 @@ct_run -step [opts] , or by passing the -{step,Opts} option toct:run_test/1 , it is possible +{step,Opts} option to, it is possible to get the Erlang Debugger started automatically and use its graphical interface to investigate the state of the current test case and to execute it step by step and/or set execution breakpoints. ct:run_test/1 ct_run . This forces Common Test to ignore unrecognizable terms. Note that in this mode, Common Test is not able to check the specification for errors as efficiently as if the scanner runs in default mode. - Ifct:run_test/1 is used for starting the tests, the relaxed scanner + Ifis used for starting the tests, the relaxed scanner mode is enabled by means of the tuple: ct:run_test/1 {allow_user_terms,true} The
-silent_connections tag (orsilent_connections tagged tuple in the call to -ct:run_test/1 ) overrides any settings in the test +) overrides any settings in the test suite. ct:run_test/1 Note that in the current Common Test version, the diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index d545c9e432..1fae50577e 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -173,7 +173,7 @@
The
end_per_testcase/2 function is called even after a - test case terminates due to a call toct:abort_current_testcase/1 , + test case terminates due to a call to, or after a timetrap timeout. However, ct:abort_current_testcase/1 end_per_testcase will then execute on a different process than the test case function, and in this situation,end_per_testcase will @@ -243,7 +243,8 @@The test case function argument
Config should not be confused with the information that can be retrieved from - configuration files (using ct:get_config/[1,2]). The Config argument + configuration files (using). The Config argument should be used for runtime configuration of the test suite and the test cases, while configuration files should typically contain data related to the SUT. These two types of configuration data are handled @@ -302,7 +303,7 @@ + ct:get_config/1/2 - @@ -347,7 +348,8 @@
Use this to specify arbitrary data related to the testcase. This - data can be retrieved at any time using the
ct:userdata/3 + data can be retrieved at any time using theutility function. ct:userdata/3 See the
@@ -824,7 +826,7 @@ Common Test to create one dedicated private directory per test case and execution instead. This is accomplished by means of the flag/option:Config files - chapter and thect:require/[1,2] function in the + chapter and thefunction in the + ct:require/1/2 ct reference manual for more information aboutrequire .create_priv_dir (to be used with the -ct_run program, thect:run_test/1 function, or +ct_run program, thefunction, or as test specification term). There are three possible values for this option: ct:run_test/1 @@ -840,7 +842,7 @@ become very inefficient for test runs with many test cases and/or repetitions. Therefore, in case the manual version is instead used, the test case must tell Common Test to create priv_dir when it needs it. - It does this by calling the function
ct:make_priv_dir/0 . + It does this by calling the function. ct:make_priv_dir/0 You should not depend on current working directory for @@ -888,7 +890,7 @@
It is also possible to dynamically set/reset a timetrap during the excution of a test case, or configuration function. This is done by calling -
@@ -901,12 +903,12 @@ct:timetrap/1 . This function cancels the current timetrap +. This function cancels the current timetrap and starts a new one (that stays active until timeout, or end of the current function). ct:timetrap/1 If a test case needs to suspend itself for a time that also gets multipled by
multiply_timetraps (and possibly also scaled up if -scale_timetraps is enabled), the functionct:sleep/1 +scale_timetraps is enabled), the functionmay be used (instead of e.g. ct:sleep/1 timer:sleep/1 ).A function (
+ well as argument to thefun/0 orMFA ) may be specified as timetrap value in the suite-, group- and test case info function, as - well as argument to thect:timetrap/1 function. Examples:function. Examples: ct:timetrap/1
{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}} diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl index d1d511f137..723715c986 100644 --- a/lib/common_test/src/ct_ftp.erl +++ b/lib/common_test/src/ct_ftp.erl @@ -66,7 +66,7 @@ %%% {unix,[{ftp,IpAddr}, %%% {username,Username}, %%% {password,Password}]}.
ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)
The config file must be as for put/3.
%%% @see put/3 -%%% @see ct:require/3 +%%% @see ct:require/2 get(KeyOrName,RemoteFile,LocalFile) -> Fun = fun(Ftp) -> recv(Ftp,RemoteFile,LocalFile) end, open_and_do(KeyOrName,Fun). @@ -108,9 +108,9 @@ get(KeyOrName,RemoteFile,LocalFile) -> %%% specify the target. Note that a connection that has no associated target %%% name can only be closed with the handle value. %%% -%%%See
See
Name
. If Key
is
+%%% associated with Name
. If Key
is
%%% used, the returned handle must be used for subsequent calls
%%% (multiple connections may be opened using the config
-%%% data specified by Key
). See Key
). See ConnType
will always override the type
@@ -154,7 +154,7 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) ->
%%% same key in the config data. For details on valid SSH
%%% options, see the documentation for the OTP ssh application.