From a71c6b976fa79fa3bcd0e61850a1a57071159b28 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 9 Aug 2012 18:00:13 +0200 Subject: Update user config to use nested tuple keys ct:get_config and ct:require can now use nested tuples to fetch data from user configuration. E.g. ct:get_config({localhost,ip,v4}). This introduces a backwards incompatability with how names are associated with keys when using require/2. E.g. ct:require(a_name,{localhost,ip}) will associate a_name with ip instead of localhost. --- lib/common_test/doc/src/common_test_app.xml | 6 +- lib/common_test/doc/src/config_file_chapter.xml | 2 +- lib/common_test/doc/src/event_handler_chapter.xml | 4 +- lib/common_test/doc/src/write_test_chapter.xml | 3 +- lib/common_test/src/ct.erl | 97 ++++---- lib/common_test/src/ct_config.erl | 250 +++++++++++---------- lib/common_test/src/ct_ftp.erl | 6 + lib/common_test/src/ct_netconfc.erl | 1 + lib/common_test/src/ct_ssh.erl | 5 +- lib/common_test/src/ct_telnet.erl | 2 + lib/common_test/test/ct_config_SUITE.erl | 112 ++++----- .../test/ct_config_SUITE_data/config/config.txt | 3 +- .../test/ct_config_SUITE_data/config/config.xml | 1 + .../test/ct_config_SUITE_data/config/shadow.txt | 12 + .../config/test/config_static_SUITE.erl | 90 +++++++- 15 files changed, 358 insertions(+), 236 deletions(-) create mode 100644 lib/common_test/test/ct_config_SUITE_data/config/shadow.txt diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index 6babdb93af..addeed002a 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -162,7 +162,7 @@ Func = atom() Args = list() Fun = fun() - Required = Key | {Key,SubKeys} + Required = Key | {Key,SubKeys} | {Key,SubKey} | {Key,SubKey,SubKeys} Key = atom() SubKeys = SubKey | [SubKey] SubKey = atom() @@ -289,7 +289,7 @@ Func = atom() Args = list() Fun = fun() - Required = Key | {Key,SubKeys} + Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys} Key = atom() SubKeys = SubKey | [SubKey] SubKey = atom() @@ -476,7 +476,7 @@ Func = atom() Args = list() Fun = fun() - Required = Key | {Key,SubKeys} + Required = Key | {Key,SubKeys} | {Key,Subkey} | {Key,Subkey,SubKeys} Key = atom() SubKeys = SubKey | [SubKey] SubKey = atom() diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml index 6a860bb58b..706d0d5f4e 100644 --- a/lib/common_test/doc/src/config_file_chapter.xml +++ b/lib/common_test/doc/src/config_file_chapter.xml @@ -295,7 +295,7 @@
         [{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.

-

RequireInfo = {not_available,atom()}, why require has failed.

+

RequireInfo = {not_available,atom() | tuple()}, why require has failed.

FailInfo = {timetrap_timeout,integer()} | {RunTimeError,StackTrace} | UserTerm, @@ -233,7 +233,7 @@ reason for auto skipping Func.

FailReason = {Suite,ConfigFunc,FailInfo}} | {Suite,FailedCaseInSequence}, reason for failure.

-

RequireInfo = {not_available,atom()}, why require has failed.

+

RequireInfo = {not_available,atom() | tuple()}, why require has failed.

ConfigFunc = init_per_suite | init_per_group

FailInfo = {timetrap_timeout,integer()} | {RunTimeError,StackTrace} | diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 7b7e7af8ea..d545c9e432 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -338,7 +338,8 @@

 	    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 Required. Note that it is +%%% only the last element of the tuple which can be a list of SubKeys. %%% -%%%

Example: require the variable myvar:
-%%% ok = ct:require(myvar)

+%%%

Example 1: require the variable myvar:

+%%%
ok = ct:require(myvar).
%%% %%%

In this case the config file must at least contain:

-%%%
-%%% {myvar,Value}.
+%%%
{myvar,Value}.
%%% -%%%

Example: require the variable myvar with -%%% subvariable sub1:
-%%% ok = ct:require({myvar,sub1})

+%%%

Example 2: require the key myvar with +%%% subkeys sub1 and sub2:

+%%%
ok = ct:require({myvar,[sub1,sub2]}).
%%% %%%

In this case the config file must at least contain:

-%%%
-%%% {myvar,[{sub1,Value}]}.
+%%%
{myvar,[{sub1,Value},{sub2,Value}]}.
+%%% +%%%

Example 3: require the key myvar with +%%% subkey sub1 with subsub1:

+%%%
ok = ct:require({myvar,sub1,sub2}).
+%%% +%%%

In this case the config file must at least contain:

+%%%
{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 Required is the same as in +%%% required/1 except that it is not possible to specify a list +%%% of SubKeys. %%% -%%%

If the requested data is available, the main entry will be +%%%

If the requested data is available, the sub entry will be %%% associated with 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).

+%%% ftp connection. Name the node 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}]}]}.
+%%% +%%% The behaviour of this function changed radically in common_test +%%% 1.6.2. In order too keep some backwards compatability it is still possible +%%% to do:
ct:require(a,{node,[telnet,ftp]}).
+%%% This will associate the name a with the top level node entry. +%%% For this to work, the config file must at least contain:
+%%% {node,[{telnet,IpAddr},{ftp,IpAddr}]}.
%%% %%% @see require/1 %%% @see get_config/1 @@ -344,7 +357,7 @@ get_config(Required,Default) -> %%%----------------------------------------------------------------- %%% @spec get_config(Required,Default,Opts) -> ValueOrElement -%%% Required = KeyOrName | {KeyOrName,SubKey} +%%% Required = KeyOrName | {KeyOrName,SubKey} | {KeyOrName,SubKey,SubKey} %%% KeyOrName = atom() %%% SubKey = atom() %%% Default = term() @@ -362,25 +375,25 @@ get_config(Required,Default) -> %%%

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

