aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/doc/src/common_test_app.xml12
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml12
-rw-r--r--lib/common_test/src/ct.erl1
-rw-r--r--lib/common_test/src/ct_framework.erl39
-rw-r--r--lib/common_test/src/ct_logs.erl224
-rw-r--r--lib/common_test/src/ct_run.erl14
-rw-r--r--lib/common_test/src/ct_util.erl38
-rw-r--r--lib/common_test/src/vts.erl30
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl129
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_9_SUITE.erl9
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl26
-rw-r--r--lib/common_test/test/ct_event_handler_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_groups_test_1_SUITE.erl25
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE.erl61
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl51
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl42
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl7
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_repeat_1_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_sequence_1_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_skip_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_smoke_test_SUITE.erl39
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl5
-rw-r--r--lib/common_test/test/ct_test_support.erl223
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl20
-rw-r--r--lib/crypto/c_src/crypto.c61
-rw-r--r--lib/crypto/doc/src/crypto.xml42
-rw-r--r--lib/crypto/doc/src/notes.xml17
-rw-r--r--lib/crypto/src/crypto.erl25
-rw-r--r--lib/crypto/test/crypto_SUITE.erl33
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/erl_interface/src/legacy/erl_timeout.c1
-rw-r--r--lib/kernel/src/inet_res.erl203
-rw-r--r--lib/kernel/test/file_SUITE.erl4
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl127
-rw-r--r--lib/ssh/doc/src/notes.xml13
-rw-r--r--lib/ssh/src/ssh.appup.src26
-rwxr-xr-xlib/ssh/src/ssh_bits.erl56
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl4
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/ssl.xml4
-rw-r--r--lib/ssl/src/ssl.erl2
-rw-r--r--lib/ssl/src/ssl_connection.erl4
-rw-r--r--lib/stdlib/doc/src/supervisor.xml43
-rw-r--r--lib/stdlib/src/pool.erl3
-rw-r--r--lib/stdlib/src/supervisor.erl58
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl32
-rw-r--r--lib/test_server/src/test_server.erl75
-rw-r--r--lib/test_server/src/test_server_ctrl.erl63
-rw-r--r--lib/test_server/src/test_server_sup.erl2
-rw-r--r--lib/test_server/src/ts_install.erl7
-rw-r--r--lib/test_server/src/ts_run.erl8
-rw-r--r--lib/tools/emacs/erlang.el13
54 files changed, 1410 insertions, 560 deletions
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 1ee73b890b..c92566de37 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -296,7 +296,7 @@
</func>
<func>
- <name>Module:init_per_testcase(TestCase, Config) -> NewConfig | {skip,Reason}</name>
+ <name>Module:init_per_testcase(TestCase, Config) -> NewConfig | {fail,Reason} | {skip,Reason}</name>
<fsummary>Test case initialization.</fsummary>
<type>
<v> TestCase = atom()</v>
@@ -311,10 +311,12 @@
<p>This function is called before each test case. The
<c>TestCase</c> argument is the name of the test case, and
- <c>Config</c> is the configuration which can be modified
- here. Whatever is returned from this function is given as
- <c>Config</c> to the test case. If <c>{skip,Reason}</c> is returned,
- the test case will be skipped and <c>Reason</c> printed
+ <c>Config</c> (list of key-value tuples) is the configuration
+ data that can be modified here. The <c>NewConfig</c> list returned
+ from this function is given as <c>Config</c> to the test case.
+ If <c>{fail,Reason}</c> is returned, the test case is
+ marked as failed without being executed. If <c>{skip,Reason}</c> is
+ returned, the test case will be skipped and <c>Reason</c> printed
in the overview log for the suite.</p>
</desc>
</func>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 723492d8f3..3f9fdb7121 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -167,12 +167,16 @@
returning <c>{fail,Reason}</c>, nor will it be able to save data with
<c>{save_config,Data}</c>.</p>
- <p>If <c>init_per_testcase</c> crashes, the test case itself is skipped
+ <p>If <c>init_per_testcase</c> crashes, the test case itself gets skipped
automatically (so called <em>auto skipped</em>). If <c>init_per_testcase</c>
- returns a <c>skip</c> tuple, also then will the test case be skipped (so
- called <em>user skipped</em>). In either event, the <c>end_per_testcase</c> is
- never called.
+ returns a tuple <c>{skip,Reason}</c>, also then the test case gets skipped
+ (so called <em>user skipped</em>). It is also possible, by returning a tuple
+ <c>{fail,Reason}</c> from <c>init_per_testcase</c>, to mark the test case
+ as failed without actually executing it.
</p>
+ <note><p>If <c>init_per_testcase</c> crashes, or returns <c>{skip,Reason}</c>
+ or <c>{fail,Reason}</c>, the <c>end_per_testcase</c> function is not called.
+ </p></note>
<p>If it is determined during execution of <c>end_per_testcase</c> that
the status of a successful test case should be changed to failed,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index dfec2b7a67..66da3ef742 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -861,6 +861,7 @@ remove_config(Callback, Config) ->
%%%
%%% @doc <p>Use this function to set a new timetrap for the running test case.</p>
timetrap(Time) ->
+ test_server:timetrap_cancel(),
test_server:timetrap(Time).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 3d4f674160..c016b9c66b 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -493,9 +493,9 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
case ct_hooks:end_tc(
Mod, FuncSpec, Args, Result, Return) of
'$ct_no_change' ->
- {FinalResult = ok,Result};
- FinalResult ->
- {FinalResult,FinalResult}
+ {ok,Result};
+ FinalResult1 ->
+ {FinalResult1,FinalResult1}
end,
% send sync notification so that event handlers may print
% in the log file before it gets closed
@@ -737,7 +737,7 @@ get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
%% (and only) test case so we can report Error properly
[{?MODULE,error_in_suite,[[Error]]}];
[] ->
- {error,{invalid_group_spec,Name}};
+ [];
ConfTests ->
case lists:member(skipped, Props) of
true ->
@@ -767,23 +767,7 @@ get_suite(Mod, Name) ->
find_groups(Mod, Name, TCs, GroupDefs) ->
Found = find(Mod, Name, TCs, GroupDefs, [], GroupDefs, false),
- Trimmed = trim(Found),
- %% I cannot find a reason to why this function is called,
- %% It deletes any group which is referenced in any other
- %% group. i.e.
- %% groups() ->
- %% [{test, [], [testcase1]},
- %% {testcases, [], [{group, test}]}].
- %% Would be changed to
- %% groups() ->
- %% [{testcases, [], [testcase1]}].
- %% instead of what I believe is correct:
- %% groups() ->
- %% [{test, [], [testcase1]},
- %% {testcases, [], [testcase1]}].
- %% Have to double check with peppe
- delete_subs(Trimmed, Trimmed),
- Trimmed.
+ trim(Found).
find(Mod, all, _TCs, [{Name,Props,Tests} | Gs], Known, Defs, _)
when is_atom(Name), is_list(Props), is_list(Tests) ->
@@ -1173,12 +1157,14 @@ error_in_suite(Config) ->
%% if the group config functions are missing in the suite,
%% use these instead
ct_init_per_group(GroupName, Config) ->
- ct_logs:log("WARNING", "init_per_group/2 for ~w missing in suite, using default.",
+ ct_logs:log("WARNING", "init_per_group/2 for ~w missing "
+ "in suite, using default.",
[GroupName]),
Config.
ct_end_per_group(GroupName, _) ->
- ct_logs:log("WARNING", "end_per_group/2 for ~w missing in suite, using default.",
+ ct_logs:log("WARNING", "end_per_group/2 for ~w missing "
+ "in suite, using default.",
[GroupName]),
ok.
@@ -1187,6 +1173,13 @@ ct_end_per_group(GroupName, _) ->
%%% @spec report(What,Data) -> ok
report(What,Data) ->
case What of
+ loginfo ->
+ %% logfiles and direcories have been created for a test and the
+ %% top level test index page needs to be refreshed
+ TestName = filename:basename(proplists:get_value(topdir, Data), ".logs"),
+ RunDir = proplists:get_value(rundir, Data),
+ ct_logs:make_all_suites_index({TestName,RunDir}),
+ ok;
tests_start ->
case ct_util:get_testdata(cover) of
undefined ->
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index f8ace73cbf..ba4adb8683 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -97,11 +97,11 @@ logdir_node_prefix() ->
logdir_prefix()++"."++atom_to_list(node()).
%%%-----------------------------------------------------------------
-%%% @spec close(How) -> ok
+%%% @spec close(Info) -> ok
%%%
%%% @doc Create index pages with test results and close the CT Log
%%% (tool-internal use only).
-close(How) ->
+close(Info) ->
make_last_run_index(),
ct_event:notify(#event{name=stop_logging,node=node(),data=[]}),
@@ -118,7 +118,7 @@ close(How) ->
ok
end,
- if How == clean ->
+ if Info == clean ->
case cleanup() of
ok ->
ok;
@@ -427,8 +427,8 @@ logger(Parent,Mode) ->
file:make_dir(Dir),
ct_event:notify(#event{name=start_logging,node=node(),
data=?abs(Dir)}),
- make_all_suites_index(start),
make_all_runs_index(start),
+ make_all_suites_index(start),
case Mode of
interactive -> interactive_link();
_ -> ok
@@ -796,24 +796,29 @@ make_one_index_entry(SuiteName, LogDir, Label, All, Missing) ->
{Succ,Fail,UserSkip,AutoSkip} ->
NotBuilt = not_built(SuiteName, LogDir, All, Missing),
NewResult = make_one_index_entry1(SuiteName, LogDir, Label, Succ, Fail,
- UserSkip, AutoSkip, NotBuilt, All),
+ UserSkip, AutoSkip, NotBuilt, All,
+ normal),
{NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt};
error ->
error
end.
make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
- NotBuilt, All) ->
+ NotBuilt, All, Mode) ->
LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
- CrashDumpName = SuiteName ++ "_erl_crash.dump",
- CrashDumpLink =
- case filelib:is_file(CrashDumpName) of
- true ->
- ["&nbsp;<A HREF=\"", CrashDumpName,
- "\">(CrashDump)</A>"];
- false ->
- ""
- end,
+ CrashDumpLink = case Mode of
+ cached ->
+ "";
+ normal ->
+ CrashDumpName = SuiteName ++ "_erl_crash.dump",
+ case filelib:is_file(CrashDumpName) of
+ true ->
+ ["&nbsp;<A HREF=\"", CrashDumpName,
+ "\">(CrashDump)</A>"];
+ false ->
+ ""
+ end
+ end,
{Lbl,Timestamp,Node,AllInfo} =
case All of
{true,OldRuns} ->
@@ -975,9 +980,13 @@ index_header(Label, StartTime) ->
"<th>Missing<br>Suites</th>\n"
"\n"]].
+
all_suites_index_header() ->
{ok,Cwd} = file:get_cwd(),
- LogDir = filename:basename(Cwd),
+ all_suites_index_header(Cwd).
+
+all_suites_index_header(IndexDir) ->
+ LogDir = filename:basename(IndexDir),
AllRuns = "All test runs in \"" ++ LogDir ++ "\"",
[header("Test Results") |
["<CENTER>\n",
@@ -1414,15 +1423,72 @@ timestamp(Dir) ->
[S,Min,H,D,M,Y] = [list_to_integer(N) || N <- lists:sublist(TsR,6)],
format_time({{Y,M,D},{H,Min,S}}).
-make_all_suites_index(When) ->
+%% ----------------------------- NOTE --------------------------------------
+%% The top level index file is generated based on the file contents under
+%% logdir. This takes place initially when the test run starts (When = start)
+%% and an update takes place at the end of the test run, or when the user
+%% requests an explicit refresh (When = refresh).
+%% The index file needs to be updated also at the start of each individual
+%% test (in order for the user to be able to track test progress by refreshing
+%% the browser). Since it would be too expensive to generate a new file from
+%% scratch every time (by reading the data from disk), a copy of the dir tree
+%% is cached as a result of the first index file creation. This copy is then
+%% used for all top level index page updates that occur during the test run.
+%% This means that any changes to the dir tree under logdir during the test
+%% run will not show until after the final refresh.
+%% -------------------------------------------------------------------------
+
+%% Creates the top level index file. When == start | refresh.
+%% A copy of the dir tree under logdir is cached as a result.
+make_all_suites_index(When) when is_atom(When) ->
AbsIndexName = ?abs(?index_name),
notify_and_lock_file(AbsIndexName),
LogDirs = filelib:wildcard(logdir_prefix()++".*/*"++?logdir_ext),
- Sorted = sort_logdirs(LogDirs,[]),
- Result = make_all_suites_index1(When,Sorted),
+ Sorted = sort_logdirs(LogDirs, []),
+ Result = make_all_suites_index1(When, AbsIndexName, Sorted),
notify_and_unlock_file(AbsIndexName),
- Result.
-
+ Result;
+
+%% This updates the top level index file using cached data from
+%% the initial index file creation.
+make_all_suites_index(NewTestData = {_TestName,DirName}) ->
+ %% AllLogDirs = [{TestName,Label,Missing,{LastLogDir,Summary},OldDirs}|...]
+ {AbsIndexName,LogDirData} = ct_util:get_testdata(test_index),
+
+ CtRunDirPos = length(filename:split(AbsIndexName)),
+ CtRunDir = filename:join(lists:sublist(filename:split(DirName),
+ CtRunDirPos)),
+
+ Label = case read_totals_file(filename:join(CtRunDir, ?totals_name)) of
+ {_,"-",_,_} -> "...";
+ {_,Lbl,_,_} -> Lbl;
+ _ -> "..."
+ end,
+ notify_and_lock_file(AbsIndexName),
+ Result =
+ case catch make_all_suites_ix_cached(AbsIndexName,
+ NewTestData,
+ Label,
+ LogDirData) of
+ {'EXIT',Reason} ->
+ io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
+ io:format("~p~n", [Reason]),
+ {error,Reason};
+ {error,Reason} ->
+ io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
+ io:format("~p~n", [Reason]),
+ {error,Reason};
+ ok ->
+ ok;
+ Err ->
+ io:format("Unknown internal error while updating ~s. "
+ "Please report.\n(Err: ~p, ID: 1)",
+ [AbsIndexName,Err]),
+ {error, Err}
+ end,
+ notify_and_unlock_file(AbsIndexName),
+ Result.
+
sort_logdirs([Dir|Dirs],Groups) ->
TestName = filename:rootname(filename:basename(Dir)),
case filelib:wildcard(filename:join(Dir,"run.*")) of
@@ -1448,13 +1514,12 @@ sort_each_group([{Test,IxDirs}|Groups]) ->
sort_each_group([]) ->
[].
-make_all_suites_index1(When,AllSuitesLogDirs) ->
+make_all_suites_index1(When, AbsIndexName, AllLogDirs) ->
IndexName = ?index_name,
- AbsIndexName = ?abs(IndexName),
if When == start -> ok;
true -> io:put_chars("Updating " ++ AbsIndexName ++ "... ")
end,
- case catch make_all_suites_index2(IndexName,AllSuitesLogDirs) of
+ case catch make_all_suites_index2(IndexName, AllLogDirs) of
{'EXIT', Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
io:format("~p~n", [Reason]),
@@ -1463,11 +1528,16 @@ make_all_suites_index1(When,AllSuitesLogDirs) ->
io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
io:format("~p~n", [Reason]),
{error, Reason};
- ok ->
- if When == start -> ok;
- true -> io:put_chars("done\n")
- end,
- ok;
+ {ok,CacheData} ->
+ case When of
+ start ->
+ ct_util:set_testdata_async({test_index,{AbsIndexName,
+ CacheData}}),
+ ok;
+ _ ->
+ io:put_chars("done\n"),
+ ok
+ end;
Err ->
io:format("Unknown internal error while updating ~s. "
"Please report.\n(Err: ~p, ID: 1)",
@@ -1475,56 +1545,124 @@ make_all_suites_index1(When,AllSuitesLogDirs) ->
{error, Err}
end.
-make_all_suites_index2(IndexName,AllSuitesLogDirs) ->
- {ok,Index0,_Totals} = make_all_suites_index3(AllSuitesLogDirs,
- all_suites_index_header(),
- 0, 0, 0, 0, 0, []),
+make_all_suites_index2(IndexName, AllTestLogDirs) ->
+ {ok,Index0,_Totals,CacheData} =
+ make_all_suites_index3(AllTestLogDirs,
+ all_suites_index_header(),
+ 0, 0, 0, 0, 0, [], []),
Index = [Index0|index_footer()],
case force_write_file(IndexName, Index) of
ok ->
- ok;
+ {ok,CacheData};
{error, Reason} ->
{error,{index_write_error, Reason}}
end.
-make_all_suites_index3([{SuiteName,[LastLogDir|OldDirs]}|Rest],
+make_all_suites_index3([{TestName,[LastLogDir|OldDirs]}|Rest],
Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,
- Labels) ->
+ Labels, CacheData) ->
[EntryDir|_] = filename:split(LastLogDir),
Missing =
- case file:read_file(filename:join(EntryDir,?missing_suites_info)) of
+ case file:read_file(filename:join(EntryDir, ?missing_suites_info)) of
{ok,Bin} -> binary_to_term(Bin);
_ -> []
end,
{Label,Labels1} =
case proplists:get_value(EntryDir, Labels) of
undefined ->
- case read_totals_file(filename:join(EntryDir,?totals_name)) of
+ case read_totals_file(filename:join(EntryDir, ?totals_name)) of
{_,Lbl,_,_} -> {Lbl,[{EntryDir,Lbl}|Labels]};
_ -> {"-",[{EntryDir,"-"}|Labels]}
end;
Lbl ->
{Lbl,Labels}
end,
- case make_one_index_entry(SuiteName, LastLogDir, Label, {true,OldDirs}, Missing) of
+ case make_one_index_entry(TestName, LastLogDir, Label, {true,OldDirs}, Missing) of
{Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
{'EXIT',_} -> undefined;
Res -> Res
end,
+ IxEntry = {TestName,Label,Missing,
+ {LastLogDir,{Succ,Fail,USkip,ASkip}},OldDirs},
make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ,
TotFail+Fail, UserSkip+USkip, AutoSkip1,
- TotNotBuilt+NotBuilt,Labels1);
+ TotNotBuilt+NotBuilt, Labels1,
+ [IxEntry|CacheData]);
error ->
+ IxEntry = {TestName,Label,Missing,{LastLogDir,error},OldDirs},
make_all_suites_index3(Rest, Result, TotSucc, TotFail,
- UserSkip, AutoSkip, TotNotBuilt,Labels1)
+ UserSkip, AutoSkip, TotNotBuilt, Labels1,
+ [IxEntry|CacheData])
end;
make_all_suites_index3([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt,_) ->
+ TotNotBuilt, _, CacheData) ->
{ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,true)],
- {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}.
+ {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}, lists:reverse(CacheData)}.
+
+
+make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) ->
+ AllTestLogDirs1 = insert_new_test_data(NewTestData, Label, AllTestLogDirs),
+ IndexDir = filename:dirname(AbsIndexName),
+ Index0 = make_all_suites_ix_cached1(AllTestLogDirs1,
+ all_suites_index_header(IndexDir),
+ 0, 0, 0, 0, 0),
+ Index = [Index0|index_footer()],
+ case force_write_file(AbsIndexName, Index) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error,{index_write_error, Reason}}
+ end.
+
+insert_new_test_data({NewTestName,NewTestDir}, NewLabel, AllTestLogDirs) ->
+ AllTestLogDirs1 =
+ case lists:keysearch(NewTestName, 1, AllTestLogDirs) of
+ {value,{_,_,_,{LastLogDir,_},OldDirs}} ->
+ [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}},
+ [LastLogDir|OldDirs]} |
+ lists:keydelete(NewTestName, 1, AllTestLogDirs)];
+ false ->
+ [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}},[]} |
+ AllTestLogDirs]
+ end,
+ lists:keysort(1, AllTestLogDirs1).
+
+make_all_suites_ix_cached1([{TestName,Label,Missing,LastLogDirData,OldDirs}|Rest],
+ Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt) ->
+ case make_one_ix_entry_cached(TestName, LastLogDirData,
+ Label, {true,OldDirs}, Missing) of
+ {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
+ %% for backwards compatibility
+ AutoSkip1 = case catch AutoSkip+ASkip of
+ {'EXIT',_} -> undefined;
+ Res -> Res
+ end,
+ make_all_suites_ix_cached1(Rest, [Result|Result1], TotSucc+Succ,
+ TotFail+Fail, UserSkip+USkip, AutoSkip1,
+ TotNotBuilt+NotBuilt);
+ error ->
+ make_all_suites_ix_cached1(Rest, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt)
+ end;
+make_all_suites_ix_cached1([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt) ->
+ [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, true)].
+
+make_one_ix_entry_cached(TestName, {LogDir,Summary}, Label, All, Missing) ->
+ case Summary of
+ {Succ,Fail,UserSkip,AutoSkip} ->
+ NotBuilt = not_built(TestName, LogDir, All, Missing),
+ NewResult = make_one_index_entry1(TestName, LogDir, Label,
+ Succ, Fail, UserSkip, AutoSkip,
+ NotBuilt, All, cached),
+ {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt};
+ error ->
+ error
+ end.
%%-----------------------------------------------------------------
%% Remove log files.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 7bd7dc7d66..c01e97b358 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -262,15 +262,15 @@ run_or_refresh(StartOpts = #opts{logdir = LogDir}, Args) ->
%% give the shell time to print version etc
timer:sleep(500),
io:nl(),
- case catch ct_logs:make_all_suites_index(refresh) of
- {'EXIT',ASReason} ->
+ case catch ct_logs:make_all_runs_index(refresh) of
+ {'EXIT',ARReason} ->
file:set_cwd(Cwd),
- {error,{all_suites_index,ASReason}};
+ {error,{all_runs_index,ARReason}};
_ ->
- case catch ct_logs:make_all_runs_index(refresh) of
- {'EXIT',ARReason} ->
+ case catch ct_logs:make_all_suites_index(refresh) of
+ {'EXIT',ASReason} ->
file:set_cwd(Cwd),
- {error,{all_runs_index,ARReason}};
+ {error,{all_suites_index,ASReason}};
_ ->
file:set_cwd(Cwd),
io:format("Logs in ~s refreshed!~n~n", [LogDir1]),
@@ -1111,6 +1111,8 @@ run(TestDirs) ->
install([]),
reformat_result(catch do_run(tests(TestDirs), [])).
+reformat_result({'EXIT',{user_error,Reason}}) ->
+ {error,Reason};
reformat_result({user_error,Reason}) ->
{error,Reason};
reformat_result(Result) ->
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 115207beed..b3e345b4e5 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -37,7 +37,7 @@
read_suite_data/1,
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1,
- update_testdata/2]).
+ set_testdata_async/1, update_testdata/2]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
@@ -96,7 +96,8 @@ start(Mode,LogDir) ->
Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end),
receive
{Pid,started} -> Pid;
- {Pid,Error} -> exit(Error)
+ {Pid,Error} -> exit(Error);
+ {_Ref,{Pid,Error}} -> exit(Error)
end;
Pid ->
case get_mode() of
@@ -162,21 +163,19 @@ do_start(Parent,Mode,LogDir) ->
end,
{StartTime,TestLogDir} = ct_logs:init(Mode),
- %% Initiate ct_hooks
+ ct_event:notify(#event{name=test_start,
+ node=node(),
+ data={StartTime,
+ lists:flatten(TestLogDir)}}),
+ %% Initialize ct_hooks
case catch ct_hooks:init(Opts) of
ok ->
- ok;
+ Parent ! {self(),started};
{_,CTHReason} ->
ct_logs:tc_print('Suite Callback',CTHReason,[]),
- Parent ! {self(), CTHReason},
- self() ! {{stop,normal},{self(),make_ref()}}
+ self() ! {{stop,{self(),{user_error,CTHReason}}},
+ {Parent,make_ref()}}
end,
-
- ct_event:notify(#event{name=test_start,
- node=node(),
- data={StartTime,
- lists:flatten(TestLogDir)}}),
- Parent ! {self(),started},
loop(Mode,[],StartDir).
create_table(TableName,KeyPos) ->
@@ -232,6 +231,9 @@ update_testdata(Key, Fun) ->
set_testdata(TestData) ->
call({set_testdata, TestData}).
+set_testdata_async(TestData) ->
+ cast({set_testdata, TestData}).
+
get_testdata(Key) ->
call({get_testdata, Key}).
@@ -317,7 +319,7 @@ loop(Mode,TestData,StartDir) ->
{reset_cwd,From} ->
return(From,file:set_cwd(StartDir)),
loop(From,TestData,StartDir);
- {{stop,How},From} ->
+ {{stop,Info},From} ->
Time = calendar:local_time(),
ct_event:sync_notify(#event{name=test_done,
node=node(),
@@ -330,11 +332,11 @@ loop(Mode,TestData,StartDir) ->
ets:delete(?conn_table),
ets:delete(?board_table),
ets:delete(?suite_table),
- ct_logs:close(How),
+ ct_logs:close(Info),
ct_event:stop(),
ct_config:stop(),
file:set_cwd(StartDir),
- return(From,ok);
+ return(From, Info);
{Ref, _Msg} when is_reference(Ref) ->
%% This clause is used when doing cast operations.
loop(Mode,TestData,StartDir);
@@ -537,16 +539,16 @@ reset_silent_connections() ->
%%%-----------------------------------------------------------------
-%%% @spec stop(How) -> ok
+%%% @spec stop(Info) -> ok
%%%
%%% @doc Stop the ct_util_server and close all existing connections
%%% (tool-internal use only).
%%%
%%% @see ct
-stop(How) ->
+stop(Info) ->
case whereis(ct_util_server) of
undefined -> ok;
- _ -> call({stop,How})
+ _ -> call({stop,Info})
end.
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index 2ee982d726..081f98e889 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -281,9 +281,7 @@ run_test1(State=#state{tests=Tests,current_log_dir=LogDir}) ->
end,
unlink(Self)
end,
-
Pid = spawn_link(RunTest),
-
Total =
receive
{{test_info,start_info,{_,_,Cases}},From} ->
@@ -480,7 +478,7 @@ create_testdir_entries([],_N) ->
[].
testdir_entry(Dir,Suite,Case,N) ->
- NStr = integer_to_list(N),
+ NStr = vts_integer_to_list(N),
tr([td(delete_button(NStr)),
td(Dir),
td(suite_select(Dir,Suite,NStr)),
@@ -691,11 +689,11 @@ result_summary_frame1(State) ->
result_summary_body(State) ->
N = State#state.ok + State#state.fail + State#state.skip,
[h2("Result Summary"),
- p([b(integer_to_list(N))," cases executed (of ",
- b(integer_to_list(State#state.total)),")"]),
- p([green([b(integer_to_list(State#state.ok))," successful"]),br(),
- red([b(integer_to_list(State#state.fail))," failed"]),br(),
- orange([b(integer_to_list(State#state.skip))," skipped"])]),
+ p([b(vts_integer_to_list(N))," cases executed (of ",
+ b(vts_integer_to_list(State#state.total)),")"]),
+ p([green([b(vts_integer_to_list(State#state.ok))," successful"]),br(),
+ red([b(vts_integer_to_list(State#state.fail))," failed"]),br(),
+ orange([b(vts_integer_to_list(State#state.skip))," skipped"])]),
executed_test_list(State)].
executed_test_list(#state{testruns=[]}) ->
@@ -735,6 +733,14 @@ report1(tc_done,{_Suite,init_per_suite,_},State) ->
State;
report1(tc_done,{_Suite,end_per_suite,_},State) ->
State;
+report1(tc_done,{_Suite,init_per_group,_},State) ->
+ State;
+report1(tc_done,{_Suite,end_per_group,_},State) ->
+ State;
+report1(tc_done,{_Suite,ct_init_per_group,_},State) ->
+ State;
+report1(tc_done,{_Suite,ct_end_per_group,_},State) ->
+ State;
report1(tc_done,{_Suite,_Case,ok},State) ->
State#state{ok=State#state.ok+1};
report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) ->
@@ -742,7 +748,9 @@ report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) ->
report1(tc_done,{_Suite,_Case,{skipped,_Reason}},State) ->
State#state{skip=State#state.skip+1};
report1(tc_user_skip,{_Suite,_Case,_Reason},State) ->
- State#state{skip=State#state.skip+1}.
+ State#state{skip=State#state.skip+1};
+report1(loginfo,_,State) ->
+ State.
get_test_log(TestName,LogDir) ->
[Log] =
@@ -882,3 +890,7 @@ get_input_data(Input,Key)->
parse(Input) ->
httpd:parse_query(Input).
+vts_integer_to_list(X) when is_atom(X) ->
+ atom_to_list(X);
+vts_integer_to_list(X) when is_integer(X) ->
+ integer_to_list(X).
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
index b6b50f33e0..8ce75f582a 100644
--- a/lib/common_test/test/ct_config_SUITE.erl
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -174,7 +174,8 @@ run_test(Name, Config, CTConfig, SuiteNames)->
TestEvents = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(Name,
reformat_events(TestEvents, ?eh),
- ?config(config_dir, Config)),
+ ?config(config_dir, Config),
+ Opts),
ExpEvents = events_to_check(Name),
ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config).
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index ad6cf1ba8f..6867e59b60 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -102,8 +102,9 @@ cfg_error(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(cfg_error,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(cfg_error),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -120,8 +121,9 @@ lib_error(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(lib_error,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(lib_error),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -138,8 +140,9 @@ no_compile(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(no_compile,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(no_compile),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -156,7 +159,8 @@ timetrap_end_conf(Config) when is_list(Config) ->
ct_test_support:log_events(timetrap_end_conf,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(timetrap_end_conf),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -176,7 +180,8 @@ timetrap_normal(Config) when is_list(Config) ->
ct_test_support:log_events(timetrap_normal,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(timetrap_normal),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -198,12 +203,31 @@ timetrap_extended(Config) when is_list(Config) ->
ct_test_support:log_events(timetrap_extended,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(timetrap_extended),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
+%%%
+timetrap_parallel(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
+ Suite = Join(DataDir, "timetrap_3_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(timetrap_parallel,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(timetrap_parallel),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -236,7 +260,7 @@ test_events(cfg_error) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{14,14,42}},
+ {?eh,start_info,{14,14,43}},
{?eh,tc_start,{cfg_error_1_SUITE,init_per_suite}},
{?eh,tc_done,
@@ -405,7 +429,6 @@ test_events(cfg_error) ->
{cfg_error_8_SUITE,{init_per_group,g3,[]},
{failed,{error,{{badmatch,42},
[{cfg_error_8_SUITE,init_per_group,2},
- {cfg_error_8_SUITE,init_per_group,2},
{test_server,my_apply,3},
{test_server,ts_tc,3},
{test_server,run_test_case_eval1,6},
@@ -415,7 +438,6 @@ test_events(cfg_error) ->
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{{badmatch,42},
[{cfg_error_8_SUITE,init_per_group,2},
- {cfg_error_8_SUITE,init_per_group,2},
{test_server,my_apply,3},
{test_server,ts_tc,3},
{test_server,run_test_case_eval1,6},
@@ -426,7 +448,6 @@ test_events(cfg_error) ->
{failed,{cfg_error_8_SUITE,init_per_group,
{'EXIT',{{badmatch,42},
[{cfg_error_8_SUITE,init_per_group,2},
- {cfg_error_8_SUITE,init_per_group,2},
{test_server,my_apply,3},
{test_server,ts_tc,3},
{test_server,run_test_case_eval1,6},
@@ -520,16 +541,19 @@ test_events(cfg_error) ->
%%! end_tc failes the testcase
{?eh,tc_done,{cfg_error_9_SUITE,tc6,ok}},
{?eh,test_stats,{9,2,{0,18}}},
+ {?eh,tc_start,{cfg_error_9_SUITE,tc7}},
+ {?eh,tc_done,{cfg_error_9_SUITE,tc7,{failed,{error,tc7_should_be_failed}}}},
+ {ct_test_support_eh,test_stats,{9,3,{0,18}}},
{?eh,tc_start,{cfg_error_9_SUITE,tc11}},
{?eh,tc_done,{cfg_error_9_SUITE,tc11,
{failed,{cfg_error_9_SUITE,end_per_testcase,
{'EXIT',warning_should_be_printed}}}}},
- {?eh,test_stats,{10,2,{0,18}}},
+ {?eh,test_stats,{10,3,{0,18}}},
{?eh,tc_start,{cfg_error_9_SUITE,tc12}},
{?eh,tc_done,{cfg_error_9_SUITE,tc12,
{failed,{cfg_error_9_SUITE,end_per_testcase,
{timetrap_timeout,2000}}}}},
- {?eh,test_stats,{11,2,{0,18}}},
+ {?eh,test_stats,{11,3,{0,18}}},
{?eh,tc_start,{cfg_error_9_SUITE,tc13}},
{?eh,tc_done,{cfg_error_9_SUITE,tc13,
{failed,{cfg_error_9_SUITE,end_per_testcase,
@@ -539,11 +563,11 @@ test_events(cfg_error) ->
{test_server,do_end_per_testcase,4},
{test_server,run_test_case_eval1,6},
{test_server,run_test_case_eval,8}]}}}}}},
- {?eh,test_stats,{12,2,{0,18}}},
+ {?eh,test_stats,{12,3,{0,18}}},
{?eh,tc_start,{cfg_error_9_SUITE,tc14}},
{?eh,tc_done,
{cfg_error_9_SUITE,tc14,{failed,{error,tc14_should_be_failed}}}},
- {?eh,test_stats,{12,3,{0,18}}},
+ {?eh,test_stats,{12,4,{0,18}}},
{?eh,tc_start,{cfg_error_9_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_9_SUITE,end_per_suite,ok}},
@@ -554,7 +578,7 @@ test_events(cfg_error) ->
{?eh,tc_auto_skip,{cfg_error_10_SUITE,tc1,
{failed,{cfg_error_10_SUITE,init_per_suite,
{failed,fail_init_per_suite}}}}},
- {?eh,test_stats,{12,3,{0,19}}},
+ {?eh,test_stats,{12,4,{0,19}}},
{?eh,tc_auto_skip,{cfg_error_10_SUITE,end_per_suite,
{failed,{cfg_error_10_SUITE,init_per_suite,
{failed,fail_init_per_suite}}}}},
@@ -563,40 +587,40 @@ test_events(cfg_error) ->
{?eh,tc_start,{cfg_error_11_SUITE,tc1}},
{?eh,tc_done,{cfg_error_11_SUITE,tc1,
{skipped,{config_name_already_in_use,[dummy0]}}}},
- {?eh,test_stats,{12,3,{1,19}}},
+ {?eh,test_stats,{12,4,{1,19}}},
{?eh,tc_start,{cfg_error_11_SUITE,tc2}},
{?eh,tc_done,{cfg_error_11_SUITE,tc2,ok}},
- {?eh,test_stats,{13,3,{1,19}}},
+ {?eh,test_stats,{13,4,{1,19}}},
{?eh,tc_start,{cfg_error_11_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_11_SUITE,end_per_suite,ok}},
{?eh,tc_start,{cfg_error_12_SUITE,tc1}},
{?eh,tc_done,{cfg_error_12_SUITE,tc1,{failed,{timetrap_timeout,500}}}},
- {?eh,test_stats,{13,4,{1,19}}},
+ {?eh,test_stats,{13,5,{1,19}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc2}},
{?eh,tc_done,{cfg_error_12_SUITE,tc2,{failed,
{cfg_error_12_SUITE,end_per_testcase,
{timetrap_timeout,500}}}}},
- {?eh,test_stats,{14,4,{1,19}}},
+ {?eh,test_stats,{14,5,{1,19}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc3}},
{?eh,tc_done,{cfg_error_12_SUITE,tc3,ok}},
- {?eh,test_stats,{15,4,{1,19}}},
+ {?eh,test_stats,{15,5,{1,19}}},
{?eh,tc_start,{cfg_error_12_SUITE,tc4}},
{?eh,tc_done,{cfg_error_12_SUITE,tc4,{failed,
{cfg_error_12_SUITE,end_per_testcase,
{timetrap_timeout,500}}}}},
- {?eh,test_stats,{16,4,{1,19}}},
+ {?eh,test_stats,{16,5,{1,19}}},
{?eh,tc_start,{cfg_error_13_SUITE,init_per_suite}},
{?eh,tc_done,{cfg_error_13_SUITE,init_per_suite,ok}},
{?eh,tc_start,{cfg_error_13_SUITE,tc1}},
{?eh,tc_done,{cfg_error_13_SUITE,tc1,ok}},
- {?eh,test_stats,{17,4,{1,19}}},
+ {?eh,test_stats,{17,5,{1,19}}},
{?eh,tc_start,{cfg_error_13_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_13_SUITE,end_per_suite,ok}},
{?eh,tc_start,{cfg_error_14_SUITE,init_per_suite}},
{?eh,tc_done,{cfg_error_14_SUITE,init_per_suite,ok}},
{?eh,tc_start,{cfg_error_14_SUITE,tc1}},
{?eh,tc_done,{cfg_error_14_SUITE,tc1,ok}},
- {?eh,test_stats,{18,4,{1,19}}},
+ {?eh,test_stats,{18,5,{1,19}}},
{?eh,tc_start,{cfg_error_14_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_14_SUITE,end_per_suite,
{comment,
@@ -729,7 +753,7 @@ test_events(timetrap_normal) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,3}},
+ {?eh,start_info,{1,1,4}},
{?eh,tc_start,{timetrap_2_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_2_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_2_SUITE,tc0}},
@@ -744,6 +768,9 @@ test_events(timetrap_normal) ->
{?eh,tc_done,
{timetrap_2_SUITE,tc2,{failed,{timetrap_timeout,500}}}},
{?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_2_SUITE,tc3,ok}},
+ {?eh,test_stats,{1,3,{0,0}}},
{?eh,tc_start,{timetrap_2_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_2_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -754,7 +781,7 @@ test_events(timetrap_extended) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,3}},
+ {?eh,start_info,{1,1,4}},
{?eh,tc_start,{timetrap_2_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_2_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_2_SUITE,tc0}},
@@ -769,8 +796,52 @@ test_events(timetrap_extended) ->
{?eh,tc_done,
{timetrap_2_SUITE,tc2,{failed,{timetrap_timeout,1000}}}},
{?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_2_SUITE,tc3,ok}},
+ {?eh,test_stats,{1,3,{0,0}}},
{?eh,tc_start,{timetrap_2_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_2_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
- ].
+ ];
+
+test_events(timetrap_parallel) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,7}},
+ {?eh,tc_done,{timetrap_3_SUITE,init_per_suite,ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {timetrap_3_SUITE,{init_per_group,g1,[parallel]}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,{init_per_group,g1,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc0}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc1}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc2}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc3}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc4}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc6}},
+ {?eh,tc_start,{timetrap_3_SUITE,tc7}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc1,{failed,{timetrap_timeout,500}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc2,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc6,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc7,{failed,{timetrap_timeout,1500}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc0,{failed,{timetrap_timeout,2000}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc4,{failed,{timetrap_timeout,2000}}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,tc3,{failed,{timetrap_timeout,3000}}}},
+ {?eh,test_stats,{0,7,{0,0}}},
+ {?eh,tc_start,
+ {timetrap_3_SUITE,{end_per_group,g1,[parallel]}}},
+ {?eh,tc_done,
+ {timetrap_3_SUITE,{end_per_group,g1,[parallel]},ok}}]},
+ {?eh,tc_done,{timetrap_3_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_9_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_9_SUITE.erl
index d73287ad62..40b7d2da47 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_9_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_9_SUITE.erl
@@ -83,6 +83,8 @@ init_per_testcase(tc3, Config) ->
Config;
init_per_testcase(tc4, _) ->
ok;
+init_per_testcase(tc7, _) ->
+ {fail,tc7_should_be_failed};
init_per_testcase(_, Config) ->
Config.
@@ -136,7 +138,7 @@ groups() ->
%% Reason = term()
%%--------------------------------------------------------------------
all() ->
- [tc1,tc2,tc3,tc4,tc5,tc6,
+ [tc1,tc2,tc3,tc4,tc5,tc6,tc7,
tc11,tc12,tc13,tc14].
tc1(_) ->
@@ -171,6 +173,11 @@ tc6(_) ->
ct:comment("This one should succeed but then get failed by end_tc!"),
fini.
+tc7(_) ->
+ ct:comment("This one should get failed by iptc!"),
+ fini.
+
+
tc11(_) ->
fini.
tc12(_) ->
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl
index 99bb400137..7fcb631d06 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl
@@ -77,8 +77,8 @@ init_per_testcase(tc1, Config) ->
ct:timetrap({seconds,1}),
Config;
-init_per_testcase(tc3, Config) ->
- ct:timetrap({seconds,1}),
+init_per_testcase(tc2, Config) ->
+ ct:timetrap(250),
Config;
init_per_testcase(_TestCase, Config) ->
@@ -90,7 +90,7 @@ init_per_testcase(_TestCase, Config) ->
%% TestCase = atom()
%% Config0 = Config1 = [tuple()]
%%--------------------------------------------------------------------
-end_per_testcase(_, Config) ->
+end_per_testcase(_, _Config) ->
ok.
%%--------------------------------------------------------------------
@@ -116,7 +116,7 @@ groups() ->
%% Reason = term()
%%--------------------------------------------------------------------
all() ->
- [tc0,tc1,tc2].
+ [tc0,tc1,tc2,tc3].
tc0(_) ->
N = list_to_integer(ct:get_config(multiply)),
@@ -131,8 +131,24 @@ tc1(_) ->
ok.
tc2(_) ->
+ ct:timetrap(500),
N = list_to_integer(ct:get_config(multiply)),
ct:comment(io_lib:format("TO after ~w sec", [0.5*N])),
- ct:timetrap(500),
ct:sleep(2000),
ok.
+
+tc3() ->
+ [{timetrap,{seconds,2}}].
+
+tc3(_) ->
+ T0 = now(),
+ ct:timetrap(infinity),
+ N = list_to_integer(ct:get_config(multiply)),
+ ct:comment(io_lib:format("Sleeping for ~w sec...", [4*N])),
+ ct:sleep(4000),
+ Diff = timer:now_diff(now(), T0),
+ if ((Diff < (N*4000000)) or (Diff > (N*4500000))) ->
+ exit(not_expected);
+ true ->
+ ok
+ end.
diff --git a/lib/common_test/test/ct_event_handler_SUITE.erl b/lib/common_test/test/ct_event_handler_SUITE.erl
index 5ef04c0e75..b534a7141d 100644
--- a/lib/common_test/test/ct_event_handler_SUITE.erl
+++ b/lib/common_test/test/ct_event_handler_SUITE.erl
@@ -102,8 +102,9 @@ start_stop(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(start_stop,
- ct_test_support:reformat(Events, eh_A),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, eh_A),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents =
[{eh_A,start_logging,{'DEF','RUNDIR'}},
@@ -148,8 +149,9 @@ results(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(results,
- ct_test_support:reformat(Events, eh_A),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, eh_A),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents =
[{eh_A,start_logging,{'DEF','RUNDIR'}},
diff --git a/lib/common_test/test/ct_groups_test_1_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE.erl
index 7775d8a55d..e520a72227 100644
--- a/lib/common_test/test/ct_groups_test_1_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_1_SUITE.erl
@@ -89,8 +89,9 @@ groups_suite_1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suite_1,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(groups_suite_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -109,8 +110,9 @@ groups_suite_2(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suite_2,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(groups_suite_2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -130,8 +132,9 @@ groups_suites_1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suites_1,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(groups_suites_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -150,8 +153,9 @@ groups_dir_1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_dir_1,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(groups_dir_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -170,8 +174,9 @@ groups_dirs_1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_dirs_1,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(groups_dirs_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl
index 2ae63f4f99..f33be8a9d4 100644
--- a/lib/common_test/test/ct_groups_test_2_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl
@@ -59,7 +59,7 @@ end_per_testcase(TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [missing_conf, repeat_1].
+ [missing_conf, repeat_1, empty_group].
groups() ->
[].
@@ -83,13 +83,14 @@ missing_conf(Config) when is_list(Config) ->
Suite = filename:join(DataDir, "groups_1/missing_conf_SUITE"),
- {Opts,ERPid} = setup({suite,Suite}, Config),
+ {Opts,ERPid} = setup([{suite,Suite}], Config),
ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(missing_conf_SUITE,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(missing_conf),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -102,18 +103,41 @@ repeat_1(Config) when is_list(Config) ->
Suite = filename:join(DataDir, "groups_1/repeat_1_SUITE"),
- {Opts,ERPid} = setup({suite,Suite}, Config),
+ {Opts,ERPid} = setup([{suite,Suite}], Config),
ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(repeat_1,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(repeat_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
+%%%
+
+empty_group(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ Suite = filename:join(DataDir, "groups_2/groups_22_SUITE"),
+
+ {Opts,ERPid} = setup([{suite,Suite},
+ {group,[test_group_8,test_group_9,test_group_10]}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(empty_group,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(empty_group),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -121,7 +145,7 @@ 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 ++ [Test,{event_handler,{?eh,EvHArgs}}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test],
ERPid = ct_test_support:start_event_receiver(Config),
{Opts,ERPid}.
@@ -256,4 +280,27 @@ test_events(repeat_1) ->
{?eh,tc_done,{repeat_1_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
+ ];
+
+test_events(empty_group) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{groups_22_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,init_per_suite,ok}},
+ [{?eh,tc_start,
+ {groups_22_SUITE,{init_per_group,test_group_8,[]}}},
+ {?eh,tc_done,
+ {groups_22_SUITE,{init_per_group,test_group_8,[]},ok}},
+ {?eh,tc_start,{groups_22_SUITE,testcase_8}},
+ {?eh,tc_done,{groups_22_SUITE,testcase_8,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_22_SUITE,{end_per_group,test_group_8,[]}}},
+ {?eh,tc_done,
+ {groups_22_SUITE,{end_per_group,test_group_8,[]},ok}}],
+ {?eh,tc_start,{groups_22_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,end_per_suite,init}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl
index cd517876df..14eb8769ad 100644
--- a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl
@@ -31,27 +31,33 @@ suite() ->
groups() ->
[
- {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]},
+ {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]},
- {test_group_1b, [parallel], [testcase_1a,testcase_1b]},
+ {test_group_1b, [parallel], [testcase_1a,testcase_1b]},
- {test_group_2, [parallel], [testcase_2a,
+ {test_group_2, [parallel], [testcase_2a,
- {test_group_3, [{repeat,1}],
- [testcase_3a, testcase_3b]},
+ {test_group_3, [{repeat,1}],
+ [testcase_3a, testcase_3b]},
- testcase_2b]},
+ testcase_2b]},
- {test_group_4, [{test_group_5, [parallel], [testcase_5a,
+ {test_group_4, [{test_group_5, [parallel], [testcase_5a,
- {group, test_group_6},
+ {group, test_group_6},
- testcase_5b]}]},
+ testcase_5b]}]},
- {test_group_6, [parallel], [{group, test_group_7}]},
+ {test_group_6, [parallel], [{group, test_group_7}]},
- {test_group_7, [sequence], [testcase_7a,testcase_7b]}
- ].
+ {test_group_7, [sequence], [testcase_7a,testcase_7b]},
+
+ {test_group_8, [], [{group, test_group_9}, testcase_8]},
+
+ {test_group_9, [], []},
+
+ {test_group_10, [], [{group, test_group_9}]}
+ ].
all() ->
[{group, test_group_1a},
@@ -60,7 +66,10 @@ all() ->
testcase_2,
{group, test_group_2},
testcase_3,
- {group, test_group_4}].
+ {group, test_group_4},
+ {group, test_group_8},
+ {group, test_group_9},
+ {group, test_group_10}].
%% this func only for internal test purposes
grs_and_tcs() ->
@@ -68,7 +77,9 @@ grs_and_tcs() ->
test_group_1a, test_group_1b,
test_group_2, test_group_3,
test_group_4, test_group_5,
- test_group_6, test_group_7
+ test_group_6, test_group_7,
+ test_group_8, test_group_9,
+ test_group_10
],
[
testcase_1a, testcase_1b, testcase_1c,
@@ -78,7 +89,8 @@ grs_and_tcs() ->
testcase_3a, testcase_3b,
testcase_3,
testcase_5a, testcase_5b,
- testcase_7a, testcase_7b
+ testcase_7a, testcase_7b,
+ testcase_8
]}.
%%--------------------------------------------------------------------
@@ -107,7 +119,10 @@ init_per_group(Group, Config) ->
{test_group_4,[{name,test_group_4}]} -> ok;
{test_group_5,[{name,test_group_5},parallel]} -> "parallel";
{test_group_6,[{name,test_group_6},parallel]} -> "parallel";
- {test_group_7,[{name,test_group_7},sequence]} -> "sequence"
+ {test_group_7,[{name,test_group_7},sequence]} -> "sequence";
+ {test_group_8,[{name,test_group_8}]} -> ok;
+ {test_group_9,[{name,test_group_9}]} -> ok;
+ {test_group_10,[{name,test_group_10}]} -> ok
end,
{Grs,_} = grs_and_tcs(),
case lists:member(Group, Grs) of
@@ -312,3 +327,7 @@ testcase_7b(Config) ->
undefined = ?config(testcase_7a,Config),
testcase_7b = ?config(testcase_7b,Config),
ok.
+testcase_8() ->
+ [].
+testcase_8(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 64f4e277ff..be1c02f163 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -225,8 +225,9 @@ do_test(Tag, SuiteWildCard, CTHs, Config, Res, EC) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(Tag,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(Tag, EC),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -259,9 +260,9 @@ events_to_check(Test, N) ->
test_events(one_empty_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{empty_cth,id,[[]]}},
{?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{empty_cth,pre_init_per_suite,
[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -287,11 +288,11 @@ test_events(one_empty_cth) ->
test_events(two_empty_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -329,8 +330,8 @@ test_events(faulty_cth_no_init) ->
test_events(faulty_cth_id_no_init) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',id,[[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',id,[[]]}},
{negative,{?eh,tc_start,'_'},
{?eh,test_done,{'DEF','STOP_TIME'}}},
{?eh,stop_logging,[]}
@@ -339,9 +340,9 @@ test_events(faulty_cth_id_no_init) ->
test_events(minimal_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{negative,{?eh,cth,{'_',id,['_',[]]}},
{?eh,cth,{'_',init,['_',[]]}}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
@@ -357,11 +358,11 @@ test_events(minimal_cth) ->
test_events(minimal_and_maximal_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{negative,{?eh,cth,{'_',id,['_',[]]}},
{?eh,cth,{'_',init,['_',[]]}}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,['_',[]]}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
@@ -387,8 +388,8 @@ test_events(faulty_cth_undef) ->
{failed,FailReasonStr}},
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
{failed, {error,FailReasonStr}}}},
@@ -433,15 +434,15 @@ test_events(faulty_cth_exit_in_init_scope_suite) ->
test_events(faulty_cth_exit_in_init) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{empty_cth,init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,['_',[]]}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}];
test_events(faulty_cth_exit_in_id) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{empty_cth,id,[[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,id,[[]]}},
{negative, {?eh,tc_start,'_'},
{?eh,test_done,{'DEF','STOP_TIME'}}},
{?eh,stop_logging,[]}];
@@ -609,9 +610,8 @@ test_events(scope_per_group_state_cth) ->
test_events(fail_pre_suite_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
-
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -649,8 +649,8 @@ test_events(fail_pre_suite_cth) ->
test_events(fail_post_suite_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
@@ -676,8 +676,8 @@ test_events(fail_post_suite_cth) ->
test_events(skip_pre_suite_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',{skip,"Test skip"},[]]}},
@@ -699,8 +699,8 @@ test_events(skip_pre_suite_cth) ->
test_events(skip_post_suite_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
@@ -724,8 +724,8 @@ test_events(recover_post_suite_cth) ->
Suite = ct_cth_fail_per_suite_SUITE,
[
{?eh,start_logging,'_'},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{Suite,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[Suite,'$proplist','$proplist']}},
{?eh,cth,{'_',post_init_per_suite,[Suite,contains([tc_status]),
@@ -753,8 +753,8 @@ test_events(recover_post_suite_cth) ->
test_events(update_config_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{ct_update_config_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,
@@ -864,9 +864,9 @@ test_events(update_config_cth) ->
test_events(state_update_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',init,['_',[]]}},
{?eh,cth,{'_',init,['_',[]]}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{'_',init_per_suite}},
{?eh,tc_done,{'_',end_per_suite,ok}},
@@ -902,8 +902,8 @@ test_events(state_update_cth) ->
test_events(options_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{empty_cth,init,['_',[test]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,['_',[test]]}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{empty_cth,pre_init_per_suite,
[ct_cth_empty_SUITE,'$proplist',[test]]}},
@@ -929,10 +929,10 @@ test_events(options_cth) ->
test_events(same_id_cth) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,cth,{'_',id,[[]]}},
{?eh,cth,{'_',init,[same_id_cth,[]]}},
{?eh,cth,{'_',id,[[]]}},
- {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
{?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
{negative,
@@ -969,8 +969,8 @@ test_events(same_id_cth) ->
test_events(fail_n_skip_with_minimal_cth) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,cth,{'_',init,['_',[]]}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{'_',init,['_',[]]}},
{?eh,tc_start,{'_',init_per_suite}},
{?eh,tc_done,{'_',end_per_suite,ok}},
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index e89b6f7de6..1471cc1e0c 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -119,8 +119,9 @@ ct_master_test(Config) when is_list(Config)->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suite_1,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ PrivDir, []),
+
find_events(NodeNames, [{tc_start,{master_SUITE,init_per_suite}},
{tc_start,{master_SUITE,first_testcase}},
{tc_start,{master_SUITE,second_testcase}},
@@ -174,7 +175,7 @@ make_spec(DataDir, FileName, NodeNames, Suites, Config)->
ct_test_support:write_testspec(N++Include++EH++C++S++LD++NS, FileName).
-get_log_dir({win32,_},PrivDir, NodeName)->
+get_log_dir({win32,_}, _PrivDir, NodeName)->
case filelib:is_dir(?TEMP_DIR) of
false ->
file:make_dir(?TEMP_DIR);
diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl
index a8bd2c2189..cb17af9ab5 100644
--- a/lib/common_test/test/ct_misc_1_SUITE.erl
+++ b/lib/common_test/test/ct_misc_1_SUITE.erl
@@ -111,7 +111,8 @@ beam_me_up(Config) when is_list(Config) ->
ct_test_support:log_events(beam_me_up,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(beam_me_up, 1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE.erl
index e674315526..4e842bd6d6 100644
--- a/lib/common_test/test/ct_repeat_1_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_1_SUITE.erl
@@ -159,7 +159,8 @@ execute(TestCase, SuiteName, Group, Config) ->
ct_test_support:log_events(TestCase,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(TestCase),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -561,7 +562,6 @@ test_events(repeat_cs_until_any_fail) ->
{error,
{{badmatch,2},
[{repeat_1_SUITE,tc_fail_1,1},
- {repeat_1_SUITE,tc_fail_1,1},
{test_server,my_apply,3},
{test_server,ts_tc,3},
{test_server,run_test_case_eval1,6},
diff --git a/lib/common_test/test/ct_sequence_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE.erl
index c7650b169c..5facf90656 100644
--- a/lib/common_test/test/ct_sequence_1_SUITE.erl
+++ b/lib/common_test/test/ct_sequence_1_SUITE.erl
@@ -132,7 +132,8 @@ execute(TestCase, SuiteName, Group, Config) ->
ct_test_support:log_events(TestCase,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(TestCase),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_skip_SUITE.erl b/lib/common_test/test/ct_skip_SUITE.erl
index 62c5f10b7c..4ba4479208 100644
--- a/lib/common_test/test/ct_skip_SUITE.erl
+++ b/lib/common_test/test/ct_skip_SUITE.erl
@@ -99,8 +99,9 @@ auto_skip(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(auto_skip,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(auto_skip),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -122,8 +123,9 @@ user_skip(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(user_skip,
- reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(user_skip),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_smoke_test_SUITE.erl b/lib/common_test/test/ct_smoke_test_SUITE.erl
index c3d49a5afa..49b38361e2 100644
--- a/lib/common_test/test/ct_smoke_test_SUITE.erl
+++ b/lib/common_test/test/ct_smoke_test_SUITE.erl
@@ -175,8 +175,9 @@ dir1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(dir1,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(dir1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -204,8 +205,9 @@ dir2(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(dir2,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(dir2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -234,8 +236,9 @@ dir1_2(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(dir1_2,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(dir1_2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -264,8 +267,8 @@ suite11(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(suite11,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(suite11),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -293,8 +296,8 @@ suite21(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(suite21,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(suite21),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -324,8 +327,8 @@ suite11_21(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(suite11_21,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(suite11_21),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -355,8 +358,8 @@ tc111(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(tc111,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(tc111),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -385,8 +388,8 @@ tc211(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(tc211,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(tc211),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -416,8 +419,8 @@ tc111_112(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(tc111_112,
- ct_test_support:reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ct_test_support:reformat(Events, ?eh),
+ ?config(priv_dir, Config), Opts),
TestEvents = events_to_check(tc111_112),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index 9d3e6a9e59..4471915e69 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -98,8 +98,9 @@ ts_if_1(Config) when is_list(Config) ->
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(ts_if_1,
- reformat(Events, ?eh),
- PrivDir),
+ reformat(Events, ?eh),
+ PrivDir,
+ Opts),
TestEvents = events_to_check(ts_if_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index b4f1a0e71f..601d5315ce 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -32,7 +32,8 @@
run/2, run/4, get_opts/1, wait_for_ct_stop/1]).
-export([handle_event/2, start_event_receiver/1, get_events/2,
- verify_events/3, reformat/2, log_events/3]).
+ verify_events/3, reformat/2, log_events/4,
+ join_abs_dirs/2]).
-include_lib("kernel/include/file.hrl").
@@ -63,7 +64,6 @@ init_per_suite(Config, Level) ->
start_slave(Config,Level) ->
[_,Host] = string:tokens(atom_to_list(node()), "@"),
-
test_server:format(0, "Trying to start ~s~n", ["ct@"++Host]),
case slave:start(Host, ct, []) of
{error,Reason} ->
@@ -72,18 +72,19 @@ start_slave(Config,Level) ->
test_server:format(0, "Node ~p started~n", [CTNode]),
IsCover = test_server:is_cover(),
if IsCover ->
- cover:start(CTNode);
- true->
- ok
+ cover:start(CTNode);
+ true->
+ ok
end,
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
+
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
%% PrivDir as well as directory of Test Server suites
%% have to be in code path on Common Test node.
[_ | Parts] = lists:reverse(filename:split(DataDir)),
TSDir = filename:join(lists:reverse(Parts)),
- AddPathDirs = case ?config(path_dirs, Config) of
+ AddPathDirs = case proplists:get_value(path_dirs, Config) of
undefined -> [];
Ds -> Ds
end,
@@ -110,8 +111,8 @@ start_slave(Config,Level) ->
%%% end_per_suite/1
end_per_suite(Config) ->
- CTNode = ?config(ct_node, Config),
- PrivDir = ?config(priv_dir, Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
true = rpc:call(CTNode, code, del_path, [filename:join(PrivDir,"")]),
cover:stop(CTNode),
slave:stop(CTNode),
@@ -121,7 +122,9 @@ end_per_suite(Config) ->
%%% init_per_testcase/2
init_per_testcase(_TestCase, Config) ->
- {_,{_,LogDir}} = lists:keysearch(logdir, 1, get_opts(Config)),
+ Opts = get_opts(Config),
+ NetDir = proplists:get_value(net_dir, Opts),
+ LogDir = join_abs_dirs(NetDir, proplists:get_value(logdir, Opts)),
case lists:keysearch(master, 1, Config) of
false->
test_server:format("See Common Test logs here:\n\n"
@@ -139,7 +142,7 @@ init_per_testcase(_TestCase, Config) ->
%%% end_per_testcase/2
end_per_testcase(_TestCase, Config) ->
- CTNode = ?config(ct_node, Config),
+ CTNode = proplists:get_value(ct_node, Config),
case wait_for_ct_stop(CTNode) of
%% Common test was not stopped to we restart node.
false ->
@@ -169,7 +172,7 @@ write_testspec(TestSpec, TSFile) ->
%%%
get_opts(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
TempDir = case os:getenv("TMP") of
false ->
case os:getenv("TEMP") of
@@ -195,20 +198,48 @@ get_opts(Config) ->
_ ->
TempDir
end,
- InitOpts = ?config(ct_opts, Config),
- [{logdir,LogDir} | InitOpts].
+
+ %% Copy test variables to app environment on new node
+ CtTestVars =
+ case init:get_argument(ct_test_vars) of
+ {ok,[Vars]} ->
+ [begin {ok,Ts,_} = erl_scan:string(Str++"."),
+ {ok,Expr} = erl_parse:parse_term(Ts),
+ Expr
+ end || Str <- Vars];
+ _ ->
+ []
+ end,
+ %% test_server:format("Test variables added to Config: ~p\n\n",
+ %% [CtTestVars]),
+ InitOpts =
+ case proplists:get_value(ct_opts, Config) of
+ undefined -> [];
+ CtOpts -> CtOpts
+ end,
+ [{logdir,LogDir} | InitOpts ++ CtTestVars].
%%%-----------------------------------------------------------------
%%%
run(Opts, Config) ->
- CTNode = ?config(ct_node, Config),
- Level = ?config(trace_level, Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
%% use ct interface
test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
[Opts, CTNode]),
Result1 = rpc:call(CTNode, ct, run_test, [Opts]),
+ case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
+ undefined ->
+ ok;
+ _ ->
+ test_server:format(Level,
+ "ct_util_server not stopped on ~p yet, waiting 5 s...~n",
+ [CTNode]),
+ timer:sleep(5000),
+ undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
+ end,
%% use run_test interface (simulated)
test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode,Opts]),
rpc:call(CTNode, application, set_env, [common_test, run_test_start_opts, Opts]),
@@ -224,8 +255,8 @@ run(Opts, Config) ->
end.
run(M, F, A, Config) ->
- CTNode = ?config(ct_node, Config),
- Level = ?config(trace_level, Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
test_server:format(Level, "~nCalling ~w:~w(~p) on ~p~n",
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
@@ -261,11 +292,11 @@ handle_event(EH, Event) ->
ok.
start_event_receiver(Config) ->
- CTNode = ?config(ct_node, Config),
+ CTNode = proplists:get_value(ct_node, Config),
spawn_link(CTNode, fun() -> er() end).
get_events(_, Config) ->
- CTNode = ?config(ct_node, Config),
+ CTNode = proplists:get_value(ct_node, Config),
{event_receiver,CTNode} ! {self(),get_events},
Events = receive {event_receiver,Evs} -> Evs end,
{event_receiver,CTNode} ! stop,
@@ -288,7 +319,7 @@ er_loop(Evs) ->
end.
verify_events(TEvs, Evs, Config) ->
- Node = ?config(ct_node, Config),
+ Node = proplists:get_value(ct_node, Config),
case catch verify_events1(TEvs, Evs, Node, Config) of
{'EXIT',Reason} ->
Reason;
@@ -349,10 +380,15 @@ locate(TEvs, Node, Evs, Config) when is_list(TEvs) ->
data={M,{init_per_group,GroupName,Props}}}},
{TEH,#event{name=tc_done,
node=Node,
- data={M,{init_per_group,GroupName,Props},R}}} | Evs1] ->
- test_server:format("Found ~p!", [InitStart]),
- test_server:format("Found ~p!", [InitDone]),
- verify_events1(TEvs1, Evs1, Node, Config);
+ data={M,{init_per_group,GroupName,Props},Res}}} | Evs1] ->
+ case result_match(R, Res) of
+ false ->
+ nomatch;
+ true ->
+ test_server:format("Found ~p!", [InitStart]),
+ test_server:format("Found ~p!", [InitDone]),
+ verify_events1(TEvs1, Evs1, Node, Config)
+ end;
_ ->
nomatch
end;
@@ -384,9 +420,11 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EvProps},EvR}}})
when TEH == EH, EvNode == Node, EvM == M,
EvGroupName == GroupName,
- EvProps == Props,
- EvR == R ->
- false;
+ EvProps == Props ->
+ case result_match(R, EvR) of
+ true -> false;
+ false -> true
+ end;
({EH,#event{name=stop_logging,
node=EvNode,data=_}})
when EH == TEH, EvNode == Node ->
@@ -466,7 +504,7 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
node=EvNode,
data={Mod,Func,Result}}} <- Done,
EH == TEH, EvNode == Node, Mod == M,
- Func == F, Result == R] of
+ Func == F, result_match(R, Result)] of
[TcDone|_] ->
test_server:format("Found ~p!", [TEv]),
{lists:delete(TcDone, Done),RemEvs,RemSize};
@@ -509,8 +547,13 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
data={Mod,{end_per_group,
EvGName,EvProps},Res}}}) when
EH == TEH, EvNode == Node, Mod == M,
- EvGName == GroupName, EvProps == Props, Res == R ->
- false;
+ EvGName == GroupName, EvProps == Props ->
+ case result_match(R, Res) of
+ true ->
+ false;
+ false ->
+ true
+ end;
({EH,#event{name=stop_logging,
node=EvNode,data=_}}) when
EH == TEH, EvNode == Node ->
@@ -603,23 +646,29 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
data={M,{init_per_group,GroupName,EvProps}}}},
{TEH,#event{name=tc_done,
node=Node,
- data={M,{init_per_group,GroupName,EvProps},R}}} | Es] ->
- case proplists:get_value(shuffle, Props) of
- '_' ->
- case proplists:get_value(shuffle, EvProps) of
- false ->
- exit({no_shuffle_prop_found,{M,init_per_group,
- GroupName,EvProps}});
+ data={M,{init_per_group,GroupName,EvProps},Res}}} | Es] ->
+ case result_match(R, Res) of
+ true ->
+ case proplists:get_value(shuffle, Props) of
+ '_' ->
+ case proplists:get_value(shuffle, EvProps) of
+ false ->
+ exit({no_shuffle_prop_found,
+ {M,init_per_group,
+ GroupName,EvProps}});
+ _ ->
+ PropsCmp = proplists:delete(shuffle, EvProps),
+ PropsCmp = proplists:delete(shuffle, Props)
+ end;
_ ->
- PropsCmp = proplists:delete(shuffle, EvProps),
- PropsCmp = proplists:delete(shuffle, Props)
- end;
- _ ->
- Props = EvProps
- end,
- test_server:format("Found ~p!", [InitStart]),
- test_server:format("Found ~p!", [InitDone]),
- {TEs,Es};
+ Props = EvProps
+ end,
+ test_server:format("Found ~p!", [InitStart]),
+ test_server:format("Found ~p!", [InitDone]),
+ {TEs,Es};
+ false ->
+ nomatch
+ end;
_ ->
nomatch
end;
@@ -670,7 +719,7 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
node=EvNode,
data={Mod,Func,Result}}} <- Done,
EH == TEH, EvNode == Node, Mod == M,
- Func == F, Result == R] of
+ Func == F, result_match(R, Result)] of
[TcDone|_] ->
test_server:format("Found ~p!", [TEv]),
{lists:delete(TcDone, Done),RemEvs,RemSize};
@@ -726,8 +775,13 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
data={Mod,{end_per_group,
EvGName,_},Res}}}) when
EH == TEH, EvNode == Node, Mod == M,
- EvGName == GroupName, Res == R ->
- false;
+ EvGName == GroupName ->
+ case result_match(R, Res) of
+ true ->
+ false;
+ false ->
+ true
+ end;
({EH,#event{name=stop_logging,
node=EvNode,data=_}}) when
EH == TEH, EvNode == Node ->
@@ -864,25 +918,34 @@ locate({TEH,Name,{'DEF','STOP_TIME'}}, Node, [Ev|Evs], Config) ->
nomatch
end;
-%% to match variable data as a result of a failed test case
-locate({TEH,tc_done,{Mod,Func,{failed,{error,{Slogan,'_'}}}}}, Node, [Ev|Evs], Config) ->
+%% to match variable data as a result of an aborted test case
+locate({TEH,tc_done,{undefined,undefined,{testcase_aborted,
+ {abort_current_testcase,Func},'_'}}},
+ Node, [Ev|Evs], Config) ->
case Ev of
- {TEH,#event{name=tc_done, node=Node,
- data={Mod,Func,{failed,{error,{Slogan,_}}}}}} ->
+ {TEH,#event{name=tc_done, node=Node,
+ data={undefined,undefined,
+ {testcase_aborted,{abort_current_testcase,Func},_}}}} ->
{Config,Evs};
_ ->
nomatch
end;
-%% to match variable data as a result of an aborted test case
-locate({TEH,tc_done,{undefined,undefined,{testcase_aborted,
- {abort_current_testcase,Func},'_'}}},
- Node, [Ev|Evs], Config) ->
+%% to match variable data as a result of a failed test case
+locate({TEH,tc_done,{Mod,Func,R={SkipOrFail,{_ErrInd,ErrInfo}}}},
+ Node, [Ev|Evs], Config) when ((SkipOrFail == skipped) or
+ (SkipOrFail == failed)) and
+ ((size(ErrInfo) == 2) or
+ (size(ErrInfo) == 3)) ->
case Ev of
{TEH,#event{name=tc_done, node=Node,
- data={undefined,undefined,
- {testcase_aborted,{abort_current_testcase,Func},_}}}} ->
- {Config,Evs};
+ data={Mod,Func,Result}}} ->
+ case result_match(R, Result) of
+ true ->
+ {Config,Evs};
+ false ->
+ nomatch
+ end;
_ ->
nomatch
end;
@@ -931,14 +994,27 @@ match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) ->
match_data([],[]) ->
match.
-log_events(TC, Events, PrivDir) ->
- LogFile = filename:join(PrivDir, atom_to_list(TC)++".events"),
+result_match({SkipOrFail,{ErrorInd,{Why,'_'}}},
+ {SkipOrFail,{ErrorInd,{Why,_Stack}}}) ->
+ true;
+result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}},
+ {SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,_Stack}}}}) ->
+ true;
+result_match(Result, Result) ->
+ true;
+result_match(_, _) ->
+ false.
+
+log_events(TC, Events, EvLogDir, Opts) ->
+ LogFile = filename:join(EvLogDir, atom_to_list(TC)++".events"),
{ok,Dev} = file:open(LogFile, [write]),
io:format(Dev, "[~n", []),
log_events1(Events, Dev, " "),
file:close(Dev),
+ FullLogFile = join_abs_dirs(proplists:get_value(net_dir, Opts),
+ LogFile),
io:format("Events written to logfile: <a href=\"file://~s\">~s</a>~n",
- [LogFile,LogFile]),
+ [FullLogFile,FullLogFile]),
io:format(user, "Events written to logfile: ~p~n", [LogFile]).
log_events1(Evs, Dev, "") ->
@@ -1024,13 +1100,25 @@ reformat([], _EH) ->
%%%-----------------------------------------------------------------
%%% MISC HELP FUNCTIONS
+join_abs_dirs(undefined, Dir2) ->
+ Dir2;
+join_abs_dirs(Dir1, Dir2) ->
+ case filename:pathtype(Dir2) of
+ relative ->
+ filename:join(Dir1, Dir2);
+ _ ->
+ [_Abs|Parts] = filename:split(Dir2),
+ filename:join(Dir1, filename:join(Parts))
+ end.
+
create_tmp_logdir(Tmp) ->
LogDir = filename:join(Tmp,"ct"),
file:make_dir(LogDir),
LogDir.
delete_old_logs({win32,_}, Config) ->
- case {?config(priv_dir, Config),?config(logdir, get_opts(Config))} of
+ case {proplists:get_value(priv_dir, Config),
+ proplists:get_value(logdir, get_opts(Config))} of
{LogDir,LogDir} ->
ignore;
{_,LogDir} -> % using tmp for logs
@@ -1042,7 +1130,8 @@ delete_old_logs(_, Config) ->
false ->
ignore;
_ ->
- catch delete_dirs(?config(logdir, get_opts(Config)))
+ catch delete_dirs(proplists:get_value(logdir,
+ get_opts(Config)))
end.
delete_dirs(LogDir) ->
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index 616c2db869..b6dcf63fdf 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -612,6 +612,12 @@ setup_and_execute(TCName, TestSpec, Config) ->
false -> [{spec,SpecFile},{label,TCName}]
end,
{Opts,ERPid} = setup(TestTerms, Config),
+
+ FullSpecFile = ct_test_support:join_abs_dirs(?config(net_dir, Opts),
+ SpecFile),
+ io:format("~nTest spec created here~n~n<a href=\"file://~s\">~s</a>~n",
+ [FullSpecFile,FullSpecFile]),
+
ok = ct_test_support:run(Opts, Config),
TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)},
{label,proplists:get_value(label,TestTerms)} | TestSpec],
@@ -620,7 +626,8 @@ setup_and_execute(TCName, TestSpec, Config) ->
ct_test_support:log_events(TCName,
reformat(Events, ?eh),
- ?config(priv_dir, Config)),
+ ?config(priv_dir, Config),
+ Opts),
TestEvents = events_to_check(TCName),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -631,8 +638,6 @@ create_spec_file(SpecDir, TCName, TestSpec) ->
{ok,Dev} = file:open(FileName, [write]),
[io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
file:close(Dev),
- io:format("~nTest spec created here~n~n<a href=\"file://~s\">~s</a>~n",
- [FileName,FileName]),
FileName.
setup(Test, Config) when is_tuple(Test) ->
@@ -791,7 +796,9 @@ test_events(skip_group) ->
{?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}},
{?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_1b},"SKIPPED!"}},
- {?eh,test_stats,{2,0,{1,0}}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_2},"SKIPPED!"}},
+ %%! But not test_group_7 since it's a sub-group!
+ {?eh,test_stats,{2,0,{2,0}}},
{negative,{?eh,tc_user_skip,'_'},{?eh,stop_logging,'_'}}
];
@@ -1188,10 +1195,9 @@ test_events(sub_skipped_by_top) ->
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_4},"SKIPPED!"}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_4},"SKIPPED!"}},
- {negative,
- {?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_4},"SKIPPED!"}},
- {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
{negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
];
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index b8786f6f94..3ebf62d87c 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -134,7 +134,9 @@ static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_T
static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -204,7 +206,9 @@ static ErlNifFunc nif_funcs[] = {
{"aes_ctr_encrypt", 3, aes_ctr_encrypt},
{"aes_ctr_decrypt", 3, aes_ctr_encrypt},
{"rand_bytes", 1, rand_bytes_1},
+ {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif},
{"rand_bytes", 3, rand_bytes_3},
+ {"strong_rand_mpint_nif", 3, strong_rand_mpint_nif},
{"rand_uniform_nif", 2, rand_uniform_nif},
{"mod_exp_nif", 3, mod_exp_nif},
{"dss_verify", 4, dss_verify},
@@ -704,6 +708,22 @@ static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes);
return ret;
}
+static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Bytes) */
+ unsigned bytes;
+ unsigned char* data;
+ ERL_NIF_TERM ret;
+ if (!enif_get_uint(env, argv[0], &bytes)) {
+ return enif_make_badarg(env);
+ }
+ data = enif_make_new_binary(env, bytes, &ret);
+ if ( RAND_bytes(data, bytes) != 1) {
+ return atom_false;
+ }
+ ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes);
+ return ret;
+}
+
static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Bytes, TopMask, BottomMask) */
unsigned bytes;
@@ -724,6 +744,47 @@ static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
}
return ret;
}
+static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Bytes, TopMask, BottomMask) */
+ unsigned bits;
+ BIGNUM *bn_rand;
+ int top, bottom;
+ unsigned char* data;
+ unsigned dlen;
+ ERL_NIF_TERM ret;
+ if (!enif_get_uint(env, argv[0], &bits)
+ || !enif_get_int(env, argv[1], &top)
+ || !enif_get_int(env, argv[2], &bottom)) {
+ return enif_make_badarg(env);
+ }
+ if (! (top == -1 || top == 0 || top == 1) ) {
+ return enif_make_badarg(env);
+ }
+ if (! (bottom == 0 || bottom == 1) ) {
+ return enif_make_badarg(env);
+ }
+
+ bn_rand = BN_new();
+ if (! bn_rand ) {
+ return enif_make_badarg(env);
+ }
+
+ /* Get a (bits) bit random number */
+ if (!BN_rand(bn_rand, bits, top, bottom)) {
+ ret = atom_false;
+ }
+ else {
+ /* Copy the bignum into an erlang mpint binary. */
+ dlen = BN_num_bytes(bn_rand);
+ data = enif_make_new_binary(env, dlen+4, &ret);
+ put_int32(data, dlen);
+ BN_bn2bin(bn_rand, data+4);
+ ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen);
+ }
+ BN_free(bn_rand);
+
+ return ret;
+}
static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp)
{
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index dfafe67348..1ccea6df79 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2010</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -619,6 +619,21 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>strong_rand_bytes(N) -> binary()</name>
+ <fsummary>Generate a binary of random bytes</fsummary>
+ <type>
+ <v>N = integer()</v>
+ </type>
+ <desc>
+ <p>Generates N bytes randomly uniform 0..255, and returns the
+ result in a binary. Uses a cryptographically secure prng seeded and
+ periodically mixed with operating system provided entropy. By default
+ this is the <c>RAND_bytes</c> method from OpenSSL.</p>
+ <p>May throw exception <c>low_entropy</c> in case the random generator
+ failed due to lack of secure "randomness".</p>
+ </desc>
+ </func>
+ <func>
<name>rand_uniform(Lo, Hi) -> N</name>
<fsummary>Generate a random number</fsummary>
<type>
@@ -633,6 +648,31 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>strong_rand_mpint(N, Top, Bottom) -> Mpint</name>
+ <fsummary>Generate an N bit random number</fsummary>
+ <type>
+ <v>N = non_neg_integer()</v>
+ <v>Top = -1 | 0 | 1</v>
+ <v>Bottom = 0 | 1</v>
+ <v>Mpint = binary()</v>
+ </type>
+ <desc>
+ <p>Generate an N bit random number using OpenSSL's
+ cryptographically strong pseudo random number generator
+ <c>BN_rand</c>.</p>
+ <p>The parameter <c>Top</c> places constraints on the most
+ significant bits of the generated number. If <c>Top</c> is 1, then the
+ two most significant bits will be set to 1, if <c>Top</c> is 0, the
+ most significant bit will be 1, and if <c>Top</c> is -1 then no
+ constraints are applied and thus the generated number may be less than
+ N bits long.</p>
+ <p>If <c>Bottom</c> is 1, then the generated number is
+ constrained to be odd.</p>
+ <p>May throw exception <c>low_entropy</c> in case the random generator
+ failed due to lack of secure "randomness".</p>
+ </desc>
+ </func>
+ <func>
<name>mod_exp(N, P, M) -> Result</name>
<fsummary>Perform N ^ P mod M</fsummary>
<type>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 5e9bda3920..ab1ffa9e5c 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2010</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,21 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 2.0.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Strengthened random number generation. (Thanks to Geoff Cant)</p>
+ <p>
+ Own Id: OTP-9225</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 2.0.2.1</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index d6e2e033c0..cc7b3acc9c 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-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
@@ -46,6 +46,7 @@
-export([rsa_private_encrypt/3, rsa_public_decrypt/3]).
-export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]).
-export([rand_bytes/1, rand_bytes/3, rand_uniform/2]).
+-export([strong_rand_bytes/1, strong_rand_mpint/3]).
-export([mod_exp/3, mpint/1, erlint/1]).
%% -export([idea_cbc_encrypt/3, idea_cbc_decrypt/3]).
-export([aes_cbc_128_encrypt/3, aes_cbc_128_decrypt/3]).
@@ -68,6 +69,8 @@
des_ede3_cbc_encrypt, des_ede3_cbc_decrypt,
aes_cfb_128_encrypt, aes_cfb_128_decrypt,
rand_bytes,
+ strong_rand_bytes,
+ strong_rand_mpint,
rand_uniform,
mod_exp,
dss_verify,dss_sign,
@@ -361,12 +364,32 @@ aes_cfb_128_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%% RAND - pseudo random numbers using RN_ functions in crypto lib
%%
-spec rand_bytes(non_neg_integer()) -> binary().
+-spec strong_rand_bytes(non_neg_integer()) -> binary().
-spec rand_uniform(crypto_integer(), crypto_integer()) ->
crypto_integer().
+-spec strong_rand_mpint(Bits::non_neg_integer(),
+ Top::-1..1,
+ Bottom::0..1) -> binary().
rand_bytes(_Bytes) -> ?nif_stub.
+
+strong_rand_bytes(Bytes) ->
+ case strong_rand_bytes_nif(Bytes) of
+ false -> erlang:error(low_entropy);
+ Bin -> Bin
+ end.
+strong_rand_bytes_nif(_Bytes) -> ?nif_stub.
+
rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub.
+strong_rand_mpint(Bits, Top, Bottom) ->
+ case strong_rand_mpint_nif(Bits,Top,Bottom) of
+ false -> erlang:error(low_entropy);
+ Bin -> Bin
+ end.
+strong_rand_mpint_nif(_Bits, _Top, _Bottom) -> ?nif_stub.
+
+
rand_uniform(From,To) when is_binary(From), is_binary(To) ->
case rand_uniform_nif(From,To) of
<<Len:32/integer, MSB, Rest/binary>> when MSB > 127 ->
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index fe8f8e69a0..854a8b4485 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -46,6 +46,7 @@
aes_ctr/1,
mod_exp_test/1,
rand_uniform_test/1,
+ strong_rand_test/1,
rsa_verify_test/1,
dsa_verify_test/1,
rsa_sign_test/1,
@@ -68,7 +69,8 @@ all() ->
md5_mac_io, sha, sha_update,
%% sha256, sha256_update, sha512,sha512_update,
des_cbc, aes_cfb, aes_cbc,
- aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb, rand_uniform_test,
+ aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb,
+ rand_uniform_test, strong_rand_test,
rsa_verify_test, dsa_verify_test, rsa_sign_test,
dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test,
rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64,
@@ -710,6 +712,33 @@ rand_uniform_aux_test(N) ->
%%
%%
+strong_rand_test(doc) ->
+ "strong_rand_mpint and strong_random_bytes testing";
+strong_rand_test(suite) ->
+ [];
+strong_rand_test(Config) when is_list(Config) ->
+ strong_rand_aux_test(180),
+ ?line 10 = byte_size(crypto:strong_rand_bytes(10)).
+
+strong_rand_aux_test(0) ->
+ ?line t(crypto:strong_rand_mpint(0,0,0) =:= <<0,0,0,0>>),
+ ok;
+strong_rand_aux_test(1) ->
+ ?line t(crypto:erlint(crypto:strong_rand_mpint(1,0,1)) =:= 1),
+ ?line strong_rand_aux_test(0);
+strong_rand_aux_test(N) ->
+ ?line t(sru_length(crypto:strong_rand_mpint(N,-1,0)) =< N),
+ ?line t(sru_length(crypto:strong_rand_mpint(N,0,0)) =:= N),
+ ?line t(crypto:erlint(crypto:strong_rand_mpint(N,0,1)) band 1 =:= 1),
+ ?line t(crypto:erlint(crypto:strong_rand_mpint(N,1,0)) bsr (N - 2) =:= 2#11),
+ ?line strong_rand_aux_test(N-1).
+
+sru_length(Mpint) ->
+ I = crypto:erlint(Mpint),
+ length(erlang:integer_to_list(I, 2)).
+
+%%
+%%
%%
%%
rsa_verify_test(doc) ->
@@ -1097,7 +1126,7 @@ worker_loop(0, _) ->
ok;
worker_loop(N, Config) ->
Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc,
- aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test,
+ aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test,
rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test },
F = element(random:uniform(size(Funcs)),Funcs),
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index e2d6fd0b37..740c68d8fa 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 2.0.2.1
+CRYPTO_VSN = 2.0.2.2
diff --git a/lib/erl_interface/src/legacy/erl_timeout.c b/lib/erl_interface/src/legacy/erl_timeout.c
index d9560eebc8..146a106e7c 100644
--- a/lib/erl_interface/src/legacy/erl_timeout.c
+++ b/lib/erl_interface/src/legacy/erl_timeout.c
@@ -43,6 +43,7 @@
# endif
#endif
+#include "erl_interface.h"
#include "erl_timeout.h"
typedef struct jmp_s {
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index de0f23bf24..93563c6011 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-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
@@ -539,27 +539,41 @@ udp_send(#sock{inet=I}, {A,B,C,D}=IP, Port, Buffer)
when ?ip(A,B,C,D), ?port(Port) ->
gen_udp:send(I, IP, Port, Buffer).
-udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout)
+udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout, Decode)
when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) ->
- do_udp_recv(fun(T) -> gen_udp:recv(I, 0, T) end, IP, Port, Timeout);
-udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout)
+ do_udp_recv(I, IP, Port, Timeout, Decode, erlang:now(), Timeout);
+udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout, Decode)
when ?ip(A,B,C,D), ?port(Port) ->
- do_udp_recv(fun(T) -> gen_udp:recv(I, 0, T) end, IP, Port, Timeout).
-
-do_udp_recv(Recv, IP, Port, Timeout) ->
- do_udp_recv(Recv, IP, Port, Timeout,
- if Timeout =/= 0 -> erlang:now(); true -> undefined end).
-
-do_udp_recv(Recv, IP, Port, Timeout, Then) ->
- case Recv(Timeout) of
- {ok,{IP,Port,Answer}} ->
- {ok,Answer,erlang:max(0, Timeout - now_ms(erlang:now(), Then))};
- {ok,_} when Timeout =:= 0 ->
- {error,timeout};
- {ok,_} ->
- Now = erlang:now(),
- T = erlang:max(0, Timeout - now_ms(Now, Then)),
- do_udp_recv(Recv, IP, Port, T, Now);
+ do_udp_recv(I, IP, Port, Timeout, Decode, erlang:now(), Timeout).
+
+do_udp_recv(_I, _IP, _Port, 0, _Decode, _Start, _T) ->
+ timeout;
+do_udp_recv(I, IP, Port, Timeout, Decode, Start, T) ->
+ case gen_udp:recv(I, 0, T) of
+ {ok,Reply} ->
+ case Decode(Reply) of
+ false when T =:= 0 ->
+ %% This is a compromize between the hard way i.e
+ %% in the clause below if NewT becomes 0 bailout
+ %% immediately and risk that the right reply lies
+ %% ahead after some bad id replies, and the
+ %% forgiving way i.e go on with Timeout 0 until
+ %% the right reply comes or no reply (timeout)
+ %% which opens for a DOS attack by a malicious
+ %% DNS server flooding with bad id replies causing
+ %% an infinite loop here.
+ %%
+ %% Timeout is used as a sanity limit counter
+ %% just to put an end to the loop.
+ NewTimeout = erlang:max(0, Timeout - 50),
+ do_udp_recv(I, IP, Port, NewTimeout, Decode, Start, T);
+ false ->
+ Now = erlang:now(),
+ NewT = erlang:max(0, Timeout - now_ms(Now, Start)),
+ do_udp_recv(I, IP, Port, Timeout, Decode, Start, NewT);
+ Result ->
+ Result
+ end;
Error -> Error
end.
@@ -580,6 +594,17 @@ udp_close(#sock{inet=I,inet6=I6}) ->
%% end
%% end
%%
+%% But that man page also says dig always use num_servers = 1.
+%%
+%% Our man page says: timeout/retry, then double for next retry, i.e
+%% for i = 0 to retry - 1
+%% foreach nameserver
+%% send query
+%% wait((time * (2**i)) / retry)
+%% end
+%% end
+%%
+%% And that is what the code seems to do, now fixed, hopefully...
do_query(_Q, [], _Timer) ->
{error,nxdomain};
@@ -589,19 +614,16 @@ do_query(#q{options=#options{retry=Retry}}=Q, NSs, Timer) ->
query_retries(_Q, _NSs, _Timer, Retry, Retry, S) ->
udp_close(S),
{error,timeout};
+query_retries(_Q, [], _Timer, _Retry, _I, S) ->
+ udp_close(S),
+ {error,timeout};
query_retries(Q, NSs, Timer, Retry, I, S0) ->
- Num = length(NSs),
- if Num =:= 0 ->
- udp_close(S0),
- {error,timeout};
- true ->
- case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
- {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers
- query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S);
- {S,Result} ->
- udp_close(S),
- Result
- end
+ case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
+ {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers
+ query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S);
+ {S,Result} ->
+ udp_close(S),
+ Result
end.
query_nss(_Q, [], _Timer, _Retry, _I, S, ErrNSs) ->
@@ -611,13 +633,13 @@ query_nss(#q{edns=undefined}=Q, NSs, Timer, Retry, I, S, ErrNSs) ->
query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) ->
query_nss_edns(Q, NSs, Timer, Retry, I, S, ErrNSs).
-query_nss_edns(#q{options=#options{udp_payload_size=PSz}=Options,
- edns={Id,Buffer}}=Q,
- [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) ->
- {S,Res}=Reply = query_ns(S0, Id, Buffer, IP, Port, Timer,
- Retry, I, Options, PSz),
+query_nss_edns(
+ #q{options=#options{udp_payload_size=PSz}=Options,edns={Id,Buffer}}=Q,
+ [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) ->
+ {S,Res}=Reply =
+ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz),
case Res of
- timeout -> {S,{error,timeout}};
+ timeout -> {S,{error,timeout}}; % Bailout timeout
{ok,_} -> Reply;
{error,{nxdomain,_}} -> Reply;
{error,{E,_}} when E =:= qfmterror; E =:= notimp; E =:= servfail;
@@ -629,17 +651,19 @@ query_nss_edns(#q{options=#options{udp_payload_size=PSz}=Options,
query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs)
end.
-query_nss_dns(#q{dns=Qdns}=Q0, [{IP,Port}=NS|NSs],
- Timer, Retry, I, S0, ErrNSs) ->
+query_nss_dns(
+ #q{dns=Qdns}=Q0,
+ [{IP,Port}=NS|NSs], Timer, Retry, I, S0, ErrNSs) ->
#q{options=Options,dns={Id,Buffer}}=Q =
if
is_function(Qdns, 0) -> Q0#q{dns=Qdns()};
true -> Q0
end,
- {S,Res}=Reply = query_ns(S0, Id, Buffer, IP, Port, Timer,
- Retry, I, Options, ?PACKETSZ),
+ {S,Res}=Reply =
+ query_ns(
+ S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ),
case Res of
- timeout -> {S,{error,timeout}};
+ timeout -> {S,{error,timeout}}; % Bailout timeout
{ok,_} -> Reply;
{error,{E,_}} when E =:= nxdomain; E =:= qfmterror -> Reply;
{error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused ->
@@ -653,48 +677,66 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
PSz) ->
case UseVC orelse iolist_size(Buffer) > PSz of
true ->
- {S0,query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose)};
+ TcpTimeout = inet:timeout(Tm*5, Timer),
+ {S0,query_tcp(TcpTimeout, Id, Buffer, IP, Port, Verbose)};
false ->
case udp_open(S0, IP) of
{ok,S} ->
- {S,case query_udp(S, Id, Buffer, IP, Port, Timer,
- Retry, I, Tm, Verbose) of
- {ok,#dns_rec{header=H}} when H#dns_header.tc ->
- query_tcp(Tm, Id, Buffer,
- IP, Port, Timer, Verbose);
- Reply -> Reply
- end};
+ Timeout =
+ inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
+ {S,
+ case query_udp(
+ S, Id, Buffer, IP, Port, Timeout, Verbose) of
+ {ok,#dns_rec{header=H}} when H#dns_header.tc ->
+ TcpTimeout = inet:timeout(Tm*5, Timer),
+ query_tcp(
+ TcpTimeout, Id, Buffer, IP, Port, Verbose);
+ Reply -> Reply
+ end};
Error ->
{S0,Error}
end
end.
-query_udp(S, Id, Buffer, IP, Port, Timer, Retry, I, Tm, Verbose) ->
- Timeout = inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
+query_udp(_S, _Id, _Buffer, _IP, _Port, 0, Verbose) ->
+ timeout;
+query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
?verbose(Verbose, "Try UDP server : ~p:~p (timeout=~w)\n",
- [IP, Port, Timeout]),
- udp_connect(S, IP, Port),
- udp_send(S, IP, Port, Buffer),
- query_udp_recv(S, IP, Port, Id, Timeout, Verbose).
-
-query_udp_recv(S, IP, Port, Id, Timeout, Verbose) ->
- case udp_recv(S, IP, Port, Timeout) of
- {ok,Answer,T} ->
- case decode_answer(Answer, Id, Verbose) of
- {error, badid} ->
- query_udp_recv(S, IP, Port, Id, T, Verbose);
- Reply -> Reply
+ [IP,Port,Timeout]),
+ case
+ case udp_connect(S, IP, Port) of
+ ok ->
+ udp_send(S, IP, Port, Buffer);
+ E1 ->
+ E1 end of
+ ok ->
+ Decode =
+ fun ({RecIP,RecPort,Answer})
+ when RecIP =:= IP, RecPort =:= Port ->
+ case decode_answer(Answer, Id, Verbose) of
+ {error,badid} ->
+ false;
+ Reply ->
+ Reply
+ end;
+ ({_,_,_}) ->
+ false
+ end,
+ case udp_recv(S, IP, Port, Timeout, Decode) of
+ {ok,_}=Result ->
+ Result;
+ E2 ->
+ ?verbose(Verbose, "UDP server error: ~p\n", [E2]),
+ E2
end;
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "UDP server timeout\n", []),
- timeout;
- Error ->
- ?verbose(Verbose, "UDP server error: ~p\n", [Error]),
- Error
+ E3 ->
+ ?verbose(Verbose, "UDP send failed: ~p\n", [E3]),
+ {error,econnrefused}
end.
-query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose) ->
- Timeout = inet:timeout(Tm*5, Timer),
+query_tcp(0, _Id, _Buffer, _IP, _Port, Verbose) ->
+ timeout;
+query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
?verbose(Verbose, "Try TCP server : ~p:~p (timeout=~w)\n",
[IP, Port, Timeout]),
Family = case IP of
@@ -716,19 +758,10 @@ query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose) ->
end;
Error ->
gen_tcp:close(S),
- case Error of
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "TCP server recv timeout\n", []),
- timeout;
- _ ->
- ?verbose(Verbose, "TCP server recv error: ~p\n",
- [Error]),
- Error
- end
+ ?verbose(Verbose, "TCP server recv error: ~p\n",
+ [Error]),
+ Error
end;
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "TCP server connect timeout\n", []),
- timeout;
Error ->
?verbose(Verbose, "TCP server error: ~p\n", [Error]),
Error
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 8078c7d021..2f73394c4e 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -2055,6 +2055,10 @@ try_read_file_list(Fd) ->
?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n",
?line Title = io:get_line(Fd, ''),
+ %% Seek past the end of the file.
+
+ ?line {ok, _} = ?FILE_MODULE:position(Fd, 25000),
+
%% Done.
?line ?FILE_MODULE:close(Fd),
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index 5fc8df475d..6064a9b2d9 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -27,7 +27,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2]).
--export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1]).
+-export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1,
+ last_ms_answer/1]).
-export([
gethostbyaddr/0, gethostbyaddr/1,
gethostbyaddr_v6/0, gethostbyaddr_v6/1,
@@ -45,6 +46,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, resolve, edns0, txt_record, files_monitor,
+ last_ms_answer,
gethostbyaddr, gethostbyaddr_v6, gethostbyname,
gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6,
host_and_addr].
@@ -64,16 +66,15 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-zone_dir(basic) ->
- otptest;
-zone_dir(resolve) ->
- otptest;
-zone_dir(edns0) ->
- otptest;
-zone_dir(files_monitor) ->
- otptest;
-zone_dir(_) ->
- undefined.
+zone_dir(TC) ->
+ case TC of
+ basic -> otptest;
+ resolve -> otptest;
+ edns0 -> otptest;
+ files_monitor -> otptest;
+ last_ms_answer -> otptest;
+ _ -> undefined
+ end.
init_per_testcase(Func, Config) ->
PrivDir = ?config(priv_dir, Config),
@@ -184,6 +185,88 @@ ns_printlog(Fname) ->
ok
end.
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Behaviour modifying nameserver proxy
+
+proxy_start(TC, {NS,P}) ->
+ Tag = make_ref(),
+ Parent = self(),
+ Pid =
+ spawn_link(
+ fun () ->
+ try proxy_start(TC, NS, P, Parent, Tag)
+ catch C:X ->
+ io:format(
+ "~w: ~w:~p ~p~n",
+ [self(),C,X,erlang:get_stacktrace()])
+ end
+ end),
+ receive {started,Tag,Port} ->
+ ProxyNS = {{127,0,0,1},Port},
+ {proxy,Pid,Tag,ProxyNS}
+ end.
+
+proxy_start(TC, NS, P, Parent, Tag) ->
+ {ok,Outbound} = gen_udp:open(0, [binary]),
+ ok = gen_udp:connect(Outbound, NS, P),
+ {ok,Inbound} = gen_udp:open(0, [binary]),
+ {ok,Port} = inet:port(Inbound),
+ Parent ! {started,Tag,Port},
+ proxy(TC, Outbound, NS, P, Inbound).
+
+
+%% To provoke the last_ms_answer bug (OTP-9221) the proxy
+%% * Relays the query to the right nameserver
+%% * Intercepts the reply but holds it until the timer that
+%% was started when receiving the query fires.
+%% * Repeats the reply with incorrect query ID a number of
+%% times with a short interval.
+%% * Sends the correct reply, to give a correct test result
+%% after bug correction.
+%%
+%% The repetition of an incorrect answer with tight interval will keep
+%% inet_res in an inner loop in the code that decrements the remaining
+%% time until it hits 0 which triggers a crash, if the outer timeout
+%% parameter to inet_res:resolve is so short that it runs out during
+%% these repetitions.
+proxy(last_ms_answer, Outbound, NS, P, Inbound) ->
+ receive
+ {udp,Inbound,SrcIP,SrcPort,Data} ->
+ Time =
+ inet_db:res_option(timeout) div inet_db:res_option(retry),
+ Tag = erlang:make_ref(),
+ erlang:send_after(Time - 10, self(), {time,Tag}),
+ ok = gen_udp:send(Outbound, NS, P, Data),
+ receive
+ {udp,Outbound,NS,P,Reply} ->
+ {ok,Msg} = inet_dns:decode(Reply),
+ Hdr = inet_dns:msg(Msg, header),
+ Id = inet_dns:header(Hdr, id),
+ BadHdr =
+ inet_dns:make_header(Hdr, id, (Id+1) band 16#ffff),
+ BadMsg = inet_dns:make_msg(Msg, header, BadHdr),
+ BadReply = inet_dns:encode(BadMsg),
+ receive
+ {time,Tag} ->
+ proxy__last_ms_answer(
+ Inbound, SrcIP, SrcPort, BadReply, Reply, 30)
+ end
+ end
+ end.
+
+proxy__last_ms_answer(Socket, IP, Port, _, Reply, 0) ->
+ ok = gen_udp:send(Socket, IP, Port, Reply);
+proxy__last_ms_answer(Socket, IP, Port, BadReply, Reply, N) ->
+ ok = gen_udp:send(Socket, IP, Port, BadReply),
+ receive after 1 -> ok end,
+ proxy__last_ms_answer(Socket, IP, Port, BadReply, Reply, N-1).
+
+proxy_wait({proxy,Pid,_,_}) ->
+ Mref = erlang:monitor(process, Pid),
+ receive {'DOWN',Mref,_,_,_} -> ok end.
+
+proxy_ns({proxy,_,_,ProxyNS}) -> ProxyNS.
+
%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -204,7 +287,7 @@ basic(Config) when is_list(Config) ->
{ok,Msg1} = inet_dns:decode(Bin1),
%%
%% resolve
- {ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]}]),
+ {ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]},verbose]),
io:format("~p~n", [Msg2]),
[RR2] = inet_dns:msg(Msg2, anlist),
IP = inet_dns:rr(RR2, data),
@@ -474,6 +557,26 @@ do_files_monitor(Config) ->
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+last_ms_answer(doc) ->
+ ["Answer just when timeout is triggered (OTP-9221)"];
+last_ms_answer(Config) when is_list(Config) ->
+ NS = ns(Config),
+ Name = "ns.otptest",
+ %%IP = {127,0,0,254},
+ Time = inet_db:res_option(timeout) div inet_db:res_option(retry),
+ PSpec = proxy_start(last_ms_answer, NS),
+ ProxyNS = proxy_ns(PSpec),
+ %%
+ %% resolve; whith short timeout to trigger Timeout =:= 0 in inet_res
+ {error,timeout} =
+ inet_res:resolve(
+ Name, in, a, [{nameservers,[ProxyNS]},verbose], Time + 10),
+ %%
+ proxy_wait(PSpec),
+ ok.
+
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compatibility tests. Call the inet_SUITE tests, but with
%% lookup = [file,dns] instead of [native]
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index af667b1a71..224b9d4af7 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,19 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 2.0.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Strengthened random number generation. (Thanks to Geoff Cant)</p>
+ <p>
+ Own Id: OTP-9225</p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>Ssh 2.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 501da8ceb9..9be8c3c7d5 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,34 +19,44 @@
{"%VSN%",
[
- {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {"2.0.4", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
+ {"2.0.3", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []},
{load_module, ssh, soft_purge, soft_purge, []},
{load_module, ssh_rsa, soft_purge, soft_purge, []},
{load_module, ssh_acceptor, soft_purge, soft_purge, []},
{load_module, ssh_transport, soft_purge, soft_purge, []},
{load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
- {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {"2.0.2", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []},
{load_module, ssh, soft_purge, soft_purge, []},
{load_module, ssh_rsa, soft_purge, soft_purge, []},
{load_module, ssh_acceptor, soft_purge, soft_purge, []},
{load_module, ssh_transport, soft_purge, soft_purge, []},
- {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
- {"2.0.1", [{restart_application, ssh}]}
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}
],
[
- {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {"2.0.4", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []}]},
+ {"2.0.3", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []},
{load_module, ssh, soft_purge, soft_purge, []},
{load_module, ssh_rsa, soft_purge, soft_purge, []},
{load_module, ssh_acceptor, soft_purge, soft_purge, []},
{load_module, ssh_transport, soft_purge, soft_purge, []},
{load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
- {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {"2.0.2", [{load_module, ssh_bits, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_file, soft_purge, soft_purge, []},
{load_module, ssh, soft_purge, soft_purge, []},
{load_module, ssh_rsa, soft_purge, soft_purge, []},
{load_module, ssh_acceptor, soft_purge, soft_purge, []},
{load_module, ssh_transport, soft_purge, soft_purge, []},
- {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
- {"2.0.1", [{restart_application, ssh}]}
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]}
]
}.
diff --git a/lib/ssh/src/ssh_bits.erl b/lib/ssh/src/ssh_bits.erl
index 399581a0fd..3f0a06575c 100755
--- a/lib/ssh/src/ssh_bits.erl
+++ b/lib/ssh/src/ssh_bits.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -34,7 +34,7 @@
%% integer utils
-export([isize/1]).
-export([irandom/1, irandom/3]).
--export([random/1, random/3]).
+-export([random/1]).
-export([xor_bits/2, fill_bits/2]).
-export([i2bin/2, bin2i/1]).
@@ -401,9 +401,6 @@ xor_bits(XBits, YBits) ->
irandom(Bits) ->
irandom(Bits, 1, 0).
-%% irandom_odd(Bits) ->
-%% irandom(Bits, 1, 1).
-
%%
%% irandom(N, Top, Bottom)
%%
@@ -414,57 +411,16 @@ irandom(Bits) ->
%% Bot = 0 - do not set the least signifcant bit
%% Bot = 1 - set the least signifcant bit (i.e always odd)
%%
-irandom(0, _Top, _Bottom) ->
- 0;
-irandom(Bits, Top, Bottom) ->
- Bytes = (Bits+7) div 8,
- Skip = (8-(Bits rem 8)) rem 8,
- TMask = case Top of
- 0 -> 0;
- 1 -> 16#80;
- 2 -> 16#c0
- end,
- BMask = case Bottom of
- 0 -> 0;
- 1 -> (1 bsl Skip)
- end,
- <<X:Bits/big-unsigned-integer, _:Skip>> = random(Bytes, TMask, BMask),
- X.
+irandom(Bits, Top, Bottom) when is_integer(Top),
+ 0 =< Top, Top =< 2 ->
+ crypto:erlint(crypto:strong_rand_mpint(Bits, Top - 1, Bottom)).
%%
%% random/1
%% Generate N random bytes
%%
random(N) ->
- random(N, 0, 0).
-
-random(N, TMask, BMask) ->
- list_to_binary(rnd(N, TMask, BMask)).
-
-%% random/3
-%% random(Bytes, TopMask, BotMask)
-%% where
-%% Bytes is the number of bytes to generate
-%% TopMask is bitwised or'ed to the first byte
-%% BotMask is bitwised or'ed to the last byte
-%%
-rnd(0, _TMask, _BMask) ->
- [];
-rnd(1, TMask, BMask) ->
- [(rand8() bor TMask) bor BMask];
-rnd(N, TMask, BMask) ->
- [(rand8() bor TMask) | rnd_n(N-1, BMask)].
-
-rnd_n(1, BMask) ->
- [rand8() bor BMask];
-rnd_n(I, BMask) ->
- [rand8() | rnd_n(I-1, BMask)].
-
-rand8() ->
- (rand32() bsr 8) band 16#ff.
-
-rand32() ->
- random:uniform(16#100000000) -1.
+ crypto:strong_rand_bytes(N).
%%
%% Base 64 encode/decode
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 0ba11b0a26..2d82e6d77d 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -106,8 +106,6 @@ peer_address(ConnectionHandler) ->
%% initialize.
%%--------------------------------------------------------------------
init([Role, Manager, Socket, SshOpts]) ->
- {A,B,C} = erlang:now(),
- random:seed(A, B, C),
{NumVsn, StrVsn} = ssh_transport:versions(Role, SshOpts),
ssh_bits:install_messages(ssh_transport:transport_messages(NumVsn)),
{Protocol, Callback, CloseTag} =
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 51f9f47446..8c9f671fd5 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.0.4
+SSH_VSN = 2.0.5
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index f0a4d5ea3e..f3164cdb46 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>11999</year><year>2011</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -583,7 +583,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
<fsummary>Write data to a socket.</fsummary>
<type>
<v>Socket = sslsocket()</v>
- <v>Data = iolist() | binary()</v>
+ <v>Data = iodata()</v>
</type>
<desc>
<p>Writes <c>Data</c> to <c>Socket</c>. </p>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 7b1fda4cf9..ae0f8477fc 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -264,7 +264,7 @@ close(Socket = #sslsocket{}) ->
ssl_broker:close(Socket).
%%--------------------------------------------------------------------
--spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}.
+-spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 574e1e9468..0a86e9bd29 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -107,12 +107,14 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec send(pid(), iolist()) -> ok | {error, reason()}.
+-spec send(pid(), iodata()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
send(Pid, Data) ->
sync_send_all_state_event(Pid, {application_data,
+ %% iolist_to_binary should really
+ %% be called iodata_to_binary()
erlang:iolist_to_binary(Data)}, infinity).
%%--------------------------------------------------------------------
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 45fa0847a8..d6203bdaa0 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2010</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -83,11 +83,17 @@
supervisor, where all child processes are dynamically added
instances of the same process type, i.e. running the same
code.</p>
- <p>The functions <c>terminate_child/2</c>, <c>delete_child/2</c>
+ <p>The functions <c>delete_child/2</c>
and <c>restart_child/2</c> are invalid for
<c>simple_one_for_one</c> supervisors and will return
<c>{error,simple_one_for_one}</c> if the specified supervisor
uses this restart strategy.</p>
+ <p>The function <c>terminate_child/2</c> can be used for
+ children under <c>simple_one_for_one</c> supervisors by
+ giving the child's <c>pid()</c> as the second argument. If
+ instead the child specification identifier is used,
+ <c>terminate_child/2</c> will return
+ <c>{error,simple_one_for_one}</c>.</p>
</item>
</list>
<p>To prevent a supervisor from getting into an infinite loop of
@@ -311,24 +317,33 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<type>
<v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>Id = term()</v>
+ <v>Id = pid() | term()</v>
<v>Result = ok | {error,Error}</v>
<v>&nbsp;Error = not_found | simple_one_for_one</v>
</type>
<desc>
- <p>Tells the supervisor <c>SupRef</c> to terminate the child
- process corresponding to the child specification identified
- by <c>Id</c>. The process, if there is one, is terminated but
- the child specification is kept by the supervisor. This means
- that the child process may be later be restarted by
- the supervisor. The child process can also be restarted
- explicitly by calling <c>restart_child/2</c>. Use
- <c>delete_child/2</c> to remove the child specification.</p>
+ <p>Tells the supervisor <c>SupRef</c> to terminate the given
+ child.</p>
+ <p>If the supervisor is not <c>simple_one_for_one</c>,
+ <c>Id</c> must be the child specification identifier. The
+ process, if there is one, is terminated but the child
+ specification is kept by the supervisor. The child process
+ may later be restarted by the supervisor. The child process
+ can also be restarted explicitly by calling
+ <c>restart_child/2</c>. Use <c>delete_child/2</c> to remove
+ the child specification.</p>
+ <p>If the supervisor is <c>simple_one_for_one</c>, <c>Id</c>
+ must be the child process' <c>pid()</c>. I the specified
+ process is alive, but is not a child of the given
+ supervisor, the function will return
+ <c>{error,not_found}</c>. If the child specification
+ identifier is given instead instead of a <c>pid()</c>, the
+ function will return <c>{error,simple_one_for_one}</c>.</p>
+ <p>If successful, the function returns <c>ok</c>. If there is
+ no child specification with the specified <c>Id</c>, the
+ function returns <c>{error,not_found}</c>.</p>
<p>See <c>start_child/2</c> for a description of
<c>SupRef</c>.</p>
- <p>If successful, the function returns <c>ok</c>. If there is
- no child specification with the specified <c>Id</c>,
- the function returns <c>{error,not_found}</c>.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl
index 7f5f23e26d..a3c9927ee9 100644
--- a/lib/stdlib/src/pool.erl
+++ b/lib/stdlib/src/pool.erl
@@ -95,6 +95,9 @@ pspawn_link(M, F, A) ->
start_nodes([], _, _) -> [];
start_nodes([Host|Tail], Name, Args) ->
case slave:start(Host, Name, Args) of
+ {error, {already_running, Node}} ->
+ io:format("Can't start node on host ~w due to ~w~n",[Host, {already_running, Node}]),
+ [Node | start_nodes(Tail, Name, Args)];
{error, R} ->
io:format("Can't start node on host ~w due to ~w~n",[Host, R]),
start_nodes(Tail, Name, Args);
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 368dc2e3e5..4fd7f1d47c 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -138,7 +138,7 @@ delete_child(Supervisor, Name) ->
%%-----------------------------------------------------------------
-type term_err() :: 'not_found' | 'simple_one_for_one'.
--spec terminate_child(sup_ref(), term()) -> 'ok' | {'error', term_err()}.
+-spec terminate_child(sup_ref(), pid() | term()) -> 'ok' | {'error', term_err()}.
terminate_child(Supervisor, Name) ->
call(Supervisor, {terminate_child, Name}).
@@ -297,8 +297,26 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
{reply, What, State}
end;
-%%% The requests terminate_child, delete_child and restart_child are
-%%% invalid for simple_one_for_one supervisors.
+%% terminate_child for simple_one_for_one can only be done with pid
+handle_call({terminate_child, Name}, _From, State) when not is_pid(Name),
+ ?is_simple(State) ->
+ {reply, {error, simple_one_for_one}, State};
+
+handle_call({terminate_child, Name}, _From, State) ->
+ case get_child(Name, State, ?is_simple(State)) of
+ {value, Child} ->
+ case do_terminate(Child, State#state.name) of
+ #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) ->
+ {reply, ok, state_del_child(Child, State)};
+ NChild ->
+ {reply, ok, replace_child(NChild, State)}
+ end;
+ false ->
+ {reply, {error, not_found}, State}
+ end;
+
+%%% The requests delete_child and restart_child are invalid for
+%%% simple_one_for_one supervisors.
handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
{reply, {error, simple_one_for_one}, State};
@@ -341,19 +359,6 @@ handle_call({delete_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
-handle_call({terminate_child, Name}, _From, State) ->
- case get_child(Name, State) of
- {value, Child} ->
- case do_terminate(Child, State#state.name) of
- #child{restart_type = temporary} = NChild ->
- {reply, ok, state_del_child(NChild, State)};
- NChild ->
- {reply, ok, replace_child(NChild, State)}
- end;
- _ ->
- {reply, {error, not_found}, State}
- end;
-
handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
child_type = CT,
modules = Mods}]} =
@@ -849,7 +854,28 @@ split_child(_, [], After) ->
{lists:reverse(After), []}.
get_child(Name, State) ->
+ get_child(Name, State, false).
+get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) ->
+ get_dynamic_child(Pid, State);
+get_child(Name, State, _) ->
lists:keysearch(Name, #child.name, State#state.children).
+
+get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
+ case is_dynamic_pid(Pid, dynamics_db(Child#child.restart_type, Dynamics)) of
+ true ->
+ {value, Child#child{pid=Pid}};
+ false ->
+ case erlang:is_process_alive(Pid) of
+ true -> false;
+ false -> {value, Child}
+ end
+ end.
+
+is_dynamic_pid(Pid, Dynamics) when is_list(Dynamics) ->
+ lists:member(Pid, Dynamics);
+is_dynamic_pid(Pid, Dynamics) ->
+ dict:is_key(Pid, Dynamics).
+
replace_child(Child, State) ->
Chs = do_replace_child(Child, State#state.children),
State#state{children = Chs}.
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index f9ceed8f84..cc271bd047 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -20,7 +20,7 @@
-module(supervisor_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(TIMEOUT, 1000).
%% Testserver specific export
@@ -349,8 +349,7 @@ child_adm(Config) when is_list(Config) ->
ok = supervisor:terminate_child(sup_test, child1),
%% Start of already existing but not running process
- {error,already_present} =
- supervisor:start_child(sup_test, Child),
+ {error,already_present} = supervisor:start_child(sup_test, Child),
%% Restart
{ok, CPid2} = supervisor:restart_child(sup_test, child1),
@@ -377,6 +376,11 @@ child_adm(Config) when is_list(Config) ->
[{child1, CPid3, worker, []}] = supervisor:which_children(sup_test),
[1,1,0,1] = get_child_counts(sup_test),
+ %% Terminate with Pid not allowed when not simple_one_for_one
+ {error,not_found} = supervisor:terminate_child(sup_test, CPid3),
+ [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test),
+ [1,1,0,1] = get_child_counts(sup_test),
+
{'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}}
= (catch supervisor:which_children(foo)),
{'EXIT',{noproc,{gen_server,call,[foo,count_children,infinity]}}}
@@ -412,16 +416,26 @@ child_adm_simple(Config) when is_list(Config) ->
[1,2,0,2] = get_child_counts(sup_test),
%% Termination
- {error, simple_one_for_one} =
- supervisor:terminate_child(sup_test, child1),
+ {error, simple_one_for_one} = supervisor:terminate_child(sup_test, child1),
+ [1,2,0,2] = get_child_counts(sup_test),
+ ok = supervisor:terminate_child(sup_test,CPid1),
+ [_] = supervisor:which_children(sup_test),
+ [1,1,0,1] = get_child_counts(sup_test),
+ false = erlang:is_process_alive(CPid1),
+ %% Terminate non-existing proccess is ok
+ ok = supervisor:terminate_child(sup_test,CPid1),
+ [_] = supervisor:which_children(sup_test),
+ [1,1,0,1] = get_child_counts(sup_test),
+ %% Terminate pid which is not a child of this supervisor is not ok
+ NoChildPid = spawn_link(fun() -> receive after infinity -> ok end end),
+ {error, not_found} = supervisor:terminate_child(sup_test, NoChildPid),
+ true = erlang:is_process_alive(NoChildPid),
%% Restart
- {error, simple_one_for_one} =
- supervisor:restart_child(sup_test, child1),
+ {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1),
%% Deletion
- {error, simple_one_for_one} =
- supervisor:delete_child(sup_test, child1),
+ {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1),
ok.
%%-------------------------------------------------------------------------
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 8fe7d72270..591329b361 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -36,7 +36,7 @@
-export([capture_start/0,capture_stop/0,capture_get/0]).
-export([messages_get/0]).
-export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]).
--export([timetrap_scale_factor/0,timetrap/1,timetrap_cancel/1]).
+-export([timetrap_scale_factor/0,timetrap/1,timetrap_cancel/1,timetrap_cancel/0]).
-export([m_out_of_n/3,do_times/4,do_times/2]).
-export([call_crash/3,call_crash/4,call_crash/5]).
-export([temp_name/1]).
@@ -1077,7 +1077,7 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit,
{{Time,Value},Loc,Opts} =
case test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0],
- {ok, Args0}) of
+ {ok,Args0}) of
{ok,Args} ->
run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback);
Error = {error,_Reason} ->
@@ -1085,18 +1085,17 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit,
{skip,{failed,Error}}),
{{0,NewResult},{Mod,Func},[]};
{fail,Reason} ->
- [Conf] = Args0,
- Conf1 = [{tc_status,{failed,Reason}} | Conf],
+ Conf = [{tc_status,{failed,Reason}} | hd(Args0)],
fw_error_notify(Mod, Func, Conf, Reason),
- NewResult = do_end_tc_call(Mod,Func, {{error,Reason},[Conf1]},
- {fail, Reason}),
+ NewResult = do_end_tc_call(Mod,Func, {{error,Reason},[Conf]},
+ {fail,Reason}),
{{0,NewResult},{Mod,Func},[]};
Skip = {skip,_Reason} ->
NewResult = do_end_tc_call(Mod,Func,{Skip,Args0},Skip),
{{0,NewResult},{Mod,Func},[]};
{auto_skip,Reason} ->
NewResult = do_end_tc_call(Mod, Func, {{skip,Reason},Args0},
- {skip, {fw_auto_skip,Reason}}),
+ {skip,{fw_auto_skip,Reason}}),
{{0,NewResult},{Mod,Func},[]}
end,
exit({Ref,Time,Value,Loc,Opts}).
@@ -1116,9 +1115,15 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
{skip_and_save,Reason,SaveCfg} ->
Line = get_loc(),
Conf = [{tc_status,{skipped,Reason}},{save_config,SaveCfg}],
- NewRes = do_end_tc_call(Mod, Func, {{skip, Reason}, [Conf]},
+ NewRes = do_end_tc_call(Mod, Func, {{skip,Reason},[Conf]},
{skip, Reason}),
{{0,NewRes},Line,[]};
+ FailTC = {fail,Reason} -> % user fails the testcase
+ EndConf = [{tc_status,{failed,Reason}} | hd(Args)],
+ fw_error_notify(Mod, Func, EndConf, Reason),
+ NewRes = do_end_tc_call(Mod, Func, {{error,Reason},[EndConf]},
+ FailTC),
+ {{0,NewRes},{Mod,Func},[]};
{ok,NewConf} ->
put(test_server_init_or_end_conf,undefined),
%% call user callback function if defined
@@ -1153,8 +1158,9 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
{FWReturn1,TSReturn1,EndConf2} =
case end_per_testcase(Mod, Func, EndConf1) of
SaveCfg1={save_config,_} ->
- {FWReturn,TSReturn,[SaveCfg1|lists:keydelete(save_config, 1, EndConf1)]};
- {fail,ReasonToFail} -> % user has failed the testcase
+ {FWReturn,TSReturn,[SaveCfg1|lists:keydelete(save_config,1,
+ EndConf1)]};
+ {fail,ReasonToFail} -> % user has failed the testcase
fw_error_notify(Mod, Func, EndConf1, ReasonToFail),
{{error,ReasonToFail},{failed,ReasonToFail},EndConf1};
{failed,{_,end_per_testcase,_}} = Failure -> % unexpected termination
@@ -1301,7 +1307,7 @@ init_per_testcase(Mod, Func, Args) ->
false -> code:load_file(Mod);
_ -> ok
end,
-%% init_per_testcase defined, returns new configuration
+ %% init_per_testcase defined, returns new configuration
case erlang:function_exported(Mod,init_per_testcase,2) of
true ->
case catch my_apply(Mod, init_per_testcase, [Func|Args]) of
@@ -1321,6 +1327,8 @@ init_per_testcase(Mod, Func, Args) ->
"bad elements in Config: ~p\n",[Bad]},
{skip,{failed,{Mod,init_per_testcase,bad_return}}}
end;
+ {'$test_server_ok',Res={fail,_Reason}} ->
+ Res;
{'$test_server_ok',_Other} ->
group_leader() ! {printout,12,
"ERROR! init_per_testcase did not return "
@@ -1695,7 +1703,7 @@ fail() ->
break(Comment) ->
case erase(test_server_timetraps) of
undefined -> ok;
- List -> lists:foreach(fun(Ref) -> timetrap_cancel(Ref) end,List)
+ List -> lists:foreach(fun({Ref,_}) -> timetrap_cancel(Ref) end, List)
end,
io:format(user,
"\n\n\n--- SEMIAUTOMATIC TESTING ---"
@@ -1776,14 +1784,16 @@ timetrap(Timeout0) ->
{undefined,false} -> timetrap1(Timeout, false);
{undefined,_} -> timetrap1(Timeout, true);
{infinity,_} -> infinity;
+ {_Int,_Scale} when Timeout == infinity -> infinity;
{Int,Scale} -> timetrap1(Timeout*Int, Scale)
end.
timetrap1(Timeout, Scale) ->
- Ref = spawn_link(test_server_sup,timetrap,[Timeout,Scale,self()]),
+ TCPid = self(),
+ Ref = spawn_link(test_server_sup,timetrap,[Timeout,Scale,TCPid]),
case get(test_server_timetraps) of
- undefined -> put(test_server_timetraps,[Ref]);
- List -> put(test_server_timetraps,[Ref|List])
+ undefined -> put(test_server_timetraps,[{Ref,TCPid}]);
+ List -> put(test_server_timetraps,[{Ref,TCPid}|List])
end,
Ref.
@@ -1796,14 +1806,16 @@ ensure_timetrap(Config) ->
undefined -> ok;
Garbage ->
erase(test_server_default_timetrap),
- format("=== WARNING: garbage in test_server_default_timetrap: ~p~n",
+ format("=== WARNING: garbage in "
+ "test_server_default_timetrap: ~p~n",
[Garbage])
end,
DTmo = case lists:keysearch(default_timeout,1,Config) of
{value,{default_timeout,Tmo}} -> Tmo;
_ -> ?DEFAULT_TIMETRAP_SECS
end,
- format("=== test_server setting default timetrap of ~p seconds~n",
+ format("=== test_server setting default "
+ "timetrap of ~p seconds~n",
[DTmo]),
put(test_server_default_timetrap, timetrap(seconds(DTmo)))
end.
@@ -1815,11 +1827,13 @@ cancel_default_timetrap() ->
TimeTrap when is_pid(TimeTrap) ->
timetrap_cancel(TimeTrap),
erase(test_server_default_timetrap),
- format("=== test_server canceled default timetrap since another timetrap was set~n"),
+ format("=== test_server canceled default timetrap "
+ "since another timetrap was set~n"),
ok;
Garbage ->
erase(test_server_default_timetrap),
- format("=== WARNING: garbage in test_server_default_timetrap: ~p~n",
+ format("=== WARNING: garbage in "
+ "test_server_default_timetrap: ~p~n",
[Garbage]),
error
end.
@@ -1833,6 +1847,7 @@ time_ms({Other,_N}) ->
"Should be seconds, minutes, or hours.~n", [Other]),
exit({invalid_time_spec,Other});
time_ms(Ms) when is_integer(Ms) -> Ms;
+time_ms(infinity) -> infinity;
time_ms(Other) -> exit({invalid_time_spec,Other}).
@@ -1846,11 +1861,29 @@ timetrap_cancel(infinity) ->
timetrap_cancel(Handle) ->
case get(test_server_timetraps) of
undefined -> ok;
- [Handle] -> erase(test_server_timetraps);
- List -> put(test_server_timetraps,lists:delete(Handle,List))
+ [{Handle,_}] -> erase(test_server_timetraps);
+ Timers -> put(test_server_timetraps,
+ lists:keydelete(Handle, 1, Timers))
end,
test_server_sup:timetrap_cancel(Handle).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% timetrap_cancel() -> ok
+%%
+%% Cancels timetrap for current test case.
+timetrap_cancel() ->
+ case get(test_server_timetraps) of
+ undefined ->
+ ok;
+ Timers ->
+ case lists:keysearch(self(), 2, Timers) of
+ {value,{Handle,_}} ->
+ timetrap_cancel(Handle);
+ _ ->
+ ok
+ end
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% hours(N) -> Milliseconds
%% minutes(N) -> Milliseconds
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 30d7314058..de9b962dfc 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1812,6 +1812,9 @@ start_log_file() ->
ok = file:make_dir(PrivDir),
put(test_server_priv_dir,PrivDir++"/"),
print_timestamp(13,"Suite started at "),
+
+ LogInfo = [{topdir,Dir},{rundir,lists:flatten(TestDir)}],
+ test_server_sup:framework_call(report, [loginfo,LogInfo]),
ok.
make_html_link(LinkName, Target, Explanation) ->
@@ -1925,7 +1928,6 @@ html_convert_modules(TestSpec, _Config) ->
copy_html_files(get(test_server_dir), get(test_server_log_dir_base)).
%% Retrieve a list of modules out of the test spec.
-
html_isolate_modules(List) -> html_isolate_modules(List, sets:new()).
html_isolate_modules([], Set) -> sets:to_list(Set);
@@ -1939,37 +1941,56 @@ html_isolate_modules([{Mod,_Case,_Args}|Cases], Set) ->
html_isolate_modules(Cases, sets:add_element(Mod, Set)).
%% Given a list of modules, convert each module's source code to HTML.
-
html_convert_modules([Mod|Mods]) ->
case code:which(Mod) of
Path when is_list(Path) ->
SrcFile = filename:rootname(Path) ++ ".erl",
- DestDir = get(test_server_dir),
- Name = atom_to_list(Mod),
- DestFile = filename:join(DestDir, downcase(Name) ++ ?src_listing_ext),
- html_possibly_convert(SrcFile, DestFile),
- html_convert_modules(Mods);
- _Other -> ok
+ FoundSrcFile =
+ case file:read_file_info(SrcFile) of
+ {ok,SInfo} ->
+ {SrcFile,SInfo};
+ {error,_} ->
+ ModInfo = Mod:module_info(compile),
+ case proplists:get_value(source, ModInfo) of
+ undefined ->
+ undefined;
+ OtherSrcFile ->
+ case file:read_file_info(OtherSrcFile) of
+ {ok,SInfo} ->
+ {OtherSrcFile,SInfo};
+ {error,_} ->
+ undefined
+ end
+ end
+ end,
+ case FoundSrcFile of
+ undefined ->
+ html_convert_modules(Mods);
+ {SrcFile1,SrcFileInfo} ->
+ DestDir = get(test_server_dir),
+ Name = atom_to_list(Mod),
+ DestFile = filename:join(DestDir,
+ downcase(Name)++?src_listing_ext),
+ html_possibly_convert(SrcFile1, SrcFileInfo, DestFile),
+ html_convert_modules(Mods)
+ end;
+ _Other ->
+ html_convert_modules(Mods)
end;
html_convert_modules([]) -> ok.
%% Convert source code to HTML if possible and needed.
-
-html_possibly_convert(Src, Dest) ->
- case file:read_file_info(Src) of
- {ok,SInfo} ->
- case file:read_file_info(Dest) of
- {error,_Reason} -> % no dest file
- erl2html2:convert(Src, Dest);
- {ok,DInfo} when DInfo#file_info.mtime < SInfo#file_info.mtime ->
- erl2html2:convert(Src, Dest);
- {ok,_DInfo} -> ok % dest file up to date
- end;
- {error,_Reason} -> ok % no source code found
+html_possibly_convert(Src, SrcInfo, Dest) ->
+ case file:read_file_info(Dest) of
+ {error,_Reason} -> % no dest file
+ erl2html2:convert(Src, Dest);
+ {ok,DestInfo} when DestInfo#file_info.mtime < SrcInfo#file_info.mtime ->
+ erl2html2:convert(Src, Dest);
+ {ok,_DestInfo} ->
+ ok % dest file up to date
end.
%% Copy all HTML files in InDir to OutDir.
-
copy_html_files(InDir, OutDir) ->
Files = filelib:wildcard(filename:join(InDir, "*" ++ ?src_listing_ext)),
lists:foreach(fun (Src) -> copy_html_file(Src, OutDir) end, Files).
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 1a614d74d5..53dfb45e3a 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -83,13 +83,13 @@ timetrap(Timeout0, Scale, Pid) ->
%% Handle = term()
%%
%% Cancels a time trap.
-
timetrap_cancel(Handle) ->
unlink(Handle),
MonRef = erlang:monitor(process, Handle),
exit(Handle, kill),
receive {'DOWN',MonRef,_,_,_} -> ok after 2000 -> ok end.
+
capture_get(Msgs) ->
receive
{captured,Msg} ->
diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl
index 2ddffccf5b..8332ccfb40 100644
--- a/lib/test_server/src/ts_install.erl
+++ b/lib/test_server/src/ts_install.erl
@@ -22,6 +22,7 @@
-export([install/2, platform_id/1]).
-include("ts.hrl").
+-include_lib("kernel/include/file.hrl").
install(install_local, Options) ->
install(os:type(), Options);
@@ -150,11 +151,17 @@ add_vars(Vars0, Opts0) ->
end,
{PlatformId, PlatformLabel, PlatformFilename, Version} =
platform([{longnames, LongNames}|Vars0]),
+ NetDir = lists:concat(["/net", hostname()]),
+ Mounted = case file:read_file_info(NetDir) of
+ {ok, #file_info{type = directory}} -> NetDir;
+ _ -> ""
+ end,
{Opts, [{longnames, LongNames},
{platform_id, PlatformId},
{platform_filename, PlatformFilename},
{rsh_name, get_rsh_name()},
{platform_label, PlatformLabel},
+ {ts_net_dir, Mounted},
{erl_flags, []},
{erl_release, Version},
{ts_testcase_callback, get_testcase_callback()} | Vars0]}.
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index 067961a216..d145290820 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -212,6 +212,12 @@ make_command(Vars, Spec, State) ->
false ->
ok
end,
+
+ %% If Common Test specific variables are needed, add them here
+ %% on form: "{key1,value1}" "{key2,value2}" ...
+ NetDir = ts_lib:var(ts_net_dir, Vars),
+ TestVars = [ "\"{net_dir,\\\"",NetDir,"\\\"}\"" ],
+
%% NOTE: Do not use ' in these commands as it wont work on windows
Cmd = [Erl, Naming, "test_server"
" -rsh ", ts_lib:var(rsh_name, Vars),
@@ -224,6 +230,7 @@ make_command(Vars, Spec, State) ->
%% " -test_server_format_exception false",
" -boot start_sasl -sasl errlog_type error",
" -pz ",Cwd,
+ " -ct_test_vars ",TestVars,
" -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" "
" -eval \"ct:run_test(",
backslashify(lists:flatten(State#state.test_server_args)),")\""
@@ -369,7 +376,6 @@ make_common_test_args(Args0, Options, _Vars) ->
end,
ConfigFiles = [{config,[filename:join(ConfigPath,File)
|| File <- get_config_files()]}],
-
io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
ConfigFiles++Options]).
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index e1c0d31371..6728bef2a4 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -386,7 +386,8 @@ then no prototype is inserted.
The test is performed by the function `erlang-test-criteria-list'.")
(defvar erlang-electric-arrow-criteria
- '(erlang-next-lines-empty-p
+ '(erlang-stop-when-in-type-spec
+ erlang-next-lines-empty-p
erlang-at-end-of-function-p)
"*List of functions controlling the arrow aspect of `erlang-electric-gt'.
The functions in this list are called, in order, whenever a `>'
@@ -4045,6 +4046,16 @@ This function is designed to be a member of a criteria list."
nil)))
+(defun erlang-stop-when-in-type-spec ()
+ "Return `stop' when in a type spec line.
+
+This function is designed to be a member of a criteria list."
+ (save-excursion
+ (beginning-of-line)
+ (when (save-match-data (looking-at "-\\(spec\\|type\\)"))
+ 'stop)))
+
+
(defun erlang-next-lines-empty-p ()
"Return non-nil if next lines are empty.