aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erlang.xml108
-rw-r--r--erts/test/otp_SUITE.erl3
-rw-r--r--lib/common_test/priv/ct_default.css10
-rw-r--r--lib/common_test/src/ct.erl2
-rw-r--r--lib/common_test/src/ct_framework.erl37
-rw-r--r--lib/common_test/src/ct_logs.erl34
-rw-r--r--lib/common_test/src/ct_util.erl23
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl4
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl23
-rw-r--r--lib/test_server/src/test_server.erl22
-rw-r--r--lib/test_server/src/test_server_ctrl.erl22
-rw-r--r--lib/test_server/src/ts.erl25
12 files changed, 232 insertions, 81 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index fbe7b36163..8c438b0bd7 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4881,6 +4881,7 @@ true</pre>
<v>Type, Res -- see below</v>
</type>
<desc>
+ <p>All times are in milliseconds unless otherwise specified.</p>
<p>Returns information about the system as specified by
<c>Type</c>:</p>
<taglist>
@@ -4894,15 +4895,20 @@ true</pre>
<item>
<p>Returns
<c>{Total_Exact_Reductions, Exact_Reductions_Since_Last_Call}</c>.</p>
- <p><em>NOTE:</em><c>statistics(exact_reductions)</c> is
- a more expensive operation than
- <seealso marker="#statistics_reductions">statistics(reductions)</seealso>
- especially on an Erlang machine with SMP support.</p>
+ <note><p><c>statistics(exact_reductions)</c> is
+ a more expensive operation than
+ <seealso marker="#statistics_reductions">statistics(reductions)</seealso>
+ especially on an Erlang machine with SMP support.</p>
+ </note>
</item>
<tag><c>garbage_collection</c></tag>
<item>
<p>Returns <c>{Number_of_GCs, Words_Reclaimed, 0}</c>. This
information may not be valid for all implementations.</p>
+ <pre>
+> <input>statistics(garbage_collection).</input>
+{85,23961,0}
+</pre>
</item>
<tag><c>io</c></tag>
<item>
@@ -4914,12 +4920,18 @@ true</pre>
<tag><marker id="statistics_reductions"><c>reductions</c></marker></tag>
<item>
<p>Returns
- <c>{Total_Reductions, Reductions_Since_Last_Call}</c>.</p>
- <p><em>NOTE:</em> From erts version 5.5 (OTP release R11B)
- this value does not include reductions performed in current
- time slices of currently scheduled processes. If an
- exact value is wanted, use
- <seealso marker="#statistics_exact_reductions">statistics(exact_reductions)</seealso>.</p>
+ <c>{Total_Reductions, Reductions_Since_Last_Call}</c>.</p>
+ <note>
+ <p>From erts version 5.5 (OTP release R11B)
+ this value does not include reductions performed in current
+ time slices of currently scheduled processes. If an
+ exact value is wanted, use
+ <seealso marker="#statistics_exact_reductions">statistics(exact_reductions)</seealso>.</p>
+ </note>
+ <pre>
+> <input>statistics(reductions).</input>
+{2046,11}
+</pre>
</item>
<tag><c>run_queue</c></tag>
<item>
@@ -4932,20 +4944,72 @@ true</pre>
Note that the run-time is the sum of the run-time for all
threads in the Erlang run-time system and may therefore be greater
than the wall-clock time.</p>
+ <pre>
+> <input>statistics(runtime).</input>
+{1690,1620}
+</pre>
</item>
<tag><marker id="statistics_scheduler_wall_time"><c>scheduler_wall_time</c></marker></tag>
<item>
- <p>Returns
- <c>[{Scheduler_Id, Scheduler_Worked_Time, Scheduler_Total_Time}]</c>, time lapses are since the
- the system flag <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
- was set to true.
+ <p>Returns a list of tuples with
+ <c>{SchedulerId, ActiveTime, TotalTime}</c>, where <c>SchedulerId</c> is an integer id of the scheduler, <c>ActiveTime</c> is
+ the duration the scheduler has been busy, <c>TotalTime</c> is the total time duration since
+ <seealso marker="#system_flag_scheduler_wall_time">scheduler_wall_time</seealso>
+ activation. The time unit is not defined and may be subject to change
+ between releases, operating systems and system restarts.
+ <c>scheduler_wall_time</c> should only be used to calculate relative
+ values for scheduler-utilization. <c>ActiveTime</c> can never exceed <c>TotalTime</c>.
+ </p>
+
+ <p>The definition of a busy scheduler is when it is not idle or not
+ scheduling (selecting) a process or port, meaning; executing process
+ code, executing linked-in-driver or NIF code, executing
+ built-in-functions or any other runtime handling, garbage collecting
+ or handling any other memory management. Note, a scheduler may also be
+ busy even if the operating system has scheduled out the scheduler
+ thread.
+ </p>
+
+ <p>
Returns <c>undefined</c> if the system flag <seealso marker="#system_flag_scheduler_wall_time">
- scheduler_wall_time</seealso> is set to false.
+ scheduler_wall_time</seealso> is turned off.
</p>
- <p>The list of scheduler information is unsorted and may come in different order
- between calls. The time unit is undefined and may be changed and should only be used
- to calculate relative utilization.
+
+ <p>The list of scheduler information is unsorted and may appear in different order
+ between calls.
</p>
+ <p>Using <c>scheduler_wall_time</c> to calculate scheduler utilization.</p>
+<pre>
+> <input>erlang:system_flag(scheduler_wall_time, true).</input>
+false
+> <input>Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
+ok
+</pre>
+ <p>Some time later we will take another snapshot and calculate scheduler-utilization per scheduler.</p>
+<pre>
+> <input>Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.</input>
+ok
+> <input>lists:map(fun({{I, A0, T0}, {I, A1, T1}}) ->
+ {I, (A1 - A0)/(T1 - T0)} end, lists:zip(Ts0,Ts1)).</input>
+[{1,0.9743474730177548},
+ {2,0.9744843782751444},
+ {3,0.9995902361669045},
+ {4,0.9738012596572161},
+ {5,0.9717956667018103},
+ {6,0.9739235846420741},
+ {7,0.973237033077876},
+ {8,0.9741297293248656}]
+</pre>
+ <p>Using the same snapshots to calculate a total scheduler-utilization.</p>
+<pre>
+> <input>{A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) ->
+ {Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), A/T.</input>
+0.9769136803764825
+</pre>
+
+ <note>
+ <p><c>scheduler_wall_time</c> is by default disabled. Use <c>erlang:system_flag(scheduler_wall_time, true)</c> to enable it. </p>
+ </note>
</item>
<tag><c>wall_clock</c></tag>
@@ -4957,14 +5021,6 @@ true</pre>
opposed to runtime or CPU time.</p>
</item>
</taglist>
- <p>All times are in milliseconds.</p>
- <pre>
-> <input>statistics(runtime).</input>
-{1690,1620}
-> <input>statistics(reductions).</input>
-{2046,11}
-> <input>statistics(garbage_collection).</input>
-{85,23961,0}</pre>
</desc>
</func>
<func>
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 79cd91221f..b34d9a5422 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -151,6 +151,9 @@ ssl_crypto_filter(Undef) ->
{{error,bad_name},{error,bad_name}} ->
filter(fun({_,{ssl,_,_}}) -> false;
({_,{crypto,_,_}}) -> false;
+ ({_,{ssh,_,_}}) -> false;
+ ({_,{ssh_connection,_,_}}) -> false;
+ ({_,{ssh_sftp,_,_}}) -> false;
(_) -> true
end, Undef);
{_,_} -> Undef
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css
index 75f8d5db8a..8ae6990cd8 100644
--- a/lib/common_test/priv/ct_default.css
+++ b/lib/common_test/priv/ct_default.css
@@ -81,13 +81,21 @@ div.copyright {
color: #000000;
}
-div.ct_internal {
+div.ct_internal {
background: lightgrey; color: black;
font-family: "Monaco", "Andale Mono", "Consolas", monospace;
font-size: .95em;
margin: .2em 0 0 0;
}
+div.ct_error_notify {
+ background: #CC0000;
+ color: #FFFFFF;
+ font-family: "Monaco", "Andale Mono", "Consolas", monospace;
+ font-size: 1.05em;
+ margin: .2em 0 0 0;
+}
+
div.default {
background: lightgreen; color: black;
font-family: "Monaco", "Andale Mono", "Consolas", monospace;
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 00701f5a2d..0a77527b2f 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -861,6 +861,8 @@ get_status() ->
get_testdata(Key) ->
case catch ct_util:get_testdata(Key) of
+ {error,ct_util_server_not_running} ->
+ no_tests_running;
Error = {error,_Reason} ->
Error;
{'EXIT',_Reason} ->
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index c24a7c238b..cdd8a6a596 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -806,31 +806,36 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
end
end,
- io:format(user, "~n- - - - - - - - - - - - - - - - "
- "- - - - - - - - - -~n", []),
+ PrintErr = fun(ErrFormat, ErrArgs) ->
+ Div = "~n- - - - - - - - - - - - - - - - "
+ "- - - - - - - - - -~n",
+ io:format(user, lists:concat([Div,ErrFormat,Div,"~n"]),
+ ErrArgs),
+ ct_logs:tc_log(ct_error_notify, "CT Error Notification",
+ ErrFormat, ErrArgs)
+ end,
case Loc of
- %% we don't use the line parse transform as we compile this
- %% module so location will be on form {M,F}
[{?MODULE,error_in_suite}] ->
- io:format(user, "Error in suite detected: ~s", [ErrStr]);
+ PrintErr("Error in suite detected: ~s", [ErrStr]);
- R when R == unknown; R == undefined ->
- io:format(user, "Error detected: ~s", [ErrStr]);
+ R when R == unknown; R == undefined ->
+ PrintErr("Error detected: ~s", [ErrStr]);
%% if a function specified by all/0 does not exist, we
%% pick up undef here
- [{LastMod,LastFunc}] ->
- io:format(user, "~w:~w could not be executed~n",
- [LastMod,LastFunc]),
- io:format(user, "Reason: ~s", [ErrStr]);
+ [{LastMod,LastFunc}|_] when ErrStr == "undef" ->
+ PrintErr("~w:~w could not be executed~nReason: ~s",
+ [LastMod,LastFunc,ErrStr]);
+
+ [{LastMod,LastFunc}|_] ->
+ PrintErr("~w:~w failed~nReason: ~s", [LastMod,LastFunc,ErrStr]);
[{LastMod,LastFunc,LastLine}|_] ->
%% print error to console, we are only
%% interested in the last executed expression
- io:format(user, "~w:~w failed on line ~w~n",
- [LastMod,LastFunc,LastLine]),
- io:format(user, "Reason: ~s", [ErrStr]),
-
+ PrintErr("~w:~w failed on line ~w~nReason: ~s",
+ [LastMod,LastFunc,LastLine,ErrStr]),
+
case ct_util:read_suite_data({seq,Mod,Func}) of
undefined ->
ok;
@@ -839,8 +844,6 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
mark_as_failed(Seq,Mod,Func,SeqTCs)
end
end,
- io:format(user, "~n- - - - - - - - - - - - - - - - "
- "- - - - - - - - - -~n~n", []),
ok.
%% cases in seq that have already run
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 5f0626c0b0..b2f669fefe 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -38,7 +38,7 @@
-export([get_ts_html_wrapper/3]).
%% Logging stuff directly from testcase
--export([tc_log/3,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3,
+-export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3,
basic_html/0]).
%% Simulate logger process for use without ct environment running
@@ -333,7 +333,10 @@ add_link(Heading,File,Type) ->
%%% stuff directly from a testcase (i.e. not from within the CT
%%% framework).</p>
tc_log(Category,Format,Args) ->
- cast({log,sync,self(),group_leader(),[{div_header(Category),[]},
+ tc_log(Category,"User",Format,Args).
+
+tc_log(Category,Printer,Format,Args) ->
+ cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]},
{Format,Args},
{div_footer(),[]}]}),
ok.
@@ -369,19 +372,18 @@ tc_log_async(Category,Format,Args) ->
%%% <p>This function is called by <code>ct</code> when printing
%%% stuff a testcase on the user console.</p>
tc_print(Category,Format,Args) ->
- print_heading(Category),
- io:format(user,Format,Args),
- io:format(user,"\n\n",[]),
+ Head = get_heading(Category),
+ io:format(user, lists:concat([Head,Format,"\n\n"]), Args),
ok.
-print_heading(default) ->
- io:format(user,
- "----------------------------------------------------\n~s\n",
- [log_timestamp(now())]);
-print_heading(Category) ->
- io:format(user,
- "----------------------------------------------------\n~s ~w\n",
- [log_timestamp(now()),Category]).
+get_heading(default) ->
+ io_lib:format("-----------------------------"
+ "-----------------------\n~s\n",
+ [log_timestamp(now())]);
+get_heading(Category) ->
+ io_lib:format("-----------------------------"
+ "-----------------------\n~s ~w\n",
+ [log_timestamp(now()),Category]).
%%%-----------------------------------------------------------------
@@ -428,8 +430,10 @@ int_footer() ->
"</div>".
div_header(Class) ->
- "<div class=\"" ++ atom_to_list(Class) ++ "\"><b>*** User " ++
- log_timestamp(now()) ++ " ***</b>".
+ div_header(Class,"User").
+div_header(Class,Printer) ->
+ "<div class=\"" ++ atom_to_list(Class) ++ "\"><b>*** " ++ Printer ++
+ " " ++ log_timestamp(now()) ++ " ***</b>".
div_footer() ->
"</div>".
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 3b6ad6f98d..e9bfb2590b 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -827,15 +827,20 @@ get_profile_data(Profile, Key, StartDir) ->
%%%-----------------------------------------------------------------
%%% Internal functions
call(Msg) ->
- MRef = erlang:monitor(process,whereis(ct_util_server)),
- Ref = make_ref(),
- ct_util_server ! {Msg,{self(),Ref}},
- receive
- {Ref, Result} ->
- erlang:demonitor(MRef, [flush]),
- Result;
- {'DOWN',MRef,process,_,Reason} ->
- {error,{ct_util_server_down,Reason}}
+ case whereis(ct_util_server) of
+ undefined ->
+ {error,ct_util_server_not_running};
+ Pid ->
+ MRef = erlang:monitor(process, Pid),
+ Ref = make_ref(),
+ ct_util_server ! {Msg,{self(),Ref}},
+ receive
+ {Ref, Result} ->
+ erlang:demonitor(MRef, [flush]),
+ Result;
+ {'DOWN',MRef,process,_,Reason} ->
+ {error,{ct_util_server_down,Reason}}
+ end
end.
return({To,Ref},Result) ->
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index bd218dc05f..053edba846 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -946,10 +946,10 @@ test_events(misc_errors) ->
{failed,{error,{suite_failed,this_is_expected}}}}},
{?eh,test_stats,{0,5,{0,0}}},
{?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_1}},
- {?eh,tc_done,{undefined,undefined,i_die_now}},
+ {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_1,i_die_now}},
{?eh,test_stats,{0,6,{0,0}}},
{?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_2}},
- {?eh,tc_done,{undefined,undefined,
+ {?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_2,
{failed,testcase_aborted_or_killed}}},
{?eh,test_stats,{0,7,{0,0}}},
{?eh,test_done,{'DEF','STOP_TIME'}},
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index dc9bc565ee..20daae8215 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -187,6 +187,7 @@ chapter_title(#xmlElement{content=Es}) -> % name = h3 | h4
%% 8) <blockquote> contents may need to be made into paragraphs
%% 9) <th> (table header) is not allowed - is replaced by
%% <td><em>...</em></td>.
+%% 10) <img src=""> is not allowed, replace with <image file="">
otp_xmlify([]) ->
[];
otp_xmlify(Es0) ->
@@ -416,6 +417,9 @@ otp_xmlify_e(#xmlElement{name=tr} = E) ->
otp_xmlify_e(#xmlElement{name=td} = E) ->
Content = otp_xmlify_e(E#xmlElement.content),
[E#xmlElement{content=Content}];
+otp_xmlify_e(#xmlElement{name=img} = E) -> % 10)
+ Content = otp_xmlify_e(E#xmlElement.content),
+ [otp_xmlify_img(E#xmlElement{ content = Content })];
otp_xmlify_e([E | Es]) ->
otp_xmlify_e(E) ++ otp_xmlify_e(Es);
otp_xmlify_e([]) ->
@@ -634,6 +638,20 @@ otp_xmlify_table([#xmlElement{name=td, content=Content}|Es]) ->
otp_xmlify_table([]) ->
[].
+%% otp_xmlify_img(E) -> Es.
+%% Transforms a <img src=""> into <image file="">
+otp_xmlify_img(E0) ->
+ Attrs = lists:map(
+ fun(#xmlAttribute{ name = src, value = Path} = A) ->
+ V = otp_xmlify_a_fileref(Path,this),
+ A#xmlAttribute{ name = file,
+ value = V };
+ (A) ->
+ A
+ end,E0#xmlElement.attributes),
+ E0#xmlElement{name = image, expanded_name = image,
+ attributes = Attrs}.
+
%%--Misc help functions used by otp_xmlify/1 et al---------------------
%% find_next(Tag, Es) -> {Es1, Es2}
@@ -975,6 +993,8 @@ t_type([E=#xmlElement{name = atom}]) ->
t_atom(E);
t_type([E=#xmlElement{name = integer}]) ->
t_integer(E);
+t_type([E=#xmlElement{name = range}]) ->
+ t_range(E);
t_type([E=#xmlElement{name = float}]) ->
t_float(E);
t_type([#xmlElement{name = nil}]) ->
@@ -1001,6 +1021,9 @@ t_atom(E) ->
t_integer(E) ->
[get_attrval(value, E)].
+t_range(E) ->
+ [get_attrval(value, E)].
+
t_float(E) ->
[get_attrval(value, E)].
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 1433eef193..08197ee36e 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -852,7 +852,11 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) ->
%% result of an exit(TestCase,kill) call, which is the
%% only way to abort a testcase process that traps exits
%% (see abort_current_testcase)
- spawn_fw_call(undefined,undefined,CurrConf,Pid,
+ {Mod,Func} = case CurrConf of
+ {MF,_} -> MF;
+ _ -> {undefined,undefined}
+ end,
+ spawn_fw_call(Mod,Func,CurrConf,Pid,
testcase_aborted_or_killed,
unknown,self()),
run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
@@ -863,8 +867,11 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) ->
_Other ->
%% the testcase has terminated because of Reason (e.g. an exit
%% because a linked process failed)
- spawn_fw_call(undefined,undefined,CurrConf,Pid,Reason,
- unknown,self()),
+ {Mod,Func} = case CurrConf of
+ {MF,_} -> MF;
+ _ -> {undefined,undefined}
+ end,
+ spawn_fw_call(Mod,Func,CurrConf,Pid,Reason,unknown,self()),
run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf)
end;
{EndConfPid,{call_end_conf,Data,_Result}} ->
@@ -1594,13 +1601,20 @@ mod_loc(Loc) ->
%% handle diff line num versions
case Loc of
[{{_M,_F},_L}|_] ->
- [{?pl2a(M),F,L} || {{M,F},L} <- Loc];
+ [begin if L /= 0 -> {?pl2a(M),F,L};
+ true -> {?pl2a(M),F} end end || {{M,F},L} <- Loc];
[{_M,_F}|_] ->
[{?pl2a(M),F} || {M,F} <- Loc];
+ {{M,F},0} ->
+ [{?pl2a(M),F}];
{{M,F},L} ->
[{?pl2a(M),F,L}];
{M,ForL} ->
[{?pl2a(M),ForL}];
+ {M,F,0} ->
+ [{M,F}];
+ [{M,F,0}|Stack] ->
+ [{M,F}|Stack];
_ ->
Loc
end.
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 3bea9e39ee..4b649c3ec5 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1390,7 +1390,7 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
end,
OkN = get(test_server_ok),
FailedN = get(test_server_failed),
- print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td>"
+ print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>"
"<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n",
[Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]).
@@ -1775,8 +1775,9 @@ do_test_cases(TopCases, SkipCases,
"<p>~s</p>\n" ++
xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">",
"<table>") ++
- "<tr><th>Num</th><th>Module</th><th>Case</th><th>Log</th>"
- "<th>Time</th><th>Result</th><th>Comment</th></tr>\n",
+ "<tr><th>Num</th><th>Module</th><th>Group</th>" ++
+ "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++
+ "<th>Comment</th></tr>\n",
[print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]},
{"",[]})]),
print(html, xhtml("<br>", "<br />")),
@@ -3254,15 +3255,20 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
print(major, "=result skipped: ~s", [Comment1]),
print(2,"*** Skipping test case #~w ~p ***", [CaseNum,{Mod,Func}]),
TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]),
+ GroupName = case get_name(Mode) of
+ undefined -> "";
+ Name -> cast_to_list(Name)
+ end,
print(html,
TR ++ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "< >" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "0.000s" ++ Col1 ++ "</td>"
"<td><font color=\"~s\">SKIPPED</font></td>"
"<td>~s</td></tr>\n",
- [num2str(CaseNum),Mod,Func,ResultCol,Comment1]),
+ [num2str(CaseNum),Mod,GroupName,Func,ResultCol,Comment1]),
if CaseNum > 0 ->
{US,AS} = get(test_server_skipped),
case Type of
@@ -3656,16 +3662,20 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
Args1 = [[{tc_logfile,MinorName} | proplists:delete(tc_logfile,hd(Args))]],
test_server_sup:framework_call(report, [tc_start,{{?pl2a(Mod),Func},MinorName}]),
-
print_props((RunInit==skip_init), get_props(Mode)),
+ GroupName = case get_name(Mode) of
+ undefined -> "";
+ Name -> cast_to_list(Name)
+ end,
print(major, "=started ~s", [lists:flatten(timestamp_get(""))]),
{{Col0,Col1},Style} = get_font_style((RunInit==run_init), Mode),
TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]),
print(html, TR ++ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
"<td>" ++ Col0 ++ "~p" ++ Col1 ++ "</td>"
+ "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>"
"<td><a href=\"~s\">~p</a></td>"
"<td><a href=\"~s#top\"><</a> <a href=\"~s#end\">></a></td>",
- [num2str(Num),Mod,MinorBase,Func,MinorBase,MinorBase]),
+ [num2str(Num),Mod,GroupName,MinorBase,Func,MinorBase,MinorBase]),
do_if_parallel(Main, ok, fun erlang:yield/0),
%% run the test case
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index 729a2b11fc..7e48a11f33 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -301,7 +301,15 @@ run(List, Opts) when is_list(List), is_list(Opts) ->
run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
File=atom_to_list(Testspec),
- run_test(File, [{spec,[File++".spec"]}], Options);
+ Spec = case code:lib_dir(Testspec) of
+ {error, bad_name} when Testspec /= emulator,
+ Testspec /= system,
+ Testspec /= epmd ->
+ create_skip_spec(Testspec, tests(Testspec));
+ _ ->
+ File++".spec"
+ end,
+ run_test(File, [{spec,[Spec]}], Options);
%% Runs one module in a spec (interactive)
run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
run_test({atom_to_list(Testspec), Mod},
@@ -332,6 +340,21 @@ run(Testspec, Mod, Case, Config) when is_atom(Testspec),
Args = [{suite,atom_to_list(Mod)}, {testcase,atom_to_list(Case)}],
run_test(atom_to_list(Testspec), Args, Options).
+%% Create a spec to skip all SUITES, this is used when the application
+%% to be tested is not part of the OTP release to be tested.
+create_skip_spec(Testspec, SuitesToSkip) ->
+ {ok,Cwd} = file:get_cwd(),
+ TestspecString = atom_to_list(Testspec),
+ Specname = TestspecString++"_skip.spec",
+ {ok,D} = file:open(filename:join([filename:dirname(Cwd),
+ TestspecString++"_test",Specname]),
+ [write]),
+ TestDir = "\"../"++TestspecString++"_test\"",
+ io:format(D,"{suites, "++TestDir++", all}.~n",[]),
+ io:format(D,"{skip_suites, "++TestDir++", ~w, \"Skipped as application"
+ " is not in path!\"}.",[SuitesToSkip]),
+ Specname.
+
%% Check testspec to be valid and get possible Options
%% from the config.
check_test_get_opts(Testspec, Config) ->