+%%% {user, [{username,Username}, +%%% {password,Password}]}]
+%%% 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}

+%%% The returned elements will then be on the form {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 use Key, 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 ct:require/3 for how to create a new Name

+%%% +%%% @see ct:require/3 open(KeyOrName) -> case ct_util:get_key_from_name(KeyOrName) of {ok,node} -> diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index d9c4a962dc..8d2e25b946 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -408,6 +408,7 @@ open(Options) -> %% server. It is not used for any other purposes during the lifetime %% of the connection. %% +%% @see ct:require/3 %% @end %%---------------------------------------------------------------------- open(KeyOrName, ExtraOpts) -> diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index aebb28bc42..21553f5551 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -136,7 +136,8 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) -> %%% 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).

+%%% data specified by Key). See ct:require/3 +%%% for how to create a new Name

%%% %%%

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.

+%%% +%%% @see ct:require/3 connect(KeyOrName, ConnType, ExtraOpts) -> case ct:get_config(KeyOrName) of undefined -> diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index f4a551e3ff..e37a657617 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -155,6 +155,8 @@ open(KeyOrName,ConnType,TargetMod) -> %%%

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).

+%%% +%%% @see ct:require/2 open(KeyOrName,ConnType,TargetMod,Extra) -> case ct:get_config({KeyOrName,ConnType}) of undefined -> diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl index 18218bee47..83b8c00458 100644 --- a/lib/common_test/test/ct_config_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE.erl @@ -88,7 +88,8 @@ require(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), run_test(config_static_SUITE, Config, - {config, filename:join(DataDir, "config/config.txt")}, + [{config, filename:join(DataDir, "config/shadow.txt")}, + {config, filename:join(DataDir, "config/config.txt")}], ["config_static_SUITE"]). install_config(Config) when is_list(Config) -> @@ -106,7 +107,8 @@ userconfig_static(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), run_test(config_static_SUITE, Config, - {userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + {config, filename:join(DataDir, "config/shadow.txt")}], ["config_static_SUITE"]). userconfig_dynamic(Config) when is_list(Config) -> @@ -121,7 +123,8 @@ testspec_legacy(Config) when is_list(Config) -> make_spec(DataDir, ConfigDir, "spec_legacy.spec", [config_static_SUITE], - [{config, filename:join(DataDir, "config/config.txt")}]), + [{config, filename:join(DataDir, "config/shadow.txt")}, + {config, filename:join(DataDir, "config/config.txt")}]), run_test(config_static_SUITE, Config, {spec, filename:join(ConfigDir, "spec_legacy.spec")}, @@ -134,7 +137,8 @@ testspec_static(Config) when is_list(Config) -> make_spec(DataDir, ConfigDir, "spec_static.spec", [config_static_SUITE], - [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}]), + [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}, + {config, filename:join(DataDir, "config/shadow.txt")}]), run_test(config_static_SUITE, Config, {spec, filename:join(ConfigDir, "spec_static.spec")}, @@ -179,13 +183,15 @@ run_test(Name, Config, CTConfig, SuiteNames)-> ExpEvents = events_to_check(Name), ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config). -setup_env(Test, Config, CTConfig) -> +setup_env(Test, Config, CTConfig) when is_list(CTConfig) -> Opts0 = ct_test_support:get_opts(Config), Level = ?config(trace_level, Config), EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], - Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}}, CTConfig], + Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}} | CTConfig], ERPid = ct_test_support:start_event_receiver(Config), - {Opts,ERPid}. + {Opts,ERPid}; +setup_env(Test, Config, CTConfig) -> + setup_env(Test, Config, [CTConfig]). reformat_events(Events, EH) -> ct_test_support:reformat(Events, EH). @@ -202,40 +208,49 @@ events_to_check(_, 0) -> events_to_check(Test, N) -> expected_events(Test) ++ events_to_check(Test, N-1). +-define(ok(Name,Suite,Stat),{?eh,tc_start,{Suite,Name}}, + {?eh,tc_done,{Suite,Name,ok}}, + {?eh,test_stats,Stat}). +-define(nok(Name,Suite,Reason,Stat),{?eh,tc_start,{Suite,Name}}, + {?eh,tc_done,{Suite,Name,Reason}}, + {?eh,test_stats,Stat}). + +-define(sok(Name,Stat),?ok(Name,config_static_SUITE,Stat)). +-define(snok(Name,Reason,Stat),?nok(Name,config_static_SUITE,Reason,Stat)). + +-define(dok(Name,Stat),?ok(Name,config_dynamic_SUITE,Stat)). +-define(dnok(Name,Reason,Stat),?nok(Name,config_dynamic_SUITE,Reason,Stat)). + expected_events(config_static_SUITE)-> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{1,1,8}}, + {?eh,start_info,{1,1,'_'}}, {?eh,tc_start,{config_static_SUITE,init_per_suite}}, {?eh,tc_done,{config_static_SUITE,init_per_suite,ok}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_simple}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_simple,ok}}, - {?eh,test_stats,{1,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_nested}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_nested,ok}}, - {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_default_suitewide}}, - {?eh,tc_done,{config_static_SUITE,test_default_suitewide,ok}}, - {?eh,test_stats,{3,0,{0,0}}}, - {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use1}}, - {?eh,tc_done, - {config_static_SUITE,test_config_name_already_in_use1,{skipped,{config_name_already_in_use,[x1]}}}}, - {?eh,test_stats,{3,0,{1,0}}}, - {?eh,tc_start,{config_static_SUITE,test_default_tclocal}}, - {?eh,tc_done,{config_static_SUITE,test_default_tclocal,ok}}, - {?eh,test_stats,{4,0,{1,0}}}, - {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use2}}, - {?eh,tc_done, - {config_static_SUITE,test_config_name_already_in_use2, - {skipped,{config_name_already_in_use,[alias,x1]}}}}, - {?eh,test_stats,{4,0,{2,0}}}, - {?eh,tc_start,{config_static_SUITE,test_alias_tclocal}}, - {?eh,tc_done,{config_static_SUITE,test_alias_tclocal,ok}}, - {?eh,test_stats,{5,0,{2,0}}}, - {?eh,tc_start,{config_static_SUITE,test_get_config_undefined}}, - {?eh,tc_done,{config_static_SUITE,test_get_config_undefined,ok}}, - {?eh,test_stats,{6,0,{2,0}}}, + ?sok(test_get_config_simple,{1,0,{0,0}}), + ?sok(test_get_config_nested,{2,0,{0,0}}), + ?sok(test_get_config_deep_nested,{3,0,{0,0}}), + ?sok(test_default_suitewide,{4,0,{0,0}}), + ?snok(test_config_name_already_in_use1, + {skipped,{config_name_already_in_use,[x1]}},{4,0,{1,0}}), + ?sok(test_default_tclocal,{5,0,{1,0}}), + ?snok(test_config_name_already_in_use2, + {skipped,{config_name_already_in_use,[alias,x1]}},{5,0,{2,0}}), + ?sok(test_alias_tclocal,{6,0,{2,0}}), + ?sok(test_get_config_undefined,{7,0,{2,0}}), + ?sok(test_require_subvals,{8,0,{2,0}}), + ?snok(test_require_subvals2, + {skipped,{require_failed, + {not_available,{gen_cfg,[a,b,c,d]}}}},{8,0,{2,1}}), + ?sok(test_require_deep_config,{9,0,{2,1}}), + ?sok(test_shadow_all,{10,0,{2,1}}), + ?sok(test_element,{11,0,{2,1}}), + ?sok(test_shadow_all_element,{12,0,{2,1}}), + ?sok(test_internal_deep,{13,0,{2,1}}), + ?sok(test_alias_tclocal_nested,{14,0,{2,1}}), + ?sok(test_alias_tclocal_nested_backward_compat,{15,0,{2,1}}), + ?sok(test_alias_tclocal_nested_backward_compat_subvals,{16,0,{2,1}}), {?eh,tc_start,{config_static_SUITE,end_per_suite}}, {?eh,tc_done,{config_static_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, @@ -246,29 +261,14 @@ expected_events(config_dynamic_SUITE)-> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{1,1,5}}, + {?eh,start_info,{1,1,'_'}}, {?eh,tc_start,{config_dynamic_SUITE,init_per_suite}}, {?eh,tc_done,{config_dynamic_SUITE,init_per_suite,ok}}, - {?eh,tc_start,{config_dynamic_SUITE,test_get_known_variable}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_get_known_variable,ok}}, - {?eh,test_stats,{1,0,{0,0}}}, - {?eh,tc_start,{config_dynamic_SUITE,test_localtime_update}}, - {?eh,tc_done,{config_dynamic_SUITE,test_localtime_update,ok}}, - {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{config_dynamic_SUITE,test_server_pid}}, - {?eh,tc_done,{config_dynamic_SUITE,test_server_pid,ok}}, - {?eh,test_stats,{3,0,{0,0}}}, - {?eh,tc_start, - {config_dynamic_SUITE,test_disappearable_variable}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_disappearable_variable,ok}}, - {?eh,test_stats,{4,0,{0,0}}}, - {?eh,tc_start, - {config_dynamic_SUITE,test_disappearable_variable_alias}}, - {?eh,tc_done, - {config_dynamic_SUITE,test_disappearable_variable_alias,ok}}, - {?eh,test_stats,{5,0,{0,0}}}, + ?dok(test_get_known_variable,{1,0,{0,0}}), + ?dok(test_localtime_update,{2,0,{0,0}}), + ?dok(test_server_pid,{3,0,{0,0}}), + ?dok(test_disappearable_variable,{4,0,{0,0}}), + ?dok(test_disappearable_variable_alias,{5,0,{0,0}}), {?eh,tc_start,{config_dynamic_SUITE,end_per_suite}}, {?eh,tc_done,{config_dynamic_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.txt b/lib/common_test/test/ct_config_SUITE_data/config/config.txt index fcbffcd7f3..e4bcc5ba6b 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/config.txt +++ b/lib/common_test/test/ct_config_SUITE_data/config/config.txt @@ -2,7 +2,8 @@ {gen_cfg, [ {a,a_value}, - {b,b_value} + {b,b_value}, + {c,[{d,d_value}]} ]}. {gen_cfg2, [ diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.xml b/lib/common_test/test/ct_config_SUITE_data/config/config.xml index 0a3e5f2e31..8eeff1482f 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/config.xml +++ b/lib/common_test/test/ct_config_SUITE_data/config/config.xml @@ -3,6 +3,7 @@ a_value b_value + d_value "Hello, world!" diff --git a/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt new file mode 100644 index 0000000000..865bf9255a --- /dev/null +++ b/lib/common_test/test/ct_config_SUITE_data/config/shadow.txt @@ -0,0 +1,12 @@ +{x, suite}. +{gen_cfg3, + [ + {l, + [ + {m, + [ + {n, "n"}, + {o, 'o'} + ]} + ]} + ]}. diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl index 8751a2e8f3..d7119d7fde 100644 --- a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl +++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl @@ -46,7 +46,7 @@ suite() -> {require, gen_cfg3}, {require, alias, gen_cfg}, %% x1 default value - {x1, {x,suite}} + {default_config, x1, {x,suite}} ]. init_per_suite(Config) -> @@ -55,14 +55,24 @@ init_per_suite(Config) -> end_per_suite(_) -> ok. -all() -> [test_get_config_simple, test_get_config_nested, test_default_suitewide, +all() -> [test_get_config_simple, test_get_config_nested, + test_get_config_deep_nested, test_default_suitewide, test_config_name_already_in_use1, test_default_tclocal, test_config_name_already_in_use2, test_alias_tclocal, - test_get_config_undefined]. - -init_per_testcase(_, Config) -> + test_get_config_undefined, + test_require_subvals,test_require_subvals2,test_require_deep_config, + test_shadow_all,test_element,test_shadow_all_element, + test_internal_deep, test_alias_tclocal_nested, + test_alias_tclocal_nested_backward_compat, + test_alias_tclocal_nested_backward_compat_subvals +]. + +init_per_testcase(_,Config) -> Config. +end_per_testcase(test_alias_tclocal_nested_backward_compat, _) -> + os:putenv("COMMON_TEST_ALIAS_TOP",""), + ok; end_per_testcase(_, _) -> ok. @@ -76,6 +86,11 @@ test_get_config_nested(_)-> a_value = ct:get_config({gen_cfg, a}), ok. +%% test getting a deep nested value +test_get_config_deep_nested(_)-> + d_value = ct:get_config({gen_cfg, c, d}), + ok. + %% test suite-wide default value test_default_suitewide(_)-> suite = ct:get_config(x1), @@ -112,12 +127,73 @@ test_config_name_already_in_use2(_) -> %% test aliases test_alias_tclocal() -> [{require,newalias,gen_cfg}]. -test_alias_tclocal(_) -> - A = [{a,a_value},{b,b_value}] = ct:get_config(newalias), +test_alias_tclocal(C) when is_list(C) -> + test_alias_tclocal(newalias); +test_alias_tclocal(Alias) when is_atom(Alias) -> + A = [{a,a_value},{b,b_value},{c,[{d,d_value}]}] = ct:get_config(Alias), A = ct:get_config(gen_cfg), + B = b_value = ct:get_config({Alias,b}), + B = ct:get_config({gen_cfg,b}), + ok. + +%% test nested aliases +test_alias_tclocal_nested() -> + [{require,newalias2,{gen_cfg,c}}]. +test_alias_tclocal_nested(_) -> + A = [{d,d_value}] = ct:get_config(newalias2), + A = ct:get_config({gen_cfg,c}), + B = d_value = ct:get_config({newalias2,d}), + B = ct:get_config({gen_cfg,c,d}), ok. +%% test nested aliases backward compat option +test_alias_tclocal_nested_backward_compat() -> + os:putenv("COMMON_TEST_ALIAS_TOP","true"), + [{require,newalias3,{gen_cfg,c}}]. +test_alias_tclocal_nested_backward_compat(_) -> + test_alias_tclocal(newalias3). + +%% test nested aliases backward compat option +test_alias_tclocal_nested_backward_compat_subvals() -> + [{require,newalias4,{gen_cfg,[c]}}]. +test_alias_tclocal_nested_backward_compat_subvals(_) -> + test_alias_tclocal(newalias4). + %% test for getting undefined variables test_get_config_undefined(_) -> undefined = ct:get_config(y1), ok. + +test_require_subvals() -> + [{require, {gen_cfg,[a,b,c]}}]. +test_require_subvals(_) -> + ok. + +test_require_subvals2() -> + [{require, {gen_cfg,[a,b,c,d]}}]. +test_require_subvals2(_) -> + ct:fail("Test should've been skipped, you shouldn't see this!"), + ok. + +test_require_deep_config() -> + [{require, {gen_cfg3, m, n}}]. +test_require_deep_config(_) -> + ok. + + +test_shadow_all(_) -> + ["n","N"] = ct:get_config({gen_cfg3,l, m, n}, [], [all]). + +test_element(_) -> + {{gen_cfg3,l, m, n},"n"} = ct:get_config({gen_cfg3,l, m, n}, [], [element]). + +test_shadow_all_element(_) -> + [{{gen_cfg3,l, m, n},"n"},{{gen_cfg3,l, m, n},"N"}] = + ct:get_config({gen_cfg3,l, m, n}, [], [all,element]). + +%% The tests below are needed to verify that things like ct:telnet can use +%% nested configs +test_internal_deep(_) -> + "n" = ct:get_config({{gen_cfg3,l,m},n}), + a_value = ct:get_config({{gen_cfg},a}), + undefined = ct:get_config({{gen_cfg3,l,m},p}). -- cgit v1.2.3 From a62b355f2122385564f5ea2080d9705e2ef45efa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 20 Aug 2012 11:29:34 +0200 Subject: Remove config option from common_test args This is needed because it is no longer ignored by common_test --- lib/test_server/src/ts_run.erl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl index a61028e4bc..2e8c092400 100644 --- a/lib/test_server/src/ts_run.erl +++ b/lib/test_server/src/ts_run.erl @@ -334,9 +334,9 @@ path_separator() -> end. -make_common_test_args(Args0, Options, _Vars) -> +make_common_test_args(Args0, Options0, _Vars) -> Trace = - case lists:keysearch(trace,1,Options) of + case lists:keysearch(trace,1,Options0) of {value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) -> ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])), [{ct_trace,?tracefile}]; @@ -348,7 +348,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, Cover = - case lists:keysearch(cover,1,Options) of + case lists:keysearch(cover,1,Options0) of {value,{cover, App, none, _Analyse}} -> io:format("No cover file found for ~p~n",[App]), []; @@ -358,7 +358,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, - Logdir = case lists:keysearch(logdir, 1, Options) of + Logdir = case lists:keysearch(logdir, 1, Options0) of {value,{logdir, _}} -> []; false -> @@ -373,15 +373,16 @@ make_common_test_args(Args0, Options, _Vars) -> {scale_timetraps, true}] end, - ConfigPath = case {os:getenv("TEST_CONFIG_PATH"), - lists:keysearch(config, 1, Options)} of - {false,{value, {config, Path}}} -> - Path; - {false,false} -> - "../test_server"; - {Path,_} -> - Path - end, + {ConfigPath, + Options} = case {os:getenv("TEST_CONFIG_PATH"), + lists:keysearch(config, 1, Options0)} of + {_,{value, {config, Path}}} -> + {Path,lists:keydelete(config, 1, Options0)}; + {false,false} -> + {"../test_server",Options0}; + {Path,_} -> + {Path,Options0} + end, ConfigFiles = [{config,[filename:join(ConfigPath,File) || File <- get_config_files()]}], io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++ -- cgit v1.2.3 From a95a238719369feb1002daeacf4e1485439a38d2 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 21 Aug 2012 17:44:49 +0200 Subject: Add more cross reference links to ct docs --- lib/common_test/doc/src/common_test_app.xml | 53 +++++++++++----------- lib/common_test/doc/src/config_file_chapter.xml | 17 +++---- lib/common_test/doc/src/cover_chapter.xml | 2 +- lib/common_test/doc/src/ct_hooks_chapter.xml | 4 +- lib/common_test/doc/src/ct_run.xml | 2 +- lib/common_test/doc/src/event_handler_chapter.xml | 6 +-- .../doc/src/getting_started_chapter.xml | 2 +- lib/common_test/doc/src/run_test_chapter.xml | 23 +++++----- lib/common_test/doc/src/write_test_chapter.xml | 20 ++++---- lib/common_test/src/ct_ftp.erl | 8 ++-- lib/common_test/src/ct_netconfc.erl | 2 +- lib/common_test/src/ct_ssh.erl | 6 +-- 12 files changed, 75 insertions(+), 70 deletions(-) diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index addeed002a..a0fa45c71f 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -99,11 +99,11 @@ be executed by Common Test. A test case is represented by an atom, the name of the test case function. A test case group is represented by a group tuple, where GroupName, - an atom, is the name of the group (defined in groups/0). + an atom, is the name of the group (defined in groups/0). Execution properties for groups may also be specified, both for a top level group and for any of its sub-groups. Group execution properties specified here, will override - properties in the group definition (see groups/0). + properties in the group definition (see groups/0). (With value default, the group definition properties will be used).

