diff options
-rw-r--r-- | lib/common_test/src/ct_run.erl | 52 | ||||
-rw-r--r-- | lib/common_test/test/Makefile | 14 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE.erl | 35 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl | 22 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl | 47 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl | 46 | ||||
-rw-r--r-- | lib/test_server/src/test_server.erl | 136 | ||||
-rw-r--r-- | lib/test_server/src/test_server_sup.erl | 1 |
8 files changed, 282 insertions, 71 deletions
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 2d79021ce4..9dc0bff26a 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -77,6 +77,12 @@ script_start() -> Init = init:get_arguments(), CtArgs = lists:takewhile(fun({ct_erl_args,_}) -> false; (_) -> true end, Init), + + %% convert relative dirs added with pa or pz (pre erl_args on + %% the run_test command line) to absolute so that app modules + %% can be found even after CT changes CWD to logdir + rel_to_abs(CtArgs), + Args = case application:get_env(common_test, run_test_start_opts) of {ok,EnvStartOpts} -> @@ -1910,9 +1916,51 @@ event_handler_init_args2opts([EH, Arg]) -> event_handler_init_args2opts([]) -> []. -%% this function translates ct:run_test/1 start options +%% This function reads pa and pz arguments, converts dirs from relative +%% to absolute, and re-inserts them in the code path. The order of the +%% dirs in the code path remain the same. Note however that since this +%% function is only used for arguments "pre run_test erl_args", the order +%% 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 -> + io:format(user, "Converting ~p to ~p and re-inserting " + "with add_pathz/1~n", + [D, Abs]); + true -> + ok + end + end || D <- PZ], + [begin + code:del_path(filename:basename(D)), + Abs = filename:absname(D), + code:add_patha(Abs), + if D /= Abs -> + io:format(user, "Converting ~p to ~p and re-inserting " + "with add_patha/1~n", + [D, Abs]); + true ->ok + end + end || D <- PA], + io:format(user, "~n", []). + +get_pa_pz([{pa,Dirs} | Args], PA, PZ) -> + get_pa_pz(Args, PA ++ Dirs, PZ); +get_pa_pz([{pz,Dirs} | Args], PA, PZ) -> + get_pa_pz(Args, PA, PZ ++ Dirs); +get_pa_pz([_ | Args], PA, PZ) -> + get_pa_pz(Args, PA, PZ); +get_pa_pz([], PA, PZ) -> + {PA,PZ}. + +%% This function translates ct:run_test/1 start options %% to run_test start arguments (on the init arguments format) - -%% this is useful mainly for testing the ct_run start functions +%% this is useful mainly for testing the ct_run start functions. opts2args(EnvStartOpts) -> lists:flatmap(fun({config,CfgFiles}) -> [{ct_config,[CfgFiles]}]; diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 2f1ff9a835..97ded5eb9a 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -65,18 +65,18 @@ EBIN = . # Targets # ---------------------------------------------------- -.PHONY: make_emakefile +#.PHONY: make_emakefile -make_emakefile: - $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ - > $(EMAKEFILE) +#make_emakefile: +# $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ +# > $(EMAKEFILE) -tests debug opt: make_emakefile +tests debug opt: erl $(ERL_MAKE_FLAGS) -make clean: - rm -f $(EMAKEFILE) rm -f $(TARGET_FILES) +# rm -f $(EMAKEFILE) rm -f core docs: @@ -88,7 +88,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt -release_tests_spec: make_emakefile +release_tests_spec: $(INSTALL_DIR) $(RELSYSDIR) $(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) $(INSTALL_PROGRAM) common_test.spec $(RELSYSDIR) diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index 7327fa5816..2fa031b884 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -90,7 +90,9 @@ cfg_error(Config) when is_list(Config) -> Join(DataDir, "cfg_error_9_SUITE"), Join(DataDir, "cfg_error_10_SUITE"), Join(DataDir, "cfg_error_11_SUITE"), - Join(DataDir, "cfg_error_12_SUITE") + Join(DataDir, "cfg_error_12_SUITE"), + Join(DataDir, "cfg_error_13_SUITE"), + Join(DataDir, "cfg_error_14_SUITE") ], {Opts,ERPid} = setup([{suite,Suites}], Config), ok = ct_test_support:run(Opts, Config), @@ -231,7 +233,7 @@ test_events(cfg_error) -> [ {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, - {?eh,start_info,{12,12,39}}, + {?eh,start_info,{14,14,42}}, {?eh,tc_start,{cfg_error_1_SUITE,init_per_suite}}, {?eh,tc_done, @@ -568,15 +570,34 @@ test_events(cfg_error) -> {?eh,tc_done,{cfg_error_12_SUITE,tc1,{failed,{timetrap_timeout,500}}}}, {?eh,test_stats,{13,4,{1,19}}}, {?eh,tc_start,{cfg_error_12_SUITE,tc2}}, - {?eh,tc_done,{cfg_error_12_SUITE,tc2, - {failed, - {cfg_error_12_SUITE,end_per_testcase, - {timetrap_timeout,500}}}}}, + {?eh,tc_done,{cfg_error_12_SUITE,tc2,{failed, + {cfg_error_12_SUITE,end_per_testcase, + {timetrap_timeout,500}}}}}, {?eh,test_stats,{14,4,{1,19}}}, {?eh,tc_start,{cfg_error_12_SUITE,tc3}}, {?eh,tc_done,{cfg_error_12_SUITE,tc3,ok}}, {?eh,test_stats,{15,4,{1,19}}}, - + {?eh,tc_start,{cfg_error_12_SUITE,tc4}}, + {?eh,tc_done,{cfg_error_12_SUITE,tc4,{failed, + {cfg_error_12_SUITE,end_per_testcase, + {timetrap_timeout,500}}}}}, + {?eh,test_stats,{16,4,{1,19}}}, + {?eh,tc_start,{cfg_error_13_SUITE,init_per_suite}}, + {?eh,tc_done,{cfg_error_13_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{cfg_error_13_SUITE,tc1}}, + {?eh,tc_done,{cfg_error_13_SUITE,tc1,ok}}, + {?eh,test_stats,{17,4,{1,19}}}, + {?eh,tc_start,{cfg_error_13_SUITE,end_per_suite}}, + {?eh,tc_done,{cfg_error_13_SUITE,end_per_suite,ok}}, + {?eh,tc_start,{cfg_error_14_SUITE,init_per_suite}}, + {?eh,tc_done,{cfg_error_14_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{cfg_error_14_SUITE,tc1}}, + {?eh,tc_done,{cfg_error_14_SUITE,tc1,ok}}, + {?eh,test_stats,{18,4,{1,19}}}, + {?eh,tc_start,{cfg_error_14_SUITE,end_per_suite}}, + {?eh,tc_done,{cfg_error_14_SUITE,end_per_suite, + {comment, + "should succeed since ct_fw cancels timetrap in end_tc"}}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl index 271eaa673e..75f00c1de3 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl @@ -22,14 +22,20 @@ -include_lib("common_test/include/ct.hrl"). +init_per_testcase(_, Config) -> + Config. + end_per_testcase(tc2, _Config) -> timer:sleep(2000), exit(this_should_not_be_printed); +end_per_testcase(tc4, _Config) -> + timer:sleep(2000), + exit(this_should_not_be_printed); end_per_testcase(_, _) -> ok. all() -> - [tc1, tc2, tc3]. + [tc1, tc2, tc3, tc4]. %%%----------------------------------------------------------------- tc1() -> @@ -66,3 +72,17 @@ tc3(_) -> (_, Default) -> Default end), {comment,"should succeed since ct_fw cancels timetrap in end_tc"}. + +%%%----------------------------------------------------------------- +tc4() -> + put('$test_server_framework_test', + fun(end_tc, _Default) -> + ct:pal("end_tc(~p): Night time...",[self()]), + timer:sleep(1000), + ct:pal("end_tc(~p): Day time!",[self()]); + (_, Default) -> Default + end), + [{timetrap,500}]. + +tc4(_) -> + ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl new file mode 100644 index 0000000000..d1c18d0bb0 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl @@ -0,0 +1,47 @@ +%% +%% %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(cfg_error_13_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +init_per_suite() -> + put('$test_server_framework_test', + fun(end_tc, _Default) -> + ct:pal("end_tc(~p): Night time...",[self()]), + timer:sleep(1000), + ct:pal("end_tc(~p): Day time!",[self()]); + (_, Default) -> Default + end), + [{timetrap,500}]. + +init_per_suite(Config) -> + ct:comment("should succeed since ct_fw cancels timetrap in end_tc"), + Config. + +end_per_suite(_) -> + ok. + +all() -> + [tc1]. + +%%%----------------------------------------------------------------- +tc1(_) -> + dummy. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl new file mode 100644 index 0000000000..367f5f06d6 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl @@ -0,0 +1,46 @@ +%% +%% %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(cfg_error_14_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +init_per_suite(Config) -> + Config. + +end_per_suite() -> + put('$test_server_framework_test', + fun(end_tc, _Default) -> + ct:pal("end_tc(~p): Night time...",[self()]), + timer:sleep(1000), + ct:pal("end_tc(~p): Day time!",[self()]); + (_, Default) -> Default + end), + [{timetrap,500}]. + +end_per_suite(_Config) -> + {comment,"should succeed since ct_fw cancels timetrap in end_tc"}. + +all() -> + [tc1]. + +%%%----------------------------------------------------------------- +tc1(_) -> + dummy. diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 940935edf5..acc9dbaab8 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -737,64 +737,91 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> case Reason of {timetrap_timeout,TVal,Loc} -> %% convert Loc to form that can be formatted - Loc1 = mod_loc(Loc), - {Mod,Func} = get_mf(Loc1), - %% call end_per_testcase on a separate process, only so that the - %% user has a chance to clean up after init_per_testcase, even - %% after a timetrap timeout - NewCurrConf = - case CurrConf of - {{Mod,Func},Conf} -> - EndConfPid = - call_end_conf(Mod,Func,Pid, - {timetrap_timeout,TVal}, - Loc1,[{tc_status, - {failed, - timetrap_timeout}}|Conf], - TVal), - {EndConfPid,{Mod,Func},Conf}; - _ -> - %% The framework functions mustn't execute on this - %% group leader process or io will cause deadlock, - %% so we spawn a dedicated process for the operation - %% and let the group leader go back to handle io. - spawn_fw_call(Mod,Func,Pid,{timetrap_timeout,TVal}, - Loc1,self(),Comment), - undefined - end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,NewCurrConf); + case mod_loc(Loc) of + {FwMod,FwFunc,framework} -> + %% timout during framework call + spawn_fw_call(FwMod,FwFunc,Pid, + {framework_error,{timetrap,TVal}}, + unknown,self(),Comment), + run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + Comment,undefined); + Loc1 -> + {Mod,Func} = get_mf(Loc1), + %% call end_per_testcase on a separate process, + %% only so that the user has a chance to clean up + %% after init_per_testcase, even after a timetrap timeout + NewCurrConf = + case CurrConf of + {{Mod,Func},Conf} -> + EndConfPid = + call_end_conf(Mod,Func,Pid, + {timetrap_timeout,TVal}, + Loc1,[{tc_status, + {failed, + timetrap_timeout}}|Conf], + TVal), + {EndConfPid,{Mod,Func},Conf}; + _ -> + %% The framework functions mustn't execute on this + %% group leader process or io will cause deadlock, + %% so we spawn a dedicated process for the operation + %% and let the group leader go back to handle io. + spawn_fw_call(Mod,Func,Pid,{timetrap_timeout,TVal}, + Loc1,self(),Comment), + undefined + end, + run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + Comment,NewCurrConf) + end; {timetrap_timeout,TVal,Loc,InitOrEnd} -> - Loc1 = mod_loc(Loc), - {Mod,_Func} = get_mf(Loc1), - spawn_fw_call(Mod,InitOrEnd,Pid,{timetrap_timeout,TVal}, - Loc1,self(),Comment), + case mod_loc(Loc) of + {FwMod,FwFunc,framework} -> + %% timout during framework call + spawn_fw_call(FwMod,FwFunc,Pid, + {framework_error,{timetrap,TVal}}, + unknown,self(),Comment); + Loc1 -> + {Mod,_Func} = get_mf(Loc1), + spawn_fw_call(Mod,InitOrEnd,Pid,{timetrap_timeout,TVal}, + Loc1,self(),Comment) + end, run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); {testcase_aborted,AbortReason,AbortLoc} -> - Loc1 = mod_loc(AbortLoc), - {Mod,Func} = get_mf(Loc1), - %% call end_per_testcase on a separate process, only so that the - %% user has a chance to clean up after init_per_testcase, even - %% after abortion ErrorMsg = {testcase_aborted,AbortReason}, - NewCurrConf = - case CurrConf of - {{Mod,Func},Conf} -> - TVal = case lists:keysearch(default_timeout,1,Conf) of - {value,{default_timeout,Tmo}} -> Tmo; - _ -> ?DEFAULT_TIMETRAP_SECS*1000 - end, - EndConfPid = - call_end_conf(Mod,Func,Pid,ErrorMsg, - Loc1,[{tc_status,{failed,ErrorMsg}}|Conf], - TVal), - {EndConfPid,{Mod,Func},Conf}; - _ -> - spawn_fw_call(Mod,Func,Pid,ErrorMsg, - Loc1,self(),Comment), - undefined - end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,NewCurrConf); + case mod_loc(AbortLoc) of + {FwMod,FwFunc,framework} -> + %% abort during framework call + spawn_fw_call(FwMod,FwFunc,Pid, + {framework_error,ErrorMsg}, + unknown,self(),Comment), + run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + Comment,undefined); + Loc1 -> + {Mod,Func} = get_mf(Loc1), + %% call end_per_testcase on a separate process, only so + %% that the user has a chance to clean up after init_per_testcase, + %% even after abortion + NewCurrConf = + case CurrConf of + {{Mod,Func},Conf} -> + TVal = case lists:keysearch(default_timeout,1,Conf) of + {value,{default_timeout,Tmo}} -> Tmo; + _ -> ?DEFAULT_TIMETRAP_SECS*1000 + end, + EndConfPid = + call_end_conf(Mod,Func,Pid,ErrorMsg, + Loc1, + [{tc_status,{failed,ErrorMsg}}|Conf], + TVal), + {EndConfPid,{Mod,Func},Conf}; + _ -> + spawn_fw_call(Mod,Func,Pid,ErrorMsg, + Loc1,self(),Comment), + undefined + end, + run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + Comment,NewCurrConf) + end; killed -> %% result of an exit(TestCase,kill) call, which is the %% only way to abort a testcase process that traps exits @@ -920,6 +947,7 @@ spawn_fw_call(Mod,{init_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why, {TVal/1000,Skip,Loc,[],Comment}} end, spawn_link(FwCall); + spawn_fw_call(Mod,{end_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why, Loc,SendTo,_Comment) -> FwCall = diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl index 2b4e1efb71..625724fbb5 100644 --- a/lib/test_server/src/test_server_sup.erl +++ b/lib/test_server/src/test_server_sup.erl @@ -504,6 +504,7 @@ framework_call(Callback,Func,Args,DefaultReturn) -> end, case erlang:function_exported(Mod,Func,length(Args)) of true -> + put(test_server_loc, {Mod,Func,framework}), EH = fun(Reason) -> exit({fw_error,{Mod,Func,Reason}}) end, try apply(Mod,Func,Args) of Result -> |