aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test')
-rw-r--r--lib/common_test/doc/src/ct.xml96
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml59
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml18
-rw-r--r--lib/common_test/doc/src/ct_netconfc.xml13
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml28
-rw-r--r--lib/common_test/src/ct.erl2
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl46
-rw-r--r--lib/common_test/src/ct_logs.erl14
-rw-r--r--lib/common_test/src/ct_release_test.erl25
-rw-r--r--lib/common_test/src/cth_conn_log.erl2
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_release_test_SUITE.erl190
-rw-r--r--lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl118
-rw-r--r--lib/common_test/test/ct_test_support.erl4
14 files changed, 532 insertions, 86 deletions
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) -&gt; 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) -&gt; 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) -&gt; 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) -&gt; ok</name>
+ <name>log(X1, X2, X3, X4) -&gt; 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) -&gt; 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 (&lt;, &gt; and &amp;)
+ 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) -&gt; 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) -&gt; 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) -&gt; ok</name>
+ <name>pal(Category, Importance, Format, FormatArgs) -&gt; 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 (&lt;, &gt; and &amp;) 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) -&gt; 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) -&gt; 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) -&gt; ok</name>
+ <name>print(Category, Importance, Format, FormatArgs) -&gt; 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) -&gt; 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) -&gt; 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) -&gt; 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 (&lt;, &gt; and &amp;) 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, "") ->