diff options
20 files changed, 557 insertions, 128 deletions
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index e10460ce78..2400505159 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1714,7 +1714,9 @@ tmpmem() -> false -> undefined; MemInfo -> MSBCS = lists:foldl( - fun ({instance, _, L}, Acc) -> + fun ({instance, 0, _}, Acc) -> + Acc; % Ignore instance 0 + ({instance, _, L}, Acc) -> {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), [MBCS,SBCS | Acc] diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml index d215d68f30..a87be21d73 100644 --- a/lib/common_test/doc/src/ct.xml +++ b/lib/common_test/doc/src/ct.xml @@ -661,52 +661,70 @@ <func> <name>log(Format) -> ok</name> - <fsummary>Equivalent to log(default, 50, Format, []).</fsummary> + <fsummary>Equivalent to log(default, 50, Format, [], []).</fsummary> <desc><marker id="log-1"/> <p>Equivalent to - <seealso marker="#log-4"><c>ct:log(default, 50, Format, [])</c></seealso>.</p> + <seealso marker="#log-5"><c>ct:log(default, 50, Format, [], [])</c></seealso>.</p> </desc> </func> <func> <name>log(X1, X2) -> ok</name> <fsummary>Equivalent to log(Category, Importance, Format, - Args).</fsummary> + FormatArgs, []).</fsummary> <type> <v>X1 = Category | Importance | Format</v> - <v>X2 = Format | Args</v> + <v>X2 = Format | FormatArgs</v> </type> <desc><marker id="log-2"/> - <p>Equivalent to <seealso marker="#log-4"><c>ct:log(Category, - Importance, Format, Args)</c></seealso>.</p> + <p>Equivalent to <seealso marker="#log-5"><c>ct:log(Category, + Importance, Format, FormatArgs, [])</c></seealso>.</p> </desc> </func> <func> <name>log(X1, X2, X3) -> ok</name> <fsummary>Equivalent to log(Category, Importance, Format, - Args).</fsummary> + FormatArgs, Opts).</fsummary> <type> <v>X1 = Category | Importance</v> <v>X2 = Importance | Format</v> - <v>X3 = Format | Args</v> + <v>X3 = Format | FormatArgs | Opts</v> </type> <desc><marker id="log-3"/> - <p>Equivalent to <seealso marker="#log-4"><c>ct:log(Category, - Importance, Format, Args)</c></seealso>.</p> + <p>Equivalent to <seealso marker="#log-5"><c>ct:log(Category, + Importance, Format, FormatArgs, Opts)</c></seealso>.</p> </desc> </func> <func> - <name>log(Category, Importance, Format, Args) -> ok</name> + <name>log(X1, X2, X3, X4) -> ok</name> + <fsummary>Equivalent to log(Category, Importance, Format, + FormatArgs, Opts).</fsummary> + <type> + <v>X1 = Category | Importance</v> + <v>X2 = Importance | Format</v> + <v>X3 = Format | FormatArgs</v> + <v>X4 = FormatArgs | Opts</v> + </type> + <desc><marker id="log-4"/> + <p>Equivalent to <seealso marker="#log-5"><c>ct:log(Category, + Importance, Format, FormatArgs, Opts)</c></seealso>.</p> + </desc> + </func> + + <func> + <name>log(Category, Importance, Format, FormatArgs, Opts) -> ok</name> <fsummary>Prints from a test case to the log file.</fsummary> <type> <v>Category = atom()</v> <v>Importance = integer()</v> <v>Format = string()</v> - <v>Args = list()</v> + <v>FormatArgs = list()</v> + <v>Opts = [Opt]</v> + <v>Opt = no_css | esc_chars</v> </type> - <desc><marker id="log-4"/> + <desc><marker id="log-5"/> <p>Prints from a test case to the log file.</p> <p>This function is meant for printing a string directly from a @@ -714,11 +732,15 @@ <p>Default <c>Category</c> is <c>default</c>, default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, - and default value for <c>Args</c> is <c>[]</c>.</p> + and default value for <c>FormatArgs</c> is <c>[]</c>.</p> - <p>For details on <c>Category</c> and <c>Importance</c>, see section - <seealso marker="write_test_chapter#logging">Logging - Categories - and Verbosity Levels</seealso> in the User's Guide.</p> + <p>For details on <c>Category</c>, <c>Importance</c> and the <c>no_css</c> + option, see section <seealso marker="write_test_chapter#logging"> + Logging - Categories and Verbosity Levels</seealso> in the User's Guide.</p> + + <p>Common Test will not escape special HTML characters (<, > and &) + in the text printed with this function, unless the <c>esc_chars</c> + option is used.</p> </desc> </func> @@ -769,40 +791,40 @@ <func> <name>pal(X1, X2) -> ok</name> <fsummary>Equivalent to pal(Category, Importance, Format, - Args).</fsummary> + FormatArgs).</fsummary> <type> <v>X1 = Category | Importance | Format</v> - <v>X2 = Format | Args</v> + <v>X2 = Format | FormatArgs</v> </type> <desc><marker id="pal-2"/> <p>Equivalent to <seealso marker="#pal-4"><c>ct:pal(Category, - Importance, Format, Args)</c></seealso>.</p> + Importance, Format, FormatArgs)</c></seealso>.</p> </desc> </func> <func> <name>pal(X1, X2, X3) -> ok</name> <fsummary>Equivalent to pal(Category, Importance, Format, - Args).</fsummary> + FormatArgs).</fsummary> <type> <v>X1 = Category | Importance</v> <v>X2 = Importance | Format</v> - <v>X3 = Format | Args</v> + <v>X3 = Format | FormatArgs</v> </type> <desc><marker id="pal-3"/> <p>Equivalent to <seealso marker="#pal-4"><c>ct:pal(Category, - Importance, Format, Args)</c></seealso>.</p> + Importance, Format, FormatArgs)</c></seealso>.</p> </desc> </func> <func> - <name>pal(Category, Importance, Format, Args) -> ok</name> + <name>pal(Category, Importance, Format, FormatArgs) -> ok</name> <fsummary>Prints and logs from a test case.</fsummary> <type> <v>Category = atom()</v> <v>Importance = integer()</v> <v>Format = string()</v> - <v>Args = list()</v> + <v>FormatArgs = list()</v> </type> <desc><marker id="pal-4"/> <p>Prints and logs from a test case.</p> @@ -812,11 +834,15 @@ <p>Default <c>Category</c> is <c>default</c>, default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, - and default value for <c>Args</c> is <c>[]</c>.</p> + and default value for <c>FormatArgs</c> is <c>[]</c>.</p> <p>For details on <c>Category</c> and <c>Importance</c>, see section <seealso marker="write_test_chapter#logging">Logging - Categories and Verbosity Levels</seealso> in the User's Guide.</p> + + <p>Note that special characters in the text (<, > and &) will + be escaped by Common Test before the text is printed to the log + file.</p> </desc> </func> @@ -854,40 +880,40 @@ <func> <name>print(X1, X2) -> ok</name> <fsummary>Equivalent to print(Category, Importance, Format, - Args).</fsummary> + FormatArgs).</fsummary> <type> <v>X1 = Category | Importance | Format</v> - <v>X2 = Format | Args</v> + <v>X2 = Format | FormatArgs</v> </type> <desc><marker id="print-2"/> <p>Equivalent to <seealso marker="#print-4"><c>ct:print(Category, - Importance, Format, Args)</c></seealso>.</p> + Importance, Format, FormatArgs)</c></seealso>.</p> </desc> </func> <func> <name>print(X1, X2, X3) -> ok</name> <fsummary>Equivalent to print(Category, Importance, Format, - Args).</fsummary> + FormatArgs).</fsummary> <type> <v>X1 = Category | Importance</v> <v>X2 = Importance | Format</v> - <v>X3 = Format | Args</v> + <v>X3 = Format | FormatArgs</v> </type> <desc><marker id="print-3"/> <p>Equivalent to <seealso marker="#print-4"><c>ct:print(Category, - Importance, Format, Args)</c></seealso>.</p> + Importance, Format, FormatArgs)</c></seealso>.</p> </desc> </func> <func> - <name>print(Category, Importance, Format, Args) -> ok</name> + <name>print(Category, Importance, Format, FormatArgs) -> ok</name> <fsummary>Prints from a test case to the console.</fsummary> <type> <v>Category = atom()</v> <v>Importance = integer()</v> <v>Format = string()</v> - <v>Args = list()</v> + <v>FormatArgs = list()</v> </type> <desc><marker id="print-4"/> <p>Prints from a test case to the console.</p> @@ -897,7 +923,7 @@ <p>Default <c>Category</c> is <c>default</c>, default <c>Importance</c> is <c>?STD_IMPORTANCE</c>, - and default value for <c>Args</c> is <c>[]</c>.</p> + and default value for <c>FormatArgs</c> is <c>[]</c>.</p> <p>For details on <c>Category</c> and <c>Importance</c>, see section <seealso marker="write_test_chapter#logging">Logging - Categories diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml index 9c959945d2..12ec3bcec3 100644 --- a/lib/common_test/doc/src/ct_hooks.xml +++ b/lib/common_test/doc/src/ct_hooks.xml @@ -293,6 +293,63 @@ </func> <func> + <name>Module:post_init_per_testcase(TestcaseName, Config, Return, CTHState) -> Result</name> + <fsummary>Called after init_per_testcase.</fsummary> + <type> + <v>TestcaseName = atom()</v> + <v>Config = [{Key,Value}]</v> + <v>Return = NewReturn = Config | SkipOrFail | term()</v> + <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewReturn, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>OPTIONAL</p> + + <p>This function is called after + <seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso> + if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:post_init_per_suite-4"><c>post_init_per_suite</c></seealso>, + but for function + <seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso> + instead.</p> + </desc> + </func> + + <func> + <name>Module:pre_end_per_testcase(TestcaseName, InitData, CTHState) -> Result</name> + <fsummary>Called before end_per_testcase.</fsummary> + <type> + <v>TestcaseName = atom()</v> + <v>InitData = Config</v> + <v>Config = NewConfig = [{Key,Value}]</v> + <v>CTHState = NewCTHState = term()</v> + <v>Result = {NewConfig, NewCTHState}</v> + <v>Key = atom()</v> + <v>Value = term()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>OPTIONAL</p> + + <p>This function is called before + <seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso> + if it exists. It behaves the same way as + <seealso marker="ct_hooks#Module:pre_end_per_suite-3"><c>pre_end_per_suite</c></seealso>, + but for function + <seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso> + instead.</p> + + <p>This function can not change the result of the test case by returning skip or fail + tuples, but it may insert items in <c>Config</c> that can be read in + <c>end_per_testcase/2</c> or in <c>post_end_per_testcase/4</c>.</p> + </desc> + </func> + + <func> <name>Module:post_end_per_testcase(TestcaseName, Config, Return, CTHState) -> Result</name> <fsummary>Called after end_per_testcase.</fsummary> <type> @@ -312,7 +369,7 @@ <p>This function is called after <seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso> if it exists. It behaves the same way as - <seealso marker="ct_hooks#Module:post_init_per_suite-4"><c>post_init_per_suite</c></seealso>, + <seealso marker="ct_hooks#Module:post_end_per_suite-4"><c>post_end_per_suite</c></seealso>, but for function <seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso> instead.</p> diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 95943ced94..3eb1945a61 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -239,6 +239,7 @@ <item><seealso marker="common_test#Module:init_per_suite-1"><c>init_per_suite</c></seealso></item> <item><seealso marker="common_test#Module:init_per_group-2"><c>init_per_group</c></seealso></item> <item><seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso></item> + <item><seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso></item> <item><seealso marker="common_test#Module:end_per_group-2"><c>end_per_group</c></seealso></item> <item><seealso marker="common_test#Module:end_per_suite-1"><c>end_per_suite</c></seealso></item> </list> @@ -281,6 +282,7 @@ <list type="bulleted"> <item><seealso marker="common_test#Module:init_per_suite-1"><c>init_per_suite</c></seealso></item> <item><seealso marker="common_test#Module:init_per_group-2"><c>init_per_group</c></seealso></item> + <item><seealso marker="common_test#Module:init_per_testcase-2"><c>init_per_testcase</c></seealso></item> <item><seealso marker="common_test#Module:end_per_testcase-2"><c>end_per_testcase</c></seealso></item> <item><seealso marker="common_test#Module:end_per_group-2"><c>end_per_group</c></seealso></item> <item><seealso marker="common_test#Module:end_per_suite-1"><c>end_per_suite</c></seealso></item> @@ -393,6 +395,8 @@ -export([post_end_per_group/4]). -export([pre_init_per_testcase/3]). + -export([post_init_per_testcase/4]). + -export([pre_end_per_testcase/3]). -export([post_end_per_testcase/4]). -export([on_tc_fail/3]). @@ -438,7 +442,7 @@ post_init_per_group(Group,Config,Return,State) -> {Return, State}. - %% @doc Called after each end_per_group. + %% @doc Called before each end_per_group. pre_end_per_group(Group,Config,State) -> {Config, State}. @@ -446,11 +450,19 @@ post_end_per_group(Group,Config,Return,State) -> {Return, State}. - %% @doc Called before each test case. + %% @doc Called before each init_per_testcase. pre_init_per_testcase(TC,Config,State) -> {Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }. - %% @doc Called after each test case. + %% Called after each init_per_testcase (immediately before the test case). + post_init_per_testcase(TC,Config,Return,State) -> + {Return, State} + +%% @doc Called before each end_per_testcase (immediately after the test case). + pre_end_per_testcase(TC,Config,State) -> + {Config, State}. + + %% @doc Called after each end_per_testcase. post_end_per_testcase(TC,Config,Return,State) -> TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)}, {Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }. diff --git a/lib/common_test/doc/src/ct_netconfc.xml b/lib/common_test/doc/src/ct_netconfc.xml index 77366da891..d8c82c7f2c 100644 --- a/lib/common_test/doc/src/ct_netconfc.xml +++ b/lib/common_test/doc/src/ct_netconfc.xml @@ -218,13 +218,14 @@ <tag><c>notification_content() = [event_time() | simple_xml()]</c></tag> <item><marker id="type-notification_content"/> </item> - <tag><c>option() = {ssh, host()} | {port, <seealso marker="kernel:inet#type-port_number"><c>inet:port_number()</c></seealso>} | {timeout, timeout()} | ssh_connect_option()</c></tag> + <tag><c>option() = {ssh, host()} | {port, <seealso marker="kernel:inet#type-port_number"><c>inet:port_number()</c></seealso>} | {timeout, timeout()} | SshConnectOption</c></tag> <item><marker id="type-option"/> - <p>For <c>ssh_connect_option()</c>, see - <seealso marker="ssh:ssh#connect-3"><c>ssh:connect/3,4</c></seealso>. Common - SSH connect options are <c>user</c>, <c>password</c> - and <c>user_dir</c>, but any option that is verified as valid - by application <c>SSH</c> is allowed.</p></item> + + <p><c>SshConnectOption</c> is any valid option to + <seealso marker="ssh:ssh#connect-3"><c>ssh:connect/3,4</c></seealso>. + Common options used are <c>user</c>, <c>password</c> + and <c>user_dir</c>. The <c>SshConnectOptions</c> are + verfied by the SSH application.</p></item> <tag><c>options() = [option()]</c></tag> <item><marker id="type-options"/> diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 8c1d104949..e3811ec4cf 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -953,11 +953,11 @@ <p><c>Common Test</c> provides the following three main functions for printing strings:</p> <list type="bulleted"> - <item><c>ct:log(Category, Importance, Format, Args)</c></item> - <item><c>ct:print(Category, Importance, Format, Args)</c></item> - <item><c>ct:pal(Category, Importance, Format, Args)</c></item> + <item><c>ct:log(Category, Importance, Format, FormatArgs, Opts)</c></item> + <item><c>ct:print(Category, Importance, Format, FormatArgs)</c></item> + <item><c>ct:pal(Category, Importance, Format, FormatArgs)</c></item> </list> - <p>The <seealso marker="ct#log-1"><c>log/1,2,3,4</c></seealso> function + <p>The <seealso marker="ct#log-1"><c>log/1,2,3,4,5</c></seealso> function prints a string to the test case log file. The <seealso marker="ct#print-1"><c>print/1,2,3,4</c></seealso> function prints the string to screen. @@ -1031,14 +1031,26 @@ 4. Categorized info, importance = 25 6. Categorized error, importance = 99</pre> + <p>The arguments <c>Format</c> and <c>FormatArgs</c> in <c>ct:log/print/pal</c> are + always passed on to the <c>stdlib</c> function <c>io:format/3</c> (For details, + see the <seealso marker="stdlib:io"><c>stdlib:io</c></seealso> manual page).</p> + + <p><c>ct:pal/4</c> and <c>ct:log/5</c> add headers to strings being printed to the + log file. The strings are also wrapped in div tags with a CSS class + attribute, so that stylesheet formatting can be applied. To disable this feature for + a printout (i.e. to get a result similar to using <c>io:format/2</c>), + call <c>ct:log/5</c> with the <c>no_css</c> option.</p> + <p>How categories can be mapped to CSS tags is documented in section <seealso marker="run_test_chapter#html_stylesheet">HTML Style Sheets</seealso> in section Running Tests and Analyzing Results.</p> - - <p>The arguments <c>Format</c> and <c>Args</c> in <c>ct:log/print/pal</c> are - always passed on to function <c>stdlib:io:format/3</c> (For details, see the - <seealso marker="stdlib:io"><c>stdlib:io</c></seealso> manual page).</p> + <p>Common Test will escape special HTML characters (<, > and &) in printouts + to the log file made with <c>ct:pal/4</c> and <c>io:format/2</c>. In order to print + strings with HTML tags to the log, use the <c>ct:log/5</c> function. The character escaping + feature is per default disabled for <c>ct:log/5</c>, but can be enabled with the + <c>esc_chars</c> option.</p> + <p>For more information about log files, see section <seealso marker="run_test_chapter#log_files">Log Files</seealso> in section Running Tests and Analyzing Results.</p> diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 1c1b46c2af..538be514d6 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -591,7 +591,7 @@ log(X1,X2,X3,X4) -> %%% Format = string() %%% Args = list() %%% Opts = [Opt] -%%% Opt = esc_chars +%%% Opt = esc_chars | no_css %%% %%% @doc Printout from a test case to the log file. %%% diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl index 93f358462d..034906a3ba 100644 --- a/lib/common_test/src/ct_conn_log_h.erl +++ b/lib/common_test/src/ct_conn_log_h.erl @@ -105,52 +105,62 @@ terminate(_,#state{logs=Logs}) -> %%% Writing reports write_report(_Time,#conn_log{header=false,module=ConnMod}=Info,Data,GL,State) -> case get_log(Info,GL,State) of - {silent,_} -> + {silent,_,_} -> ok; - {LogType,Fd} -> - io:format(Fd,"~n~ts",[format_data(ConnMod,LogType,Data)]) + {LogType,Dest,Fd} -> + Str = if LogType == html, Dest == gl -> ["$tc_html","~n~ts"]; + true -> "~n~ts" + end, + io:format(Fd,Str,[format_data(ConnMod,LogType,Data)]) end; write_report(Time,#conn_log{module=ConnMod}=Info,Data,GL,State) -> case get_log(Info,GL,State) of - {silent,_} -> + {silent,_,_} -> ok; - {LogType,Fd} -> + {LogType,Dest,Fd} -> case format_data(ConnMod,LogType,Data) of [] when Info#conn_log.action==send; Info#conn_log.action==recv -> ok; FormattedData -> - io:format(Fd,"~n~ts~ts~ts",[format_head(ConnMod,LogType,Time), - format_title(LogType,Info), - FormattedData]) + Str = if LogType == html, Dest == gl -> + ["$tc_html","~n~ts~ts~ts"]; + true -> + "~n~ts~ts~ts" + end, + io:format(Fd,Str,[format_head(ConnMod,LogType,Time), + format_title(LogType,Info), + FormattedData]) end end. write_error(Time,#conn_log{module=ConnMod}=Info,Report,GL,State) -> case get_log(Info,GL,State) of - {LogType,_} when LogType==html; LogType==silent -> + {LogType,_,_} when LogType==html; LogType==silent -> %% The error will anyway be written in the html log by the %% sasl error handler, so don't write it again. ok; - {LogType,Fd} -> - io:format(Fd,"~n~ts~ts~ts", - [format_head(ConnMod,LogType,Time," ERROR"), - format_title(LogType,Info), - format_error(LogType,Report)]) + {LogType,Dest,Fd} -> + Str = if LogType == html, Dest == gl -> ["$tc_html","~n~ts~ts~ts"]; + true -> "~n~ts~ts~ts" + end, + io:format(Fd,Str,[format_head(ConnMod,LogType,Time," ERROR"), + format_title(LogType,Info), + format_error(LogType,Report)]) end. get_log(Info,GL,State) -> case proplists:get_value(GL,State#state.logs) of undefined -> - {html,State#state.default_gl}; + {html,gl,State#state.default_gl}; ConnLogs -> case proplists:get_value(Info#conn_log.module,ConnLogs) of {html,_} -> - {html,GL}; + {html,gl,GL}; {LogType,Fds} -> - {LogType,get_fd(Info,Fds)}; + {LogType,file,get_fd(Info,Fds)}; undefined -> - {html,GL} + {html,gl,GL} end end. diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index e3f995ad3f..4920383f39 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -432,10 +432,16 @@ tc_log(Category,Importance,Format,Args,Opts) -> %%% stuff directly from a testcase (i.e. not from within the CT %%% framework).</p> tc_log(Category,Importance,Printer,Format,Args,Opts) -> - cast({log,sync,self(),group_leader(),Category,Importance, - [{hd,div_header(Category,Printer),[]}, - {Format,Args}, - {ft,div_footer(),[]}], + Data = + case lists:member(no_css, Opts) of + true -> + [{Format,Args}]; + false -> + [{hd,div_header(Category,Printer),[]}, + {Format,Args}, + {ft,div_footer(),[]}] + end, + cast({log,sync,self(),group_leader(),Category,Importance,Data, lists:member(esc_chars, Opts)}), ok. diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl index 6438ea01c1..5d7e945cc3 100644 --- a/lib/common_test/src/ct_release_test.erl +++ b/lib/common_test/src/ct_release_test.erl @@ -131,7 +131,7 @@ -include_lib("kernel/include/file.hrl"). %%----------------------------------------------------------------- --define(testnode, otp_upgrade). +-define(testnode, 'ct_release_test-upgrade'). -define(exclude_apps, [hipe, typer, dialyzer]). % never include these apps %%----------------------------------------------------------------- @@ -304,7 +304,13 @@ upgrade(Apps,Level,Callback,Config) -> %% Note, we will not reach this if the test fails with a %% timetrap timeout in the test suite! Thus we can have %% hanging nodes... - Nodes = nodes(), + Nodes = lists:filter(fun(Node) -> + case atom_to_list(Node) of + "ct_release_test-" ++_ -> true; + _ -> false + end + end, + nodes()), [rpc:call(Node,erlang,halt,[]) || Node <- Nodes] end. @@ -328,7 +334,14 @@ upgrade(Apps,Level,Callback,Config) -> %% ct_release_test:cleanup(Config).''' %% cleanup(Config) -> - Nodes = [node_name(?testnode)|nodes()], + AllNodes = [node_name(?testnode)|nodes()], + Nodes = lists:filter(fun(Node) -> + case atom_to_list(Node) of + "ct_release_test-" ++_ -> true; + _ -> false + end + end, + AllNodes), [rpc:call(Node,erlang,halt,[]) || Node <- Nodes], Config. @@ -455,9 +468,9 @@ get_rels(minor) -> {CurrentMajor,Current}. init_upgrade_test(FromVsn,ToVsn,OldRel) -> - OtpRel = list_to_atom("otp-"++FromVsn), + Name = list_to_atom("ct_release_test-otp-"++FromVsn), ct:log("Starting node to fetch application versions to upgrade from"), - {ok,Node} = test_server:start_node(OtpRel,peer,[{erl,[OldRel]}]), + {ok,Node} = test_server:start_node(Name,peer,[{erl,[OldRel]}]), {Apps,Path} = fetch_all_apps(Node), test_server:stop_node(Node), {FromVsn,ToVsn,Apps,Path}. @@ -723,7 +736,7 @@ do_callback(Node,Mod,Func,Args) -> ct:log("~p:~p/~w returned: ~p",[Mod,Func,length(Args),R]), case R of {badrpc,Error} -> - test_server:fail({test_upgrade_callback,Mod,Func,Args,Error}); + throw({fail,{test_upgrade_callback,Mod,Func,Args,Error}}); NewState -> NewState end. diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl index 9b3dc0b5f1..954b4239af 100644 --- a/lib/common_test/src/cth_conn_log.erl +++ b/lib/common_test/src/cth_conn_log.erl @@ -132,7 +132,7 @@ pre_init_per_testcase(TestCase,Config,CthState) -> [S,ct_logs:uri(L),filename:basename(L)]) || {S,L} <- Ls] ++ "</table>", - io:format(Str,[]), + ct:log(Str,[],[no_css]), {ConnMod,{LogType,Ls}}; _ -> {ConnMod,{LogType,[]}} diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index ff4495b104..a1cead4550 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -65,7 +65,8 @@ MODULES= \ ct_cover_nomerge_SUITE \ ct_groups_search_SUITE \ ct_surefire_SUITE \ - ct_telnet_SUITE + ct_telnet_SUITE \ + ct_release_test_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/common_test/test/ct_release_test_SUITE.erl b/lib/common_test/test/ct_release_test_SUITE.erl new file mode 100644 index 0000000000..66d07155ac --- /dev/null +++ b/lib/common_test/test/ct_release_test_SUITE.erl @@ -0,0 +1,190 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2014. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_release_test_SUITE +%%% +%%% Description: +%%% Test ct_release_test module +%%% +%%%------------------------------------------------------------------- +-module(ct_release_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). +-define(suite, release_test_SUITE). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case os:type() of + {win32,_} -> + {skipped, "Upgrade tests do currently not work on windows"}; + _ -> + ct_test_support:init_per_suite(Config) + end. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + minor, + major, + major_fail_init, + major_fail_upgraded, + major_fail_downgraded, + major_fail_no_init + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +minor(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,minor}, + {label,minor}|Cfg], Config), + execute(minor, Opts, ERPid, Config). + +major(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,major}, + {label,major}|Cfg], Config), + execute(major, Opts, ERPid, Config). + +major_fail_init(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,major_fail_init}, + {label,major_fail_init}|Cfg], Config), + execute(major_fail_init, Opts, ERPid, Config). + +major_fail_upgraded(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,major_fail_upgraded}, + {label,major_fail_upgraded}|Cfg], Config), + execute(major_fail_upgraded, Opts, ERPid, Config). + +major_fail_downgraded(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,major_fail_downgraded}, + {label,major_fail_downgraded}|Cfg], Config), + execute(major_fail_downgraded, Opts, ERPid, Config). + +major_fail_no_init(Config) when is_list(Config) -> + {Suite,Cfg} = setup1(Config), + {Opts,ERPid} = setup([{suite,Suite}, + {testcase,major_fail_no_init}, + {label,major_fail_no_init}|Cfg], Config), + execute(major_fail_no_init, Opts, ERPid, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- +setup1(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, atom_to_list(?suite)), + Cfg = case ct:get_config(otp_releases) of + undefined -> + []; + Rels -> + CfgFile = filename:join(DataDir, "release_test.cfg"), + file:write_file(CfgFile, + io_lib:format("{otp_releases,~p}.",[Rels])), + [{config,CfgFile}] + end, + {Suite,Cfg}. + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + verify_events(Name,Events,Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +verify_events(TC,Events,Config) -> + Ok = expected_events(TC,ok), + case ct_test_support:verify_events(Ok, Events, Config) of + ok -> + ok; + {event_not_found,{?eh,tc_done,{_Suite,TC,ok}}}=R1 -> + ct:log("Did not find 'ok', checking if skipped...",[]), + Skipped = expected_events(TC,{skipped,"Old release not available"}), + case ct_test_support:verify_events(Skipped, Events, Config) of + ok -> + {skipped,"Old release not available"}; + R2 -> + ct:log("Did not find skipped case either: ~n~p",[R2]), + exit(R1) + end + end. + +expected_events(TC,Result) -> + OneTest = + [{?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,tc_done,{?suite,TC,Result}}, + {?eh,stop_logging,[]}], + %% 2 tests (ct:run_test + script_start) is default + OneTest ++ OneTest. diff --git a/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl new file mode 100644 index 0000000000..04c92be0d1 --- /dev/null +++ b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl @@ -0,0 +1,118 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%%%---------------------------------------------------------------- +%%% Purpose: Test the support for application upgrade/code_change test +%%%----------------------------------------------------------------- +-module(release_test_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +-define(APP,runtime_tools). % "randomly" selected 'application under test' + +%% +%% all/1 +%% +all() -> + [minor, + major, + major_fail_init, + major_fail_upgraded, + major_fail_downgraded, + major_fail_no_init]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(major_fail_no_init, Config) -> + Config; +init_per_testcase(_Case, Config) -> + ct_release_test:init(Config). +end_per_testcase(_Case, Config) -> + ct_release_test:cleanup(Config). + +%%%----------------------------------------------------------------- +%%% Test cases +minor(Config) -> + ct_release_test:upgrade(?APP,minor,{?MODULE,[]},Config). + +major(Config) -> + ct_release_test:upgrade(?APP,major,{?MODULE,[]},Config). + +major_fail_init(Config) -> + try ct_release_test:upgrade(?APP,major,{?MODULE,fail_init},Config) + catch exit:{test_case_failed, + {test_upgrade_callback,_Mod,_Func,_Args, + {'EXIT',{test_case_failed,upgrade_init_failed}}}} -> + ok + end. + +major_fail_upgraded(Config) -> + try ct_release_test:upgrade(?APP,major,{?MODULE,fail_upgraded},Config) + catch exit:{test_case_failed, + {test_upgrade_callback,_Mod,_Func,_Args, + {'EXIT',{test_case_failed,upgrade_upgraded_failed}}}} -> + ok + end. + +major_fail_downgraded(Config) -> + try ct_release_test:upgrade(?APP,major,{?MODULE,fail_downgraded},Config) + catch exit:{test_case_failed, + {test_upgrade_callback,_Mod,_Func,_Args, + {'EXIT',{test_case_failed,upgrade_downgraded_failed}}}} -> + ok + end. + +major_fail_no_init(Config) -> + try ct_release_test:upgrade(?APP,major,[],Config) + catch exit:{test_case_failed,"ct_release_test:init/1 not run"} -> + ok + end. + +%%%----------------------------------------------------------------- +%%% ct_release_test callbacks + +%% Version numbers are checked by ct_release_test, so there is nothing +%% more to check here... +upgrade_init(CtData,fail_init) -> + ct:fail(upgrade_init_failed); +upgrade_init(CtData,State) -> + {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData,?APP), + case ct_release_test:get_appup(CtData,?APP) of + {ok,{FromVsn,ToVsn,UpInstrs,DownInstrs}} -> + io:format("Upgrade/downgrade ~p: ~p <--> ~p~n" + "Upgrade instructions: ~p~n" + "Downgrade instructions: ~p", + [?APP,FromVsn,ToVsn,UpInstrs,DownInstrs]); + {error,{vsn_not_found,_}} when FromVsn==ToVsn -> + io:format("No upgrade test for ~p, same version",[?APP]) + end, + State. +upgrade_upgraded(CtData,fail_upgraded) -> + ct:fail(upgrade_upgraded_failed); +upgrade_upgraded(_CtData,State) -> + State. +upgrade_downgraded(CtData,fail_downgraded) -> + ct:fail(upgrade_downgraded_failed); +upgrade_downgraded(_CtData,State) -> + State. diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 248ec6c4df..8e7ac9395c 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -1228,8 +1228,8 @@ log_events(TC, Events, EvLogDir, Opts) -> 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", - [FullLogFile,FullLogFile]), + ct:log("Events written to logfile: <a href=\"file://~s\">~s</a>~n", + [FullLogFile,FullLogFile],[no_css]), io:format(user, "Events written to logfile: ~p~n", [LogFile]). log_events1(Evs, Dev, "") -> diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 6f19f67fb5..73ade14fa1 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1189,7 +1189,7 @@ bad_erl_libs(Config) when is_list(Config) -> ?t:stop_node(Node2), %% Test that code path is not affected by the faulty ERL_LIBS - Code == Code2, + Code = Code2, ok. diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl index a586729b30..fa9d0f6b55 100644 --- a/lib/stdlib/test/stdlib_SUITE.erl +++ b/lib/stdlib/test/stdlib_SUITE.erl @@ -31,10 +31,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app_test, appup_test, assert_test, {group,upgrade}]. - -groups() -> - [{upgrade,[minor_upgrade,major_upgrade]}]. + [app_test, appup_test, assert_test]. init_per_suite(Config) -> Config. @@ -42,13 +39,9 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. -init_per_group(upgrade, Config) -> - ct_release_test:init(Config); init_per_group(_GroupName, Config) -> Config. -end_per_group(upgrade, Config) -> - ct_release_test:cleanup(Config); end_per_group(_GroupName, Config) -> Config. @@ -165,29 +158,6 @@ check_appup([],_,_) -> ok. -minor_upgrade(Config) -> - ct_release_test:upgrade(stdlib,minor,{?MODULE,[]},Config). - -major_upgrade(Config) -> - ct_release_test:upgrade(stdlib,major,{?MODULE,[]},Config). - -%% Version numbers are checked by ct_release_test, so there is nothing -%% more to check here... -upgrade_init(CtData,State) -> - {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData,stdlib), - case ct_release_test:get_appup(CtData,stdlib) of - {ok,{FromVsn,ToVsn,[restart_new_emulator],[restart_new_emulator]}} -> - io:format("Upgrade/downgrade ~p <--> ~p",[FromVsn,ToVsn]); - {error,{vsn_not_found,_}} when FromVsn==ToVsn -> - io:format("No upgrade test for stdlib, same version") - end, - State. -upgrade_upgraded(_CtData,State) -> - State. -upgrade_downgraded(_CtData,State) -> - State. - - -include_lib("stdlib/include/assert.hrl"). -include_lib("stdlib/include/assert.hrl"). % test repeated inclusion assert_test(suite) -> diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 958fe1a2b7..e0975ab744 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -1825,13 +1825,14 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName, MFA) -> case {filelib:is_file(filename:join(LogDir, SrcListing)), lists:member(no_src, get(test_server_logopts))} of {true,false} -> - print(Lev, Info ++ "<a href=\"~ts#~ts\">~w:~w/~w</a> " - "(click for source code)\n", + print(Lev, ["$tc_html", + Info ++ "<a href=\"~ts#~ts\">~w:~w/~w</a> " + "(click for source code)\n"], [uri_encode(SrcListing), uri_encode(atom_to_list(Func)++"-1",utf8), Mod,Func,Arity]); _ -> - print(Lev, Info ++ "~w:~w/~w\n", [Mod,Func,Arity]) + print(Lev, ["$tc_html",Info ++ "~w:~w/~w\n"], [Mod,Func,Arity]) end end, @@ -4356,6 +4357,10 @@ print(Detail, Format) -> print(Detail, Format, Args) -> print(Detail, Format, Args, internal). +print(Detail, ["$tc_html",Format], Args, Printer) -> + Msg = io_lib:format(Format, Args), + print_or_buffer(Detail, ["$tc_html",Msg], Printer); + print(Detail, Format, Args, Printer) -> Msg = io_lib:format(Format, Args), print_or_buffer(Detail, Msg, Printer). diff --git a/lib/test_server/src/test_server_gl.erl b/lib/test_server/src/test_server_gl.erl index 6abc68db54..0acc73047c 100644 --- a/lib/test_server/src/test_server_gl.erl +++ b/lib/test_server/src/test_server_gl.erl @@ -37,7 +37,8 @@ reject_io :: boolean(), %Reject I/O requests... permit_io, %... and exceptions auto_nl=true :: boolean(), %Automatically add NL - levels %{Stdout,Major,Minor} + levels, %{Stdout,Major,Minor} + escape_chars=true %Switch escaping HTML on/off }). %% start_link() @@ -137,7 +138,8 @@ init([]) -> reject_io=false, permit_io=gb_sets:empty(), auto_nl=true, - levels={1,19,10} + levels={1,19,10}, + escape_chars=true }}. req(GL, Req) -> @@ -193,10 +195,11 @@ handle_info({io_request,From,ReplyAs,Req}=IoReq, St) -> #st{capture=CapturePid} -> CapturePid ! {captured,Data} end, - if EscapeHtml -> + case EscapeHtml andalso St#st.escape_chars of + true -> output(minor, test_server_ctrl:escape_chars(Data), From, From, St); - not EscapeHtml -> + false -> output(minor, Data, From, From, St) end end, @@ -218,7 +221,11 @@ handle_info({printout,Detail,Fun}, St) when is_function(Fun)-> {noreply,St}; handle_info({printout,Detail,Format,Args}, St) -> Str = io_lib:format(Format, Args), - output(Detail, Str, internal, none, St), + if not St#st.escape_chars -> + output(Detail, ["$tc_html",Str], internal, none, St); + true -> + output(Detail, Str, internal, none, St) + end, {noreply,St}; handle_info(Msg, #st{tc_supervisor=Pid}=St) when is_pid(Pid) -> %% The process overseeing the testcase process also used to be diff --git a/lib/webtool/src/webtool.erl b/lib/webtool/src/webtool.erl index 80dad53f8f..a0f2b5aab0 100644 --- a/lib/webtool/src/webtool.erl +++ b/lib/webtool/src/webtool.erl @@ -583,7 +583,6 @@ rest_of_standard_data() -> mod_esi, mod_actions, mod_cgi, - mod_include, mod_dir, mod_get, mod_head, |