diff options
Diffstat (limited to 'lib/common_test')
-rw-r--r-- | lib/common_test/priv/ct_default.css | 10 | ||||
-rw-r--r-- | lib/common_test/src/ct.erl | 26 | ||||
-rw-r--r-- | lib/common_test/src/ct_framework.erl | 37 | ||||
-rw-r--r-- | lib/common_test/src/ct_logs.erl | 193 | ||||
-rw-r--r-- | lib/common_test/src/ct_repeat.erl | 2 | ||||
-rw-r--r-- | lib/common_test/src/ct_run.erl | 80 | ||||
-rw-r--r-- | lib/common_test/src/ct_util.erl | 23 | ||||
-rw-r--r-- | lib/common_test/src/cth_log_redirect.erl | 15 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE.erl | 66 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl | 154 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl | 57 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl | 7 | ||||
-rw-r--r-- | lib/common_test/vsn.mk | 2 |
13 files changed, 520 insertions, 152 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..0a77527b2f 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -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() @@ -845,6 +861,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..cdd8a6a596 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -806,31 +806,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 +844,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 diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 19ad7b26d8..b2f669fefe 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 %% @@ -1894,7 +1959,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(); 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..45436392d8 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"). @@ -1746,25 +1746,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), @@ -1858,7 +1864,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. @@ -2347,31 +2355,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 +2397,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. 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/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/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index 2b3157ff3b..053edba846 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,8}}, {?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,14 @@ 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,end_per_suite}}, {?eh,tc_done,{timetrap_1_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, @@ -892,4 +918,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..faa0a7305c 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,19 @@ 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. %%-------------------------------------------------------------------- %% Function: groups() -> [Group] @@ -170,25 +170,42 @@ groups() -> %% Reason = term() %%-------------------------------------------------------------------- all() -> - [tc1, tc2, tc3, tc4, tc5, tc6]. + [tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8]. 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. + +%%%----------------------------------------------------------------- +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/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 |