aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src/ct_framework.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/ct_framework.erl')
-rw-r--r--lib/common_test/src/ct_framework.erl224
1 files changed, 162 insertions, 62 deletions
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index f2ca023cff..b61eda7152 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -24,7 +24,7 @@
-module(ct_framework).
--export([init_tc/3, end_tc/3, get_suite/2, report/2, warn/1]).
+-export([init_tc/3, end_tc/4, get_suite/2, report/2, warn/1]).
-export([error_notification/4]).
-export([overview_html_header/1]).
@@ -207,7 +207,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
{skip,{require_failed_in_suite0,Reason}};
{error,Reason} ->
{auto_skip,{require_failed,Reason}};
- FinalConfig ->
+ {ok, FinalConfig} ->
case MergeResult of
{error,Reason} ->
%% suite0 configure finished now, report that
@@ -216,13 +216,25 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
_ ->
case get('$test_server_framework_test') of
undefined ->
- FinalConfig;
+ ct_suite_init(Mod, FuncSpec, FinalConfig);
Fun ->
- Fun(init_tc, FinalConfig)
+ case Fun(init_tc, FinalConfig) of
+ NewConfig when is_list(NewConfig) ->
+ {ok,NewConfig};
+ Else ->
+ Else
+ end
end
end
end.
-
+
+ct_suite_init(Mod, Func, [Config]) when is_list(Config) ->
+ case ct_hooks:init_tc( Mod, Func, Config) of
+ NewConfig when is_list(NewConfig) ->
+ {ok, [NewConfig]};
+ Else ->
+ Else
+ end.
add_defaults(Mod,Func,FuncInfo,DoInit) ->
case (catch Mod:suite()) of
@@ -239,7 +251,9 @@ add_defaults(Mod,Func,FuncInfo,DoInit) ->
(_) -> false
end, SuiteInfo) of
true ->
- SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfo),
+ SuiteInfoNoCTH =
+ lists:keydelete(ct_hooks,1,SuiteInfo),
+ SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfoNoCTH),
case add_defaults1(Mod,Func,FuncInfo,SuiteInfo1,DoInit) of
Error = {error,_} -> {SuiteInfo1,Error};
MergedInfo -> {SuiteInfo1,MergedInfo}
@@ -362,6 +376,8 @@ configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) ->
configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) ->
Dog = test_server:timetrap(Time),
configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]);
+configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) ->
+ configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]);
configure([_|Rest],Info,SuiteInfo,Scope,Config) ->
configure(Rest,Info,SuiteInfo,Scope,Config);
configure([],_,_,_,Config) ->
@@ -418,14 +434,14 @@ try_set_default(Name,Key,Info,Where) ->
%%%
%%% @doc Test server framework callback, called by the test_server
%%% when a test case is finished.
-end_tc(?MODULE,error_in_suite,_) -> % bad start!
+end_tc(?MODULE,error_in_suite,_, _) -> % bad start!
ok;
-end_tc(Mod,Func,{TCPid,Result,[Args]}) when is_pid(TCPid) ->
- end_tc(Mod,Func,TCPid,Result,Args);
-end_tc(Mod,Func,{Result,[Args]}) ->
- end_tc(Mod,Func,self(),Result,Args).
+end_tc(Mod,Func,{TCPid,Result,[Args]}, Return) when is_pid(TCPid) ->
+ end_tc(Mod,Func,TCPid,Result,Args,Return);
+end_tc(Mod,Func,{Result,[Args]}, Return) ->
+ end_tc(Mod,Func,self(),Result,Args,Return).
-end_tc(Mod,Func,TCPid,Result,Args) ->
+end_tc(Mod,Func,TCPid,Result,Args,Return) ->
case lists:keysearch(watchdog,1,Args) of
{value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog);
false -> ok
@@ -448,8 +464,10 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
{_,GroupName,_Props} = Group ->
case lists:keysearch(save_config,1,Args) of
{value,{save_config,SaveConfig}} ->
- ct_util:save_suite_data(last_saved_config,
- {Mod,{group,GroupName}},SaveConfig),
+ ct_util:save_suite_data(
+ last_saved_config,
+ {Mod,{group,GroupName}},
+ SaveConfig),
Group;
false ->
Group
@@ -466,12 +484,33 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
end,
ct_util:reset_silent_connections(),
- %% send sync notification so that event handlers may print
- %% in the log file before it gets closed
- ct_event:sync_notify(#event{name=tc_done,
- node=node(),
- data={Mod,FuncSpec,tag(Result)}}),
- case Result of
+ case get('$test_server_framework_test') of
+ undefined ->
+ {FinalResult,FinalNotify} =
+ case ct_hooks:end_tc(
+ Mod, FuncSpec, Args, Result, Return) of
+ '$ct_no_change' ->
+ {FinalResult = ok,Result};
+ FinalResult ->
+ {FinalResult,FinalResult}
+ end,
+ % send sync notification so that event handlers may print
+ % in the log file before it gets closed
+ ct_event:sync_notify(#event{name=tc_done,
+ node=node(),
+ data={Mod,FuncSpec,
+ tag_cth(FinalNotify)}});
+ Fun ->
+ % send sync notification so that event handlers may print
+ % in the log file before it gets closed
+ ct_event:sync_notify(#event{name=tc_done,
+ node=node(),
+ data={Mod,FuncSpec,tag(Result)}}),
+ FinalResult = Fun(end_tc, Return)
+ end,
+
+
+ case FinalResult of
{skip,{sequence_failed,_,_}} ->
%% ct_logs:init_tc is never called for a skipped test case
%% in a failing sequence, so neither should end_tc
@@ -490,12 +529,7 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
_ ->
ok
end,
- case get('$test_server_framework_test') of
- undefined ->
- ok;
- Fun ->
- Fun(end_tc, ok)
- end.
+ FinalResult.
%% {error,Reason} | {skip,Reason} | {timetrap_timeout,TVal} |
%% {testcase_aborted,Reason} | testcase_aborted_or_killed |
@@ -511,6 +545,21 @@ tag(E = testcase_aborted_or_killed) ->
tag(Other) ->
Other.
+tag_cth({STag,Reason}) when STag == skip; STag == skipped ->
+ {skipped,Reason};
+tag_cth({fail, Reason}) ->
+ {failed, {error,Reason}};
+tag_cth(E = {ETag,_}) when ETag == error; ETag == 'EXIT';
+ ETag == timetrap_timeout;
+ ETag == testcase_aborted ->
+ {failed,E};
+tag_cth(E = testcase_aborted_or_killed) ->
+ {failed,E};
+tag_cth(List) when is_list(List) ->
+ ok;
+tag_cth(Other) ->
+ Other.
+
%%%-----------------------------------------------------------------
%%% @spec error_notification(Mod,Func,Args,Error) -> ok
%%% Mod = atom()
@@ -694,12 +743,12 @@ get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
%% init/end functions for top groups will be executed
case catch proplists:get_value(name, element(2, hd(ConfTests))) of
Name -> % top group
- ConfTests;
+ delete_subs(ConfTests, ConfTests);
_ ->
[]
end;
false ->
- ConfTests
+ delete_subs(ConfTests, ConfTests)
end
end;
_ ->
@@ -716,9 +765,25 @@ get_suite(Mod, Name) ->
find_groups(Mod, Name, TCs, GroupDefs) ->
Found = find(Mod, Name, TCs, GroupDefs, [], GroupDefs, false),
Trimmed = trim(Found),
- delete_subs(Trimmed, Trimmed).
-
-find(Mod, all, _TCs, [{Name,Props,Tests} | Gs], Known, Defs, _) ->
+ %% I cannot find a reason to why this function is called,
+ %% It deletes any group which is referenced in any other
+ %% group. i.e.
+ %% groups() ->
+ %% [{test, [], [testcase1]},
+ %% {testcases, [], [{group, test}]}].
+ %% Would be changed to
+ %% groups() ->
+ %% [{testcases, [], [testcase1]}].
+ %% instead of what I believe is correct:
+ %% groups() ->
+ %% [{test, [], [testcase1]},
+ %% {testcases, [], [testcase1]}].
+ %% Have to double check with peppe
+ delete_subs(Trimmed, Trimmed),
+ Trimmed.
+
+find(Mod, all, _TCs, [{Name,Props,Tests} | Gs], Known, Defs, _)
+ when is_atom(Name), is_list(Props), is_list(Tests) ->
cyclic_test(Mod, Name, Known),
[make_conf(Mod, Name, Props,
find(Mod, all, all, Tests, [Name | Known], Defs, true)) |
@@ -740,8 +805,8 @@ find(Mod, Name, TCs, [{Name,Props,Tests} | _Gs], Known, Defs, false)
find(Mod, Name, TCs, [{Name1,Props,Tests} | Gs], Known, Defs, false)
when is_atom(Name1), is_list(Props), is_list(Tests) ->
cyclic_test(Mod, Name1, Known),
- [make_conf(Mod, Name1, Props,
- find(Mod, Name, TCs, Tests, [Name1 | Known], Defs, false)) |
+ [make_conf(Mod,Name1,Props,
+ find(Mod, Name, TCs, Tests, [Name1 | Known], Defs, false)) |
find(Mod, Name, TCs, Gs, [], Defs, false)];
find(Mod, Name, _TCs, [{Name,_Props,_Tests} | _Gs], _Known, _Defs, true)
@@ -757,17 +822,31 @@ find(Mod, Name, all, [{Name1,Props,Tests} | Gs], Known, Defs, true)
find(Mod, Name, all, Tests, [Name1 | Known], Defs, true)) |
find(Mod, Name, all, Gs, [], Defs, true)];
-find(Mod, Name, TCs, [{group,Name1} | Gs], Known, Defs, Found) when is_atom(Name1) ->
+find(Mod, Name, TCs, [{group,Name1} | Gs], Known, Defs, Found)
+ when is_atom(Name1) ->
find(Mod, Name, TCs, [expand(Mod, Name1, Defs) | Gs], Known, Defs, Found);
+%% Undocumented remote group feature, use with caution
+find(Mod, Name, TCs, [{group, ExtMod, ExtGrp} | Gs], Known, Defs, true)
+ when is_atom(ExtMod), is_atom(ExtGrp) ->
+ ExternalDefs = ExtMod:groups(),
+ ExternalTCs = find(ExtMod, ExtGrp, TCs, [{group, ExtGrp}],
+ [], ExternalDefs, false),
+ ExternalTCs ++ find(Mod, Name, TCs, Gs, Known, Defs, true);
+
find(Mod, Name, TCs, [{Name1,Tests} | Gs], Known, Defs, Found)
when is_atom(Name1), is_list(Tests) ->
find(Mod, Name, TCs, [{Name1,[],Tests} | Gs], Known, Defs, Found);
-find(Mod, Name, TCs, [TC | Gs], Known, Defs, false) when is_atom(TC) ->
+find(Mod, Name, TCs, [_TC | Gs], Known, Defs, false) ->
find(Mod, Name, TCs, Gs, Known, Defs, false);
find(Mod, Name, TCs, [TC | Gs], Known, Defs, true) when is_atom(TC) ->
+ [{Mod, TC} | find(Mod, Name, TCs, Gs, Known, Defs, true)];
+
+find(Mod, Name, TCs, [{ExternalTC, Case} = TC | Gs], Known, Defs, true)
+ when is_atom(ExternalTC),
+ is_atom(Case) ->
[TC | find(Mod, Name, TCs, Gs, Known, Defs, true)];
find(Mod, _Name, _TCs, [BadTerm | _Gs], Known, _Defs, _Found) ->
@@ -787,7 +866,7 @@ find(_Mod, _Name, _TCs, [], _Known, _Defs, false) ->
find(_Mod, _Name, _TCs, [], _Known, _Defs, _Found) ->
[].
-delete_subs([Conf | Confs], All) ->
+delete_subs([{conf, _,_,_,_} = Conf | Confs], All) ->
All1 = delete_conf(Conf, All),
case is_sub(Conf, All1) of
true ->
@@ -795,7 +874,8 @@ delete_subs([Conf | Confs], All) ->
false ->
delete_subs(Confs, All)
end;
-
+delete_subs([_Else | Confs], All) ->
+ delete_subs(Confs, All);
delete_subs([], All) ->
All.
@@ -887,7 +967,9 @@ make_all_conf(Mod) ->
[] ->
{error,{invalid_group_spec,Mod}};
ConfTests ->
- [{conf,Props,Init,all,End} || {conf,Props,Init,_,End} <- ConfTests]
+ [{conf,Props,Init,all,End} ||
+ {conf,Props,Init,_,End}
+ <- delete_subs(ConfTests, ConfTests)]
end
end.
@@ -933,31 +1015,11 @@ get_all(Mod, ConfTests) ->
[{?MODULE,error_in_suite,[[{error,What}]]}];
SeqsAndTCs ->
%% expand group references in all() using ConfTests
- Expand =
- fun({group,Name}) ->
- FindConf =
- fun({conf,Props,_,_,_}) ->
- case proplists:get_value(name, Props) of
- Name -> true;
- _ -> false
- end
- end,
- case lists:filter(FindConf, ConfTests) of
- [ConfTest|_] ->
- ConfTest;
- [] ->
- E = "Invalid reference to group "++
- atom_to_list(Name)++" in "++
- atom_to_list(Mod)++":all/0",
- throw({error,list_to_atom(E)})
- end;
- (SeqOrTC) -> SeqOrTC
- end,
- case catch lists:map(Expand, SeqsAndTCs) of
+ case catch expand_groups(SeqsAndTCs, ConfTests, Mod) of
{error,_} = Error ->
[{?MODULE,error_in_suite,[[Error]]}];
Tests ->
- Tests
+ delete_subs(Tests, Tests)
end
end;
Skip = {skip,_Reason} ->
@@ -968,6 +1030,30 @@ get_all(Mod, ConfTests) ->
[{?MODULE,error_in_suite,[[{error,Reason}]]}]
end.
+expand_groups([H | T], ConfTests, Mod) ->
+ [expand_groups(H, ConfTests, Mod) | expand_groups(T, ConfTests, Mod)];
+expand_groups([], _ConfTests, _Mod) ->
+ [];
+expand_groups({group,Name}, ConfTests, Mod) ->
+ FindConf =
+ fun({conf,Props,_,_,_}) ->
+ case proplists:get_value(name, Props) of
+ Name -> true;
+ _ -> false
+ end
+ end,
+ case lists:filter(FindConf, ConfTests) of
+ [ConfTest|_] ->
+ expand_groups(ConfTest, ConfTests, Mod);
+ [] ->
+ E = "Invalid reference to group "++
+ atom_to_list(Name)++" in "++
+ atom_to_list(Mod)++":all/0",
+ throw({error,list_to_atom(E)})
+ end;
+expand_groups(SeqOrTC, _ConfTests, _Mod) ->
+ SeqOrTC.
+
%%!============================================================
%%! The support for sequences by means of using sequences/0
@@ -1137,6 +1223,18 @@ report(What,Data) ->
ok;
tc_done ->
{_Suite,Case,Result} = Data,
+ case Result of
+ {failed, _} ->
+ ct_hooks:on_tc_fail(What, Data);
+ {skipped,{failed,{_,init_per_testcase,_}}} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ {skipped,{require_failed,_}} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ {skipped,_} ->
+ ct_hooks:on_tc_skip(tc_user_skip, Data);
+ _Else ->
+ ok
+ end,
case {Case,Result} of
{init_per_suite,_} ->
ok;
@@ -1154,8 +1252,8 @@ report(What,Data) ->
add_to_stats(auto_skipped);
{_,{skipped,_}} ->
add_to_stats(user_skipped);
- {_,{FailOrSkip,_Reason}} ->
- add_to_stats(FailOrSkip)
+ {_,{SkipOrFail,_Reason}} ->
+ add_to_stats(SkipOrFail)
end;
tc_user_skip ->
%% test case specified as skipped in testspec
@@ -1163,6 +1261,7 @@ report(What,Data) ->
ct_event:sync_notify(#event{name=tc_user_skip,
node=node(),
data=Data}),
+ ct_hooks:on_tc_skip(What, Data),
add_to_stats(user_skipped);
tc_auto_skip ->
%% test case skipped because of error in init_per_suite
@@ -1175,6 +1274,7 @@ report(What,Data) ->
ct_event:sync_notify(#event{name=tc_auto_skip,
node=node(),
data=Data}),
+ ct_hooks:on_tc_skip(What, Data),
if Case /= end_per_suite, Case /= end_per_group ->
add_to_stats(auto_skipped);
true ->