aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src
diff options
context:
space:
mode:
authorPeter Andersson <[email protected]>2012-08-23 16:07:23 +0200
committerPeter Andersson <[email protected]>2012-08-23 16:08:16 +0200
commit4173064b7044d72f650cf459683793acc9e29352 (patch)
treed0def2875976ba6fcf2cf9e2775ade845ad5cace /lib/common_test/src
parent474e2e4485eaf6da0b325ad7c7e79b29fac262b7 (diff)
parentecf119feda32c0692bf688462ced24efa35789f1 (diff)
downloadotp-4173064b7044d72f650cf459683793acc9e29352.tar.gz
otp-4173064b7044d72f650cf459683793acc9e29352.tar.bz2
otp-4173064b7044d72f650cf459683793acc9e29352.zip
Merge remote branch 'origin/peppe/common_test/testspec_updates' into maint
* origin/peppe/common_test/testspec_updates: Test new testspec functionality and correct bugs Implement constants and new features for test specifications Conflicts: lib/common_test/src/ct_run.erl lib/common_test/test/Makefile OTP-10049 OTP-10089 OTP-10145
Diffstat (limited to 'lib/common_test/src')
-rw-r--r--lib/common_test/src/ct_run.erl179
-rw-r--r--lib/common_test/src/ct_testspec.erl926
-rw-r--r--lib/common_test/src/ct_util.hrl4
3 files changed, 662 insertions, 447 deletions
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 42d450f67f..ddb0d36c0f 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -58,11 +58,13 @@
step,
logdir,
logopts = [],
+ basic_html,
config = [],
event_handlers = [],
ct_hooks = [],
enable_builtin_hooks,
include = [],
+ auto_compile,
silent_connections,
stylesheet,
multiply_timetraps = 1,
@@ -106,7 +108,8 @@ script_start() ->
end, Flags)
end,
%% used for purpose of testing the run_test interface
- io:format(user, "~n-------------------- START ARGS --------------------~n", []),
+ io:format(user, "~n-------------------- START ARGS "
+ "--------------------~n", []),
io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]),
io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]),
EnvArgs = opts2args(EnvStartOpts),
@@ -114,7 +117,8 @@ script_start() ->
[EnvStartOpts,EnvArgs]),
Merged = merge_arguments(CtArgs ++ EnvArgs),
io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]),
- io:format(user, "----------------------------------------------------~n~n", []),
+ io:format(user, "-----------------------------------"
+ "-----------------~n~n", []),
Merged;
_ ->
merge_arguments(CtArgs)
@@ -137,17 +141,20 @@ script_start(Args) ->
_ -> ""
end
end,
- io:format("~nCommon Test~s starting (cwd is ~s)~n~n", [CTVsn,Cwd]),
+ io:format("~nCommon Test~s starting (cwd is ~s)~n~n",
+ [CTVsn,Cwd]),
Self = self(),
Pid = spawn_link(fun() -> script_start1(Self, Args) end),
receive
{'EXIT',Pid,Reason} ->
case Reason of
{user_error,What} ->
- io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]),
+ io:format("\nTest run failed!\nReason: ~p\n\n\n",
+ [What]),
finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args);
_ ->
- io:format("Test run crashed! This could be an internal error "
+ io:format("Test run crashed! "
+ "This could be an internal error "
"- please report!\n\n"
"~p\n\n\n", [Reason]),
finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args)
@@ -261,7 +268,7 @@ script_start1(Parent, Args) ->
end
end,
%% no_auto_compile + include
- IncludeDirs =
+ {AutoCompile,IncludeDirs} =
case proplists:get_value(no_auto_compile, Args) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -277,16 +284,16 @@ script_start1(Parent, Args) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
AllInclDirs =
string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
_ ->
application:set_env(common_test, auto_compile, false),
- []
+ {false,[]}
end,
%% silent connections
SilentConns =
@@ -298,19 +305,23 @@ script_start1(Parent, Args) ->
Stylesheet = get_start_opt(stylesheet,
fun([SS]) -> ?abs(SS) end, Args),
%% basic_html - used by ct_logs
- case proplists:get_value(basic_html, Args) of
- undefined ->
- application:set_env(common_test, basic_html, false);
- _ ->
- application:set_env(common_test, basic_html, true)
- end,
+ BasicHtml = case proplists:get_value(basic_html, Args) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ _ ->
+ application:set_env(common_test, basic_html, true),
+ true
+ end,
StartOpts = #opts{label = Label, profile = Profile,
vts = Vts, shell = Shell, cover = Cover,
logdir = LogDir, logopts = LogOpts,
+ basic_html = BasicHtml,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = IncludeDirs,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -407,9 +418,36 @@ script_start2(StartOpts = #opts{vts = undefined,
StartOpts#opts.enable_builtin_hooks,
SpecStartOpts#opts.enable_builtin_hooks),
+ Stylesheet =
+ choose_val(StartOpts#opts.stylesheet,
+ SpecStartOpts#opts.stylesheet),
+
AllInclude = merge_vals([StartOpts#opts.include,
SpecStartOpts#opts.include]),
application:set_env(common_test, include, AllInclude),
+
+ AutoCompile =
+ case choose_val(StartOpts#opts.auto_compile,
+ SpecStartOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test,
+ auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml =
+ case choose_val(StartOpts#opts.basic_html,
+ SpecStartOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
{TS,StartOpts#opts{label = Label,
profile = Profile,
@@ -417,11 +455,14 @@ script_start2(StartOpts = #opts{vts = undefined,
cover = Cover,
logdir = LogDir,
logopts = AllLogOpts,
+ basic_html = BasicHtml,
config = SpecStartOpts#opts.config,
event_handlers = AllEvHs,
ct_hooks = AllCTHooks,
enable_builtin_hooks =
EnableBuiltinHooks,
+ stylesheet = Stylesheet,
+ auto_compile = AutoCompile,
include = AllInclude,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
@@ -795,8 +836,10 @@ run_test2(StartOpts) ->
(Lbl) when is_atom(Lbl) -> atom_to_list(Lbl)
end, StartOpts),
%% profile
- Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> Prof;
- (Prof) when is_atom(Prof) -> atom_to_list(Prof)
+ Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) ->
+ Prof;
+ (Prof) when is_atom(Prof) ->
+ atom_to_list(Prof)
end, StartOpts),
%% logdir
LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end,
@@ -861,7 +904,7 @@ run_test2(StartOpts) ->
CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts),
%% auto compile & include files
- Include =
+ {AutoCompile,Include} =
case proplists:get_value(auto_compile, StartOpts) of
undefined ->
application:set_env(common_test, auto_compile, true),
@@ -877,16 +920,16 @@ run_test2(StartOpts) ->
case os:getenv("CT_INCLUDE_PATH") of
false ->
application:set_env(common_test, include, InclDirs),
- InclDirs;
+ {undefined,InclDirs};
CtInclPath ->
InclDirs1 = string:tokens(CtInclPath, [$:,$ ,$,]),
AllInclDirs = InclDirs1++InclDirs,
application:set_env(common_test, include, AllInclDirs),
- AllInclDirs
+ {undefined,AllInclDirs}
end;
ACBool ->
application:set_env(common_test, auto_compile, ACBool),
- []
+ {ACBool,[]}
end,
%% decrypt config file
@@ -900,11 +943,14 @@ run_test2(StartOpts) ->
end,
%% basic html - used by ct_logs
- case proplists:get_value(basic_html, StartOpts) of
- undefined ->
- application:set_env(common_test, basic_html, false);
- BasicHtmlBool ->
- application:set_env(common_test, basic_html, BasicHtmlBool)
+ BasicHtml =
+ case proplists:get_value(basic_html, StartOpts) of
+ undefined ->
+ application:set_env(common_test, basic_html, false),
+ undefined;
+ BasicHtmlBool ->
+ application:set_env(common_test, basic_html, BasicHtmlBool),
+ BasicHtmlBool
end,
%% stepped execution
@@ -912,10 +958,12 @@ run_test2(StartOpts) ->
Opts = #opts{label = Label, profile = Profile,
cover = Cover, step = Step, logdir = LogDir,
- logopts = LogOpts, config = CfgFiles,
+ logopts = LogOpts, basic_html = BasicHtml,
+ config = CfgFiles,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
silent_connections = SilentConns,
stylesheet = Stylesheet,
@@ -961,6 +1009,8 @@ run_spec_file(Relaxed,
SpecOpts#opts.logdir),
AllLogOpts = merge_vals([Opts#opts.logopts,
SpecOpts#opts.logopts]),
+ Stylesheet = choose_val(Opts#opts.stylesheet,
+ SpecOpts#opts.stylesheet),
AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
Cover = choose_val(Opts#opts.cover,
SpecOpts#opts.cover),
@@ -974,7 +1024,6 @@ run_spec_file(Relaxed,
SpecOpts#opts.event_handlers]),
AllInclude = merge_vals([Opts#opts.include,
SpecOpts#opts.include]),
-
AllCTHooks = merge_vals([Opts#opts.ct_hooks,
SpecOpts#opts.ct_hooks]),
EnableBuiltinHooks = choose_val(Opts#opts.enable_builtin_hooks,
@@ -982,13 +1031,36 @@ run_spec_file(Relaxed,
application:set_env(common_test, include, AllInclude),
+ AutoCompile = case choose_val(Opts#opts.auto_compile,
+ SpecOpts#opts.auto_compile) of
+ undefined ->
+ true;
+ ACBool ->
+ application:set_env(common_test, auto_compile,
+ ACBool),
+ ACBool
+ end,
+
+ BasicHtml = case choose_val(Opts#opts.basic_html,
+ SpecOpts#opts.basic_html) of
+ undefined ->
+ false;
+ BHBool ->
+ application:set_env(common_test, basic_html,
+ BHBool),
+ BHBool
+ end,
+
Opts1 = Opts#opts{label = Label,
profile = Profile,
cover = Cover,
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
+ stylesheet = Stylesheet,
+ basic_html = BasicHtml,
config = AllConfig,
event_handlers = AllEvHs,
+ auto_compile = AutoCompile,
include = AllInclude,
testspecs = AbsSpecs,
multiply_timetraps = MultTT,
@@ -1246,12 +1318,15 @@ get_data_for_node(#testspec{label = Labels,
profile = Profiles,
logdir = LogDirs,
logopts = LogOptsList,
+ basic_html = BHs,
+ stylesheet = SSs,
cover = CoverFs,
config = Cfgs,
userconfig = UsrCfgs,
event_handler = EvHs,
ct_hooks = CTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = ACs,
include = Incl,
multiply_timetraps = MTs,
scale_timetraps = STs,
@@ -1266,6 +1341,8 @@ get_data_for_node(#testspec{label = Labels,
undefined -> [];
LOs -> LOs
end,
+ BasicHtml = proplists:get_value(Node, BHs),
+ Stylesheet = proplists:get_value(Node, SSs),
Cover = proplists:get_value(Node, CoverFs),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
@@ -1274,16 +1351,20 @@ get_data_for_node(#testspec{label = Labels,
[CBF || {N,CBF} <- UsrCfgs, N==Node],
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
+ AutoCompile = proplists:get_value(Node, ACs),
Include = [I || {N,I} <- Incl, N==Node],
#opts{label = Label,
profile = Profile,
logdir = LogDir,
logopts = LogOpts,
+ basic_html = BasicHtml,
+ stylesheet = Stylesheet,
cover = Cover,
config = ConfigFiles,
event_handlers = EvHandlers,
ct_hooks = FiltCTHooks,
enable_builtin_hooks = EnableBuiltinHooks,
+ auto_compile = AutoCompile,
include = Include,
multiply_timetraps = MT,
scale_timetraps = ST,
@@ -1606,23 +1687,29 @@ verify_suites(TestSuites) ->
{[DS|Found],NotFound};
true ->
Beam = filename:join(TestDir,
- atom_to_list(Suite)++".beam"),
+ atom_to_list(Suite)++
+ ".beam"),
case filelib:is_regular(Beam) of
true ->
{[DS|Found],NotFound};
false ->
case code:is_loaded(Suite) of
{file,SuiteFile} ->
- %% test suite is already loaded and
- %% since auto_compile == false,
+ %% test suite is already
+ %% loaded and since
+ %% auto_compile == false,
%% let's assume the user has
- %% loaded the beam file explicitly
- ActualDir = filename:dirname(SuiteFile),
- {[{ActualDir,Suite}|Found],NotFound};
+ %% loaded the beam file
+ %% explicitly
+ ActualDir =
+ filename:dirname(SuiteFile),
+ {[{ActualDir,Suite}|Found],
+ NotFound};
false ->
Name =
filename:join(TestDir,
- atom_to_list(Suite)),
+ atom_to_list(
+ Suite)),
io:format(user,
"Suite ~w not found"
"in directory ~s~n",
@@ -1640,7 +1727,8 @@ verify_suites(TestSuites) ->
ActualDir = filename:dirname(SuiteFile),
{[{ActualDir,Suite}|Found],NotFound};
false ->
- io:format(user, "Directory ~s is invalid~n", [Dir]),
+ io:format(user, "Directory ~s is invalid~n",
+ [Dir]),
Name = filename:join(Dir, atom_to_list(Suite)),
{Found,[{DS,[Name]}|NotFound]}
end
@@ -1675,8 +1763,9 @@ step(TestDir, Suite, Case) ->
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:step/4
-step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case),
- Suite =/= all, Case =/= all ->
+step(TestDir, Suite, Case, Opts) when is_list(TestDir),
+ is_atom(Suite), is_atom(Case),
+ Suite =/= all, Case =/= all ->
do_run([{TestDir,Suite,Case}], [{step,Opts}]).
@@ -1828,7 +1917,8 @@ set_group_leader_same_as_shell() ->
end
end,
case [P || P <- processes(), GS2or3(P),
- true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of
+ true == lists:keymember(shell,1,
+ element(2,process_info(P,dictionary)))] of
[GL|_] ->
group_leader(GL, self());
[] ->
@@ -1874,12 +1964,14 @@ do_run_test(Tests, Skip, Opts) ->
incl_mods = CovIncl,
cross = CovCross,
src = _CovSrc}} ->
- ct_logs:log("COVER INFO","Using cover specification file: ~s~n"
+ ct_logs:log("COVER INFO",
+ "Using cover specification file: ~s~n"
"App: ~w~n"
"Cross cover: ~w~n"
"Including ~w modules~n"
"Excluding ~w modules",
- [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]),
+ [CovFile,CovApp,CovCross,length(CovIncl),
+ length(CovExcl)]),
%% cover export file will be used for export and import
%% between tests so make sure it doesn't exist initially
@@ -1887,7 +1979,8 @@ do_run_test(Tests, Skip, Opts) ->
true ->
DelResult = file:delete(CovExport),
ct_logs:log("COVER INFO",
- "Warning! Export file ~s already exists. "
+ "Warning! "
+ "Export file ~s already exists. "
"Deleting with result: ~p",
[CovExport,DelResult]);
false ->
@@ -2561,7 +2654,7 @@ opts2args(EnvStartOpts) ->
({decrypt,{file,File}}) ->
[{ct_decrypt_file,[File]}];
({basic_html,true}) ->
- ({basic_html,[]});
+ [{basic_html,[]}];
({basic_html,false}) ->
[];
({event_handler,EH}) when is_atom(EH) ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 4c05f57520..cbd7dc1f35 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -29,6 +29,8 @@
-include("ct_util.hrl").
+-define(testspec_fields, record_info(fields, testspec)).
+
%%%------------------------------------------------------------------
%%% NOTE:
%%% Multiple testspecs may be used as input with the result that
@@ -46,7 +48,8 @@
%%% Version 1 - extract and return all tests and skips for Node
%%% (incl all_nodes)
%%%-------------------------------------------------------------------
-prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), is_atom(Node) ->
+prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec),
+ is_atom(Node) ->
case lists:keysearch(Node,1,prepare_tests(TestSpec)) of
{value,{Node,Run,Skip}} ->
{Run,Skip};
@@ -249,22 +252,23 @@ collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) ->
SpecDir = filename:dirname(filename:absname(Spec)),
case file:consult(Spec) of
{ok,Terms} ->
- TestSpec1 = collect_tests(Terms,
- TestSpec#testspec{spec_dir=SpecDir},
- Relaxed),
- collect_tests_from_file1(Specs,TestSpec1,Relaxed);
+ case collect_tests(Terms,
+ TestSpec#testspec{spec_dir=SpecDir},
+ Relaxed) of
+ TS = #testspec{tests=Tests, logdir=LogDirs} when Specs == [] ->
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1};
+ TS = #testspec{alias = As, nodes = Ns} ->
+ TS1 = TS#testspec{alias = lists:reverse(As),
+ nodes = lists:reverse(Ns)},
+ collect_tests_from_file1(Specs,TS1,Relaxed)
+ end;
{error,Reason} ->
ReasonStr =
lists:flatten(io_lib:format("~s",
[file:format_error(Reason)])),
throw({error,{Spec,ReasonStr}})
- end;
-collect_tests_from_file1([],TS=#testspec{config=Cfgs,event_handler=EvHs,
- include=Incl,tests=Tests},_) ->
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}.
+ end.
collect_tests_from_list(Terms,Relaxed) ->
collect_tests_from_list(Terms,[node()],Relaxed).
@@ -278,30 +282,163 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) ->
E = {error,_} ->
E;
TS ->
- #testspec{config=Cfgs,event_handler=EvHs,include=Incl,tests=Tests} = TS,
- TS#testspec{config=lists:reverse(Cfgs),
- event_handler=lists:reverse(EvHs),
- include=lists:reverse(Incl),
- tests=lists:flatten(Tests)}
+ #testspec{tests=Tests, logdir=LogDirs} = TS,
+ LogDirs1 = lists:delete(".",LogDirs) ++ ["."],
+ TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}
end.
collect_tests(Terms,TestSpec,Relaxed) ->
put(relaxed,Relaxed),
- TestSpec1 = get_global(Terms,TestSpec),
- TestSpec2 = get_all_nodes(Terms,TestSpec1),
- {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2),
+ Terms1 = replace_names(Terms),
+ TestSpec1 = get_global(Terms1,TestSpec),
+ TestSpec2 = get_all_nodes(Terms1,TestSpec1),
+ {Terms2, TestSpec3} = filter_init_terms(Terms1, [], TestSpec2),
add_tests(Terms2,TestSpec3).
-get_global([{merge_tests, Bool} | Ts], Spec) ->
- get_global(Ts,Spec#testspec{ merge_tests = Bool });
+%% replace names (atoms) in the testspec matching those in 'define' terms by
+%% searching recursively through tuples and lists
+replace_names(Terms) ->
+ Defs =
+ lists:flatmap(fun(Def={define,Name,_Replacement}) ->
+ %% check that name follows convention
+ if not is_atom(Name) ->
+ throw({illegal_name_in_testspec,Name});
+ true ->
+ [First|_] = atom_to_list(Name),
+ if ((First == $?) or (First == $$)
+ or (First == $_)
+ or ((First >= $A)
+ and (First =< $Z))) ->
+ [Def];
+ true ->
+ throw({illegal_name_in_testspec,
+ Name})
+ end
+ end;
+ (_) -> []
+ end, Terms),
+ DefProps = replace_names_in_defs(Defs,[]),
+ replace_names(Terms,[],DefProps).
+
+replace_names_in_defs([Def|Left],ModDefs) ->
+ [{define,Name,Replacement}] = replace_names([Def],[],ModDefs),
+ replace_names_in_defs(Left,[{Name,Replacement}|ModDefs]);
+replace_names_in_defs([],ModDefs) ->
+ ModDefs.
+
+replace_names([Term|Ts],Modified,Defs) when is_tuple(Term) ->
+ [TypeTag|Data] = tuple_to_list(Term),
+ Term1 = list_to_tuple([TypeTag|replace_names_in_elems(Data,[],Defs)]),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) when is_atom(Term) ->
+ case proplists:get_value(Term,Defs) of
+ undefined ->
+ replace_names(Ts,[Term|Modified],Defs);
+ Replacement ->
+ replace_names(Ts,[Replacement|Modified],Defs)
+ end;
+replace_names([Term=[Ch|_]|Ts],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ Term1 = replace_names_in_string(Term,Defs),
+ replace_names(Ts,[Term1|Modified],Defs);
+replace_names([Term|Ts],Modified,Defs) ->
+ replace_names(Ts,[Term|Modified],Defs);
+replace_names([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_tuple(Elem) ->
+ Elem1 = list_to_tuple(replace_names_in_elems(tuple_to_list(Elem),[],Defs)),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_atom(Elem) ->
+ case proplists:get_value(Elem,Defs) of
+ undefined ->
+ %% if Term is a node name, check it for replacements as well
+ Elem1 = replace_names_in_node(Elem,Defs),
+ replace_names_in_elems(Es,[Elem1|Modified],Defs);
+ Replacement ->
+ replace_names_in_elems(Es,[Replacement|Modified],Defs)
+ end;
+replace_names_in_elems([Elem=[Ch|_]|Es],Modified,Defs) when is_integer(Ch) ->
+ %% Term *could* be a string, attempt to search through it
+ case replace_names_in_string(Elem,Defs) of
+ Elem ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+ Elem1 ->
+ replace_names_in_elems(Es,[Elem1|Modified],Defs)
+ end;
+replace_names_in_elems([Elem|Es],Modified,Defs) when is_list(Elem) ->
+ List = replace_names_in_elems(Elem,[],Defs),
+ replace_names_in_elems(Es,[List|Modified],Defs);
+replace_names_in_elems([Elem|Es],Modified,Defs) ->
+ replace_names_in_elems(Es,[Elem|Modified],Defs);
+replace_names_in_elems([],Modified,_Defs) ->
+ lists:reverse(Modified).
+
+replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds])
+ when is_integer(Ch) ->
+ try re:replace(Term,[$'|atom_to_list(Name)]++"'",
+ Replacement,[{return,list}]) of
+ Term -> % no match, proceed
+ replace_names_in_string(Term,Ds);
+ Term1 ->
+ replace_names_in_string(Term1,Defs)
+ catch
+ _:_ -> Term % Term is not a string
+ end;
+replace_names_in_string(Term,[_|Ds]) ->
+ replace_names_in_string(Term,Ds);
+replace_names_in_string(Term,[]) ->
+ Term.
+
+replace_names_in_node(Node,Defs) ->
+ String = atom_to_list(Node),
+ case lists:member($@,String) of
+ true ->
+ list_to_atom(replace_names_in_node1(String,Defs));
+ false ->
+ Node
+ end.
+
+replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) ->
+ ReplStr = case Replacement of
+ [Ch|_] when is_integer(Ch) -> Replacement;
+ _ when is_atom(Replacement) -> atom_to_list(Replacement);
+ _ -> false
+ end,
+ if ReplStr == false ->
+ replace_names_in_node1(NodeStr,Ds);
+ true ->
+ case re:replace(NodeStr,atom_to_list(Name),
+ ReplStr,[{return,list}]) of
+ NodeStr -> % no match, proceed
+ replace_names_in_node1(NodeStr,Ds);
+ NodeStr1 ->
+ replace_names_in_node1(NodeStr1,Defs)
+ end
+ end;
+replace_names_in_node1(NodeStr,[]) ->
+ NodeStr.
+
+
+%% global terms that will be used for analysing all other terms in the spec
+get_global([{merge_tests,Bool} | Ts], Spec) ->
+ get_global(Ts,Spec#testspec{merge_tests=Bool});
+
+%% the 'define' term replaces the 'alias' and 'node' terms, but we need to keep
+%% the latter two for backwards compatibility...
get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) ->
get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]});
get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) ->
- get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]});
-get_global([_|Ts],Spec) -> get_global(Ts,Spec);
-get_global([],Spec) -> Spec.
+ get_global(Ts,Spec#testspec{nodes=[{Ref,Node} |
+ lists:keydelete(Node,2,Refs)]});
-get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) ->
+get_global([_|Ts],Spec) ->
+ get_global(Ts,Spec);
+get_global([],Spec=#testspec{nodes=Ns, alias=As}) ->
+ Spec#testspec{nodes=lists:reverse(Ns), alias=lists:reverse(As)}.
+
+get_absfile(Callback,FullName,#testspec{spec_dir=SpecDir}) ->
% we need to temporary switch to new cwd here, because
% otherwise config files cannot be found
{ok, OldWd} = file:get_cwd(),
@@ -329,29 +466,45 @@ get_absfile(FullName,#testspec{spec_dir=SpecDir}) ->
get_absdir(Dir,#testspec{spec_dir=SpecDir}) ->
get_absname(Dir,SpecDir).
-get_absname(TestDir,SpecDir) ->
- AbsName = filename:absname(TestDir,SpecDir),
- TestDirName = filename:basename(AbsName),
- Path = filename:dirname(AbsName),
- TopDir = filename:basename(Path),
- Path1 =
- case TopDir of
- "." ->
- [_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- ".." ->
- [_,_|Rev] = lists:reverse(filename:split(Path)),
- filename:join(lists:reverse(Rev));
- _ ->
- Path
- end,
- filename:join(Path1,TestDirName).
+get_absname(Dir,SpecDir) ->
+ AbsName = filename:absname(Dir,SpecDir),
+ shorten_path(AbsName,SpecDir).
+
+shorten_path(Path,SpecDir) ->
+ case shorten_split_path(filename:split(Path),[]) of
+ [] ->
+ [Root|_] = filename:split(SpecDir),
+ Root;
+ Short ->
+ filename:join(Short)
+ end.
+
+shorten_split_path([".."|Path],SoFar) ->
+ shorten_split_path(Path,tl(SoFar));
+shorten_split_path(["."|Path],SoFar) ->
+ shorten_split_path(Path,SoFar);
+shorten_split_path([Dir|Path],SoFar) ->
+ shorten_split_path(Path,[Dir|SoFar]);
+shorten_split_path([],SoFar) ->
+ lists:reverse(SoFar).
%% go through all tests and register all nodes found
get_all_nodes([{suites,Nodes,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{suites,Node,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,[Char|_],_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{groups,_,_,_,{cases,_}}|Ts],Spec) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{groups,Node,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{cases,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{cases,Node,_,_,_}|Ts],Spec) ->
@@ -360,74 +513,93 @@ get_all_nodes([{skip_suites,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_suites,Node,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,[Char|_],_,_,_,_}|Ts],Spec) when is_integer(Char) ->
+ get_all_nodes(Ts,Spec);
+get_all_nodes([{skip_groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
+get_all_nodes([{skip_groups,Nodes,_,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
+ get_all_nodes(Ts,save_nodes(Nodes,Spec));
+get_all_nodes([{skip_groups,Node,_,_,_,_,_}|Ts],Spec) ->
+ get_all_nodes(Ts,save_nodes([Node],Spec));
get_all_nodes([{skip_cases,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) ->
get_all_nodes(Ts,save_nodes(Nodes,Spec));
get_all_nodes([{skip_cases,Node,_,_,_,_}|Ts],Spec) ->
get_all_nodes(Ts,save_nodes([Node],Spec));
-get_all_nodes([_|Ts],Spec) ->
+get_all_nodes([_Other|Ts],Spec) ->
get_all_nodes(Ts,Spec);
get_all_nodes([],Spec) ->
Spec.
-filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)->
- filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec);
-filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec)
- when is_atom(NodeRef)->
- filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec);
-filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) ->
- filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec);
-filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})->
- NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of
- {node_start, NSOptions}->
- case lists:keyfind(callback_module, 1, NSOptions) of
- {callback_module, _Callback}->
- NSOptions;
- false->
- [{callback_module, ct_slave}|NSOptions]
- end;
- false->
- []
- end,
- EvalTerms = case lists:keyfind(eval, 1, InitOptions) of
- {eval, MFA} when is_tuple(MFA)->
- [MFA];
- {eval, MFAs} when is_list(MFAs)->
- MFAs;
- false->
- []
- end,
+filter_init_terms([{init,InitOptions}|Ts],NewTerms,Spec) ->
+ filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts],
+ NewTerms,Spec);
+filter_init_terms([{init,all_nodes,InitOptions}|Ts],NewTerms,Spec) ->
+ filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts],
+ NewTerms,Spec);
+filter_init_terms([{init,NodeRef,InitOptions}|Ts],
+ NewTerms,Spec) when is_atom(NodeRef) ->
+ filter_init_terms([{init,[NodeRef],InitOptions}|Ts],NewTerms,Spec);
+filter_init_terms([{init,NodeRefs,InitOption}|Ts],
+ NewTerms,Spec) when is_tuple(InitOption) ->
+ filter_init_terms([{init,NodeRefs,[InitOption]}|Ts],NewTerms,Spec);
+filter_init_terms([{init,[NodeRef|NodeRefs],InitOptions}|Ts],
+ NewTerms,Spec=#testspec{init=InitData}) ->
+ NodeStartOptions =
+ case lists:keyfind(node_start,1,InitOptions) of
+ {node_start,NSOptions}->
+ case lists:keyfind(callback_module,1,NSOptions) of
+ {callback_module,_Callback}->
+ NSOptions;
+ false->
+ [{callback_module,ct_slave}|NSOptions]
+ end;
+ false->
+ []
+ end,
+ EvalTerms = case lists:keyfind(eval,1,InitOptions) of
+ {eval,MFA} when is_tuple(MFA) ->
+ [MFA];
+ {eval,MFAs} when is_list(MFAs) ->
+ MFAs;
+ false->
+ []
+ end,
Node = ref2node(NodeRef,Spec#testspec.nodes),
- InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true),
- InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false),
- filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3});
-filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)->
- filter_init_terms(Ts, NewTerms, Spec);
-filter_init_terms([Term|Ts], NewTerms, Spec)->
- filter_init_terms(Ts, [Term|NewTerms], Spec);
-filter_init_terms([], NewTerms, Spec)->
- {lists:reverse(NewTerms), Spec}.
-
-add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)->
- OldOptions = case lists:keyfind(Node, 1, List) of
- {Node, Options}->
+ InitData2 = add_option({node_start,NodeStartOptions},Node,InitData,true),
+ InitData3 = add_option({eval,EvalTerms},Node,InitData2,false),
+ filter_init_terms([{init,NodeRefs,InitOptions}|Ts],
+ NewTerms,Spec#testspec{init=InitData3});
+filter_init_terms([{init,[],_}|Ts],NewTerms,Spec) ->
+ filter_init_terms(Ts,NewTerms,Spec);
+filter_init_terms([Term|Ts],NewTerms,Spec) ->
+ filter_init_terms(Ts,[Term|NewTerms],Spec);
+filter_init_terms([],NewTerms,Spec) ->
+ {lists:reverse(NewTerms),Spec}.
+
+add_option({Key,Value},Node,List,WarnIfExists) when is_list(Value) ->
+ OldOptions = case lists:keyfind(Node,1,List) of
+ {Node,Options}->
Options;
false->
[]
end,
- NewOption = case lists:keyfind(Key, 1, OldOptions) of
- {Key, OldOption} when WarnIfExists, OldOption/=[]->
- io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n",
- [Key, OldOption, Node, Value]),
+ NewOption = case lists:keyfind(Key,1,OldOptions) of
+ {Key,OldOption} when WarnIfExists,OldOption/=[]->
+ io:format("There is an option ~w=~w already "
+ "defined for node ~p, skipping new ~w~n",
+ [Key,OldOption,Node,Value]),
OldOption;
- {Key, OldOption}->
+ {Key,OldOption}->
OldOption ++ Value;
false->
Value
end,
- lists:keystore(Node, 1, List,
- {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})});
-add_option({Key, Value}, Node, List, WarnIfExists)->
- add_option({Key, [Value]}, Node, List, WarnIfExists).
+ lists:keystore(Node,1,List,
+ {Node,lists:keystore(Key,1,OldOptions,{Key,NewOption})});
+add_option({Key,Value},Node,List,WarnIfExists) ->
+ add_option({Key,[Value]},Node,List,WarnIfExists).
save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
NodeRefs1 =
@@ -446,267 +618,18 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
end
end
end,NodeRefs,Nodes),
- Spec#testspec{nodes=NodeRefs1}.
+ Spec#testspec{nodes=NodeRefs1}.
list_nodes(#testspec{nodes=NodeRefs}) ->
lists:map(fun({_Ref,Node}) -> Node end, NodeRefs).
-
-%% ---------------------------------------------------------
-%% / \
-%% | When adding tests, remember to update valid_terms/0 also! |
-%% \ /
-%% ---------------------------------------------------------
-
-
-%% Associate a "global" logdir with all nodes
-%% except those with specific logdir, e.g:
-%% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}]
-%% means all nodes should write to /tmp/logdir
-%% except ct1@finwe that should use /tmp/logdir2.
-
-%% --- logdir ---
-add_tests([{logdir,all_nodes,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Tests = [{logdir,N,get_absdir(Dir,Spec)} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Dirs) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logdir,Node,Dir}|Ts],Spec) ->
- Dirs = Spec#testspec.logdir,
- Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)],
- add_tests(Ts,Spec#testspec{logdir=Dirs1});
-add_tests([{logdir,Dir}|Ts],Spec) ->
- add_tests([{logdir,all_nodes,Dir}|Ts],Spec);
-
-%% --- logopts ---
-add_tests([{logopts,all_nodes,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- Tests = [{logopts,N,Opts} ||
- N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),1,
- LogOpts) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{logopts,Nodes,Opts}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,logopts,[Opts],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{logopts,Node,Opts}|Ts],Spec) ->
- LogOpts = Spec#testspec.logopts,
- LogOpts1 = [{ref2node(Node,Spec#testspec.nodes),Opts} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),
- 1,LogOpts)],
- add_tests(Ts,Spec#testspec{logopts=LogOpts1});
-add_tests([{logopts,Opts}|Ts],Spec) ->
- add_tests([{logopts,all_nodes,Opts}|Ts],Spec);
-
-%% --- label ---
-add_tests([{label,all_nodes,Lbl}|Ts],Spec) ->
- Labels = Spec#testspec.label,
- Tests = [{label,N,Lbl} || N <- list_nodes(Spec),
- lists:keymember(ref2node(N,Spec#testspec.nodes),
- 1,Labels) == false],
- add_tests(Tests++Ts,Spec);
-add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{label,Node,Lbl}|Ts],Spec) ->
- Labels = Spec#testspec.label,
- Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)],
- add_tests(Ts,Spec#testspec{label=Labels1});
-add_tests([{label,Lbl}|Ts],Spec) ->
- add_tests([{label,all_nodes,Lbl}|Ts],Spec);
-
-%% --- cover ---
-add_tests([{cover,all_nodes,File}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,cover,[File],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{cover,Node,File}|Ts],Spec) ->
- CoverFs = Spec#testspec.cover,
- CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)],
- add_tests(Ts,Spec#testspec{cover=CoverFs1});
-add_tests([{cover,File}|Ts],Spec) ->
- add_tests([{cover,all_nodes,File}|Ts],Spec);
-
-%% --- multiply_timetraps ---
-add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {multiply_timetraps,N,MT} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{multiply_timetraps,Nodes,MT}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,multiply_timetraps,[MT],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{multiply_timetraps,Node,MT}|Ts],Spec) ->
- MTs = Spec#testspec.multiply_timetraps,
- MTs1 = [{ref2node(Node,Spec#testspec.nodes),MT} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,MTs)],
- add_tests(Ts,Spec#testspec{multiply_timetraps=MTs1});
-add_tests([{multiply_timetraps,MT}|Ts],Spec) ->
- add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec);
-
-%% --- scale_timetraps ---
-add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {scale_timetraps,N,ST} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{scale_timetraps,Nodes,ST}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,scale_timetraps,[ST],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{scale_timetraps,Node,ST}|Ts],Spec) ->
- STs = Spec#testspec.scale_timetraps,
- STs1 = [{ref2node(Node,Spec#testspec.nodes),ST} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,STs)],
- add_tests(Ts,Spec#testspec{scale_timetraps=STs1});
-add_tests([{scale_timetraps,ST}|Ts],Spec) ->
- add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec);
-
-%% --- create_priv_dir ---
-add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{create_priv_dir,Node,PD}|Ts],Spec) ->
- PDs = Spec#testspec.create_priv_dir,
- PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} |
- lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)],
- add_tests(Ts,Spec#testspec{create_priv_dir=PDs1});
-add_tests([{create_priv_dir,PD}|Ts],Spec) ->
- add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec);
-
-%% --- config ---
-add_tests([{config,all_nodes,Files}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,config,[Files],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) ->
- Cfgs = Spec#testspec.config,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{config,Node,Fs}|Ts],
- Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]});
-add_tests([{config,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{config,Node,F}|Ts],Spec) ->
- add_tests([{config,Node,[F]}|Ts],Spec);
-add_tests([{config,Files}|Ts],Spec) ->
- add_tests([{config,all_nodes,Files}|Ts],Spec);
-
-
-%% --- userconfig ---
-add_tests([{userconfig,all_nodes,CBF}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {userconfig,N,CBF} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{userconfig,Nodes,CBF}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,userconfig,[CBF],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{userconfig,Node,[{Callback, Config}|CBF]}|Ts],Spec) ->
- Cfgs = Spec#testspec.userconfig,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{userconfig,Node,CBF}|Ts],
- Spec#testspec{userconfig=[{Node1,{Callback,
- get_absfile(Callback, Config ,Spec)}}|Cfgs]});
-add_tests([{userconfig,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{userconfig,Node,CBF}|Ts],Spec) ->
- add_tests([{userconfig,Node,[CBF]}|Ts],Spec);
-add_tests([{userconfig,CBF}|Ts],Spec) ->
- add_tests([{userconfig,all_nodes,CBF}|Ts],Spec);
-
-%% --- event_handler ---
-add_tests([{event_handler,all_nodes,Hs}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {event_handler,N,Hs,[]} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,all_nodes,Hs,Args}|Ts],Spec) when is_list(Args) ->
- Tests = lists:map(fun(N) -> {event_handler,N,Hs,Args} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{event_handler,Hs}|Ts],Spec) ->
- add_tests([{event_handler,all_nodes,Hs,[]}|Ts],Spec);
-add_tests([{event_handler,HsOrNodes,HsOrArgs}|Ts],Spec) ->
- case is_noderef(HsOrNodes,Spec#testspec.nodes) of
- true -> % HsOrNodes == Nodes, HsOrArgs == Hs
- case {HsOrNodes,HsOrArgs} of
- {Nodes,Hs} when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,[]],Ts,
- Spec#testspec.nodes),
- add_tests(Ts1,Spec);
- {_Node,[]} ->
- add_tests(Ts,Spec);
- {Node,HOrHs} ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- case HOrHs of
- [H|Hs] when is_atom(H) ->
- add_tests([{event_handler,Node,Hs}|Ts],
- Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]});
- H when is_atom(H) ->
- add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]})
- end
- end;
- false -> % HsOrNodes == Hs, HsOrArgs == Args
- add_tests([{event_handler,all_nodes,HsOrNodes,HsOrArgs}|Ts],Spec)
- end;
-add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,event_handler,[Hs,Args],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{event_handler,Node,[H|Hs],Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{event_handler,Node,Hs,Args}|Ts],
- Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
-add_tests([{event_handler,_Node,[],_Args}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) ->
- EvHs = Spec#testspec.event_handler,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
-
-%% --- ct_hooks --
-add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) ->
- Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)],
- add_tests(Tests ++ Ts, Spec);
-add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) ->
- SuiteCbs = Spec#testspec.ct_hooks,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{ct_hooks, Node, Hooks} | Ts],
- Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]});
-add_tests([{ct_hooks, _Node, []}|Ts], Spec) ->
- add_tests(Ts, Spec);
-add_tests([{ct_hooks, Hooks}|Ts], Spec) ->
- add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec);
-
-%% -- enable_builtin_hooks --
-add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
- add_tests(Ts, Spec#testspec{ enable_builtin_hooks = Bool });
-
-%% --- include ---
-add_tests([{include,all_nodes,InclDirs}|Ts],Spec) ->
- Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)),
- add_tests(Tests++Ts,Spec);
-add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,include,[InclDirs],Ts,Spec#testspec.nodes),
- add_tests(Ts1,Spec);
-add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) ->
- Dirs = Spec#testspec.include,
- Node1 = ref2node(Node,Spec#testspec.nodes),
- add_tests([{include,Node,Ds}|Ts],
- Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]});
-add_tests([{include,_Node,[]}|Ts],Spec) ->
- add_tests(Ts,Spec);
-add_tests([{include,Node,D}|Ts],Spec) ->
- add_tests([{include,Node,[D]}|Ts],Spec);
-add_tests([{include,InclDirs}|Ts],Spec) ->
- add_tests([{include,all_nodes,InclDirs}|Ts],Spec);
+%% -----------------------------------------------------
+%% / \
+%% | When adding test/config terms, remember to update |
+%% | valid_terms/0 also! |
+%% \ /
+%% -----------------------------------------------------
%% --- suites ---
add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) ->
@@ -719,7 +642,7 @@ add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) ->
add_tests([{suites,Node,Dir,Ss}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_suites(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Ss,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -739,20 +662,22 @@ add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) ->
Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
-add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,Spec#testspec.nodes),
+add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],
+ Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,
+ Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,all,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
add_tests([{groups,Node,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -768,7 +693,7 @@ add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) ->
add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Cs,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -783,7 +708,7 @@ add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) ->
add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_suites(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Ss,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -792,7 +717,8 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,Cmt}|Ts],Spec);
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
- add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec);
+ add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],
+ Spec);
add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec);
add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
@@ -800,20 +726,22 @@ add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) ->
Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes),
add_tests(Ts1,Spec);
-add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) when is_list(Nodes) ->
- Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,Spec#testspec.nodes),
+add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],
+ Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,
+ Spec#testspec.nodes),
add_tests(Ts1,Spec);
add_tests([{skip_groups,Node,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,all,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
add_tests([{skip_groups,Node,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Gs,TCs,Cmt,Tests,
Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
@@ -829,45 +757,101 @@ add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) ->
add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_cases(ref2node(Node,Spec#testspec.nodes),
- ref2dir(Dir,Spec#testspec.alias),
+ ref2dir(Dir,Spec),
Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
+%% --- various configuration terms ---
+add_tests([{config,Nodes,CfgDir,Files}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{config,Nodes,{CfgDir,Files}}|Ts],Spec);
+add_tests([{config,Node,CfgDir,FileOrFiles}|Ts],Spec) ->
+ add_tests([{config,Node,{CfgDir,FileOrFiles}}|Ts],Spec);
+add_tests([{config,CfgDir=[Ch|_],Files}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{config,all_nodes,{CfgDir,Files}}|Ts],Spec);
+
+add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes);
+ Nodes == all_nodes ->
+ add_tests([{event_handler,Nodes,{Hs,Args}}|Ts],Spec);
+add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) ->
+ add_tests([{event_handler,Node,{HOrHs,Args}}|Ts],Spec);
+
+add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool});
+
+add_tests([{noinput,Bool}|Ts],Spec) ->
+ add_tests(Ts, Spec#testspec{noinput = Bool});
+
%% --- handled/errors ---
+add_tests([{define,_,_}|Ts],Spec) -> % handled
+ add_tests(Ts,Spec);
+
add_tests([{alias,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
add_tests([{node,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
-add_tests([{merge_tests, _} | Ts], Spec) -> % handled
+add_tests([{merge_tests, _} | Ts], Spec) -> % handled
add_tests(Ts,Spec);
-%% check if it's a CT term that has bad format or if the user seems to
-%% have added something of his/her own, which we'll let pass if relaxed
-%% mode is enabled.
-add_tests([Other|Ts],Spec) when is_tuple(Other) ->
- [Name|_] = tuple_to_list(Other),
- case lists:keymember(Name,1,valid_terms()) of
- true -> % halt
- throw({error,{bad_term_in_spec,Other}});
- false -> % ignore
- case get(relaxed) of
- true ->
- %% warn if name resembles a CT term
- case resembles_ct_term(Name,size(Other)) of
- true ->
- io:format("~nSuspicious term, please check:~n"
- "~p~n", [Other]);
- false ->
- ok
- end,
- add_tests(Ts,Spec);
- false ->
- throw({error,{undefined_term_in_spec,Other}})
- end
+%% --------------------------------------------------
+%% / \
+%% | General add_tests/2 clauses below will work for |
+%% | most test spec configuration terms |
+%% \ /
+%% --------------------------------------------------
+
+%% create one test entry per known node and reinsert
+add_tests([Term={Tag,all_nodes,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ Tests = [{Tag,Node,Data} || Node <- list_nodes(Spec),
+ should_be_added(Tag,Node,Data,Spec)],
+ add_tests(Tests++Ts,Spec);
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
end;
-
+%% create one test entry per node in Nodes and reinsert
+add_tests([{Tag,[],Data}|Ts],Spec) ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+add_tests([{Tag,String=[Ch|_],Data}|Ts],Spec) when is_integer(Ch) ->
+ add_tests([{Tag,all_nodes,{String,Data}}|Ts],Spec);
+add_tests([{Tag,NodesOrOther,Data}|Ts],Spec) when is_list(NodesOrOther) ->
+ case lists:all(fun(Test) -> is_node(Test,Spec#testspec.nodes)
+ end, NodesOrOther) of
+ true ->
+ Ts1 = separate(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+ false ->
+ add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec)
+ end;
+%% update data for testspec term of type Tag
+add_tests([Term={Tag,NodeOrOther,Data}|Ts],Spec) ->
+ case is_node(NodeOrOther,Spec#testspec.nodes) of
+ true ->
+ case check_term(Term) of
+ valid ->
+ Node = ref2node(NodeOrOther,Spec#testspec.nodes),
+ NodeIxData =
+ update_recorded(Tag,Node,Spec) ++
+ handle_data(Tag,Node,Data,Spec),
+ add_tests(Ts,mod_field(Spec,Tag,NodeIxData));
+ invalid -> % ignore term
+ add_tests(Ts,Spec)
+ end;
+ false ->
+ add_tests([{Tag,all_nodes,{NodeOrOther,Data}}|Ts],Spec)
+ end;
+%% this test should be added for all known nodes
+add_tests([Term={Tag,Data}|Ts],Spec) ->
+ case check_term(Term) of
+ valid ->
+ add_tests([{Tag,all_nodes,Data}|Ts],Spec);
+ invalid ->
+ add_tests(Ts,Spec)
+ end;
+%% some other data than a tuple
add_tests([Other|Ts],Spec) ->
case get(relaxed) of
true ->
@@ -879,6 +863,105 @@ add_tests([Other|Ts],Spec) ->
add_tests([],Spec) -> % done
Spec.
+%% check if it's a CT term that has bad format or if the user seems to
+%% have added something of his/her own, which we'll let pass if relaxed
+%% mode is enabled.
+check_term(Term) ->
+ Size = size(Term),
+ [Name|_] = tuple_to_list(Term),
+ Valid = valid_terms(),
+ case lists:member({Name,Size},Valid) of
+ true ->
+ valid;
+ false ->
+ case lists:keymember(Name,1,Valid) of
+ true -> % halt
+ throw({error,{bad_term_in_spec,Term}});
+ false -> % ignore
+ case get(relaxed) of
+ true ->
+ %% warn if name resembles a CT term
+ case resembles_ct_term(Name,size(Term)) of
+ true ->
+ io:format("~nSuspicious term, "
+ "please check:~n"
+ "~p~n", [Term]),
+ invalid;
+ false ->
+ invalid
+ end;
+ false ->
+ throw({error,{undefined_term_in_spec,Term}})
+ end
+ end
+ end.
+
+%% specific data handling before saving in testspec record, e.g.
+%% converting relative paths to absolute for directories and files
+%% (introduce a clause *only* if the data value needs processing)
+handle_data(logdir,Node,Dir,Spec) ->
+ [{Node,ref2dir(Dir,Spec)}];
+handle_data(cover,Node,File,Spec) ->
+ [{Node,get_absfile(File,Spec)}];
+handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) ->
+ [{Node,ref2dir(Dir,Spec)} || Dir <- Dirs];
+handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(include,Node,[Dir],Spec);
+handle_data(config,Node,File=[Ch|_],Spec) when is_integer(Ch) ->
+ handle_data(config,Node,[File],Spec);
+handle_data(config,Node,{CfgDir,File=[Ch|_]},Spec) when is_integer(Ch) ->
+ handle_data(config,Node,{CfgDir,[File]},Spec);
+handle_data(config,Node,Files=[F|_],Spec) when is_list(F) ->
+ [{Node,get_absfile(File,Spec)} || File <- Files];
+handle_data(config,Node,{CfgDir,Files=[F|_]},Spec) when is_list(F) ->
+ [{Node,filename:join(ref2dir(CfgDir,Spec),File)} || File <- Files];
+handle_data(userconfig,Node,CBs,Spec) when is_list(CBs) ->
+ [{Node,{Callback,get_absfile(Callback,Config,Spec)}} ||
+ {Callback,Config} <- CBs];
+handle_data(userconfig,Node,CB,Spec) when is_tuple(CB) ->
+ handle_data(userconfig,Node,[CB],Spec);
+handle_data(event_handler,Node,H,Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],[]},Spec);
+handle_data(event_handler,Node,{H,Args},Spec) when is_atom(H) ->
+ handle_data(event_handler,Node,{[H],Args},Spec);
+handle_data(event_handler,Node,Hs,_Spec) when is_list(Hs) ->
+ [{Node,EvH,[]} || EvH <- Hs];
+handle_data(event_handler,Node,{Hs,Args},_Spec) when is_list(Hs) ->
+ [{Node,EvH,Args} || EvH <- Hs];
+handle_data(ct_hooks,Node,Hooks,_Spec) when is_list(Hooks) ->
+ [{Node,Hook} || Hook <- Hooks ];
+handle_data(ct_hooks,Node,Hook,_Spec) ->
+ [{Node,Hook}];
+handle_data(stylesheet,Node,CSSFile,Spec) ->
+ [{Node,get_absfile(CSSFile,Spec)}];
+handle_data(_Tag,Node,Data,_Spec) ->
+ [{Node,Data}].
+
+%% check if duplicates should be saved or not
+should_be_added(Tag,Node,_Data,Spec) ->
+ if
+ %% list terms *without* possible duplicates here
+ Tag == logdir; Tag == logopts;
+ Tag == basic_html; Tag == label;
+ Tag == auto_compile; Tag == stylesheet ->
+ lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
+ read_field(Spec,Tag)) == false;
+ %% for terms *with* possible duplicates
+ true ->
+ true
+ end.
+
+%% check if previous elements for Node should be deleted
+update_recorded(Tag,Node,Spec) ->
+ if Tag == config; Tag == userconfig; Tag == event_handler;
+ Tag == ct_hooks; Tag == include ->
+ read_field(Spec,Tag);
+ true ->
+ %% delete previous value for Tag
+ lists:keydelete(Node,1,read_field(Spec,Tag))
+ end.
+
+%% create one test term per node
separate(Nodes,Tag,Data,Tests,Refs) ->
Separated = separate(Nodes,Tag,Data,Refs),
Separated ++ Tests.
@@ -886,7 +969,25 @@ separate([N|Ns],Tag,Data,Refs) ->
[list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)];
separate([],_,_,_) ->
[].
-
+
+%% read the value for FieldName in record Rec#testspec
+read_field(Rec, FieldName) ->
+ catch lists:foldl(fun(F, Pos) when F == FieldName ->
+ throw(element(Pos, Rec));
+ (_,Pos) ->
+ Pos+1
+ end,2,?testspec_fields).
+
+%% modify the value for FieldName in record Rec#testspec
+mod_field(Rec, FieldName, NewVal) ->
+ [_testspec|RecList] = tuple_to_list(Rec),
+ RecList1 =
+ (catch lists:foldl(fun(F, {Prev,[_OldVal|Rest]}) when F == FieldName ->
+ throw(lists:reverse(Prev) ++ [NewVal|Rest]);
+ (_,{Prev,[Field|Rest]}) ->
+ {[Field|Prev],Rest}
+ end,{[],RecList},?testspec_fields)),
+ list_to_tuple([testspec|RecList1]).
%% Representation:
%% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]},
@@ -1094,33 +1195,40 @@ ref2node(all_nodes,_Refs) ->
ref2node(master,_Refs) ->
master;
ref2node(RefOrNode,Refs) ->
- case string:chr(atom_to_list(RefOrNode),$@) of
- 0 -> % a ref
+ case lists:member($@,atom_to_list(RefOrNode)) of
+ false -> % a ref
case lists:keysearch(RefOrNode,1,Refs) of
{value,{RefOrNode,Node}} ->
Node;
false ->
throw({error,{noderef_missing,RefOrNode}})
end;
- _ -> % a node
+ true -> % a node
RefOrNode
end.
-ref2dir(Ref,Refs) when is_atom(Ref) ->
+ref2dir(Ref,Spec) ->
+ ref2dir(Ref,Spec#testspec.alias,Spec).
+
+ref2dir(Ref,Refs,Spec) when is_atom(Ref) ->
case lists:keysearch(Ref,1,Refs) of
{value,{Ref,Dir}} ->
- Dir;
+ get_absdir(Dir,Spec);
false ->
throw({error,{alias_missing,Ref}})
end;
-ref2dir(Dir,_) when is_list(Dir) ->
- Dir.
-
-is_noderef(What,Nodes) when is_atom(What) ->
- is_noderef([What],Nodes);
-is_noderef([master|_],_Nodes) ->
+ref2dir(Dir,_,Spec) when is_list(Dir) ->
+ get_absdir(Dir,Spec);
+ref2dir(What,_,_) ->
+ throw({error,{invalid_directory_name,What}}).
+
+is_node(What,Nodes) when is_atom(What) ->
+ is_node([What],Nodes);
+is_node([master|_],_Nodes) ->
true;
-is_noderef([What|_],Nodes) ->
+is_node(What={N,H},Nodes) when is_atom(N), is_atom(H) ->
+ is_node([What],Nodes);
+is_node([What|_],Nodes) ->
case lists:keymember(What,1,Nodes) or
lists:keymember(What,2,Nodes) of
true ->
@@ -1128,24 +1236,28 @@ is_noderef([What|_],Nodes) ->
false ->
false
end;
-is_noderef([],_) ->
+is_node([],_) ->
false.
valid_terms() ->
[
+ {define,3},
{node,3},
{cover,2},
{cover,3},
{config,2},
{config,3},
+ {config,4},
{userconfig,2},
{userconfig,3},
{alias,3},
- {merge_tests,1},
+ {merge_tests,2},
{logdir,2},
{logdir,3},
{logopts,2},
{logopts,3},
+ {basic_html,2},
+ {basic_html,3},
{label,2},
{label,3},
{event_handler,2},
@@ -1153,13 +1265,18 @@ valid_terms() ->
{event_handler,4},
{ct_hooks,2},
{ct_hooks,3},
- {enable_builtin_hooks,1},
+ {enable_builtin_hooks,2},
+ {noinput,2},
{multiply_timetraps,2},
{multiply_timetraps,3},
{scale_timetraps,2},
{scale_timetraps,3},
{include,2},
{include,3},
+ {auto_compile,2},
+ {auto_compile,3},
+ {stylesheet,2},
+ {stylesheet,3},
{suites,3},
{suites,4},
{groups,4},
@@ -1174,7 +1291,8 @@ valid_terms() ->
{skip_groups,7},
{skip_cases,5},
{skip_cases,6},
- {create_priv_dir,2}
+ {create_priv_dir,2},
+ {create_priv_dir,3}
].
%% this function "guesses" if the user has misspelled a term name
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 31341c7b55..297d7c665a 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -34,13 +34,17 @@
profile=[],
logdir=["."],
logopts=[],
+ basic_html=[],
cover=[],
config=[],
userconfig=[],
event_handler=[],
ct_hooks=[],
enable_builtin_hooks=true,
+ noinput=false,
include=[],
+ auto_compile=[],
+ stylesheet=[],
multiply_timetraps=[],
scale_timetraps=[],
create_priv_dir=[],