aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test')
-rw-r--r--lib/common_test/priv/ct_default.css10
-rw-r--r--lib/common_test/src/ct.erl37
-rw-r--r--lib/common_test/src/ct_framework.erl37
-rw-r--r--lib/common_test/src/ct_logs.erl278
-rw-r--r--lib/common_test/src/ct_repeat.erl2
-rw-r--r--lib/common_test/src/ct_run.erl137
-rw-r--r--lib/common_test/src/ct_testspec.erl18
-rw-r--r--lib/common_test/src/ct_util.erl23
-rw-r--r--lib/common_test/src/ct_util.hrl3
-rw-r--r--lib/common_test/src/cth_log_redirect.erl15
-rw-r--r--lib/common_test/test/Makefile1
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl70
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl154
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl70
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_helper.erl7
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE.erl277
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/default.spec3
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl127
-rw-r--r--lib/common_test/vsn.mk2
22 files changed, 1086 insertions, 200 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..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..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..666eb3c988 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(),
@@ -1746,25 +1775,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 +1877,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 +1895,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 +2386,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 +2428,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 +2478,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/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_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/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