diff options
Diffstat (limited to 'lib/common_test')
31 files changed, 1834 insertions, 752 deletions
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css index 75f8d5db8a..8ae6990cd8 100644 --- a/lib/common_test/priv/ct_default.css +++ b/lib/common_test/priv/ct_default.css @@ -81,13 +81,21 @@ div.copyright { color: #000000; } -div.ct_internal { +div.ct_internal { background: lightgrey; color: black; font-family: "Monaco", "Andale Mono", "Consolas", monospace; font-size: .95em; margin: .2em 0 0 0; } +div.ct_error_notify { + background: #CC0000; + color: #FFFFFF; + font-family: "Monaco", "Andale Mono", "Consolas", monospace; + font-size: 1.05em; + margin: .2em 0 0 0; +} + div.default { background: lightgreen; color: black; font-family: "Monaco", "Andale Mono", "Consolas", monospace; diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index e0e82283c4..63a8adbc63 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -64,7 +64,7 @@ print/1, print/2, print/3, pal/1, pal/2, pal/3, capture_start/0, capture_stop/0, capture_get/0, capture_get/1, - fail/1, fail/2, comment/1, comment/2, + fail/1, fail/2, comment/1, comment/2, make_priv_dir/0, testcases/2, userdata/2, userdata/3, timetrap/1, get_timetrap_info/0, sleep/1]). @@ -585,8 +585,16 @@ capture_get([]) -> %%% @doc Terminate a test case with the given error %%% <code>Reason</code>. fail(Reason) -> - exit({test_case_failed,Reason}). - + try + exit({test_case_failed,Reason}) + catch + Class:R -> + case erlang:get_stacktrace() of + [{?MODULE,fail,1,_}|Stk] -> ok; + Stk -> ok + end, + erlang:raise(Class, R, Stk) + end. %%%----------------------------------------------------------------- %%% @spec fail(Format, Args) -> void() @@ -599,13 +607,21 @@ fail(Reason) -> fail(Format, Args) -> try io_lib:format(Format, Args) of Str -> - exit({test_case_failed,lists:flatten(Str)}) + try + exit({test_case_failed,lists:flatten(Str)}) + catch + Class:R -> + case erlang:get_stacktrace() of + [{?MODULE,fail,2,_}|Stk] -> ok; + Stk -> ok + end, + erlang:raise(Class, R, Stk) + end catch _:BadArgs -> exit({BadArgs,{?MODULE,fail,[Format,Args]}}) end. - %%%----------------------------------------------------------------- %%% @spec comment(Comment) -> void() %%% Comment = term() @@ -657,6 +673,15 @@ send_html_comment(Comment) -> ct_util:set_testdata({comment,Html}), test_server:comment(Html). +%%%----------------------------------------------------------------- +%%% @spec make_priv_dir() -> ok | {error,Reason} +%%% Reason = term() +%%% @doc If the test has been started with the create_priv_dir +%%% option set to manual_per_tc, in order for the test case to use +%%% the private directory, it must first create it by calling +%%% this function. +make_priv_dir() -> + test_server:make_priv_dir(). %%%----------------------------------------------------------------- %%% @spec get_target_name(Handle) -> {ok,TargetName} | {error,Reason} @@ -845,6 +870,8 @@ get_status() -> get_testdata(Key) -> case catch ct_util:get_testdata(Key) of + {error,ct_util_server_not_running} -> + no_tests_running; Error = {error,_Reason} -> Error; {'EXIT',_Reason} -> diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index c24a7c238b..187794e78b 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -29,7 +29,8 @@ -export([get_logopts/0, format_comment/1, get_html_wrapper/3]). --export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]). +-export([error_in_suite/1, init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2]). -export([make_all_conf/3, make_conf/5]). @@ -54,6 +55,7 @@ init_tc(Mod,Func,Config) -> %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Config), + %% check if previous testcase was interpreted and has left %% a "dead" trace window behind - if so, kill it case ct_util:get_testdata(interpret) of @@ -63,27 +65,17 @@ init_tc(Mod,Func,Config) -> _ -> ok end, - %% check if we need to add defaults explicitly because - %% there's no init_per_suite exported from Mod - {InitFailed,DoInit} = - case ct_util:get_testdata(curr_tc) of - {Suite,{suite0_failed,_}=Failure} -> - {Failure,false}; - {?MODULE,_} -> % should not really happen - {false,false}; - {Suite,_} -> % Func is not 1st case in suite - {false,false}; - _ when Func == init_per_suite -> % defaults will be added anyway - {false,false}; - _ -> % first case in suite - {false,true} - end, - case InitFailed of - false -> + + case ct_util:get_testdata(curr_tc) of + {Suite,{suite0_failed,{require,Reason}}} -> + {skip,{require_failed_in_suite0,Reason}}; + {Suite,{suite0_failed,_}=Failure} -> + {skip,Failure}; + _ -> ct_util:set_testdata({curr_tc,{Suite,Func}}), case ct_util:read_suite_data({seq,Suite,Func}) of undefined -> - init_tc1(Mod,Func,Config,DoInit); + init_tc1(Mod,Suite,Func,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 @@ -96,17 +88,13 @@ init_tc(Mod,Func,Config) -> _ -> ok end, - init_tc1(Mod,Func,Config,DoInit); + init_tc1(Mod,Suite,Func,Config); {failed,Seq,BadFunc} -> {skip,{sequence_failed,Seq,BadFunc}} - end; - {_,{require,Reason}} -> - {skip,{require_failed_in_suite0,Reason}}; - _ -> - {skip,InitFailed} + 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(), @@ -117,14 +105,16 @@ init_tc1(?MODULE,error_in_suite,[Config0],_) when is_list(Config0) -> Reason -> {skip,Reason} end; -init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> + +init_tc1(Mod,Suite,Func,[Config0]) when is_list(Config0) -> Config1 = case ct_util:read_suite_data(last_saved_config) of - {{Mod,LastFunc},SavedConfig} -> % last testcase + {{Suite,LastFunc},SavedConfig} -> % last testcase [{saved_config,{LastFunc,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; - {{LastSuite,InitOrEnd},SavedConfig} when InitOrEnd == init_per_suite ; - InitOrEnd == end_per_suite -> + {{LastSuite,InitOrEnd}, + SavedConfig} when InitOrEnd == init_per_suite ; + InitOrEnd == end_per_suite -> %% last suite [{saved_config,{LastSuite,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; @@ -133,13 +123,14 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> end, ct_util:delete_suite_data(last_saved_config), Config = lists:keydelete(watchdog,1,Config1), - if Func /= init_per_suite, DoInit /= true -> - ok; - true -> + + if Func == init_per_suite -> %% delete all default values used in previous suite ct_config:delete_default_config(suite), %% release all name -> key bindings (once per suite) - ct_config:release_allocated() + ct_config:release_allocated(); + Func /= init_per_suite -> + ok end, GroupPath = ?val(tc_group_path, Config, []), @@ -154,9 +145,7 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> true -> ct_config:delete_default_config(testcase) end, - %% in case Mod == ct_framework, lookup the suite name - Suite = get_suite_name(Mod, Config), - case add_defaults(Mod,Func,AllGroups,DoInit) of + case add_defaults(Mod,Func,AllGroups) of Error = {suite0_failed,_} -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, @@ -166,35 +155,25 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> {error,Error}; {SuiteInfo,MergeResult} -> case MergeResult of - {error,Reason} when DoInit == false -> + {error,Reason} -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, node=node(), data={Mod,FuncSpec}}), {skip,Reason}; _ -> - init_tc2(Mod,Func,SuiteInfo,MergeResult, - Config,DoInit) + init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) end end; -init_tc1(_Mod,_Func,Args,_DoInit) -> - {ok,Args}. -init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> - %% if first testcase fails when there's no init_per_suite - %% we must do suite/0 configurations before skipping test - MergedInfo = - case MergeResult of - {error,_} when DoInit == true -> - SuiteInfo; - _ -> - MergeResult - end, +init_tc1(_Mod,_Suite,_Func,Args) -> + {ok,Args}. +init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require - MergedInfo1 = timetrap_first(MergedInfo, [], []), + MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet - case lists:keysearch(stylesheet,1,MergedInfo++Config) of + case lists:keysearch(stylesheet,1,MergeResult++Config) of {value,{stylesheet,SSFile}} -> ct_logs:set_stylesheet(Func,add_data_dir(SSFile,Config)); _ -> @@ -209,7 +188,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> %% list of {Type,Bool} tuples, e.g. {telnet,true}), case ct_util:get_overridden_silenced_connections() of undefined -> - case lists:keysearch(silent_connections,1,MergedInfo++Config) of + case lists:keysearch(silent_connections,1,MergeResult++Config) of {value,{silent_connections,Conns}} -> ct_util:silence_connections(Conns); _ -> @@ -218,18 +197,14 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> Conns -> ct_util:silence_connections(Conns) end, - if Func /= init_per_suite, DoInit /= true -> - ct_logs:init_tc(false); - true -> - ct_logs:init_tc(true) - end, + ct_logs:init_tc(Func == init_per_suite), FuncSpec = group_or_func(Func,Config), ct_event:notify(#event{name=tc_start, node=node(), data={Mod,FuncSpec}}), - case catch configure(MergedInfo1,MergedInfo1,SuiteInfo, - {FuncSpec,DoInit},Config) of + case catch configure(MergedInfo,MergedInfo,SuiteInfo, + FuncSpec,Config) of {suite0_failed,Reason} -> ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}), {skip,{require_failed_in_suite0,Reason}}; @@ -246,7 +221,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> _ -> case get('$test_server_framework_test') of undefined -> - ct_suite_init(Mod, FuncSpec, FinalConfig); + ct_suite_init(Suite, FuncSpec, FinalConfig); Fun -> case Fun(init_tc, FinalConfig) of NewConfig when is_list(NewConfig) -> @@ -258,21 +233,21 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> end end. -ct_suite_init(Mod, Func, [Config]) when is_list(Config) -> - case ct_hooks:init_tc( Mod, Func, Config) of +ct_suite_init(Suite, Func, [Config]) when is_list(Config) -> + case ct_hooks:init_tc(Suite, Func, Config) of NewConfig when is_list(NewConfig) -> {ok, [NewConfig]}; Else -> Else end. -add_defaults(Mod,Func, GroupPath, DoInit) -> +add_defaults(Mod,Func, GroupPath) -> Suite = get_suite_name(Mod, GroupPath), case (catch Suite:suite()) of {'EXIT',{undef,_}} -> SuiteInfo = merge_with_suite_defaults(Suite,[]), SuiteInfoNoCTH = [I || I <- SuiteInfo, element(1,I) =/= ct_hooks], - case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH, DoInit) of + case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH) of Error = {error,_} -> {SuiteInfo,Error}; MergedInfo -> {SuiteInfo,MergedInfo} end; @@ -292,7 +267,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) -> SuiteInfoNoCTH = [I || I <- SuiteInfo1, element(1,I) =/= ct_hooks], case add_defaults1(Mod,Func, GroupPath, - SuiteInfoNoCTH, DoInit) of + SuiteInfoNoCTH) of Error = {error,_} -> {SuiteInfo1,Error}; MergedInfo -> {SuiteInfo1,MergedInfo} end; @@ -313,7 +288,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) -> {suite0_failed,bad_return_value} end. -add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> +add_defaults1(Mod,Func, GroupPath, SuiteInfo) -> Suite = get_suite_name(Mod, GroupPath), %% GroupPathInfo (for subgroup on level X) = %% [LevelXGroupInfo, LevelX-1GroupInfo, ..., TopLevelGroupInfo] @@ -325,8 +300,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> _ -> [] end end, GroupPath), - Args = if Func == init_per_group; Func == ct_init_per_group; - Func == end_per_group; Func == ct_end_per_group -> + Args = if Func == init_per_group ; Func == end_per_group -> [?val(name, hd(GroupPath))]; true -> [] @@ -347,7 +321,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> (default_config == element(1,SDDef)))], case check_for_clashes(TestCaseInfo, GroupPathInfo, SuiteReqs) of [] -> - add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs, DoInit); + add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs); Clashes -> {error,{config_name_already_in_use,Clashes}} end. @@ -421,34 +395,25 @@ keysmember([Key,Pos|Next], List) -> keysmember([], _) -> true. -add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, false) -> - add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, true); +add_defaults2(_Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs) -> + Info = lists:flatten([IPSInfo, SuiteReqs]), + lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]); -add_defaults2(_Mod,IPG, IPGAndGroupInfo, SuiteInfo,SuiteReqs, DoInit) when - IPG == init_per_group ; IPG == ct_init_per_group -> - %% If DoInit == true, we have to process the suite() list, otherwise - %% it has already been handled (see clause for init_per_suite) - case DoInit of - true -> - %% note: we know for sure this is a top level group - Info = lists:flatten([IPGAndGroupInfo, SuiteReqs]), - Info ++ remove_info_in_prev(Info, [SuiteInfo]); - false -> - SuiteInfo1 = - remove_info_in_prev(lists:flatten([IPGAndGroupInfo, - SuiteReqs]), [SuiteInfo]), - %% don't require terms in prev groups (already processed) - case IPGAndGroupInfo of - [IPGInfo] -> - lists:flatten([IPGInfo,SuiteInfo1]); - [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] -> - PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), - lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1, - SuiteInfo1]) - end +add_defaults2(_Mod,init_per_group, IPGAndGroupInfo, SuiteInfo,SuiteReqs) -> + SuiteInfo1 = + remove_info_in_prev(lists:flatten([IPGAndGroupInfo, + SuiteReqs]), [SuiteInfo]), + %% don't require terms in prev groups (already processed) + case IPGAndGroupInfo of + [IPGInfo] -> + lists:flatten([IPGInfo,SuiteInfo1]); + [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] -> + PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), + lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1, + SuiteInfo1]) end; -add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) -> +add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs) -> %% Include require elements from test case info and current group, %% but not from previous groups or suite/0 (since we've already required %% those vars). Let test case info elements override group and suite @@ -463,14 +428,7 @@ add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) -> PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), lists:flatten([TCInfo,CurrGroupInfo,PrevGroupInfo1, SuiteInfo1]) - end; - -add_defaults2(_Mod,_Func, TCInfo, SuiteInfo,SuiteReqs, true) -> - %% Here we have to process the suite info list also (no call to - %% init_per_suite before this first test case). This TC can't belong - %% to a group, or the clause for (ct_)init_per_group would've caught this. - Info = lists:flatten([TCInfo, SuiteReqs]), - lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]). + end. delete_require_terms([Info | Prev]) -> Info1 = [T || T <- Info, @@ -558,18 +516,9 @@ configure([],_,_,_,Config) -> %% function and be scoped 'group', or come from the testcase %% info function and then be scoped 'testcase' -required_default(Name,Key,Info,SuiteInfo,{FuncSpec,true}) -> - case try_set_default(Name,Key,SuiteInfo,suite) of - ok -> - ok; - _ -> - required_default(Name,Key,Info,[],{FuncSpec,false}) - end; -required_default(Name,Key,Info,_,{init_per_suite,_}) -> +required_default(Name,Key,Info,_,init_per_suite) -> try_set_default(Name,Key,Info,suite); -required_default(Name,Key,Info,_,{{init_per_group,GrName,_},_}) -> - try_set_default(Name,Key,Info,{group,GrName}); -required_default(Name,Key,Info,_,{{ct_init_per_group,GrName,_},_}) -> +required_default(Name,Key,Info,_,{init_per_group,GrName,_}) -> try_set_default(Name,Key,Info,{group,GrName}); required_default(Name,Key,Info,_,_FuncSpec) -> try_set_default(Name,Key,Info,testcase). @@ -621,6 +570,9 @@ end_tc(Mod,Func,{Result,[Args]}, Return) -> end_tc(Mod,Func,self(),Result,Args,Return). end_tc(Mod,Func,TCPid,Result,Args,Return) -> + %% in case Mod == ct_framework, lookup the suite name + Suite = get_suite_name(Mod, Args), + case lists:keysearch(watchdog,1,Args) of {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog); false -> ok @@ -636,60 +588,62 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> end, ct_util:delete_testdata(comment), ct_util:delete_suite_data(last_saved_config), - FuncSpec = - case group_or_func(Func,Args) of - {_,GroupName,_Props} = Group -> - if Func == end_per_group; Func == ct_end_per_group -> - ct_config:delete_default_config({group,GroupName}); - true -> ok - end, - case lists:keysearch(save_config,1,Args) of - {value,{save_config,SaveConfig}} -> - ct_util:save_suite_data( - last_saved_config, - {Mod,{group,GroupName}}, - SaveConfig), - Group; - false -> - Group - end; - _ -> - case lists:keysearch(save_config,1,Args) of - {value,{save_config,SaveConfig}} -> - ct_util:save_suite_data(last_saved_config, - {Mod,Func},SaveConfig), - Func; - false -> - Func - end - end, - ct_util:reset_silent_connections(), + + FuncSpec = case group_or_func(Func,Args) of + {_,_GroupName,_} = Group -> Group; + _ -> Func + end, case get('$test_server_framework_test') of undefined -> {FinalResult,FinalNotify} = case ct_hooks:end_tc( - Mod, FuncSpec, Args, Result, Return) of + Suite, FuncSpec, Args, Result, Return) of '$ct_no_change' -> {ok,Result}; FinalResult1 -> {FinalResult1,FinalResult1} end, - % send sync notification so that event handlers may print - % in the log file before it gets closed + %% 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 + %% 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 FuncSpec of + {_,GroupName,_Props} -> + if Func == end_per_group -> + ct_config:delete_default_config({group,GroupName}); + true -> ok + end, + case lists:keysearch(save_config,1,Args) of + {value,{save_config,SaveConfig}} -> + ct_util:save_suite_data(last_saved_config, + {Suite,{group,GroupName}}, + SaveConfig); + false -> + ok + end; + _ -> + case lists:keysearch(save_config,1,Args) of + {value,{save_config,SaveConfig}} -> + ct_util:save_suite_data(last_saved_config, + {Suite,Func},SaveConfig); + false -> + ok + end end, + ct_util:reset_silent_connections(), case FinalResult of {skip,{sequence_failed,_,_}} -> @@ -706,7 +660,7 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> end, case Func of end_per_suite -> - ct_util:match_delete_suite_data({seq,Mod,'_'}); + ct_util:match_delete_suite_data({seq,Suite,'_'}); _ -> ok end, @@ -806,31 +760,36 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> end end, - io:format(user, "~n- - - - - - - - - - - - - - - - " - "- - - - - - - - - -~n", []), + PrintErr = fun(ErrFormat, ErrArgs) -> + Div = "~n- - - - - - - - - - - - - - - - " + "- - - - - - - - - -~n", + io:format(user, lists:concat([Div,ErrFormat,Div,"~n"]), + ErrArgs), + ct_logs:tc_log(ct_error_notify, "CT Error Notification", + ErrFormat, ErrArgs) + end, case Loc of - %% we don't use the line parse transform as we compile this - %% module so location will be on form {M,F} [{?MODULE,error_in_suite}] -> - io:format(user, "Error in suite detected: ~s", [ErrStr]); + PrintErr("Error in suite detected: ~s", [ErrStr]); - R when R == unknown; R == undefined -> - io:format(user, "Error detected: ~s", [ErrStr]); + R when R == unknown; R == undefined -> + PrintErr("Error detected: ~s", [ErrStr]); %% if a function specified by all/0 does not exist, we %% pick up undef here - [{LastMod,LastFunc}] -> - io:format(user, "~w:~w could not be executed~n", - [LastMod,LastFunc]), - io:format(user, "Reason: ~s", [ErrStr]); + [{LastMod,LastFunc}|_] when ErrStr == "undef" -> + PrintErr("~w:~w could not be executed~nReason: ~s", + [LastMod,LastFunc,ErrStr]); + + [{LastMod,LastFunc}|_] -> + PrintErr("~w:~w failed~nReason: ~s", [LastMod,LastFunc,ErrStr]); [{LastMod,LastFunc,LastLine}|_] -> %% print error to console, we are only %% interested in the last executed expression - io:format(user, "~w:~w failed on line ~w~n", - [LastMod,LastFunc,LastLine]), - io:format(user, "Reason: ~s", [ErrStr]), - + PrintErr("~w:~w failed on line ~w~nReason: ~s", + [LastMod,LastFunc,LastLine,ErrStr]), + case ct_util:read_suite_data({seq,Mod,Func}) of undefined -> ok; @@ -839,8 +798,6 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> mark_as_failed(Seq,Mod,Func,SeqTCs) end end, - io:format(user, "~n- - - - - - - - - - - - - - - - " - "- - - - - - - - - -~n~n", []), ok. %% cases in seq that have already run @@ -861,9 +818,7 @@ mark_as_failed1(_,_,_,[]) -> ok. group_or_func(Func, Config) when Func == init_per_group; - Func == end_per_group; - Func == ct_init_per_group; - Func == ct_end_per_group -> + Func == end_per_group -> case ?val(tc_group_properties, Config) of undefined -> {Func,unknown,[]}; @@ -1206,8 +1161,8 @@ make_conf(Mod, Name, Props, TestSpec) -> "end_per_group/2 missing for group " "~p in ~p, using default.", [Name,Mod]), - {{?MODULE,ct_init_per_group}, - {?MODULE,ct_end_per_group}, + {{?MODULE,init_per_group}, + {?MODULE,end_per_group}, [{suite,Mod}]} end, {conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}. @@ -1467,22 +1422,31 @@ error_in_suite(Config) -> Reason = test_server:lookup_config(error,Config), exit(Reason). +%% if init_per_suite and end_per_suite are missing in the suite, +%% these will be called instead (without any trace of them in the +%% log files), only so that it's possible to call hook functions +%% for configuration +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + %% if the group config functions are missing in the suite, %% use these instead -ct_init_per_group(GroupName, Config) -> +init_per_group(GroupName, Config) -> ct:comment(io_lib:format("start of ~p", [GroupName])), ct_logs:log("TEST INFO", "init_per_group/2 for ~w missing " "in suite, using default.", [GroupName]), Config. -ct_end_per_group(GroupName, _) -> +end_per_group(GroupName, _) -> ct:comment(io_lib:format("end of ~p", [GroupName])), ct_logs:log("TEST INFO", "end_per_group/2 for ~w missing " "in suite, using default.", [GroupName]), ok. - %%%----------------------------------------------------------------- %%% @spec report(What,Data) -> ok @@ -1559,10 +1523,6 @@ report(What,Data) -> ok; {end_per_group,_} -> ok; - {ct_init_per_group,_} -> - ok; - {ct_end_per_group,_} -> - ok; {_,ok} -> add_to_stats(ok); {_,{skipped,{failed,{_,init_per_testcase,_}}}} -> @@ -1599,8 +1559,7 @@ report(What,Data) -> data=Data}), ct_hooks:on_tc_skip(What, Data), if Case /= end_per_suite, - Case /= end_per_group, - Case /= ct_end_per_group -> + Case /= end_per_group -> add_to_stats(auto_skipped); true -> ok diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index c42adbbdd9..2a23cd992b 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -70,8 +70,7 @@ terminate(Hooks) -> {skip, Reason :: term()} | {auto_skip, Reason :: term()} | {fail, Reason :: term()}. -init_tc(ct_framework, _Func, Args) -> - Args; + init_tc(Mod, init_per_suite, Config) -> Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of List when is_list(List) -> @@ -104,27 +103,21 @@ init_tc(_Mod, TC, Config) -> {auto_skip, Reason :: term()} | {fail, Reason :: term()} | ok | '$ct_no_change'. -end_tc(ct_framework, _Func, _Args, Result, _Return) -> - Result; end_tc(Mod, init_per_suite, Config, _Result, Return) -> call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config], '$ct_no_change'); - end_tc(Mod, end_per_suite, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config], '$ct_no_change'); - end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) -> call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config], '$ct_no_change'); - end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) -> Res = call(fun call_generic/3, Result, [post_end_per_group, GroupName, Config], '$ct_no_change'), maybe_stop_locker(Mod, GroupName,Opts), Res; - end_tc(_Mod, TC, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], '$ct_no_change'). diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 19ad7b26d8..0cd9b5f7cb 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -38,7 +38,7 @@ -export([get_ts_html_wrapper/3]). %% Logging stuff directly from testcase --export([tc_log/3,tc_print/3,tc_pal/3,ct_log/3, +-export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3, basic_html/0]). %% Simulate logger process for use without ct environment running @@ -239,7 +239,7 @@ end_tc(TCPid) -> %%% activity it is. <code>Format</code> and <code>Args</code> is the %%% data to log (as in <code>io:format(Format,Args)</code>).</p> log(Heading,Format,Args) -> - cast({log,self(),group_leader(), + cast({log,sync,self(),group_leader(), [{int_header(),[log_timestamp(now()),Heading]}, {Format,Args}, {int_footer(),[]}]}), @@ -261,7 +261,7 @@ log(Heading,Format,Args) -> %%% @see cont_log/2 %%% @see end_log/0 start_log(Heading) -> - cast({log,self(),group_leader(), + cast({log,sync,self(),group_leader(), [{int_header(),[log_timestamp(now()),Heading]}]}), ok. @@ -276,7 +276,7 @@ cont_log([],[]) -> ok; cont_log(Format,Args) -> maybe_log_timestamp(), - cast({log,self(),group_leader(),[{Format,Args}]}), + cast({log,sync,self(),group_leader(),[{Format,Args}]}), ok. %%%----------------------------------------------------------------- @@ -287,7 +287,7 @@ cont_log(Format,Args) -> %%% @see start_log/1 %%% @see cont_log/2 end_log() -> - cast({log,self(),group_leader(),[{int_footer(), []}]}), + cast({log,sync,self(),group_leader(),[{int_footer(), []}]}), ok. @@ -333,9 +333,32 @@ add_link(Heading,File,Type) -> %%% stuff directly from a testcase (i.e. not from within the CT %%% framework).</p> tc_log(Category,Format,Args) -> - cast({log,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), + tc_log(Category,"User",Format,Args). + +tc_log(Category,Printer,Format,Args) -> + cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]}, + {Format,Args}, + {div_footer(),[]}]}), + ok. + + +%%%----------------------------------------------------------------- +%%% @spec tc_log_async(Category,Format,Args) -> ok +%%% Category = atom() +%%% Format = string() +%%% Args = list() +%%% +%%% @doc Internal use only. +%%% +%%% <p>This function is used to perform asynchronous printouts +%%% towards the test server IO handler. This is necessary in order +%%% to avoid deadlocks when e.g. the hook that handles SASL printouts +%%% prints to the test case log file at the same time test server +%%% asks ct_logs for an html wrapper.</p> +tc_log_async(Category,Format,Args) -> + cast({log,async,self(),group_leader(),[{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. %%%----------------------------------------------------------------- @@ -349,19 +372,18 @@ tc_log(Category,Format,Args) -> %%% <p>This function is called by <code>ct</code> when printing %%% stuff a testcase on the user console.</p> tc_print(Category,Format,Args) -> - print_heading(Category), - io:format(user,Format,Args), - io:format(user,"\n\n",[]), + Head = get_heading(Category), + io:format(user, lists:concat([Head,Format,"\n\n"]), Args), ok. -print_heading(default) -> - io:format(user, - "----------------------------------------------------\n~s\n", - [log_timestamp(now())]); -print_heading(Category) -> - io:format(user, - "----------------------------------------------------\n~s ~w\n", - [log_timestamp(now()),Category]). +get_heading(default) -> + io_lib:format("-----------------------------" + "-----------------------\n~s\n", + [log_timestamp(now())]); +get_heading(Category) -> + io_lib:format("-----------------------------" + "-----------------------\n~s ~w\n", + [log_timestamp(now()),Category]). %%%----------------------------------------------------------------- @@ -377,9 +399,9 @@ print_heading(Category) -> %%% log and on the console.</p> tc_pal(Category,Format,Args) -> tc_print(Category,Format,Args), - cast({log,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), + cast({log,sync,self(),group_leader(),[{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. @@ -408,8 +430,10 @@ int_footer() -> "</div>". div_header(Class) -> - "<div class=\"" ++ atom_to_list(Class) ++ "\"><b>*** User " ++ - log_timestamp(now()) ++ " ***</b>". + div_header(Class,"User"). +div_header(Class,Printer) -> + "<div class=\"" ++ atom_to_list(Class) ++ "\"><b>*** " ++ Printer ++ + " " ++ log_timestamp(now()) ++ " ***</b>". div_footer() -> "</div>". @@ -420,7 +444,7 @@ maybe_log_timestamp() -> {MS,S,_} -> ok; _ -> - cast({log,self(),group_leader(), + cast({log,sync,self(),group_leader(), [{"<i>~s</i>",[log_timestamp({MS,S,US})]}]}) end. @@ -441,7 +465,8 @@ log_timestamp({MS,S,US}) -> orig_GL, ct_log_fd, tc_groupleaders, - stylesheet}). + stylesheet, + async_print_jobs}). logger(Parent,Mode) -> register(?MODULE,self()), @@ -520,50 +545,32 @@ logger(Parent,Mode) -> start_time=Time, orig_GL=group_leader(), ct_log_fd=CtLogFd, - tc_groupleaders=[]}). + tc_groupleaders=[], + async_print_jobs=[]}). logger_loop(State) -> receive - {log,Pid,GL,List} -> - case get_groupleader(Pid,GL,State) of + {log,SyncOrAsync,Pid,GL,List} -> + case get_groupleader(Pid, GL, State) of {tc_log,TCGL,TCGLs} -> case erlang:is_process_alive(TCGL) of true -> - %% we have to build one io-list of all strings - %% before printing, or other io printouts (made in - %% parallel) may get printed between this header - %% and footer - Fun = - fun({Str,Args},IoList) -> - case catch io_lib:format(Str,Args) of - {'EXIT',_Reason} -> - Fd = State#logger_state.ct_log_fd, - io:format(Fd, - "Logging fails! " - "Str: ~p, Args: ~p~n", - [Str,Args]), - %% stop the testcase, we need - %% to see the fault - exit(Pid,{log_printout_error,Str,Args}), - []; - IoStr when IoList == [] -> - [IoStr]; - IoStr -> - [IoList,"\n",IoStr] - end - end, - io:format(TCGL,"~s",[lists:foldl(Fun,[],List)]), - logger_loop(State#logger_state{tc_groupleaders=TCGLs}); + State1 = print_to_log(SyncOrAsync, Pid, TCGL, + List, State), + logger_loop(State1#logger_state{tc_groupleaders = + TCGLs}); false -> - %% Group leader is dead, so write to the CtLog instead + %% Group leader is dead, so write to the + %% CtLog instead Fd = State#logger_state.ct_log_fd, [begin io:format(Fd,Str,Args),io:nl(Fd) end || {Str,Args} <- List], logger_loop(State) end; {ct_log,Fd,TCGLs} -> - [begin io:format(Fd,Str,Args),io:nl(Fd) end || {Str,Args} <- List], - logger_loop(State#logger_state{tc_groupleaders=TCGLs}) + [begin io:format(Fd,Str,Args),io:nl(Fd) end || + {Str,Args} <- List], + logger_loop(State#logger_state{tc_groupleaders = TCGLs}) end; {{init_tc,TCPid,GL,RefreshLog},From} -> print_style(GL, State#logger_state.stylesheet), @@ -575,11 +582,12 @@ logger_loop(State) -> make_last_run_index(State#logger_state.start_time) end, return(From,ok), - logger_loop(State#logger_state{tc_groupleaders=TCGLs}); + logger_loop(State#logger_state{tc_groupleaders = TCGLs}); {{end_tc,TCPid},From} -> set_evmgr_gl(State#logger_state.ct_log_fd), return(From,ok), - logger_loop(State#logger_state{tc_groupleaders=rm_tc_gl(TCPid,State)}); + logger_loop(State#logger_state{tc_groupleaders = + rm_tc_gl(TCPid,State)}); {{get_log_dir,true},From} -> return(From,{ok,State#logger_state.log_dir}), logger_loop(State); @@ -590,21 +598,35 @@ logger_loop(State) -> make_last_run_index(State#logger_state.start_time), return(From,filename:basename(State#logger_state.log_dir)), logger_loop(State); - {set_stylesheet,_,SSFile} when State#logger_state.stylesheet == SSFile -> + {set_stylesheet,_,SSFile} when State#logger_state.stylesheet == + SSFile -> logger_loop(State); {set_stylesheet,TC,SSFile} -> Fd = State#logger_state.ct_log_fd, - io:format(Fd, "~p loading external style sheet: ~s~n", [TC,SSFile]), - logger_loop(State#logger_state{stylesheet=SSFile}); + io:format(Fd, "~p loading external style sheet: ~s~n", + [TC,SSFile]), + logger_loop(State#logger_state{stylesheet = SSFile}); {clear_stylesheet,_} when State#logger_state.stylesheet == undefined -> logger_loop(State); {clear_stylesheet,_} -> - logger_loop(State#logger_state{stylesheet=undefined}); + logger_loop(State#logger_state{stylesheet = undefined}); {ct_log, List} -> Fd = State#logger_state.ct_log_fd, [begin io:format(Fd,Str,Args),io:nl(Fd) end || {Str,Args} <- List], logger_loop(State); + {'DOWN',Ref,_,_Pid,_} -> + %% there might be print jobs executing in parallel with ct_logs + %% and whenever one is finished (indicated by 'DOWN'), the + %% next job should be spawned + case lists:delete(Ref, State#logger_state.async_print_jobs) of + [] -> + logger_loop(State#logger_state{async_print_jobs = []}); + Jobs -> + [Next|JobsRev] = lists:reverse(Jobs), + Jobs1 = [print_next(Next)|lists:reverse(JobsRev)], + logger_loop(State#logger_state{async_print_jobs = Jobs1}) + end; stop -> io:format(State#logger_state.ct_log_fd, int_header()++int_footer(), @@ -613,6 +635,49 @@ logger_loop(State) -> ok end. +create_io_fun(FromPid, State) -> + %% we have to build one io-list of all strings + %% before printing, or other io printouts (made in + %% parallel) may get printed between this header + %% and footer + Fd = State#logger_state.ct_log_fd, + fun({Str,Args}, IoList) -> + case catch io_lib:format(Str,Args) of + {'EXIT',_Reason} -> + io:format(Fd, "Logging fails! Str: ~p, Args: ~p~n", + [Str,Args]), + %% stop the testcase, we need to see the fault + exit(FromPid, {log_printout_error,Str,Args}), + []; + IoStr when IoList == [] -> + [IoStr]; + IoStr -> + [IoList,"\n",IoStr] + end + end. + +print_to_log(sync, FromPid, TCGL, List, State) -> + IoFun = create_io_fun(FromPid, State), + io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]), + State; + +print_to_log(async, FromPid, TCGL, List, State) -> + IoFun = create_io_fun(FromPid, State), + Printer = fun() -> + io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]) + end, + case State#logger_state.async_print_jobs of + [] -> + {_Pid,Ref} = spawn_monitor(Printer), + State#logger_state{async_print_jobs = [Ref]}; + Queue -> + State#logger_state{async_print_jobs = [Printer|Queue]} + end. + +print_next(PrintFun) -> + {_Pid,Ref} = spawn_monitor(PrintFun), + Ref. + %% #logger_state.tc_groupleaders == [{Pid,{Type,GLPid}},...] %% Type = tc | io %% @@ -855,33 +920,48 @@ insert_dir(D,[D1|Ds]) -> insert_dir(D,[]) -> [D]. -make_last_run_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt, Missing) -> - case last_test(Name) of +make_last_run_index([Name|Rest], Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Missing) -> + case get_run_dirs(Name) of false -> %% Silently skip. - make_last_run_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt, Missing); - LastLogDir -> + make_last_run_index(Rest, Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Missing); + LogDirs -> SuiteName = filename:rootname(filename:basename(Name)), - case make_one_index_entry(SuiteName, LastLogDir, "-", false, Missing) of - {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> - %% for backwards compatibility - AutoSkip1 = case catch AutoSkip+ASkip of - {'EXIT',_} -> undefined; - Res -> Res - end, - make_last_run_index(Rest, [Result|Result1], TotSucc+Succ, - TotFail+Fail, UserSkip+USkip, AutoSkip1, - TotNotBuilt+NotBuilt, Missing); - error -> - make_last_run_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt, Missing) - end + {Result1,TotSucc1,TotFail1,UserSkip1,AutoSkip1,TotNotBuilt1} = + make_last_run_index1(SuiteName, LogDirs, Result, + TotSucc, TotFail, + UserSkip, AutoSkip, + TotNotBuilt, Missing), + make_last_run_index(Rest, Result1, TotSucc1, TotFail1, + UserSkip1, AutoSkip1, + TotNotBuilt1, Missing) end; + make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, _) -> {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)], {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}. + +make_last_run_index1(SuiteName, [LogDir | LogDirs], Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Missing) -> + case make_one_index_entry(SuiteName, LogDir, "-", false, Missing) of + {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> + %% for backwards compatibility + AutoSkip1 = case catch AutoSkip+ASkip of + {'EXIT',_} -> undefined; + Res -> Res + end, + make_last_run_index1(SuiteName, LogDirs, [Result|Result1], TotSucc+Succ, + TotFail+Fail, UserSkip+USkip, AutoSkip1, + TotNotBuilt+NotBuilt, Missing); + error -> + make_last_run_index1(SuiteName, LogDirs, Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Missing) + end; +make_last_run_index1(_, [], Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, _) -> + {Result,TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}. make_one_index_entry(SuiteName, LogDir, Label, All, Missing) -> case count_cases(LogDir) of @@ -1633,8 +1713,8 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) -> sort_logdirs([Dir|Dirs],Groups) -> TestName = filename:rootname(filename:basename(Dir)), case filelib:wildcard(filename:join(Dir,"run.*")) of - [RunDir] -> - Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups), + RunDirs = [_|_] -> + Groups1 = sort_logdirs1(TestName,RunDirs,Groups), sort_logdirs(Dirs,Groups1); _ -> % ignore missing run directory sort_logdirs(Dirs,Groups) @@ -1642,6 +1722,12 @@ sort_logdirs([Dir|Dirs],Groups) -> sort_logdirs([],Groups) -> lists:keysort(1,sort_each_group(Groups)). +sort_logdirs1(TestName,[RunDir|RunDirs],Groups) -> + Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups), + sort_logdirs1(TestName,RunDirs,Groups1); +sort_logdirs1(_,[],Groups) -> + Groups. + insert_test(Test,IxDir,[{Test,IxDirs}|Groups]) -> [{Test,[IxDir|IxDirs]}|Groups]; insert_test(Test,IxDir,[]) -> @@ -1894,7 +1980,7 @@ simulate() -> simulate_logger_loop() -> receive - {log,_,_,List} -> + {log,_,_,_,List} -> S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List], io:format("~s",[S]), simulate_logger_loop(); @@ -1933,21 +2019,17 @@ notify_and_unlock_file(File) -> end. %%%----------------------------------------------------------------- -%%% @spec last_test(Dir) -> string() | false +%%% @spec get_run_dirs(Dir) -> [string()] | false %%% %%% @doc %%% -last_test(Dir) -> - last_test(filelib:wildcard(filename:join(Dir, "run.[1-2]*")), false). - -last_test([Run|Rest], false) -> - last_test(Rest, Run); -last_test([Run|Rest], Latest) when Run > Latest -> - last_test(Rest, Run); -last_test([_|Rest], Latest) -> - last_test(Rest, Latest); -last_test([], Latest) -> - Latest. +get_run_dirs(Dir) -> + case filelib:wildcard(filename:join(Dir, "run.[1-2]*")) of + [] -> + false; + RunDirs -> + lists:sort(RunDirs) + end. %%%----------------------------------------------------------------- %%% @spec xhtml(HTML, XHTML) -> HTML | XHTML diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index be3c485b75..e6eb135ae8 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -116,7 +116,7 @@ spawn_tester(script,Ctrl,Args) -> spawn_tester(func,Ctrl,Opts) -> Tester = fun() -> - case catch ct_run:run_test1(Opts) of + case catch ct_run:run_test2(Opts) of {'EXIT',Reason} -> exit(Reason); Result -> diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 05b10bca32..72124f6f21 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -37,7 +37,7 @@ %% Misc internal functions --export([variables_file_name/1,script_start1/2,run_test1/1]). +-export([variables_file_name/1,script_start1/2,run_test2/1]). -include("ct_event.hrl"). -include("ct_util.hrl"). @@ -63,6 +63,7 @@ stylesheet, multiply_timetraps = 1, scale_timetraps = false, + create_priv_dir, testspecs = [], tests}). @@ -178,6 +179,10 @@ script_start1(Parent, Args) -> fun([CT]) -> list_to_atom(CT); ([]) -> true end, false, Args), + CreatePrivDir = get_start_opt(create_priv_dir, + fun([PD]) -> list_to_atom(PD); + ([]) -> auto_per_tc + end, Args), EvHandlers = event_handler_args2opts(Args), CTHooks = ct_hooks_args2opts(Args), EnableBuiltinHooks = get_start_opt(enable_builtin_hooks, @@ -255,7 +260,8 @@ script_start1(Parent, Args) -> silent_connections = SilentConns, stylesheet = Stylesheet, multiply_timetraps = MultTT, - scale_timetraps = ScaleTT}, + scale_timetraps = ScaleTT, + create_priv_dir = CreatePrivDir}, %% check if log files should be refreshed or go on to run tests... Result = run_or_refresh(StartOpts, Args), @@ -322,12 +328,21 @@ script_start2(StartOpts = #opts{vts = undefined, Cover = choose_val(StartOpts#opts.cover, SpecStartOpts#opts.cover), - MultTT = choose_val(StartOpts#opts.multiply_timetraps, - SpecStartOpts#opts.multiply_timetraps), - ScaleTT = choose_val(StartOpts#opts.scale_timetraps, - SpecStartOpts#opts.scale_timetraps), - AllEvHs = merge_vals([StartOpts#opts.event_handlers, - SpecStartOpts#opts.event_handlers]), + MultTT = + choose_val(StartOpts#opts.multiply_timetraps, + SpecStartOpts#opts.multiply_timetraps), + ScaleTT = + choose_val(StartOpts#opts.scale_timetraps, + SpecStartOpts#opts.scale_timetraps), + + CreatePrivDir = + choose_val(StartOpts#opts.create_priv_dir, + SpecStartOpts#opts.create_priv_dir), + + AllEvHs = + merge_vals([StartOpts#opts.event_handlers, + SpecStartOpts#opts.event_handlers]), + AllCTHooks = merge_vals( [StartOpts#opts.ct_hooks, SpecStartOpts#opts.ct_hooks]), @@ -354,7 +369,8 @@ script_start2(StartOpts = #opts{vts = undefined, EnableBuiltinHooks, include = AllInclude, multiply_timetraps = MultTT, - scale_timetraps = ScaleTT}} + scale_timetraps = ScaleTT, + create_priv_dir = CreatePrivDir}} end; _ -> {undefined,StartOpts} @@ -567,6 +583,7 @@ script_usage() -> "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" "\n\t[-scale_timetraps]" + "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]" "\n\t[-basic_html]\n\n"), io:format("Run tests from command line:\n\n" "\tct_run [-dir TestDir1 TestDir2 .. TestDirN] |" @@ -586,6 +603,7 @@ script_usage() -> "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" "\n\t[-scale_timetraps]" + "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]" "\n\t[-basic_html]" "\n\t[-repeat N [-force_stop]] |" "\n\t[-duration HHMMSS [-force_stop]] |" @@ -606,6 +624,7 @@ script_usage() -> "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" "\n\t[-scale_timetraps]" + "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]" "\n\t[-basic_html]" "\n\t[-repeat N [-force_stop]] |" "\n\t[-duration HHMMSS [-force_stop]] |" @@ -782,6 +801,9 @@ run_test2(StartOpts) -> MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts), ScaleTT = get_start_opt(scale_timetraps, value, false, StartOpts), + %% create unique priv dir names + CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts), + %% auto compile & include files Include = case proplists:get_value(auto_compile, StartOpts) of @@ -842,7 +864,8 @@ run_test2(StartOpts) -> silent_connections = SilentConns, stylesheet = Stylesheet, multiply_timetraps = MultiplyTT, - scale_timetraps = ScaleTT}, + scale_timetraps = ScaleTT, + create_priv_dir = CreatePrivDir}, %% test specification case proplists:get_value(spec, StartOpts) of @@ -889,6 +912,8 @@ run_spec_file(Relaxed, SpecOpts#opts.multiply_timetraps), ScaleTT = choose_val(Opts#opts.scale_timetraps, SpecOpts#opts.scale_timetraps), + CreatePrivDir = choose_val(Opts#opts.create_priv_dir, + SpecOpts#opts.create_priv_dir), AllEvHs = merge_vals([Opts#opts.event_handlers, SpecOpts#opts.event_handlers]), AllInclude = merge_vals([Opts#opts.include, @@ -912,6 +937,7 @@ run_spec_file(Relaxed, testspecs = AbsSpecs, multiply_timetraps = MultTT, scale_timetraps = ScaleTT, + create_priv_dir = CreatePrivDir, ct_hooks = AllCTHooks, enable_builtin_hooks = EnableBuiltinHooks }, @@ -1170,7 +1196,8 @@ get_data_for_node(#testspec{label = Labels, enable_builtin_hooks = EnableBuiltinHooks, include = Incl, multiply_timetraps = MTs, - scale_timetraps = STs}, Node) -> + scale_timetraps = STs, + create_priv_dir = PDs}, Node) -> Label = proplists:get_value(Node, Labels), Profile = proplists:get_value(Node, Profiles), LogDir = case proplists:get_value(Node, LogDirs) of @@ -1184,6 +1211,7 @@ get_data_for_node(#testspec{label = Labels, Cover = proplists:get_value(Node, CoverFs), MT = proplists:get_value(Node, MTs), ST = proplists:get_value(Node, STs), + CreatePrivDir = proplists:get_value(Node, PDs), ConfigFiles = [{?ct_config_txt,F} || {N,F} <- Cfgs, N==Node] ++ [CBF || {N,CBF} <- UsrCfgs, N==Node], EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node], @@ -1200,7 +1228,8 @@ get_data_for_node(#testspec{label = Labels, enable_builtin_hooks = EnableBuiltinHooks, include = Include, multiply_timetraps = MT, - scale_timetraps = ST}. + scale_timetraps = ST, + create_priv_dir = CreatePrivDir}. refresh_logs(LogDir) -> {ok,Cwd} = file:get_cwd(), @@ -1384,7 +1413,8 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> %% which framework it runs under. case os:getenv("TEST_SERVER_FRAMEWORK") of false -> - os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework"); + os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework"), + os:putenv("TEST_SERVER_FRAMEWORK_NAME", "common_test"); "ct_framework" -> ok; Other -> @@ -1746,25 +1776,31 @@ set_group_leader_same_as_shell() -> false end. -check_and_add([{TestDir0,M,_} | Tests], Added) -> +check_and_add([{TestDir0,M,_} | Tests], Added, PA) -> case locate_test_dir(TestDir0, M) of {ok,TestDir} -> case lists:member(TestDir, Added) of true -> - check_and_add(Tests, Added); + check_and_add(Tests, Added, PA); false -> - true = code:add_patha(TestDir), - check_and_add(Tests, [TestDir|Added]) + case lists:member(rm_trailing_slash(TestDir), + code:get_path()) of + false -> + true = code:add_patha(TestDir), + check_and_add(Tests, [TestDir|Added], [TestDir|PA]); + true -> + check_and_add(Tests, [TestDir|Added], PA) + end end; {error,_} -> {error,{invalid_directory,TestDir0}} end; -check_and_add([], _) -> - ok. +check_and_add([], _, PA) -> + {ok,PA}. do_run_test(Tests, Skip, Opts) -> - case check_and_add(Tests, []) of - ok -> + case check_and_add(Tests, [], []) of + {ok,AddedToPath} -> ct_util:set_testdata({stats,{0,0,{0,0}}}), ct_util:set_testdata({cover,undefined}), test_server_ctrl:start_link(local), @@ -1842,6 +1878,8 @@ do_run_test(Tests, Skip, Opts) -> test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps), test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps), + test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir, + auto_per_run)), ct_event:notify(#event{name=start_info, node=node(), data={NoOfTests,NoOfSuites,NoOfCases}}), @@ -1858,7 +1896,9 @@ do_run_test(Tests, Skip, Opts) -> end, lists:foreach(fun(Suite) -> maybe_cleanup_interpret(Suite, Opts#opts.step) - end, CleanUp); + end, CleanUp), + [code:del_path(Dir) || Dir <- AddedToPath], + ok; Error -> Error end. @@ -2289,7 +2329,7 @@ ct_hooks_args2opts(Args) -> Acc end,[],Args). -ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) -> +ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) when Arg /= "and" -> ct_hooks_args2opts(Rest,[{list_to_atom(CTH), parse_cth_args(Arg), parse_cth_args(Prio)}|Acc]); @@ -2347,31 +2387,38 @@ event_handler_init_args2opts([]) -> %% relative dirs "post run_test erl_args" is not kept! rel_to_abs(CtArgs) -> {PA,PZ} = get_pa_pz(CtArgs, [], []), - io:format(user, "~n", []), [begin - code:del_path(filename:basename(D)), - Abs = filename:absname(D), - code:add_pathz(Abs), - if D /= Abs -> + Dir = rm_trailing_slash(D), + Abs = make_abs(Dir), + if Dir /= Abs -> + code:del_path(Dir), + code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_pathz/1~n", - [D, Abs]); + [Dir, Abs]); true -> - ok - end + code:del_path(Dir) + end, + code:add_pathz(Abs) end || D <- PZ], [begin - code:del_path(filename:basename(D)), - Abs = filename:absname(D), - code:add_patha(Abs), - if D /= Abs -> + Dir = rm_trailing_slash(D), + Abs = make_abs(Dir), + if Dir /= Abs -> + code:del_path(Dir), + code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_patha/1~n", - [D, Abs]); - true ->ok - end + [Dir, Abs]); + true -> + code:del_path(Dir) + end, + code:add_patha(Abs) end || D <- PA], - io:format(user, "~n", []). + io:format(user, "~n", []). + +rm_trailing_slash(Dir) -> + filename:join(filename:split(Dir)). get_pa_pz([{pa,Dirs} | Args], PA, PZ) -> get_pa_pz(Args, PA ++ Dirs, PZ); @@ -2382,6 +2429,19 @@ get_pa_pz([_ | Args], PA, PZ) -> get_pa_pz([], PA, PZ) -> {PA,PZ}. +make_abs(RelDir) -> + Tokens = filename:split(filename:absname(RelDir)), + filename:join(lists:reverse(make_abs1(Tokens, []))). + +make_abs1([".."|Dirs], [_Dir|Path]) -> + make_abs1(Dirs, Path); +make_abs1(["."|Dirs], Path) -> + make_abs1(Dirs, Path); +make_abs1([Dir|Dirs], Path) -> + make_abs1(Dirs, [Dir|Path]); +make_abs1([], Path) -> + Path. + %% This function translates ct:run_test/1 start options %% to ct_run start arguments (on the init arguments format) - %% this is useful mainly for testing the ct_run start functions. @@ -2419,6 +2479,10 @@ opts2args(EnvStartOpts) -> [{scale_timetraps,[]}]; ({scale_timetraps,false}) -> []; + ({create_priv_dir,auto_per_run}) -> + []; + ({create_priv_dir,PD}) when is_atom(PD) -> + [{create_priv_dir,[atom_to_list(PD)]}]; ({force_stop,true}) -> [{force_stop,[]}]; ({force_stop,false}) -> diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index b68cbd3aa1..5b197c0c81 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -568,6 +568,21 @@ add_tests([{scale_timetraps,Node,ST}|Ts],Spec) -> 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)), @@ -1158,7 +1173,8 @@ valid_terms() -> {skip_groups,6}, {skip_groups,7}, {skip_cases,5}, - {skip_cases,6} + {skip_cases,6}, + {create_priv_dir,2} ]. %% this function "guesses" if the user has misspelled a term name diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 3b6ad6f98d..e9bfb2590b 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -827,15 +827,20 @@ get_profile_data(Profile, Key, StartDir) -> %%%----------------------------------------------------------------- %%% Internal functions call(Msg) -> - MRef = erlang:monitor(process,whereis(ct_util_server)), - Ref = make_ref(), - ct_util_server ! {Msg,{self(),Ref}}, - receive - {Ref, Result} -> - erlang:demonitor(MRef, [flush]), - Result; - {'DOWN',MRef,process,_,Reason} -> - {error,{ct_util_server_down,Reason}} + case whereis(ct_util_server) of + undefined -> + {error,ct_util_server_not_running}; + Pid -> + MRef = erlang:monitor(process, Pid), + Ref = make_ref(), + ct_util_server ! {Msg,{self(),Ref}}, + receive + {Ref, Result} -> + erlang:demonitor(MRef, [flush]), + Result; + {'DOWN',MRef,process,_,Reason} -> + {error,{ct_util_server_down,Reason}} + end end. return({To,Ref},Result) -> diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index bde832811a..082599a9c6 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -43,9 +43,10 @@ include=[], multiply_timetraps=[], scale_timetraps=[], + create_priv_dir=[], alias=[], tests=[], - merge_tests = true }). + merge_tests=true}). -record(cover, {app=none, level=details, diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl index 14663b7738..ea79251cfc 100644 --- a/lib/common_test/src/cth_log_redirect.erl +++ b/lib/common_test/src/cth_log_redirect.erl @@ -31,22 +31,22 @@ %% Event handler Callbacks -export([init/1, handle_event/2, handle_call/2, handle_info/2, - terminate/2]). + terminate/1]). id(_Opts) -> ?MODULE. init(?MODULE, _Opts) -> error_logger:add_report_handler(?MODULE), - tc_log. + tc_log_async. -post_init_per_group(Group, Config, Result, tc_log) -> +post_init_per_group(Group, Config, Result, tc_log_async) -> case lists:member(parallel,proplists:get_value( tc_group_properties,Config,[])) of true -> {Result, {set_log_func(ct_log),Group}}; false -> - {Result, tc_log} + {Result, tc_log_async} end; post_init_per_group(_Group, _Config, Result, State) -> {Result, State}. @@ -58,14 +58,14 @@ post_end_per_testcase(_TC, _Config, Result, State) -> {Result, State}. pre_end_per_group(Group, Config, {ct_log, Group}) -> - {Config, set_log_func(tc_log)}; + {Config, set_log_func(tc_log_async)}; pre_end_per_group(_Group, Config, State) -> {Config, State}. %% Copied and modified from sasl_report_tty_h.erl init(_Type) -> - {ok, tc_log}. + {ok, tc_log_async}. handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() -> {ok, State}; @@ -101,7 +101,8 @@ handle_call({set_logfunc,NewLogFunc},_) -> {ok, NewLogFunc, NewLogFunc}; handle_call(_Query, _State) -> {error, bad_query}. -terminate(_Reason, _Type) -> +terminate(_State) -> + error_logger:delete_report_handler(?MODULE), []. tag_event(Event) -> diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl index cc8a932887..9dfb0bd6b8 100644 --- a/lib/common_test/src/vts.erl +++ b/lib/common_test/src/vts.erl @@ -766,10 +766,6 @@ report1(tc_done,{_Suite,init_per_group,_},State) -> State; report1(tc_done,{_Suite,end_per_group,_},State) -> State; -report1(tc_done,{_Suite,ct_init_per_group,_},State) -> - State; -report1(tc_done,{_Suite,ct_end_per_group,_},State) -> - State; report1(tc_done,{_Suite,_Case,ok},State) -> State#state{ok=State#state.ok+1}; report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) -> diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 284612b8f7..332c444145 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -29,6 +29,7 @@ MODULES= \ ct_test_support_eh \ ct_userconfig_callback \ ct_smoke_test_SUITE \ + ct_priv_dir_SUITE \ ct_event_handler_SUITE \ ct_config_info_SUITE \ ct_groups_test_1_SUITE \ diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index 2b3157ff3b..79ed51bc28 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -61,7 +61,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [cfg_error, lib_error, no_compile, timetrap_end_conf, timetrap_normal, timetrap_extended, timetrap_parallel, - timetrap_fun]. + timetrap_fun, misc_errors]. groups() -> []. @@ -249,6 +249,24 @@ timetrap_fun(Config) when is_list(Config) -> TestEvents = events_to_check(timetrap_fun), ok = ct_test_support:verify_events(TestEvents, Events, Config). +%%%----------------------------------------------------------------- +%%% +misc_errors(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Join = fun(D, S) -> filename:join(D, "error/test/"++S) end, + Suites = [Join(DataDir, "misc_error_1_SUITE")], + {Opts,ERPid} = setup([{suite,Suites}], Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(misc_errors, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(misc_errors), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS @@ -682,7 +700,7 @@ test_events(timetrap_end_conf) -> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{1,1,6}}, + {?eh,start_info,{1,1,9}}, {?eh,tc_start,{timetrap_1_SUITE,init_per_suite}}, {?eh,tc_done,{timetrap_1_SUITE,init_per_suite,ok}}, {?eh,tc_start,{timetrap_1_SUITE,tc1}}, @@ -709,6 +727,18 @@ test_events(timetrap_end_conf) -> {?eh,tc_done, {timetrap_1_SUITE,tc6,{failed,{testcase_aborted,testing_end_conf}}}}, {?eh,test_stats,{0,6,{0,0}}}, + {?eh,tc_start,{timetrap_1_SUITE,tc7}}, + {?eh,tc_done, + {timetrap_1_SUITE,tc7,{failed,{timetrap_timeout,1000}}}}, + {?eh,test_stats,{0,7,{0,0}}}, + {?eh,tc_start,{timetrap_1_SUITE,tc8}}, + {?eh,tc_done, + {timetrap_1_SUITE,tc8,{failed,{timetrap_timeout,1000}}}}, + {?eh,test_stats,{0,8,{0,0}}}, + {?eh,tc_start,{timetrap_1_SUITE,tc9}}, + {?eh,tc_done, + {timetrap_1_SUITE,tc9,{failed,{timetrap_timeout,1000}}}}, + {?eh,test_stats,{0,9,{0,0}}}, {?eh,tc_start,{timetrap_1_SUITE,end_per_suite}}, {?eh,tc_done,{timetrap_1_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, @@ -892,4 +922,40 @@ test_events(timetrap_fun) -> {?eh,tc_done,{timetrap_7_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} + ]; + +test_events(misc_errors) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,7}}, + {?eh,tc_start,{misc_error_1_SUITE,ct_fail_1}}, + {?eh,tc_done,{misc_error_1_SUITE,ct_fail_1, + {failed,{error,{test_case_failed,{error,this_is_expected}}}}}}, + {?eh,test_stats,{0,1,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,ct_fail_2}}, + {?eh,tc_done,{misc_error_1_SUITE,ct_fail_2, + {failed,{error,{test_case_failed,"this_is_expected"}}}}}, + {?eh,test_stats,{0,2,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,ct_fail_3}}, + {?eh,tc_done,{misc_error_1_SUITE,ct_fail_3, + {failed,{error,{test_case_failed,this_is_expected}}}}}, + {?eh,test_stats,{0,3,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,ts_fail_1}}, + {?eh,tc_done,{misc_error_1_SUITE,ts_fail_1, + {failed,{error,{suite_failed,this_is_expected}}}}}, + {?eh,test_stats,{0,4,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,ts_fail_2}}, + {?eh,tc_done,{misc_error_1_SUITE,ts_fail_2, + {failed,{error,{suite_failed,this_is_expected}}}}}, + {?eh,test_stats,{0,5,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_1}}, + {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_1,i_die_now}}, + {?eh,test_stats,{0,6,{0,0}}}, + {?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_2}}, + {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_2, + {failed,testcase_aborted_or_killed}}}, + {?eh,test_stats,{0,7,{0,0}}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} ]. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl new file mode 100644 index 0000000000..4e20875505 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl @@ -0,0 +1,154 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. 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(misc_error_1_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% Function: suite() -> Info +%% Info = [tuple()] +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,3}}]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% Function: groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% Function: all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%%-------------------------------------------------------------------- +all() -> + [ct_fail_1, ct_fail_2, ct_fail_3, ts_fail_1, ts_fail_2, + killed_by_signal_1, killed_by_signal_2]. + +ct_fail_1(_) -> + ct:fail({error,this_is_expected}), + exit(this_should_not_be_seen), + ok. + +ct_fail_2(_) -> + ct:fail("~w", [this_is_expected]), + exit(this_should_not_be_seen), + ok. + +ct_fail_3(_) -> + fail_me(fun() -> ct:fail(this_is_expected) end), + exit(this_should_not_be_seen), + ok. + +ts_fail_1(_) -> + test_server:fail(this_is_expected), + exit(this_should_not_be_seen), + ok. + +ts_fail_2(_) -> + fail_me(fun() -> test_server:fail(this_is_expected) end), + exit(this_should_not_be_seen), + ok. + +fail_me(Fun) -> + Fun(), + ok. + +killed_by_signal_1(_) -> + spawn_link(fun() -> ct:sleep(100), + exit(i_die_now) + end), + ct:sleep(1000), + exit(this_should_not_be_seen). + +killed_by_signal_2(_) -> + TCPid = self(), + spawn_link(fun() -> ct:sleep(100), + exit(TCPid, kill) + end), + ct:sleep(1000), + exit(this_should_not_be_seen). diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl index cb3109349b..a44ff6d0bc 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl @@ -83,23 +83,11 @@ init_per_testcase(TC, Config) -> ets:insert(?MODULE, {last_case,fail}), init_per_testcase1(TC, Config). -init_per_testcase1(tc1, Config) -> - [{tc,tc1}|Config]; - -init_per_testcase1(tc2, Config) -> - [{tc,tc2}|Config]; - -init_per_testcase1(tc3, Config) -> - [{tc,tc3}|Config]; - init_per_testcase1(tc4, Config) -> [{tc,tc4},{default_timeout,5000}|Config]; -init_per_testcase1(tc5, Config) -> - [{tc,tc5}|Config]; - -init_per_testcase1(tc6, Config) -> - [{tc,tc6}|Config]. +init_per_testcase1(TC, Config) -> + [{tc,TC}|Config]. %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config0) -> @@ -145,7 +133,28 @@ end_per_testcase1(tc5, Config) -> end_per_testcase1(tc6, Config) -> ct:pal("end_per_testcase(tc6): ~p", [Config]), tc6 = ?config(tc, Config), - exit(end_per_tc_fail_after_abort). + exit(end_per_tc_fail_after_abort); + +end_per_testcase1(tc7, Config) -> + ct:pal("end_per_testcase(tc7): ~p", [Config]), + tc7 = ?config(tc, Config), + {failed,timetrap_timeout} = ?config(tc_status, Config), + ok; + +end_per_testcase1(tc8, Config) -> + ct:pal("end_per_testcase(tc8): ~p", [Config]), + tc8 = ?config(tc, Config), + {failed,timetrap_timeout} = ?config(tc_status, Config), + ok; + +end_per_testcase1(tc9, Config) -> + ct:pal("end_per_testcase(tc9): ~p", [Config]), + tc9 = ?config(tc, Config), + %% check that it's possible to send and receive synchronously + %% with the group leader process for end_per_testcase + test_server:stop_node(dummy@somehost), + ok. + %%-------------------------------------------------------------------- %% Function: groups() -> [Group] @@ -170,25 +179,46 @@ groups() -> %% Reason = term() %%-------------------------------------------------------------------- all() -> - [tc1, tc2, tc3, tc4, tc5, tc6]. + [tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8, tc9]. tc1(_) -> - timer:sleep(2000). + timer:sleep(2000), + ok. tc2(_) -> timer:sleep(2000). tc3(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), - timer:sleep(2000). + timer:sleep(2000), + ok. tc4(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), - timer:sleep(2000). + timer:sleep(2000), + ok. tc5(_) -> - timer:sleep(2000). + timer:sleep(2000), + ok. tc6(_) -> spawn(ct, abort_current_testcase, [testing_end_conf]), timer:sleep(2000). + +tc7(_) -> + sleep(2000), + ok. + +tc8(_) -> + timetrap_helper:sleep(2000), + ok. + +tc9(_) -> + sleep(2000), + ok. + +%%%----------------------------------------------------------------- +sleep(T) -> + timer:sleep(T), + ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl new file mode 100644 index 0000000000..1389acca11 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl @@ -0,0 +1,7 @@ +-module(timetrap_helper). + +-export([sleep/1]). + +sleep(T) -> + timer:sleep(T), + ok. diff --git a/lib/common_test/test/ct_group_info_SUITE.erl b/lib/common_test/test/ct_group_info_SUITE.erl index 2da8219196..18016c4979 100644 --- a/lib/common_test/test/ct_group_info_SUITE.erl +++ b/lib/common_test/test/ct_group_info_SUITE.erl @@ -440,72 +440,72 @@ test_events(timetrap_all_no_ipg) -> {?eh,tc_done,{group_timetrap_3_SUITE,t1,{failed,{timetrap_timeout,1000}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t11,{failed,{timetrap_timeout,500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t21,{failed,{timetrap_timeout,1500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t2,{failed,{timetrap_timeout,1000}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t41,{failed,{timetrap_timeout,250}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t31,{failed,{timetrap_timeout,500}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t51,{failed,{timetrap_timeout,1500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t3,{failed,{timetrap_timeout,250}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t61,{failed,{timetrap_timeout,500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t81,{failed,{timetrap_timeout,750}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t71,{failed,{timetrap_timeout,500}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t91,{failed,{timetrap_timeout,250}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t101,{failed,{timetrap_timeout,1000}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t111,{failed,{timetrap_timeout,1000}}}}, {?eh,test_stats,{0,14,{0,0}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} @@ -779,78 +779,78 @@ test_events(require_no_ipg) -> {?eh,start_info,{1,1,13}}, {?eh,tc_done,{group_require_3_SUITE,t1,ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t11,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t21,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t31,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]}, + [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}, {skipped,{require_failed,{name_in_use,common2_alias,common2}}}}}, {?eh,tc_auto_skip,{group_require_3_SUITE,t41, {require_failed,{name_in_use,common2_alias,common2}}}}, {?eh,test_stats,{4,0,{0,1}}}, - {?eh,tc_auto_skip,{ct_framework,ct_end_per_group, + {?eh,tc_auto_skip,{ct_framework,end_per_group, {require_failed,{name_in_use,common2_alias,common2}}}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t61,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}], {?eh,tc_done,{group_require_3_SUITE,t51,ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t71,ok}}, {?eh,tc_done,{group_require_3_SUITE,t72,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]}, + [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}, {skipped,{require_failed,{not_available,non_existing}}}}}, {?eh,tc_auto_skip,{group_require_3_SUITE,t81, {require_failed,{not_available,non_existing}}}}, {?eh,test_stats,{8,0,{0,2}}}, - {?eh,tc_auto_skip,{ct_framework,ct_end_per_group, + {?eh,tc_auto_skip,{ct_framework,end_per_group, {require_failed,{not_available,non_existing}}}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t91, {skipped,{require_failed,{not_available,non_existing}}}}}, {?eh,test_stats,{8,0,{0,3}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t101,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t111,ok}}, {?eh,test_stats,{10,0,{0,3}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl index 2392b0b850..c3601ba0ce 100644 --- a/lib/common_test/test/ct_groups_test_2_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl @@ -171,16 +171,16 @@ test_events(missing_conf) -> {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, {?eh,start_info,{1,1,2}}, - {?eh,tc_start,{ct_framework,{ct_init_per_group,group1,[]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,group1,[]},ok}}, + {?eh,tc_start,{ct_framework,{init_per_group,group1,[]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,group1,[]},ok}}, {?eh,tc_start,{missing_conf_SUITE,tc1}}, {?eh,tc_done,{missing_conf_SUITE,tc1,ok}}, {?eh,test_stats,{1,0,{0,0}}}, {?eh,tc_start,{missing_conf_SUITE,tc2}}, {?eh,tc_done,{missing_conf_SUITE,tc2,ok}}, {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,group1,[]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,group1,[]},ok}}, + {?eh,tc_start,{ct_framework,{end_per_group,group1,[]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,group1,[]},ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 2c519f08b5..efe57a7d0b 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -83,10 +83,9 @@ 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, prio_cth + fail_n_skip_with_minimal_cth, prio_cth, no_config ] - ) - . + ). %%-------------------------------------------------------------------- @@ -214,6 +213,10 @@ prio_cth(Config) when is_list(Config) -> [{empty_cth,[1000],1000},{empty_cth,[900],900}, {prio_cth,[1100,100],100},{prio_cth,[1100]}],Config). +no_config(Config) when is_list(Config) -> + do_test(no_config, "ct_no_config_SUITE.erl", + [verify_config_cth],Config). + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- @@ -1078,6 +1081,56 @@ test_events(prio_cth) -> {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; +test_events(no_config) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{empty_cth,init,[verify_config_cth,[]]}}, + {?eh,start_info,{1,1,2}}, + {?eh,tc_start,{ct_framework,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite, + [ct_no_config_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_no_config_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_framework,init_per_suite,ok}}, + {?eh,tc_start,{ct_no_config_SUITE,test_case_1}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_1,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_1,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_no_config_SUITE,test_case_1,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_init_per_group, + [test_group,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_group, + [test_group,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_framework, + {init_per_group,test_group,'$proplist'},ok}}, + {?eh,tc_start,{ct_no_config_SUITE,test_case_2}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_2,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_2,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_no_config_SUITE,test_case_2,ok}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_end_per_group, + [test_group,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_group, + [test_group,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}], + {?eh,tc_start,{ct_framework,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite, + [ct_no_config_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_suite, + [ct_no_config_SUITE,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_framework,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + test_events(ok) -> ok. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl new file mode 100644 index 0000000000..2ad80aa1e7 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl @@ -0,0 +1,64 @@ +%%
+%% %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_no_config_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify 2 things:
+%%%
+%%% 1) All hook pre/post functions get called, even if no init/end
+%%% config functions exist in the suite (new from ver 1.6.1, R15B01).
+%%%
+%%% 2) The hook functions can read Config list elements, as well as
+%%% required config variables, even if no init/end config
+%%% functions exist.
+
+suite() ->
+ [{timetrap, {seconds,1}},
+ {ct_hooks, [verify_config_cth]},
+ {require,suite_cfg},
+ {default_config,suite_cfg,?MODULE}].
+
+group(test_group) ->
+ [{require,group_cfg},
+ {default_config,group_cfg,test_group}].
+
+test_case_1() ->
+ [{require,test_case_1_cfg},
+ {default_config,test_case_1_cfg,test_case_1}].
+
+test_case_2() ->
+ [{require,test_case_2_cfg},
+ {default_config,test_case_2_cfg,test_case_2}].
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+test_case_1(Config) ->
+ ok.
+
+test_case_2(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl index 7befcfa57c..2529e806ea 100644 --- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl @@ -1,277 +1,277 @@ -%%
-%% %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%
-%%
-
-%%% @doc Common Test Example Suite Callback module.
-%%%
-%%% <p>This module gives an example of a common test CTH (Common Test Hook).
-%%% There are many ways to add a CTH to a test run, you can do it either in
-%%% the command line using -ct_hook, in a test spec using
-%%% {ct_hook,M} or in the suite it self by returning ct_hook
-%%% from either suite/0, init_per_suite/1, init_per_group/2 and
-%%% init_per_testcase/2. The scope of the CTH is determined by where is it
-%%% started. If it is started in the command line or test spec then it will
-%%% be stopped at the end of all tests. If it is started in init_per_suite,
-%%% it will be stopped after end_per_suite and so on. See terminate
-%%% documentation for a table describing the scoping machanics.
-%%%
-%%% All of callbacks except init/1 in a CTH are optional.</p>
-
--module(empty_cth).
-
-%% CT Hooks
--export([id/1]).
--export([init/2]).
-
--export([pre_init_per_suite/3]).
--export([post_init_per_suite/4]).
--export([pre_end_per_suite/3]).
--export([post_end_per_suite/4]).
-
--export([pre_init_per_group/3]).
--export([post_init_per_group/4]).
--export([pre_end_per_group/3]).
--export([post_end_per_group/4]).
-
--export([pre_init_per_testcase/3]).
--export([post_end_per_testcase/4]).
-
--export([on_tc_fail/3]).
--export([on_tc_skip/3]).
-
--export([terminate/1]).
-
--include_lib("common_test/src/ct_util.hrl").
--include_lib("common_test/include/ct_event.hrl").
-
+%% +%% %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% +%% + +%%% @doc Common Test Example Suite Callback module. +%%% +%%% <p>This module gives an example of a common test CTH (Common Test Hook). +%%% There are many ways to add a CTH to a test run, you can do it either in +%%% the command line using -ct_hook, in a test spec using +%%% {ct_hook,M} or in the suite it self by returning ct_hook +%%% from either suite/0, init_per_suite/1, init_per_group/2 and +%%% init_per_testcase/2. The scope of the CTH is determined by where is it +%%% started. If it is started in the command line or test spec then it will +%%% be stopped at the end of all tests. If it is started in init_per_suite, +%%% it will be stopped after end_per_suite and so on. See terminate +%%% documentation for a table describing the scoping machanics. +%%% +%%% All of callbacks except init/1 in a CTH are optional.</p> + +-module(empty_cth). + +%% CT Hooks +-export([id/1]). +-export([init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + -type config() :: proplists:proplist(). --type reason() :: term().
--type skip_or_fail() :: {skip, reason()} |
- {auto_skip, reason()} |
- {fail, reason()} |
- {'EXIT',reason()}.
-
--record(state, { id = ?MODULE :: term()}).
-
-%% @doc Always called before any other callback function. Use this to initiate
-%% any common state. It should return an state for this CTH.
+-type reason() :: term(). +-type skip_or_fail() :: {skip, reason()} | + {auto_skip, reason()} | + {fail, reason()} | + {'EXIT',reason()}. + +-record(state, { id = ?MODULE :: term()}). + +%% @doc Always called before any other callback function. Use this to initiate +%% any common state. It should return an state for this CTH. -spec init(Id :: term(), Opts :: proplists:proplist()) -> - {ok, State :: #state{}}.
-init(Id, Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, init, [Id, Opts]}}),
- {ok,Opts}.
-
-%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
-%% return the same ID the seconds CTH is ignored. This function should NOT
-%% have any side effects as it might be called multiple times by common test.
+ {ok, State :: #state{}}. +init(Id, Opts) -> + gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, init, [Id, Opts]}}), + {ok,Opts}. + +%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's +%% return the same ID the seconds CTH is ignored. This function should NOT +%% have any side effects as it might be called multiple times by common test. -spec id(Opts :: proplists:proplist()) -> - Id :: term().
-id(Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, id, [Opts]}}),
- now().
-
-%% @doc Called before init_per_suite is called. Note that this callback is
-%% only called if the CTH is added before init_per_suite is run (eg. in a test
-%% specification, suite/0 function etc).
-%% You can change the config in the this function.
--spec pre_init_per_suite(Suite :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after init_per_suite.
-%% you can change the return value in this function.
--spec post_init_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before end_per_suite. The config/state can be changed here,
-%% though it will only affect the *end_per_suite function.
--spec pre_end_per_suite(Suite :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after end_per_suite. Note that the config cannot be
-%% changed here, only the status of the suite.
--spec post_end_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each init_per_group.
-%% You can change the config in this function.
--spec pre_init_per_group(Group :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each init_per_group.
-%% You can change the return value in this function.
--spec post_init_per_group(Group :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after each end_per_group. The config/state can be changed here,
-%% though it will only affect the *end_per_group functions.
--spec pre_end_per_group(Group :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each end_per_group. Note that the config cannot be
-%% changed here, only the status of the group.
--spec post_end_per_group(Group :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each test case.
-%% You can change the config in this function.
--spec pre_init_per_testcase(TC :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_testcase(TC,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_testcase,
- [TC,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each test case. Note that the config cannot be
-%% changed here, only the status of the test case.
--spec post_end_per_testcase(TC :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_testcase(TC,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_testcase,
- [TC,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
-%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
-%% This function should be used for extra cleanup which might be needed.
-%% It is not possible to modify the config or the status of the test run.
--spec on_tc_fail(TC :: init_per_suite | end_per_suite |
- init_per_group | end_per_group | atom(),
- Reason :: term(), State :: #state{}) ->
- NewState :: #state{}.
-on_tc_fail(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_fail,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when a test case is skipped by either user action
-%% or due to an init function failing. Test case can be
-%% end_per_suite, init_per_group, end_per_group and the actual test cases.
--spec on_tc_skip(TC :: end_per_suite |
- init_per_group | end_per_group | atom(),
- {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
- {tc_user_skip, {skipped, Reason :: term()}},
- State :: #state{}) ->
- NewState :: #state{}.
-on_tc_skip(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_skip,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when the scope of the CTH is done, this depends on
-%% when the CTH was specified. This translation table describes when this
-%% function is called.
-%%
-%% | Started in | terminate called |
-%% |---------------------|-------------------------|
-%% | command_line | after all tests are run |
-%% | test spec | after all tests are run |
-%% | suite/0 | after SUITE is done |
-%% | init_per_suite/1 | after SUITE is done |
-%% | init_per_group/2 | after group is done |
-%% |-----------------------------------------------|
-%%
--spec terminate(State :: #state{}) ->
- term().
-terminate(State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, terminate, [State]}}),
- ok.
+ Id :: term(). +id(Opts) -> + gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, id, [Opts]}}), + now(). + +%% @doc Called before init_per_suite is called. Note that this callback is +%% only called if the CTH is added before init_per_suite is run (eg. in a test +%% specification, suite/0 function etc). +%% You can change the config in the this function. +-spec pre_init_per_suite(Suite :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_suite(Suite,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_suite, + [Suite,Config,State]}}), + {Config, State}. + +%% @doc Called after init_per_suite. +%% you can change the return value in this function. +-spec post_init_per_suite(Suite :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_suite(Suite,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_init_per_suite, + [Suite,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before end_per_suite. The config/state can be changed here, +%% though it will only affect the *end_per_suite function. +-spec pre_end_per_suite(Suite :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_suite(Suite,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_end_per_suite, + [Suite,Config,State]}}), + {Config, State}. + +%% @doc Called after end_per_suite. Note that the config cannot be +%% changed here, only the status of the suite. +-spec post_end_per_suite(Suite :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_suite(Suite,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_suite, + [Suite,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before each init_per_group. +%% You can change the config in this function. +-spec pre_init_per_group(Group :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_group(Group,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_group, + [Group,Config,State]}}), + {Config, State}. + +%% @doc Called after each init_per_group. +%% You can change the return value in this function. +-spec post_init_per_group(Group :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_group(Group,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_init_per_group, + [Group,Config,Return,State]}}), + {Return, State}. + +%% @doc Called after each end_per_group. The config/state can be changed here, +%% though it will only affect the *end_per_group functions. +-spec pre_end_per_group(Group :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_group(Group,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_end_per_group, + [Group,Config,State]}}), + {Config, State}. + +%% @doc Called after each end_per_group. Note that the config cannot be +%% changed here, only the status of the group. +-spec post_end_per_group(Group :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_group(Group,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_group, + [Group,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before each test case. +%% You can change the config in this function. +-spec pre_init_per_testcase(TC :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_testcase(TC,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_testcase, + [TC,Config,State]}}), + {Config, State}. + +%% @doc Called after each test case. Note that the config cannot be +%% changed here, only the status of the test case. +-spec post_end_per_testcase(TC :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_testcase(TC,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_testcase, + [TC,Config,Return,State]}}), + {Return, State}. + +%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group, +%% post_end_per_group and post_end_per_tc if the suite, group or test case failed. +%% This function should be used for extra cleanup which might be needed. +%% It is not possible to modify the config or the status of the test run. +-spec on_tc_fail(TC :: init_per_suite | end_per_suite | + init_per_group | end_per_group | atom(), + Reason :: term(), State :: #state{}) -> + NewState :: #state{}. +on_tc_fail(TC, Reason, State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, on_tc_fail, + [TC,Reason,State]}}), + State. + +%% @doc Called when a test case is skipped by either user action +%% or due to an init function failing. Test case can be +%% end_per_suite, init_per_group, end_per_group and the actual test cases. +-spec on_tc_skip(TC :: end_per_suite | + init_per_group | end_per_group | atom(), + {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} | + {tc_user_skip, {skipped, Reason :: term()}}, + State :: #state{}) -> + NewState :: #state{}. +on_tc_skip(TC, Reason, State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, on_tc_skip, + [TC,Reason,State]}}), + State. + +%% @doc Called when the scope of the CTH is done, this depends on +%% when the CTH was specified. This translation table describes when this +%% function is called. +%% +%% | Started in | terminate called | +%% |---------------------|-------------------------| +%% | command_line | after all tests are run | +%% | test spec | after all tests are run | +%% | suite/0 | after SUITE is done | +%% | init_per_suite/1 | after SUITE is done | +%% | init_per_group/2 | after group is done | +%% |-----------------------------------------------| +%% +-spec terminate(State :: #state{}) -> + term(). +terminate(State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, terminate, [State]}}), + ok. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl new file mode 100644 index 0000000000..99ea261e14 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl @@ -0,0 +1,130 @@ +%%
+%% %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(verify_config_cth).
+
+-include_lib("common_test/src/ct_util.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+-define(val(K, L), proplists:get_value(K, L)).
+
+id(Opts) ->
+ ?MODULE.
+
+init(Id, Opts) ->
+ {ok, State} = empty_cth:init(Id, Opts),
+ {ok, State}.
+
+pre_init_per_suite(Suite, Config, State) ->
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_init_per_suite(Suite,
+ [{pre_init_per_suite,true} | Config],
+ State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_init_per_suite, Return),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_init_per_suite(Suite,
+ Config,
+ [{post_init_per_suite,true} | Return],
+ State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_end_per_suite(Suite,
+ [{pre_end_per_suite,true} | Config],
+ State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_end_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_init_per_group(Group,
+ [{pre_init_per_group,true} | Config],
+ State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_init_per_group, Return),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_init_per_group(Group,
+ Config,
+ [{post_init_per_group,true} | Return],
+ State).
+
+pre_end_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_end_per_group(Group,
+ [{pre_end_per_group,true} | Config],
+ State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_end_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:pre_init_per_testcase(TC,
+ [{pre_init_per_testcase,true} | Config],
+ State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ true = ?val(post_init_per_suite, Config),
+ true = ?val(pre_init_per_testcase, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE.erl new file mode 100644 index 0000000000..f6942d59bf --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE.erl @@ -0,0 +1,277 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_priv_dir_SUITE +%%% +%%% Description: +%%% Test that it works to use the create_priv_dir option. +%%% +%%%------------------------------------------------------------------- +-module(ct_priv_dir_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + default, + auto_per_run, + auto_per_tc, + manual_per_tc, + spec_default, + spec_auto_per_run, + spec_auto_per_run, + spec_manual_per_tc + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +default(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "priv_dir_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{testcase,default}, + {label,default}], Config), + ok = execute(default, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +auto_per_run(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "priv_dir_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{testcase,default}, + {label,auto_per_run}, + {create_priv_dir,auto_per_run}], Config), + ok = execute(auto_per_run, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +auto_per_tc(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "priv_dir_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{testcase,auto_per_tc}, + {label,auto_per_tc}, + {create_priv_dir,auto_per_tc}], Config), + ok = execute(auto_per_tc, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +manual_per_tc(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "priv_dir_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{testcase,manual_per_tc}, + {label,manual_per_tc}, + {create_priv_dir,manual_per_tc}], Config), + ok = execute(manual_per_tc, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +spec_default(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "default.spec"), + {Opts,ERPid} = setup([{spec,Spec}, + {label,spec_default}], Config), + ok = execute(spec_default, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +spec_auto_per_run(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "auto_per_run.spec"), + {Opts,ERPid} = setup([{spec,Spec}, + {label,spec_auto_per_run}], Config), + ok = execute(spec_auto_per_run, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +spec_auto_per_tc(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "auto_per_tc.spec"), + {Opts,ERPid} = setup([{spec,Spec}, + {label,spec_auto_per_tc}], Config), + ok = execute(spec_auto_per_tc, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +spec_manual_per_tc(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "manual_per_tc.spec"), + {Opts,ERPid} = setup([{spec,Spec}, + {label,spec_manual_per_tc}], Config), + ok = execute(spec_manual_per_tc, Opts, ERPid, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + + +test_events(DEF) when DEF == default ; DEF == auto_per_run -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,default}}, + {?eh,tc_done,{priv_dir_SUITE,default,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(auto_per_tc) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,auto_per_tc}}, + {?eh,tc_done,{priv_dir_SUITE,auto_per_tc,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(manual_per_tc) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,manual_per_tc}}, + {?eh,tc_done,{priv_dir_SUITE,manual_per_tc,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(SPECDEF) when SPECDEF == spec_default ; + SPECDEF == spec_auto_per_run -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,default}}, + {?eh,tc_done,{priv_dir_SUITE,default,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(spec_auto_per_tc) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,auto_per_tc}}, + {?eh,tc_done,{priv_dir_SUITE,auto_per_tc,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]; + +test_events(spec_manual_per_tc) -> + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{priv_dir_SUITE,init_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{priv_dir_SUITE,manual_per_tc}}, + {?eh,tc_done,{priv_dir_SUITE,manual_per_tc,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{priv_dir_SUITE,end_per_suite}}, + {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]}]. + diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec new file mode 100644 index 0000000000..4dde0ed1f4 --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec @@ -0,0 +1,5 @@ +{create_priv_dir, auto_per_run}. + +{alias, curr, "./"}. + +{cases, curr, priv_dir_SUITE, default}.
\ No newline at end of file diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec new file mode 100644 index 0000000000..c265500865 --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec @@ -0,0 +1,5 @@ +{create_priv_dir, auto_per_tc}. + +{alias, curr, "./"}. + +{cases, curr, priv_dir_SUITE, auto_per_tc}.
\ No newline at end of file diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec new file mode 100644 index 0000000000..2f053e792f --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec @@ -0,0 +1,3 @@ +{alias, curr, "./"}. + +{cases, curr, priv_dir_SUITE, default}.
\ No newline at end of file diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec new file mode 100644 index 0000000000..4f98734d5f --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec @@ -0,0 +1,5 @@ +{create_priv_dir, manual_per_tc}. + +{alias, curr, "./"}. + +{cases, curr, priv_dir_SUITE, manual_per_tc}.
\ No newline at end of file diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl new file mode 100644 index 0000000000..423cb2999b --- /dev/null +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl @@ -0,0 +1,127 @@ +%%%------------------------------------------------------------------- +%%% @author Peter Andersson <[email protected]> +%%% @copyright (C) 2012, Peter Andersson +%%% @doc +%%% +%%% @end +%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]> +%%%------------------------------------------------------------------- +-module(priv_dir_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,30}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + []. + +default(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + "log_private" = filename:basename(PrivDir), + {ok,_} = file:list_dir(PrivDir). + +auto_per_tc(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + ["log_private",_] = string:tokens(filename:basename(PrivDir), "."), + {ok,_} = file:list_dir(PrivDir). + +manual_per_tc(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + ["log_private",_] = string:tokens(filename:basename(PrivDir), "."), + {error,_} = file:list_dir(PrivDir), + ok = ct:make_priv_dir(), + {ok,_} = file:list_dir(PrivDir). + diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl index efc0309781..8825d84884 100644 --- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl +++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl @@ -242,25 +242,28 @@ test_events(ts_if_1) -> {?eh,tc_auto_skip,{ts_if_5_SUITE,end_per_suite, {require_failed_in_suite0,{not_available,undef_variable}}}}, - {?eh,tc_start,{ts_if_6_SUITE,tc1}}, - {?eh,tc_done,{ts_if_6_SUITE,tc1,{failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, - {?eh,test_stats,{3,5,{6,8}}}, + {?eh,tc_start,{ct_framework,init_per_suite}}, + {?eh,tc_done,{ct_framework,init_per_suite, + {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, + {?eh,tc_auto_skip,{ts_if_6_SUITE,tc1, + {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, + {?eh,test_stats,{3,5,{5,9}}}, {?eh,tc_start,{ts_if_7_SUITE,tc1}}, {?eh,tc_done,{ts_if_7_SUITE,tc1,ok}}, - {?eh,test_stats,{4,5,{6,8}}}, + {?eh,test_stats,{4,5,{5,9}}}, {?eh,tc_start,{ts_if_8_SUITE,tc1}}, {?eh,tc_done,{ts_if_8_SUITE,tc1,{failed,{error,failed_on_purpose}}}}, - {?eh,test_stats,{4,6,{6,8}}}, + {?eh,test_stats,{4,6,{5,9}}}, {?eh,tc_user_skip,{skipped_by_spec_1_SUITE,all,"should be skipped"}}, - {?eh,test_stats,{4,6,{7,8}}}, + {?eh,test_stats,{4,6,{6,9}}}, {?eh,tc_start,{skipped_by_spec_2_SUITE,init_per_suite}}, {?eh,tc_done,{skipped_by_spec_2_SUITE,init_per_suite,ok}}, {?eh,tc_user_skip,{skipped_by_spec_2_SUITE,tc1,"should be skipped"}}, - {?eh,test_stats,{4,6,{8,8}}}, + {?eh,test_stats,{4,6,{7,9}}}, {?eh,tc_start,{skipped_by_spec_2_SUITE,end_per_suite}}, {?eh,tc_done,{skipped_by_spec_2_SUITE,end_per_suite,ok}}, diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index 2f43c1bc17..b94f7f7593 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1 +1 @@ -COMMON_TEST_VSN = 1.6 +COMMON_TEST_VSN = 1.6.1 |