@@ -184,8 +184,8 @@ test cases in the suite).

The timetrap tag sets the maximum time each - test case is allowed to execute (including init_per_testcase/2 - and end_per_testcase/2). If the timetrap time is + test case is allowed to execute (including init_per_testcase/2 + and end_per_testcase/2). If the timetrap time is exceeded, the test case fails with reason timetrap_timeout. A TimeFunc function can be used to set a new timetrap by returning a TimeVal. It may also be @@ -201,11 +201,11 @@ in any of the configuration files, all test cases are skipped. For more information about the 'require' functionality, see the reference manual for the function - ct:require/[1,2].

+ ct:require/1/2.

With userdata, it is possible for the user to specify arbitrary test suite related information which can be - read by calling ct:userdata/2.

+ read by calling ct:userdata/2.

The ct_hooks tag specifies which Common Test Hooks @@ -264,7 +264,7 @@

This function is called as the last test case in the suite. It is meant to be used for cleaning up after - init_per_suite/1. + init_per_suite/1. For information on save_config, please see Dependencies between Test Cases and Suites in the User's Guide.

@@ -309,13 +309,14 @@

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 groups/1 override + and sub-groups). Properties set by + group/1 override properties with the same key that have been previously set by - suite/0.

+ suite/0.

The timetrap tag sets the maximum time each - test case is allowed to execute (including init_per_testcase/2 - and end_per_testcase/2). If the timetrap time is + test case is allowed to execute (including init_per_testcase/2 + and end_per_testcase/2). If the timetrap time is exceeded, the test case fails with reason timetrap_timeout. A TimeFunc function can be used to set a new timetrap by returning a TimeVal. It may also be @@ -330,11 +331,11 @@ in any of the configuration files, all test cases in this group are skipped. For more information about the 'require' functionality, see the reference manual for the function - ct:require/[1,2].

