diff options
Diffstat (limited to 'lib/test_server/src')
-rw-r--r-- | lib/test_server/src/test_server.erl | 290 | ||||
-rw-r--r-- | lib/test_server/src/test_server_ctrl.erl | 85 | ||||
-rw-r--r-- | lib/test_server/src/ts_run.erl | 27 |
3 files changed, 246 insertions, 156 deletions
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 6e94e4861a..17c5f5b253 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -35,6 +35,7 @@ -export([fail/0,fail/1,format/1,format/2,format/3]). -export([capture_start/0,capture_stop/0,capture_get/0]). -export([messages_get/0]). +-export([permit_io/2]). -export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]). -export([timetrap_scale_factor/0,timetrap/1,get_timetrap_info/0, timetrap_cancel/1,timetrap_cancel/0]). @@ -49,7 +50,7 @@ -export([run_on_shielded_node/2]). -export([is_cover/0,is_debug/0,is_commercial/0]). --export([break/1,continue/0]). +-export([break/1,break/2,break/3,continue/0,continue/1]). %%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0, @@ -523,7 +524,7 @@ stick_all_sticky(Node,Sticky) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) -> +%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData,RejectIoReqs) -> %% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment} %% %% Time = float() (seconds) @@ -558,8 +559,12 @@ stick_all_sticky(Node,Sticky) -> %% ScaleTimetrap indicates if test_server should attemp to automatically %% compensate timetraps for runtime delays introduced by e.g. tools like %% cover. +%% +%% RejectIoReqs (bool) is information about whether printouts to stdout +%% should be visible in the minor log file or not. -run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> +run_test_case_apply({CaseNum,Mod,Func,Args,Name, + RunInit,TimetrapData,RejectIoReqs}) -> purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]), case os:getenv("TS_RUN_VALGRIND") of false -> @@ -570,17 +575,19 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> end, test_server_h:testcase({Mod,Func,1}), ProcBef = erlang:system_info(process_count), - Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData), + Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs), ProcAft = erlang:system_info(process_count), purify_new_leaks(), DetFail = get(test_server_detected_fail), {Result,DetFail,ProcBef,ProcAft}. -run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData, RejectIoReqs) -> case get(test_server_job_dir) of undefined -> %% i'm a local target - do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData); + do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs); JobDir -> %% i'm a remote target case Args of @@ -595,13 +602,14 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> Config2 = lists:keyreplace(priv_dir, 1, Config1, {priv_dir,TargetPrivDir}), do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit, - TimetrapData); + TimetrapData, RejectIoReqs); _other -> do_run_test_case_apply(Mod, Func, Args, Name, RunInit, - TimetrapData) + TimetrapData, RejectIoReqs) end end. -do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs) -> {ok,Cwd} = file:get_cwd(), Args2Print = case Args of [Args1] when is_list(Args1) -> @@ -628,7 +636,8 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> end), group_leader(OldGLeader, self()), put(test_server_detected_fail, []), - run_test_case_msgloop(Ref, Pid, false, false, "", undefined, starting). + run_test_case_msgloop(Ref, Pid, false, RejectIoReqs, false, "", + undefined, starting). %% Ugly bug (pre R5A): %% If this process (group leader of the test case) terminates before @@ -639,7 +648,7 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> %% A test case is known to have failed if it returns {'EXIT', _} tuple, %% or sends a message {failed, File, Line} to it's group_leader %% -run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, +run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate, Comment, CurrConf, Status) -> %% NOTE: Keep job_proxy_msgloop/0 up to date when changes %% are made in this function! @@ -655,7 +664,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, receive {test_case_initialized,Pid} -> - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,running); Abort = {abort_current_testcase,_,_} when Status == starting -> %% we're in init phase, must must postpone this operation @@ -663,7 +672,7 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, %% gets killed) self() ! Abort, erlang:yield(), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {abort_current_testcase,Reason,From} -> Line = case is_process_alive(Pid) of @@ -694,82 +703,92 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Error1 end end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, NewComment,CurrConf,Status); + {permit_io,FromPid} -> + put({permit_io,FromPid},true), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,unicode_to_latin1(Bytes),From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + unicode_to_latin1(Bytes),From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); IoReq when element(1, IoReq) == io_request -> %% something else, just pass it on group_leader() ! IoReq, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {structured_io,ClientPid,Msg} -> output(Msg, ClientPid), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {capture,NewCapture} -> - run_test_case_msgloop(Ref,Pid,NewCapture,Terminate, + run_test_case_msgloop(Ref,Pid,NewCapture,RejectIoReqs,Terminate, Comment,CurrConf,Status); {sync_apply,From,MFA} -> sync_local_or_remote_apply(false,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {sync_apply_proxy,Proxy,From,MFA} -> sync_local_or_remote_apply(Proxy,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {printout,Detail,Format,Args} -> print(Detail,Format,Args), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {comment,NewComment} -> NewComment1 = test_server_ctrl:to_string(NewComment), @@ -783,18 +802,20 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Other -> Other end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate1, NewComment2,CurrConf,Status); {read_comment,From} -> From ! {self(),read_comment,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {set_curr_conf,From,NewCurrConf} -> From ! {self(),set_curr_conf,ok}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,NewCurrConf,Status); {make_priv_dir,From} when CurrConf == undefined -> - From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}; + From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {make_priv_dir,From} -> Result = case proplists:get_value(priv_dir, element(2, CurrConf)) of @@ -811,12 +832,12 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end end, From ! {self(),make_priv_dir,Result}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, Comment,CurrConf,Status); {'EXIT',Pid,{Ref,Time,Value,Loc,Opts}} -> RetVal = {Time/1000000,Value,mod_loc(Loc),Opts,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',Pid,Reason} -> case Reason of {timetrap_timeout,TVal,Loc} -> @@ -827,7 +848,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,{timetrap,TVal}}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); Loc1 -> @@ -860,7 +882,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, NewCurrConf,Status) end; @@ -877,15 +900,16 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {timetrap_timeout,TVal}, Loc1,self()) end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {testcase_aborted,ErrorMsg={user_timetrap_error,_},AbortLoc} -> %% user timetrap function caused exit %% during start of test case {Mod,Func} = get_mf(mod_loc(AbortLoc)), spawn_fw_call(Mod,Func,CurrConf,Pid, ErrorMsg,unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); {testcase_aborted,AbortReason,AbortLoc} -> @@ -896,7 +920,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,ErrorMsg}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, undefined,Status); Loc1 -> @@ -928,7 +953,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ErrorMsg,Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout, + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, Terminate,Comment, NewCurrConf,Status) end; @@ -943,14 +969,14 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, spawn_fw_call(Mod,Func,CurrConf,Pid, testcase_aborted_or_killed, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {fw_error,{FwMod,FwFunc,FwError}} -> spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,FwError}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other -> %% the testcase has terminated because of Reason (e.g. an exit %% because a linked process failed) @@ -960,8 +986,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, spawn_fw_call(Mod,Func,CurrConf,Pid, Reason,unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {EndConfPid,{call_end_conf,Data,_Result}} -> case CurrConf of @@ -969,11 +995,11 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {_Mod,_Func,TCPid,TCExitReason,Loc} = Data, spawn_fw_call(Mod,Func,CurrConf,TCPid, TCExitReason,Loc,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,undefined,Status); _ -> - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {_FwCallPid,fw_notify_done,{T,Value,Loc,Opts,AddToComment}} -> %% the framework has been notified, we're finished @@ -993,8 +1019,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, end, {T,Value,Loc,Opts,Comment1} end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} -> %% a framework function failed CB = os:getenv("TEST_SERVER_FRAMEWORK"), @@ -1005,13 +1031,13 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, {list_to_atom(CB),Func} end, RetVal = {died,{framework_error,Loc,Error},Loc,"Framework error"}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal}, - Comment,undefined,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {failed,File,Line} -> put(test_server_detected_fail, [{File, Line}| get(test_server_detected_fail)]), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} -> case update_user_timetraps(Pid, StartTime) of @@ -1020,8 +1046,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ignore -> ok end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {user_timetrap,Pid,TrapTime,StartTime,ElapsedTime,Scale} -> %% a user timetrap is triggered, ignore it if new %% timetrap has been started since @@ -1036,49 +1062,57 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, ignore -> ok end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {timetrap_cancel_one,Handle,_From} -> timetrap_cancel_one(Handle, false), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {timetrap_cancel_all,TCPid,_From} -> timetrap_cancel_all(TCPid, false), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {get_timetrap_info,TCPid,From} -> Info = get_timetrap_info(TCPid, false), From ! {self(),get_timetrap_info,Info}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when not is_tuple(_Other) -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when element(1, _Other) /= 'EXIT', element(1, _Other) /= started, element(1, _Other) /= finished, element(1, _Other) /= print -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,CurrConf,Status) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) after Timeout -> ReturnValue end. -run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) -> +run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func) -> case Msg of {'EXIT',_} -> From ! {io_reply,ReplyAs,{error,Func}}; _ -> From ! {io_reply,ReplyAs,ok} end, - if CaptureStdout /= false -> - CaptureStdout ! {captured,Msg}; - true -> + Proceed = if RejectIoReqs -> get({permit_io,From}); + true -> true + end, + if Proceed -> + if CaptureStdout /= false -> + CaptureStdout ! {captured,Msg}; + true -> + ok + end, + output({minor,Msg},From); + true -> ok - end, - output({minor,Msg},From). + end. output(Msg,Sender) -> local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}). @@ -1097,7 +1131,8 @@ call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) -> {'EXIT',Why} -> timer:sleep(1), group_leader() ! {printout,12, - "WARNING! ~p:end_per_testcase(~p, ~p)" + "WARNING! " + "~p:end_per_testcase(~p, ~p)" " crashed!\n\tReason: ~p\n", [Mod,Func,Conf,Why]}; _ -> @@ -1213,13 +1248,18 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) -> end, spawn_link(FwCall); -spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> +spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) -> + {Mod1,Func1} = + case {Mod,Func,CurrConf} of + {undefined,undefined,{{M,F},_}} -> {M,F}; + _ -> {Mod,Func} + end, FwCall = fun() -> %% set group leader so that printouts/comments %% from the framework get printed in the logs group_leader(SendTo, self()), - case catch fw_error_notify(Mod,Func,[], + case catch fw_error_notify(Mod1,Func1,[], Error,Loc) of {'EXIT',FwErrorNotifyErr} -> exit({fw_notify_done,error_notification, @@ -1228,7 +1268,7 @@ spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> ok end, Conf = [{tc_status,{failed,timetrap_timeout}}], - case catch do_end_tc_call(Mod,Func, Loc, + case catch do_end_tc_call(Mod1,Func1, Loc, {Pid,Error,[Conf]},Error) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); @@ -1959,6 +1999,13 @@ messages_get() -> test_server_sup:messages_get([]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% permit_io(GroupLeader, FromPid) -> ok +%% +%% Make sure proceeding IO from FromPid won't get rejected +permit_io(GroupLeader, FromPid) -> + GroupLeader ! {permit_io,FromPid}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sleep(Time) -> ok %% Time = integer() | float() | infinity %% @@ -2061,31 +2108,40 @@ fail() -> %% Break a test case so part of the test can be done manually. %% Use continue/0 to continue. break(Comment) -> - case erase(test_server_timetraps) of - undefined -> ok; - List -> lists:foreach(fun({Ref,_,_}) -> - timetrap_cancel(Ref) - end, List) - end, + break(?MODULE, Comment). + +break(CBM, Comment) -> + break(CBM, '', Comment). + +break(CBM, TestCase, Comment) -> + timetrap_cancel(), + {TCName,CntArg,PName} = + if TestCase == '' -> + {"", "", test_server_break_process}; + true -> + Str = atom_to_list(TestCase), + {[32 | Str], Str, + list_to_atom("test_server_break_process_" ++ Str)} + end, io:format(user, "\n\n\n--- SEMIAUTOMATIC TESTING ---" - "\nThe test case executes on process ~w" + "\nThe test case~s executes on process ~w" "\n\n\n~s" "\n\n\n-----------------------------\n\n" - "Continue with --> test_server:continue().\n", - [self(),Comment]), - case whereis(test_server_break_process) of + "Continue with --> ~w:continue(~s).\n", + [TCName,self(),Comment,CBM,CntArg]), + case whereis(PName) of undefined -> - spawn_break_process(self()); + spawn_break_process(self(), PName); OldBreakProcess -> OldBreakProcess ! cancel, - spawn_break_process(self()) + spawn_break_process(self(), PName) end, receive continue -> ok end. -spawn_break_process(Pid) -> +spawn_break_process(Pid, PName) -> spawn(fun() -> - register(test_server_break_process,self()), + register(PName, self()), receive continue -> continue(Pid); cancel -> ok @@ -2094,13 +2150,19 @@ spawn_break_process(Pid) -> continue() -> case whereis(test_server_break_process) of - undefined -> - ok; - BreakProcess -> - BreakProcess ! continue + undefined -> ok; + BreakProcess -> BreakProcess ! continue end. -continue(Pid) -> +continue(TestCase) when is_atom(TestCase) -> + PName = list_to_atom("test_server_break_process_" ++ + atom_to_list(TestCase)), + case whereis(PName) of + undefined -> ok; + BreakProcess -> BreakProcess ! continue + end; + +continue(Pid) when is_pid(Pid) -> Pid ! continue. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 5ed296d215..df2187bc04 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -162,7 +162,7 @@ -export([jobs/0, run_test/1, wait_finish/0, idle_notify/1, abort_current_testcase/1, abort/0]). -export([start_get_totals/1, stop_get_totals/0]). --export([get_levels/0, set_levels/3]). +-export([reject_io_reqs/1, get_levels/0, set_levels/3]). -export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]). -export([create_priv_dir/1]). -export([cover/2, cover/3, cover/7, @@ -218,8 +218,9 @@ -define(auto_skip_color, "#FFA64D"). -define(user_skip_color, "#FF8000"). +-define(sortable_table_name, "SortableTable"). --record(state,{jobs=[],levels={1,19,10}, +-record(state,{jobs=[], levels={1,19,10}, reject_io_reqs=false, multiply_timetraps=1, scale_timetraps=true, create_priv_dir=auto_per_run, finish=false, target_info, trc=false, cover=false, wait_for_node=[], @@ -498,6 +499,9 @@ get_levels() -> set_levels(Show, Major, Minor) -> controller_call({set_levels,Show,Major,Minor}). +reject_io_reqs(Bool) -> + controller_call({reject_io_reqs,Bool}). + multiply_timetraps(N) -> controller_call({multiply_timetraps,N}). @@ -815,6 +819,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecName,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -825,6 +830,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecList,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -843,6 +849,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> {State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -975,6 +982,15 @@ handle_call({set_levels,Show,Major,Minor}, _From, State) -> {reply,ok,State#state{levels={Show,Major,Minor}}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% handle_call({reject_io_reqs,Bool}, _, State) -> ok +%% Bool = bool() +%% +%% May be used to switch off stdout printouts to the minor log file + +handle_call({reject_io_reqs,Bool}, _From, State) -> + {reply,ok,State#state{reject_io_reqs=Bool}}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% handle_call({multiply_timetraps,N}, _, State) -> ok %% N = integer() | infinity %% @@ -1340,14 +1356,15 @@ kill_all_jobs([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, CreatePrivDir, -%% TestCaseCallback, ExtraTools) -> Pid +%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, +%% CreatePrivDir, TestCaseCallback, ExtraTools) -> Pid %% Mod = atom() %% Func = atom() %% Args = [term(),...] %% Dir = string() %% Name = string() %% Levels = {integer(),integer(),integer()} +%% RejectIoReqs = bool() %% CreatePrivDir = auto_per_run | manual_per_tc | auto_per_tc %% TestCaseCallback = {CBMod,CBFunc} | undefined %% ExtraTools = [ExtraTool,...] @@ -1359,14 +1376,14 @@ kill_all_jobs([]) -> %% When the named function is done executing, a summary of the results %% is printed to the log files. -spawn_tester(Mod, Func, Args, Dir, Name, Levels, +spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> spawn_link( - fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, + fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) end). -init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, +init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> process_flag(trap_exit, true), put(test_server_name, Name), @@ -1378,6 +1395,7 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, put(test_server_summary_level, SumLev), put(test_server_major_level, MajLev), put(test_server_minor_level, MinLev), + put(test_server_reject_io_reqs, RejectIoReqs), put(test_server_create_priv_dir, CreatePrivDir), put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)), put(test_server_testcase_callback, TCCallback), @@ -1424,8 +1442,10 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, end, OkN = get(test_server_ok), FailedN = get(test_server_failed), - print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" - "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n", + print(html,"\n</tbody>\n<tfoot>\n" + "<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" + "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n" + "</tfoot>\n", [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]). %% timer:tc/3 @@ -1486,7 +1506,7 @@ stop_extra_tools([], _) -> %% Reads the named test suite specification file, and executes it. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> case file:consult(SpecName) of @@ -1535,7 +1555,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> %% should not be used. Use a configuration test case instead. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec_list(TermList0, TimetrapSpec) -> Nodes = [], @@ -1702,7 +1722,7 @@ add_mod(Mod, Mods) -> %% configuration information into the log files. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap); MultiplyTimetrap == infinity -> @@ -1741,7 +1761,8 @@ do_test_cases(TopCases, SkipCases, test_server_sup:framework_call(report, [tests_start,{Test,N}]), {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, - [TestDescr,true,TestDir], "") of + [TestDescr,true,TestDir, + {[],[2,3,4,7,8],[1,6]}], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -1803,14 +1824,15 @@ do_test_cases(TopCases, SkipCases, [?suitelog_name,?coverlog_name]), print(html, "<p>~s</p>\n" ++ - xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", - "<table>") ++ - "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ - "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ - "<th>Comment</th></tr>\n", - [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]}, + xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]) ++ + "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ + "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ + "<th>Comment</th></tr>\n</thead>\n<tbody>\n", + [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>" ++ + xhtml("\n<br>\n", "\n<br />\n"),[N]}, {"",[]})]), - print(html, xhtml("<br>", "<br />")), print(major, "=cases ~p", [get(test_server_cases)]), print(major, "=user ~s", [TI#target_info.username]), @@ -1964,7 +1986,8 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) -> {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, [TestDescr,false, - filename:dirname(AbsName)], "") of + filename:dirname(AbsName), + undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -2094,7 +2117,7 @@ html_possibly_convert(Src, SrcInfo, Dest) -> Header = case test_server_sup:framework_call(get_html_wrapper, ["Module "++Src,false, - OutDir], "") of + OutDir,undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> ["<!DOCTYPE HTML PUBLIC", "\"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -3809,10 +3832,12 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where, MinorBase,MinorBase]), do_if_parallel(Main, ok, fun erlang:yield/0), + + RejectIoReqs = get(test_server_reject_io_reqs), %% run the test case {Result,DetectedFail,ProcsBefore,ProcsAfter} = run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode), - RunInit, Where, TimetrapData), + RunInit, Where, TimetrapData, RejectIoReqs), {Time,RetVal,Loc,Opts,Comment} = case Result of Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal; @@ -4431,7 +4456,7 @@ do_format_exception(Reason={Error,Stack}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, -%% Where, TimetrapData) -> +%% Where, TimetrapData, RejectIoReqs) -> %% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} | %% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} %% Name = atom() @@ -4450,19 +4475,21 @@ do_format_exception(Reason={Error,Stack}) -> %% sent over socket to target, and test_server runs the case and sends the %% result back over the socket. Else test_server runs the case directly on host. -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, TimetrapData) -> +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, + TimetrapData, RejectIoReqs) -> test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapData) -> + TimetrapData,RejectIoReqs}); +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, + TimetrapData, RejectIoReqs) -> case get(test_server_ctrl_job_sock) of undefined -> %% local target test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); + TimetrapData,RejectIoReqs}); JobSock -> %% remote target request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}}), + TimetrapData,RejectIoReqs}}), read_job_sock_loop(JobSock) end. diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl index a61028e4bc..2e8c092400 100644 --- a/lib/test_server/src/ts_run.erl +++ b/lib/test_server/src/ts_run.erl @@ -334,9 +334,9 @@ path_separator() -> end. -make_common_test_args(Args0, Options, _Vars) -> +make_common_test_args(Args0, Options0, _Vars) -> Trace = - case lists:keysearch(trace,1,Options) of + case lists:keysearch(trace,1,Options0) of {value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) -> ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])), [{ct_trace,?tracefile}]; @@ -348,7 +348,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, Cover = - case lists:keysearch(cover,1,Options) of + case lists:keysearch(cover,1,Options0) of {value,{cover, App, none, _Analyse}} -> io:format("No cover file found for ~p~n",[App]), []; @@ -358,7 +358,7 @@ make_common_test_args(Args0, Options, _Vars) -> [] end, - Logdir = case lists:keysearch(logdir, 1, Options) of + Logdir = case lists:keysearch(logdir, 1, Options0) of {value,{logdir, _}} -> []; false -> @@ -373,15 +373,16 @@ make_common_test_args(Args0, Options, _Vars) -> {scale_timetraps, true}] end, - ConfigPath = case {os:getenv("TEST_CONFIG_PATH"), - lists:keysearch(config, 1, Options)} of - {false,{value, {config, Path}}} -> - Path; - {false,false} -> - "../test_server"; - {Path,_} -> - Path - end, + {ConfigPath, + Options} = case {os:getenv("TEST_CONFIG_PATH"), + lists:keysearch(config, 1, Options0)} of + {_,{value, {config, Path}}} -> + {Path,lists:keydelete(config, 1, Options0)}; + {false,false} -> + {"../test_server",Options0}; + {Path,_} -> + {Path,Options0} + end, ConfigFiles = [{config,[filename:join(ConfigPath,File) || File <- get_config_files()]}], io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++ |