aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2011-07-28 19:12:37 +0200
committerLukas Larsson <[email protected]>2011-08-31 10:41:36 +0200
commit6b9f8b54324891f2d7fed236cb2860896215d755 (patch)
tree79be0b0efce3d0b7a05c5a7076f185c354a51399
parent71a49b5c88e2149e288168beb8cb6ff0ed39c671 (diff)
downloadotp-6b9f8b54324891f2d7fed236cb2860896215d755.tar.gz
otp-6b9f8b54324891f2d7fed236cb2860896215d755.tar.bz2
otp-6b9f8b54324891f2d7fed236cb2860896215d755.zip
Add priority functionality and tests for ct hooks
Priority allows the user of ct hooks to specify which order the hooks should execute in. The priority of a hook is specified when installing the hook, and stays the same for both pre and post hooks
-rw-r--r--lib/common_test/src/ct_framework.erl12
-rw-r--r--lib/common_test/src/ct_hooks.erl21
-rw-r--r--lib/common_test/src/ct_run.erl25
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl76
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_prio_SUITE.erl58
5 files changed, 171 insertions, 21 deletions
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 809616d8e3..9e597edf38 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -240,7 +240,8 @@ add_defaults(Mod,Func,FuncInfo,DoInit) ->
case (catch Mod:suite()) of
{'EXIT',{undef,_}} ->
SuiteInfo = merge_with_suite_defaults(Mod,[]),
- case add_defaults1(Mod,Func,FuncInfo,SuiteInfo,DoInit) of
+ SuiteInfoNoCTH = [I || I <- SuiteInfo, element(1,I) =/= ct_hooks],
+ case add_defaults1(Mod,Func,FuncInfo,SuiteInfoNoCTH,DoInit) of
Error = {error,_} -> {SuiteInfo,Error};
MergedInfo -> {SuiteInfo,MergedInfo}
end;
@@ -251,10 +252,11 @@ add_defaults(Mod,Func,FuncInfo,DoInit) ->
(_) -> false
end, SuiteInfo) of
true ->
- SuiteInfoNoCTH =
- lists:keydelete(ct_hooks,1,SuiteInfo),
- SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfoNoCTH),
- case add_defaults1(Mod,Func,FuncInfo,SuiteInfo1,DoInit) of
+ SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfo),
+ SuiteInfoNoCTH = [I || I <- SuiteInfo1,
+ element(1,I) =/= ct_hooks],
+ case add_defaults1(Mod,Func,FuncInfo,
+ SuiteInfoNoCTH,DoInit) of
Error = {error,_} -> {SuiteInfo1,Error};
MergedInfo -> {SuiteInfo1,MergedInfo}
end;
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index d298873d99..bbadb657e1 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -68,11 +68,11 @@ init_tc(ct_framework, _Func, Args) ->
init_tc(Mod, init_per_suite, Config) ->
Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of
List when is_list(List) ->
- [{ct_hooks,List}];
+ [{?config_name,List}];
CTHook when is_atom(CTHook) ->
- [{ct_hooks,[CTHook]}]
+ [{?config_name,[CTHook]}]
catch error:undef ->
- [{ct_hooks,[]}]
+ [{?config_name,[]}]
end,
call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]);
init_tc(Mod, end_per_suite, Config) ->
@@ -160,8 +160,8 @@ call_generic(#ct_hook_config{ module = Mod, state = State} = Hook,
call(Fun, Config, Meta) ->
maybe_lock(),
Hooks = get_hooks(),
- Res = call([{HookId,Fun} || #ct_hook_config{id = HookId} <- Hooks] ++
- get_new_hooks(Config, Fun),
+ Res = call(get_new_hooks(Config, Fun) ++
+ [{HookId,Fun} || #ct_hook_config{id = HookId} <- Hooks],
remove(?config_name,Config), Meta, Hooks),
maybe_unlock(),
Res.
@@ -187,7 +187,7 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
{Hooks ++ [NewHook],
[{NewId, fun call_init/3},{NewId,NextFun} | Rest]}
end,
- call(NewRest, Config, Meta, NewHooks)
+ call(resort(NewRest,NewHooks), Config, Meta, NewHooks)
catch Error:Reason ->
Trace = erlang:get_stacktrace(),
ct_logs:log("Suite Hook","Failed to start a CTH: ~p:~p",
@@ -275,6 +275,15 @@ save_suite_data_async(Hooks) ->
get_hooks() ->
lists:keysort(#ct_hook_config.prio,ct_util:read_suite_data(?config_name)).
+%% Call with three element tuples are call_id so always do them first
+resort(Calls, Hooks) ->
+ [Call || {_,_,_} = Call <- Calls] ++
+ resort1(Calls, lists:keysort(#ct_hook_config.prio, Hooks)).
+resort1(Calls, [#ct_hook_config{ id = Id }|Rest]) ->
+ [Call || {CId,_} = Call <- Calls, CId =:= Id] ++ resort1(Calls,Rest);
+resort1(_,[]) ->
+ [].
+
catch_apply(M,F,A, Default) ->
try
apply(M,F,A)
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index c01e97b358..877ec9c7dd 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -521,8 +521,8 @@ script_usage() ->
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
- "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
+ "\n\t[-event_handler EvHandler1 and EvHandler2 .. EvHandlerN]"
+ "\n\t[-ct_hooks CTHook1 and CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
@@ -540,8 +540,8 @@ script_usage() ->
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
- "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
+ "\n\t[-event_handler EvHandler1 and EvHandler2 .. EvHandlerN]"
+ "\n\t[-ct_hooks CTHook1 and CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
@@ -2070,15 +2070,21 @@ ct_hooks_args2opts(Args) ->
ct_hooks_args2opts(
proplists:get_value(ct_hooks, Args, []),[]).
+ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) ->
+ ct_hooks_args2opts(Rest,[{list_to_atom(CTH),
+ parse_cth_args(Arg),
+ parse_cth_args(Prio)}|Acc]);
ct_hooks_args2opts([CTH,Arg,"and"| Rest],Acc) ->
ct_hooks_args2opts(Rest,[{list_to_atom(CTH),
- parse_cth_args(Arg)}|Acc]);
+ parse_cth_args(Arg)}|Acc]);
ct_hooks_args2opts([CTH], Acc) ->
ct_hooks_args2opts([CTH,"and"],Acc);
ct_hooks_args2opts([CTH, "and" | Rest], Acc) ->
ct_hooks_args2opts(Rest,[list_to_atom(CTH)|Acc]);
ct_hooks_args2opts([CTH, Args], Acc) ->
ct_hooks_args2opts([CTH, Args, "and"],Acc);
+ct_hooks_args2opts([CTH, Args, Prio], Acc) ->
+ ct_hooks_args2opts([CTH, Args, Prio, "and"],Acc);
ct_hooks_args2opts([],Acc) ->
lists:reverse(Acc).
@@ -2225,7 +2231,14 @@ opts2args(EnvStartOpts) ->
({ct_hooks,CTHs}) when is_list(CTHs) ->
io:format(user,"ct_hooks: ~p",[CTHs]),
Strs = lists:flatmap(
- fun({CTH,Arg}) ->
+ fun({CTH,Arg,Prio}) ->
+ [atom_to_list(CTH),
+ lists:flatten(
+ io_lib:format("~p",[Arg])),
+ lists:flatten(
+ io_lib:format("~p",[Prio])),
+ "and"];
+ ({CTH,Arg}) ->
[atom_to_list(CTH),
lists:flatten(
io_lib:format("~p",[Arg])),
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 8574d7aabc..16a95461e6 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -83,7 +83,7 @@ all(suite) ->
fail_post_suite_cth, skip_pre_suite_cth,
skip_post_suite_cth, recover_post_suite_cth, update_config_cth,
state_update_cth, options_cth, same_id_cth,
- fail_n_skip_with_minimal_cth
+ fail_n_skip_with_minimal_cth, prio_cth
]
)
.
@@ -209,6 +209,10 @@ fail_n_skip_with_minimal_cth(Config) when is_list(Config) ->
do_test(fail_n_skip_with_minimal_cth, "ct_cth_fail_one_skip_one_SUITE.erl",
[minimal_terminate_cth],Config).
+prio_cth(Config) when is_list(Config) ->
+ do_test(prio_cth, "ct_cth_prio_SUITE.erl",
+ [{empty_cth,[1000],1000},{empty_cth,[900],900}],Config).
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -296,9 +300,9 @@ test_events(two_empty_cth) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',id,[[]]}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -365,9 +369,9 @@ test_events(minimal_and_maximal_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',id,[[]]}},
{negative,{?eh,cth,{'_',id,['_',[]]}},
{?eh,cth,{'_',init,['_',[]]}}},
- {?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -954,8 +958,8 @@ test_events(same_id_cth) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',id,[[]]}},
- {?eh,cth,{'_',init,[same_id_cth,[]]}},
{?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,[same_id_cth,[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{negative,
@@ -1001,6 +1005,70 @@ test_events(fail_n_skip_with_minimal_cth) ->
{?eh,stop_logging,[]}
];
+test_events(prio_cth) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+
+ {?eh,tc_start,{ct_cth_prio_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,['_','_',[800]]}},
+ {?eh,cth,{'_',pre_init_per_suite,['_','_',[900]]}},
+ {?eh,cth,{'_',pre_init_per_suite,['_','_',[1000]]}},
+ {?eh,cth,{'_',post_init_per_suite,['_','_','_',[700]]}},
+ {?eh,cth,{'_',post_init_per_suite,['_','_','_',[800]]}},
+ {?eh,cth,{'_',post_init_per_suite,['_','_','_',[900]]}},
+ {?eh,cth,{'_',post_init_per_suite,['_','_','_',[1000]]}},
+ {?eh,tc_done,{ct_cth_prio_SUITE,init_per_suite,ok}},
+
+
+ [{?eh,tc_start,{ct_cth_prio_SUITE,{init_per_group,'_',[]}}},
+ {?eh,cth,{'_',pre_init_per_group, ['_','_',[700]]}},
+ {?eh,cth,{'_',pre_init_per_group, ['_','_',[800]]}},
+ {?eh,cth,{'_',pre_init_per_group, ['_','_',[900]]}},
+ {?eh,cth,{'_',pre_init_per_group, ['_','_',[1000]]}},
+ {?eh,cth,{'_',post_init_per_group, ['_','_','_',[600]]}},
+ {?eh,cth,{'_',post_init_per_group, ['_','_','_',[700]]}},
+ {?eh,cth,{'_',post_init_per_group, ['_','_','_',[800]]}},
+ {?eh,cth,{'_',post_init_per_group, ['_','_','_',[900]]}},
+ {?eh,cth,{'_',post_init_per_group, ['_','_','_',[1000]]}},
+ {?eh,tc_done,{ct_cth_prio_SUITE,{init_per_group,'_',[]},ok}},
+
+ {?eh,tc_start,{ct_cth_prio_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase, ['_','_',[600]]}},
+ {?eh,cth,{'_',pre_init_per_testcase, ['_','_',[700]]}},
+ {?eh,cth,{'_',pre_init_per_testcase, ['_','_',[800]]}},
+ {?eh,cth,{'_',pre_init_per_testcase, ['_','_',[900]]}},
+ {?eh,cth,{'_',pre_init_per_testcase, ['_','_',[1000]]}},
+ {?eh,cth,{'_',post_end_per_testcase, ['_','_','_',[600]]}},
+ {?eh,cth,{'_',post_end_per_testcase, ['_','_','_',[700]]}},
+ {?eh,cth,{'_',post_end_per_testcase, ['_','_','_',[800]]}},
+ {?eh,cth,{'_',post_end_per_testcase, ['_','_','_',[900]]}},
+ {?eh,cth,{'_',post_end_per_testcase, ['_','_','_',[1000]]}},
+ {?eh,tc_done,{ct_cth_prio_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_prio_SUITE,{end_per_group,'_',[]}}},
+ {?eh,cth,{'_',pre_end_per_group, ['_','_',[600]]}},
+ {?eh,cth,{'_',pre_end_per_group, ['_','_',[700]]}},
+ {?eh,cth,{'_',pre_end_per_group, ['_','_',[800]]}},
+ {?eh,cth,{'_',pre_end_per_group, ['_','_',[900]]}},
+ {?eh,cth,{'_',pre_end_per_group, ['_','_',[1000]]}},
+ {?eh,cth,{'_',post_end_per_group, ['_','_','_',[700]]}},
+ {?eh,cth,{'_',post_end_per_group, ['_','_','_',[800]]}},
+ {?eh,cth,{'_',post_end_per_group, ['_','_','_',[900]]}},
+ {?eh,cth,{'_',post_end_per_group, ['_','_','_',[1000]]}},
+ {?eh,tc_done,{ct_cth_prio_SUITE,{end_per_group,'_',[]},ok}}],
+
+ {?eh,tc_start,{ct_cth_prio_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,['_','_',[800]]}},
+ {?eh,cth,{'_',pre_end_per_suite,['_','_',[900]]}},
+ {?eh,cth,{'_',pre_end_per_suite,['_','_',[1000]]}},
+ {?eh,cth,{'_',post_end_per_suite,['_','_','_',[800]]}},
+ {?eh,cth,{'_',post_end_per_suite,['_','_','_',[900]]}},
+ {?eh,cth,{'_',post_end_per_suite,['_','_','_',[1000]]}},
+ {?eh,tc_done,{ct_cth_prio_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(ok) ->
ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_prio_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_prio_SUITE.erl
new file mode 100644
index 0000000000..0ba18b453e
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_prio_SUITE.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_prio_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+suite() ->
+ ([{timetrap, {minutes, 10}},
+ {ct_hooks, [{empty_cth,[800],800}]}]).
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{ct_hooks, [{empty_cth,[700],700}]}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_G, Config) ->
+ [{ct_hooks, [{empty_cth,[600],600}]}|Config].
+
+end_per_group(_G, _Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [{group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.