+ ct:require/1/2.

With userdata, it is possible for the user to specify arbitrary test case group related information which can be - read by calling ct:userdata/2.

+ read by calling ct:userdata/2.

The ct_hooks tag specifies which Common Test Hooks @@ -367,7 +368,7 @@ test case group. It typically contains initializations which are common for all test cases and sub-groups in the group, and which shall only be performed once. GroupName is the name of the - group, as specified in the group definition (see groups/0). The + group, as specified in the group definition (see groups/0). The Config parameter is the configuration data which can be modified here. The return value of this function is given as Config to all test cases and sub-groups in the group. If {skip,Reason} @@ -396,10 +397,10 @@

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 init_per_group/2. + It is meant to be used for cleaning up after init_per_group/2. By means of {return_group_result,Status}, it is possible to return a status value for a nested sub-group. The status can be retrieved in - end_per_group/2 for the group on the level above. The status will also + end_per_group/2 for the group on the level above. The status will also be used by Common Test for deciding if execution of a group should proceed in case the property sequence or repeat_until_* is set.

@@ -450,7 +451,7 @@

OPTIONAL

This function is called after each test case, and can be used - to clean up after init_per_testcase/2 and the test case. + to clean up after init_per_testcase/2 and the test case. Any return value (besides {fail,Reason} and {save_config,SaveConfig}) is ignored. By returning {fail,Reason}, TestCase will be marked as failed (even though it was actually successful in the sense that it returned @@ -492,15 +493,15 @@

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 Testcase/0 override + Properties set by Testcase/0 override properties that have been previously set for the test case - by group/1 or suite/0.

+ by group/1 or suite/0.

The timetrap tag sets the maximum time the test case is allowed to execute. If the timetrap time is exceeded, the test case fails with reason - timetrap_timeout. init_per_testcase/2 - and end_per_testcase/2 are included in the + timetrap_timeout. init_per_testcase/2 + and end_per_testcase/2 are included in the timetrap time. A TimeFunc function can be used to set a new timetrap by returning a TimeVal. It may also be used to trigger a timetrap timeout by, at some point, returning a @@ -514,15 +515,15 @@ configuration files, the test case is skipped. For more information about the 'require' functionality, see the reference manual for the function - ct:require/[1,2].

+ ct:require/1/2.

If timetrap and/or require is not set, the - default values specified by suite/0 (or - group/1) will be used.

+ default values specified by suite/0 (or + group/1) will be used.

With userdata, it is possible for the user to specify arbitrary test case related information which can be - read by calling ct:userdata/3.

+ read by calling ct:userdata/3.

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 ct:fail/1/2 + function causes a runtime error, or call ct:fail/1/2 (which also causes the test case process to terminate).

Elements from the Config list can e.g. be read diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml index 706d0d5f4e..e843ed3ba4 100644 --- a/lib/common_test/doc/src/config_file_chapter.xml +++ b/lib/common_test/doc/src/config_file_chapter.xml @@ -78,7 +78,7 @@ test is skipped (unless a default value has been specified, see the test case info function chapter for details). There is also a function - ct:require/[1,2] which can be called from a test case + ct:require/1/2 which can be called from a test case in order to check if a specific variable is available. The return value from this function must be checked explicitly and appropriate action be taken depending on the result (e.g. to skip the test case @@ -88,7 +88,7 @@ info-list should look like this: {require,CfgVarName} or {require,AliasName,CfgVarName}. The arguments AliasName and CfgVarName are the same as the - arguments to ct:require/[1,2] which are described in the + arguments to ct:require/1/2 which are described in the reference manual for ct. AliasName becomes an alias for the configuration variable, and can be used as reference to the configuration data value. @@ -101,7 +101,8 @@ (or test case) and improve readability.

To read the value of a config variable, use the function - get_config/[1,2,3] which is also described in the reference + get_config/1/2/3 + which is also described in the reference manual for ct.

Example:

@@ -118,7 +119,7 @@
   
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 the ct:get_config/3 function and specify 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 ct:encrypt_config_file/2/3 to have Common Test encrypt a specified file using the DES3 function in the OTP 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 + ct:decrypt_config_file/2/3, for recreating the original text files.

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. ct_ssh, ct_ftp, - and ct_telnet:

+ by means of the support functions in e.g. ct_ssh, ct_ftp, + and 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 ct:run_test/1, by adding a {cover,CoverSpec} tuple to the Opts 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 ct:get_config/1/2/3 (as explained in the External configuration data chapter). The config variables in question must, as always, first have been required by means of a suite-, group-, or test case info function, - or the ct:require/1/2 function. Note that the latter can also be used + or the ct:require/1/2 function. Note that the latter can also be used in CT hook functions.

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 ct:run_test/1, for starting Common Test from the Erlang + called ct:run_test/1, for starting Common Test from the Erlang shell (or an Erlang program). Please see the ct man page for details.

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 @@ 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 (ct:run_test/1), where the argument specifies the names of one or more event handler modules. Example:

$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1 @@ -78,7 +78,7 @@ example).

An event_handler tuple in the argument Opts has the following - definition (see also ct:run_test/1 in the reference manual):

+ definition (see also ct:run_test/1 in the reference manual):

     {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 or ct:pal/2/3) get written to the test case log + ct:log/2/3 or ct:pal/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 tc_start and tc_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 the ct:fail/1,2 help function). A succesful execution is + through the ct:fail/1,2 help function). A succesful execution is indicated by means of a normal return from the test case function.

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 @@

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 + ct:run_test/1. This function takes the same start parameters as the 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 with ct_run like:

