From 735c4bb91604f080d70de20b00ecd4711788e550 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Tue, 23 Feb 2016 23:44:42 +0100 Subject: Introduce new CT hook functions --- lib/common_test/src/ct_framework.erl | 56 ++++++++++++++++++++++++------------ lib/common_test/src/ct_hooks.erl | 16 ++++++++--- 2 files changed, 49 insertions(+), 23 deletions(-) (limited to 'lib/common_test/src') diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index f792269c41..ae3f8a69e3 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -52,7 +52,13 @@ %%% %%% @doc Test server framework callback, called by the test_server %%% when a new test case is started. -init_tc(Mod,Func,Config) -> +init_tc(Mod,Func0,Config) -> + {TCCfgFunc,Func} = case Func0 of + {init_per_testcase,_} -> Func0; + {end_per_testcase,_} -> Func0; + _ -> {undefined,Func0} + end, + %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Config), @@ -86,7 +92,7 @@ init_tc(Mod,Func,Config) -> end, [create]), case ct_util:read_suite_data({seq,Suite,Func}) of undefined -> - init_tc1(Mod,Suite,Func,Config); + init_tc1(Mod,Suite,Func,TCCfgFunc,Config); Seq when is_atom(Seq) -> case ct_util:read_suite_data({seq,Suite,Seq}) of [Func|TCs] -> % this is the 1st case in Seq @@ -102,27 +108,27 @@ init_tc(Mod,Func,Config) -> _ -> ok end, - init_tc1(Mod,Suite,Func,Config); + init_tc1(Mod,Suite,Func,TCCfgFunc,Config); {failed,Seq,BadFunc} -> {auto_skip,{sequence_failed,Seq,BadFunc}} end end end. -init_tc1(?MODULE,_,error_in_suite,[Config0]) when is_list(Config0) -> +init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, node=node(), data={?MODULE,error_in_suite}}), - ct_suite_init(?MODULE, error_in_suite, [], Config0), - case ?val(error, Config0) of + ct_suite_init(?MODULE,error_in_suite,undefined,[], Config0), + case ?val(error,Config0) of undefined -> {fail,"unknown_error_in_suite"}; Reason -> {fail,Reason} end; -init_tc1(Mod,Suite,Func,[Config0]) when is_list(Config0) -> +init_tc1(Mod,Suite,Func,TCCfgFunc,[Config0]) when is_list(Config0) -> Config1 = case ct_util:read_suite_data(last_saved_config) of {{Suite,LastFunc},SavedConfig} -> % last testcase @@ -184,14 +190,15 @@ init_tc1(Mod,Suite,Func,[Config0]) when is_list(Config0) -> Initialize(), {fail,Reason}; _ -> - init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) + init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo, + MergeResult,Config) end end; -init_tc1(_Mod,_Suite,_Func,Args) -> +init_tc1(_Mod,_Suite,_Func,_TCCfgFunc,Args) -> {ok,Args}. -init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> +init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet @@ -238,7 +245,8 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> {ok,PostInitHook,Config1} -> case get('$test_server_framework_test') of undefined -> - ct_suite_init(Suite, FuncSpec, PostInitHook, Config1); + ct_suite_init(Suite,FuncSpec,TCCfgFunc, + PostInitHook,Config1); Fun -> PostInitHookResult = do_post_init_hook(PostInitHook, Config1), @@ -251,16 +259,20 @@ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> end end. -ct_suite_init(Suite, FuncSpec, PostInitHook, Config) when is_list(Config) -> - case ct_hooks:init_tc(Suite, FuncSpec, Config) of +ct_suite_init(Suite,FuncSpec,TCCfgFunc, + PostInitHook,Config) when is_list(Config) -> + HookFunc = if TCCfgFunc /= undefined -> {TCCfgFunc,FuncSpec}; + true -> FuncSpec + end, + case ct_hooks:init_tc(Suite,HookFunc,Config) of NewConfig when is_list(NewConfig) -> - PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig), + PostInitHookResult = do_post_init_hook(PostInitHook,NewConfig), {ok, [PostInitHookResult ++ NewConfig]}; Else -> Else end. -do_post_init_hook(PostInitHook, Config) -> +do_post_init_hook(PostInitHook,Config) -> lists:flatmap(fun({Tag,Fun}) -> case lists:keysearch(Tag,1,Config) of {value,_} -> @@ -657,7 +669,12 @@ end_tc(Mod,Func,{TCPid,Result,[Args]}, Return) when is_pid(TCPid) -> end_tc(Mod,Func,{Result,[Args]}, Return) -> end_tc(Mod,Func,self(),Result,Args,Return). -end_tc(Mod,Func,TCPid,Result,Args,Return) -> +end_tc(Mod,Func0,TCPid,Result,Args,Return) -> + {TCCfgFunc,Func} = case Func0 of + {init_per_testcase,_} -> Func0; + {end_per_testcase,_} -> Func0; + _ -> {undefined,Func0} + end, %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Args), @@ -687,10 +704,11 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> ct_util:delete_suite_data(last_saved_config), FuncSpec = group_or_func(Func,Args), - + HookFunc = if TCCfgFunc /= undefined -> Func0; + true -> FuncSpec + end, {Result1,FinalNotify} = - case ct_hooks:end_tc( - Suite, FuncSpec, Args, Result, Return) of + case ct_hooks:end_tc(Suite,HookFunc,Args,Result,Return) of '$ct_no_change' -> {ok,Result}; HookResult -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 86d18696dc..4008ea998a 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -93,8 +93,10 @@ init_tc(Mod, {init_per_group, GroupName, Properties}, Config) -> call(fun call_generic/3, Config, [pre_init_per_group, GroupName]); init_tc(_Mod, {end_per_group, GroupName, _}, Config) -> call(fun call_generic/3, Config, [pre_end_per_group, GroupName]); -init_tc(_Mod, TC, Config) -> - call(fun call_generic/3, Config, [pre_init_per_testcase, TC]). +init_tc(_Mod, {init_per_testcase,TC}, Config) -> + call(fun call_generic/3, Config, [pre_init_per_testcase, TC]); +init_tc(_Mod, {end_per_testcase,TC}, Config) -> + call(fun call_generic/3, Config, [pre_end_per_testcase, TC]). %% @doc Called as each test case is completed. This includes all configuration %% tests. @@ -126,7 +128,10 @@ end_tc(Mod, {end_per_group, GroupName, Properties}, Config, Result, _Return) -> [post_end_per_group, GroupName, Config], '$ct_no_change'), maybe_stop_locker(Mod, GroupName, Properties), Res; -end_tc(_Mod, TC, Config, Result, _Return) -> +end_tc(_Mod, {init_per_testcase,TC}, Config, Result, _Return) -> + call(fun call_generic/3, Result, [post_init_per_testcase, TC, Config], + '$ct_no_change'); +end_tc(_Mod, {end_per_testcase,TC}, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], '$ct_no_change'). @@ -244,6 +249,8 @@ remove(_, Else) -> %% Translate scopes, i.e. init_per_group,group1 -> end_per_group,group1 etc scope([pre_init_per_testcase, TC|_]) -> + [post_init_per_testcase, TC]; +scope([pre_end_per_testcase, TC|_]) -> [post_end_per_testcase, TC]; scope([pre_init_per_group, GroupName|_]) -> [post_end_per_group, GroupName]; @@ -317,7 +324,8 @@ get_hooks() -> %% If we are doing a cleanup call i.e. {post,pre}_end_per_*, all priorities %% are reversed. Probably want to make this sorting algorithm pluginable %% as some point... -resort(Calls,Hooks,[F|_R]) when F == post_end_per_testcase; +resort(Calls,Hooks,[F|_R]) when F == pre_end_per_testcase; + F == post_end_per_testcase; F == pre_end_per_group; F == post_end_per_group; F == pre_end_per_suite; -- cgit v1.2.3 From 3e720193641aa92d0082586ba64fc75cda32103c Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Tue, 1 Mar 2016 16:51:33 +0100 Subject: Fix remaining issues --- lib/common_test/src/ct_framework.erl | 91 +++++++++++++++++++++--------------- lib/common_test/src/ct_hooks.erl | 8 +++- 2 files changed, 61 insertions(+), 38 deletions(-) (limited to 'lib/common_test/src') diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index ae3f8a69e3..4fcd7bfa5a 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -52,15 +52,23 @@ %%% %%% @doc Test server framework callback, called by the test_server %%% when a new test case is started. -init_tc(Mod,Func0,Config) -> - {TCCfgFunc,Func} = case Func0 of - {init_per_testcase,_} -> Func0; - {end_per_testcase,_} -> Func0; - _ -> {undefined,Func0} - end, - +init_tc(Mod,EPTC={end_per_testcase,_},[Config]) -> %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Config), + case ct_hooks:init_tc(Suite,EPTC,Config) of + NewConfig when is_list(NewConfig) -> + {ok,[NewConfig]}; + Other-> + Other + end; + +init_tc(Mod,Func0,Args) -> + %% in case Mod == ct_framework, lookup the suite name + Suite = get_suite_name(Mod, Args), + {Func,HookFunc} = case Func0 of + {init_per_testcase,F} -> {F,Func0}; + _ -> {Func0,Func0} + end, %% check if previous testcase was interpreted and has left %% a "dead" trace window behind - if so, kill it @@ -92,7 +100,7 @@ init_tc(Mod,Func0,Config) -> end, [create]), case ct_util:read_suite_data({seq,Suite,Func}) of undefined -> - init_tc1(Mod,Suite,Func,TCCfgFunc,Config); + init_tc1(Mod,Suite,Func,HookFunc,Args); Seq when is_atom(Seq) -> case ct_util:read_suite_data({seq,Suite,Seq}) of [Func|TCs] -> % this is the 1st case in Seq @@ -108,19 +116,19 @@ init_tc(Mod,Func0,Config) -> _ -> ok end, - init_tc1(Mod,Suite,Func,TCCfgFunc,Config); + init_tc1(Mod,Suite,Func,HookFunc,Args); {failed,Seq,BadFunc} -> {auto_skip,{sequence_failed,Seq,BadFunc}} end end - end. + end. init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, node=node(), data={?MODULE,error_in_suite}}), - ct_suite_init(?MODULE,error_in_suite,undefined,[], Config0), + ct_suite_init(?MODULE,error_in_suite,[],Config0), case ?val(error,Config0) of undefined -> {fail,"unknown_error_in_suite"}; @@ -128,7 +136,7 @@ init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) -> {fail,Reason} end; -init_tc1(Mod,Suite,Func,TCCfgFunc,[Config0]) when is_list(Config0) -> +init_tc1(Mod,Suite,Func,HookFunc,[Config0]) when is_list(Config0) -> Config1 = case ct_util:read_suite_data(last_saved_config) of {{Suite,LastFunc},SavedConfig} -> % last testcase @@ -162,11 +170,13 @@ init_tc1(Mod,Suite,Func,TCCfgFunc,[Config0]) when is_list(Config0) -> %% testcase info function (these should only survive the %% testcase, not the whole suite) FuncSpec = group_or_func(Func,Config0), - if is_tuple(FuncSpec) -> % group - ok; - true -> - ct_config:delete_default_config(testcase) - end, + HookFunc1 = + if is_tuple(FuncSpec) -> % group + FuncSpec; + true -> + ct_config:delete_default_config(testcase), + HookFunc + end, Initialize = fun() -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, @@ -190,15 +200,15 @@ init_tc1(Mod,Suite,Func,TCCfgFunc,[Config0]) when is_list(Config0) -> Initialize(), {fail,Reason}; _ -> - init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo, - MergeResult,Config) + init_tc2(Mod,Suite,Func,HookFunc1, + SuiteInfo,MergeResult,Config) end end; -init_tc1(_Mod,_Suite,_Func,_TCCfgFunc,Args) -> +init_tc1(_Mod,_Suite,_Func,_HookFunc,Args) -> {ok,Args}. -init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo,MergeResult,Config) -> +init_tc2(Mod,Suite,Func,HookFunc,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet @@ -245,8 +255,7 @@ init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo,MergeResult,Config) -> {ok,PostInitHook,Config1} -> case get('$test_server_framework_test') of undefined -> - ct_suite_init(Suite,FuncSpec,TCCfgFunc, - PostInitHook,Config1); + ct_suite_init(Suite,HookFunc,PostInitHook,Config1); Fun -> PostInitHookResult = do_post_init_hook(PostInitHook, Config1), @@ -259,11 +268,7 @@ init_tc2(Mod,Suite,Func,TCCfgFunc,SuiteInfo,MergeResult,Config) -> end end. -ct_suite_init(Suite,FuncSpec,TCCfgFunc, - PostInitHook,Config) when is_list(Config) -> - HookFunc = if TCCfgFunc /= undefined -> {TCCfgFunc,FuncSpec}; - true -> FuncSpec - end, +ct_suite_init(Suite,HookFunc,PostInitHook,Config) when is_list(Config) -> case ct_hooks:init_tc(Suite,HookFunc,Config) of NewConfig when is_list(NewConfig) -> PostInitHookResult = do_post_init_hook(PostInitHook,NewConfig), @@ -669,14 +674,23 @@ end_tc(Mod,Func,{TCPid,Result,[Args]}, Return) when is_pid(TCPid) -> end_tc(Mod,Func,{Result,[Args]}, Return) -> end_tc(Mod,Func,self(),Result,Args,Return). +end_tc(Mod,IPTC={init_per_testcase,_Func},_TCPid,Result,Args,Return) -> + %% in case Mod == ct_framework, lookup the suite name + Suite = get_suite_name(Mod, Args), + case ct_hooks:end_tc(Suite,IPTC,Args,Result,Return) of + '$ct_no_change' -> + ok; + HookResult -> + HookResult + end; + end_tc(Mod,Func0,TCPid,Result,Args,Return) -> - {TCCfgFunc,Func} = case Func0 of - {init_per_testcase,_} -> Func0; - {end_per_testcase,_} -> Func0; - _ -> {undefined,Func0} - end, %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Args), + {EPTC,Func} = case Func0 of + {end_per_testcase,F} -> {true,F}; + _ -> {false,Func0} + end, test_server:timetrap_cancel(), @@ -703,10 +717,13 @@ end_tc(Mod,Func0,TCPid,Result,Args,Return) -> end, ct_util:delete_suite_data(last_saved_config), - FuncSpec = group_or_func(Func,Args), - HookFunc = if TCCfgFunc /= undefined -> Func0; - true -> FuncSpec - end, + {FuncSpec,HookFunc} = + if not EPTC -> + FS = group_or_func(Func,Args), + {FS,FS}; + true -> + {Func,Func0} + end, {Result1,FinalNotify} = case ct_hooks:end_tc(Suite,HookFunc,Args,Result,Return) of '$ct_no_change' -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 4008ea998a..b604074d12 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -96,7 +96,9 @@ init_tc(_Mod, {end_per_group, GroupName, _}, Config) -> init_tc(_Mod, {init_per_testcase,TC}, Config) -> call(fun call_generic/3, Config, [pre_init_per_testcase, TC]); init_tc(_Mod, {end_per_testcase,TC}, Config) -> - call(fun call_generic/3, Config, [pre_end_per_testcase, TC]). + call(fun call_generic/3, Config, [pre_end_per_testcase, TC]); +init_tc(_Mod, TC = error_in_suite, Config) -> + call(fun call_generic/3, Config, [pre_init_per_testcase, TC]). %% @doc Called as each test case is completed. This includes all configuration %% tests. @@ -132,9 +134,13 @@ end_tc(_Mod, {init_per_testcase,TC}, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_init_per_testcase, TC, Config], '$ct_no_change'); end_tc(_Mod, {end_per_testcase,TC}, Config, Result, _Return) -> + call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], + '$ct_no_change'); +end_tc(_Mod, TC = error_in_suite, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], '$ct_no_change'). + %% Case = TestCase | {TestCase,GroupName} on_tc_skip(How, {Suite, Case, Reason}) -> call(fun call_cleanup/3, {How, Reason}, [on_tc_skip, Suite, Case]). -- cgit v1.2.3 From 13051baf9f4844d4236f221311f03135144ade88 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Sat, 5 Mar 2016 17:09:56 +0100 Subject: Add missing internal hook functions --- lib/common_test/src/ct_hooks.erl | 4 ++-- lib/common_test/src/cth_log_redirect.erl | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'lib/common_test/src') diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index b604074d12..83ad33fdd8 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -381,10 +381,10 @@ pos(Id,[_|Rest],Num) -> catch_apply(M,F,A, Default) -> try - apply(M,F,A) + erlang:apply(M,F,A) catch _:Reason -> case erlang:get_stacktrace() of - %% Return the default if it was the CTH module which did not have the function. + %% Return the default if it was the CTH module which did not have the function. [{M,F,A,_}|_] when Reason == undef -> Default; Trace -> diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl index e6970a2bad..a8c4a455e1 100644 --- a/lib/common_test/src/cth_log_redirect.erl +++ b/lib/common_test/src/cth_log_redirect.erl @@ -30,7 +30,8 @@ pre_init_per_suite/3, pre_end_per_suite/3, post_end_per_suite/4, pre_init_per_group/3, post_init_per_group/4, pre_end_per_group/3, post_end_per_group/4, - pre_init_per_testcase/3, post_end_per_testcase/4]). + pre_init_per_testcase/3, post_init_per_testcase/4, + pre_end_per_testcase/3, post_end_per_testcase/4]). %% Event handler Callbacks -export([init/1, @@ -89,6 +90,12 @@ pre_init_per_testcase(TC, Config, State) -> set_curr_func(TC, Config), {Config, State}. +post_init_per_testcase(_TC, _Config, Return, State) -> + {Return, State}. + +pre_end_per_testcase(_TC, Config, State) -> + {Config, State}. + post_end_per_testcase(_TC, _Config, Result, State) -> %% Make sure that the event queue is flushed %% before ending this test case. -- cgit v1.2.3