diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/common_test/priv/ct_default.css | 10 | ||||
-rw-r--r-- | lib/common_test/src/ct.erl | 2 | ||||
-rw-r--r-- | lib/common_test/src/ct_framework.erl | 37 | ||||
-rw-r--r-- | lib/common_test/src/ct_logs.erl | 34 | ||||
-rw-r--r-- | lib/common_test/src/ct_util.erl | 23 | ||||
-rw-r--r-- | lib/common_test/test/ct_error_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/erl_docgen/src/docgen_edoc_xml_cb.erl | 23 | ||||
-rw-r--r-- | lib/test_server/src/test_server.erl | 22 | ||||
-rw-r--r-- | lib/test_server/src/test_server_ctrl.erl | 22 | ||||
-rw-r--r-- | lib/test_server/src/ts.erl | 25 |
10 files changed, 147 insertions, 55 deletions
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css index 75f8d5db8a..8ae6990cd8 100644 --- a/lib/common_test/priv/ct_default.css +++ b/lib/common_test/priv/ct_default.css @@ -81,13 +81,21 @@ div.copyright { color: #000000; } -div.ct_internal { +div.ct_internal { background: lightgrey; color: black; font-family: "Monaco", "Andale Mono", "Consolas", monospace; font-size: .95em; margin: .2em 0 0 0; } +div.ct_error_notify { + background: #CC0000; + color: #FFFFFF; + font-family: "Monaco", "Andale Mono", "Consolas", monospace; + font-size: 1.05em; + margin: .2em 0 0 0; +} + div.default { background: lightgreen; color: black; font-family: "Monaco", "Andale Mono", "Consolas", monospace; diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 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) -> |