$ ct_run -suite ./my_SUITE -logdir ./results

-

is with ct:run_test/1 specified as:

+

is with ct:run_test/1 specified as:

1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).

For detailed documentation, please see the ct manual page.

@@ -266,9 +266,9 @@ for trying out various operations during test suite development.

To invoke the interactive shell mode, you can start an Erlang shell - manually and call ct:install/1 to install any configuration + manually and call ct:install/1 to install any configuration data you might need (use [] as argument otherwise), then - call ct:start_interactive/0 to start Common Test. If you use + call ct:start_interactive/0 to start Common Test. If you use the 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 ct:require/[1,2]. This is + data must first be required with + ct:require/1/2. This is equivalent to a require statement in the Test Suite Info Function or in the

If you wish to exit the interactive mode (e.g. to start an - automated test run with ct:run_test/1), call the function - ct:stop_interactive/0. This shuts down the + automated test run with ct:run_test/1), call the function + ct:stop_interactive/0. This shuts down the running ct application. Associations between configuration names and data created with require are - consequently deleted. ct:start_interactive/0 will get you + consequently deleted. ct:start_interactive/0 will get you back into interactive mode, but the previous state is not restored.

@@ -326,7 +327,7 @@ Step by step execution of test cases with the Erlang Debugger

By means of ct_run -step [opts], or by passing the - {step,Opts} option to ct:run_test/1, it is possible + {step,Opts} option to ct:run_test/1, 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.

@@ -586,7 +587,7 @@ 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. - If ct:run_test/1 is used for starting the tests, the relaxed scanner + If ct:run_test/1 is used for starting the tests, the relaxed scanner mode is enabled by means of the tuple: {allow_user_terms,true}

@@ -943,7 +944,7 @@

The -silent_connections tag (or silent_connections tagged tuple in the call to - ct:run_test/1) overrides any settings in the test + ct:run_test/1) overrides any settings in the test suite.

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 to ct:abort_current_testcase/1, + test case terminates due to a call to ct:abort_current_testcase/1, or after a timetrap timeout. However, 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 + ct:get_config/1/2). 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 @@

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 the ct:userdata/3 utility function.

@@ -347,7 +348,8 @@

See the Config files - chapter and the ct:require/[1,2] function in the + chapter and the + ct:require/1/2 function in the ct reference manual for more information about require.

@@ -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: create_priv_dir (to be used with the - ct_run program, the ct:run_test/1 function, or + ct_run program, the ct:run_test/1 function, or as test specification term). There are three possible values for this option: @@ -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 - ct:timetrap/1. This function cancels the current timetrap + ct:timetrap/1. This function cancels the current timetrap and starts a new one (that stays active until timeout, or end of the current function).

@@ -901,12 +903,12 @@

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 function ct:sleep/1 + scale_timetraps is enabled), the function ct:sleep/1 may be used (instead of e.g. timer:sleep/1).

A function (fun/0 or MFA) may be specified as timetrap value in the suite-, group- and test case info function, as - well as argument to the ct:timetrap/1 function. Examples:

+ well as argument to the ct:timetrap/1 function. Examples:

{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}}

ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)

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}]}.
-%%% @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) -> %%% %%%

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 ct:require/3 for how to create a new Name

+%%%

See ct:require/2 for how to create a new Name

%%% -%%% @see ct:require/3 +%%% @see ct:require/2 open(KeyOrName) -> case ct_util:get_key_from_name(KeyOrName) of {ok,node} -> diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 8d2e25b946..a6b170f07e 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -408,7 +408,7 @@ open(Options) -> %% server. It is not used for any other purposes during the lifetime %% of the connection. %% -%% @see ct:require/3 +%% @see ct:require/2 %% @end %%---------------------------------------------------------------------- open(KeyOrName, ExtraOpts) -> diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index 21553f5551..81a25849db 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -133,10 +133,10 @@ connect(KeyOrName, ExtraOpts) when is_list(ExtraOpts) -> %%% is used to identify the connection, this name may %%% be used as connection reference for subsequent calls. %%% It's only possible to have one open connection at a time -%%% associated with 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 ct:require/3 +%%% data specified by Key). See ct:require/2 %%% for how to create a new Name

%%% %%%

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.

%%% -%%% @see ct:require/3 +%%% @see ct:require/2 connect(KeyOrName, ConnType, ExtraOpts) -> case ct:get_config(KeyOrName) of undefined -> -- cgit v1.2.3