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/basics_chapter.xml5
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml38
-rw-r--r--lib/common_test/doc/src/ct_run.xml3
-rw-r--r--lib/common_test/doc/src/getting_started_chapter.xml3
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml12
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml87
-rw-r--r--lib/common_test/src/Makefile3
-rw-r--r--lib/common_test/src/common_test.app.src6
-rw-r--r--lib/common_test/src/ct.erl29
-rw-r--r--lib/common_test/src/ct_framework.erl317
-rw-r--r--lib/common_test/src/ct_hooks.erl9
-rw-r--r--lib/common_test/src/ct_logs.erl110
-rw-r--r--lib/common_test/src/ct_master.erl13
-rw-r--r--lib/common_test/src/ct_master_logs.erl175
-rw-r--r--lib/common_test/src/ct_repeat.erl2
-rw-r--r--lib/common_test/src/ct_run.erl150
-rw-r--r--lib/common_test/src/ct_testspec.erl18
-rw-r--r--lib/common_test/src/ct_util.hrl3
-rw-r--r--lib/common_test/src/cth_surefire.erl199
-rw-r--r--lib/common_test/src/vts.erl4
-rw-r--r--lib/common_test/test/Makefile1
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl143
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl15
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl84
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl9
-rw-r--r--lib/common_test/test/ct_group_info_SUITE.erl172
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl115
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl67
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl64
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl548
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl130
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl95
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl99
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE.erl277
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/default.spec3
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec5
-rw-r--r--lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl127
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl17
-rw-r--r--lib/common_test/test/ct_test_support.erl22
43 files changed, 2330 insertions, 871 deletions
diff --git a/lib/common_test/doc/src/basics_chapter.xml b/lib/common_test/doc/src/basics_chapter.xml
index 20141d2561..d43ac44ac5 100644
--- a/lib/common_test/doc/src/basics_chapter.xml
+++ b/lib/common_test/doc/src/basics_chapter.xml
@@ -141,8 +141,9 @@
individual test case.
</p>
<p>
- The test suite module must conform to a callback interface specified
- by the CT test server. See the
+ The test suite module must conform to a
+ <seealso marker="common_test">callback interface</seealso>
+ specified by the CT test server. See the
<seealso marker="write_test_chapter#intro">Writing Test Suites</seealso> chapter
for more information.
</p>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 8505ee8469..cb06e570bd 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -189,6 +189,22 @@
it.</p>
</section>
+ <section>
+ <title>External configuration data and Logging</title>
+ <p>It's possible in the CTH to read configuration data values
+ by calling <c>ct:get_config/1/2/3</c> (as explained in the
+ <seealso marker="config_file_chapter#require_config_data">
+ External configuration data</seealso>
+ chapter). The config variables in question must, as always, first have been
+ <c>required</c> by means of a suite-, group-, or test case info function,
+ or the <c>ct:require/1/2</c> function. Note that the latter can also be used
+ in CT hook functions.</p>
+ <p>The CT hook functions may call any of the logging functions available
+ in the <c>ct</c> interface to print information to the log files, or to
+ add comments in the suite overview page.
+ </p>
+ </section>
+
</section>
<marker id="manipulating"/>
@@ -201,11 +217,13 @@
functions for a CTH follow a common interface, this interface is
described below.</p>
- <p>It is only possible to hook into test function which exists in the test
- suite. So in order for a CTH to hook in before
- <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>,
- the <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>
- function must exist in the test suite.</p>
+ <p>Common Test will always call all available hook functions, even pre- and post
+ hooks for configuration functions that are not implemented in the suite.
+ For example, <c>pre_init_per_suite(x_SUITE, ...)</c> and
+ <c>post_init_per_suite(x_SUITE, ...)</c> will be called for test suite
+ <c>x_SUITE</c>, even if it doesn't export <c>init_per_suite/1</c>. This feature
+ makes it possible to use hooks as configuration fallbacks, or even
+ completely replace all configuration functions with hook functions.</p>
<marker id="pre"/>
<section>
@@ -429,6 +447,16 @@ terminate(State) ->
<seealso marker="sasl:sasl_app">SASL</seealso> events report
using the normal SASL mechanisms. </cell>
</row>
+ <row>
+ <cell>cth_surefire</cell>
+ <cell>no</cell>
+ <cell>Captures all test results and outputs them as surefire XML into
+ a file. The file which is created is by default called junit_report.xml.
+ The name can be by setting the path option for this hook. e.g.
+ <code>-ct_hooks cth_surefix [{path,"/tmp/report.xml"}]</code>
+ Surefire XML can forinstance be used by Jenkins to display test
+ results.</cell>
+ </row>
</table>
</section>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index b01ab3667d..4d4521957c 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -103,6 +103,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
@@ -130,6 +131,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
@@ -150,6 +152,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-basic_html]</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 5b5f7475c9..7919edd183 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -98,7 +98,8 @@
<section>
<title>A simple test suite</title>
<p>As you've seen in the basics chapter, the test suite module implements
- callback functions (mandatory or optional) for various purposes, e.g:
+ <seealso marker="common_test">callback functions</seealso>
+ (mandatory or optional) for various purposes, e.g:
<list>
<item>Init/end configuration function for the test suite</item>
<item>Init/end configuration function for a test case</item>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 848f278fa6..b3ec524b21 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -466,6 +466,9 @@
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
+
+ {create_priv_dir, PrivDirOption}.
+ {create_priv_dir, NodeRefs, PrivDirOption}.
{event_handler, EventHandlers}.
{event_handler, NodeRefs, EventHandlers}.
@@ -493,6 +496,9 @@
{skip_suites, DirRef, Suites, Comment}.
{skip_suites, NodeRefs, DirRef, Suites, Comment}.
+
+ {skip_groups, DirRef, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}.
{skip_cases, DirRef, Suite, Cases, Comment}.
{skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}.
@@ -512,6 +518,7 @@
DirAlias = atom()
Dir = string()
LogDir = string()
+ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
EventHandlers = atom() | [atom()]
InitArgs = [term()]
CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
@@ -521,8 +528,9 @@
Suites = atom() | [atom()] | all
Suite = atom()
Groups = GroupSpec | [GroupSpec] | all
- GroupSpec = Group | {Group,Properties} | {Group,Properties,GroupSpec}
- Group = atom()
+ GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
+ GroupName = atom()
+ GroupNames = GroupName | [GroupName]
Cases = atom() | [atom()] | all
Comment = string() | ""
</pre>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index c0ec26ddcc..09f4f8ed3b 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -29,7 +29,6 @@
<file>write_test_chapter.xml</file>
</header>
-
<section>
<marker id="intro"></marker>
<title>Support for test suite authors</title>
@@ -63,7 +62,8 @@
function in CT will not be able to locate it (at least not per default).
</p>
- <p>The <c>ct.hrl</c> header file must be included in all test suite files.
+ <p>It is also recommended that the <c>ct.hrl</c> header file is included
+ in all test suite modules.
</p>
<p>Each test suite module must export the function <c>all/0</c>
@@ -71,6 +71,12 @@
to be executed in that module.
</p>
+ <p>The callback functions that the test suite should implement, and
+ which will be described in more detail below, are
+ all listed in the <seealso marker="common_test">common_test
+ reference manual page</seealso>.
+ </p>
+
</section>
<section>
@@ -113,6 +119,14 @@
suite will be skipped automatically (so called <em>auto skipped</em>),
including <c>end_per_suite</c>.
</p>
+
+ <p>Note that if <c>init_per_suite</c> and <c>end_per_suite</c> do not exist
+ in the suite, Common Test calls dummy functions (with the same names)
+ instead, so that output generated by hook functions may be saved to the log
+ files for these dummies
+ (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test Hooks</seealso>
+ chapter for more information).
+ </p>
</section>
<marker id="per_testcase"/>
@@ -513,8 +527,7 @@
execution is immediately stopped and the rest of the cases skipped.</p>
<p>Before execution of a group begins, the configuration function
- <c>init_per_group(GroupName, Config)</c> is called (the function is
- mandatory if one or more test case groups are defined). The list of tuples
+ <c>init_per_group(GroupName, Config)</c> is called. The list of tuples
returned from this function is passed to the test cases in the usual
manner by means of the <c>Config</c> argument. <c>init_per_group/2</c>
is meant to be used for initializations common for the test cases in the
@@ -522,6 +535,14 @@
<c>end_per_group(GroupName, Config</c> function is called. This function
is meant to be used for cleaning up after <c>init_per_group/2</c>.</p>
+ <p>Whenever a group is executed, if <c>init_per_group</c> and
+ <c>end_per_group</c> do not exist in the suite, Common Test calls
+ dummy functions (with the same names) instead. Output generated by
+ hook functions will be saved to the log files for these dummies
+ (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test
+ Hooks</seealso> chapter for more information).
+ </p>
+
<note><p><c>init_per_testcase/2</c> and <c>end_per_testcase/2</c>
are always called for each individual test case, no matter if the case
belongs to a group or not.</p></note>
@@ -611,6 +632,25 @@
</section>
<section>
+ <title>Parallel test cases and IO</title>
+ <p>A parallel test case has a private IO server as its group leader.
+ (Please see the Erlang Run-Time System Application documentation for
+ a description of the group leader concept). The
+ central IO server process that handles the output from regular test
+ cases and configuration functions, does not respond to IO messages
+ during execution of parallel groups. This is important to understand
+ in order to avoid certain traps, like this one:</p>
+ <p>If a process, <c>P</c>, is spawned during execution of e.g.
+ <c>init_per_suite/1</c>, it will inherit the group leader of the
+ <c>init_per_suite</c> process. This group leader is the central IO server
+ process mentioned above. If, at a later time, <em>during parallel test case
+ execution</em>, some event triggers process <c>P</c> to call
+ <c>io:format/1/2</c>, that call will never return (since the group leader
+ is in a non-responsive state) and cause <c>P</c> to hang.
+ </p>
+ </section>
+
+ <section>
<title>Repeated groups</title>
<marker id="repeated_groups"></marker>
<p>A test case group may be repeated a certain number of times
@@ -745,7 +785,7 @@
<marker id="data_priv_dir"></marker>
<title>Data and Private Directories</title>
- <p>The data directory (<c>data_dir</c>) is the directory where the
+ <p>The data directory, <c>data_dir</c>, is the directory where the
test module has its own files needed for the testing. The name
of the <c>data_dir</c> is the the name of the test suite followed
by <c>"_data"</c>. For example,
@@ -769,12 +809,39 @@
</p>
-->
<p>
- The <c>priv_dir</c> is the test suite's private directory. This
- directory should be used when a test case needs to write to
- files. The name of the private directory is generated by the test
- server, which also creates the directory.
+ <c>priv_dir</c> is the private directory for the test cases.
+ This directory may be used whenever a test case (or configuration function)
+ needs to write something to file. The name of the private directory is
+ generated by Common Test, which also creates the directory.
</p>
-
+ <p>By default, Common Test creates one central private directory
+ per test run that all test cases share. This may not always be suitable,
+ especially if the same test cases are executed multiple times during
+ a test run (e.g. if they belong to a test case group with repeat
+ property), and there's a risk that files in the private directory get
+ overwritten. Under these circumstances, it's possible to configure
+ Common Test to create one dedicated private directory per
+ test case and execution instead. This is accomplished by means of
+ the flag/option: <c>create_priv_dir</c> (to be used with the
+ <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or
+ as test specification term). There are three possible values
+ for this option:
+ <list>
+ <item><c>auto_per_run</c></item>
+ <item><c>auto_per_tc</c></item>
+ <item><c>manual_per_tc</c></item>
+ </list>
+ The first value indicates the default priv_dir behaviour, i.e.
+ one private directory created per test run. The two latter
+ values tell Common Test to generate a unique test directory name
+ per test case and execution. If the auto version is used, <em>all</em>
+ private directories will be created automatically. This can obviously
+ become very inefficient for test runs with many test cases and/or
+ repetitions. Therefore, in case the manual version is instead used, the
+ test case must tell Common Test to create priv_dir when it needs it.
+ It does this by calling the function <c>ct:make_priv_dir/0</c>.
+ </p>
+
<note><p>You should not depend on current working directory for
reading and writing data files since this is not portable. All
scratch files are to be written in the <c>priv_dir</c> and all
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 125aa828fb..e9555de35a 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -69,7 +69,8 @@ MODULES= \
ct_slave \
ct_hooks\
ct_hooks_lock\
- cth_log_redirect
+ cth_log_redirect\
+ cth_surefire
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 7fba484b18..bdd48fbc6b 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -25,6 +25,8 @@
ct_framework,
ct_ftp,
ct_gen_conn,
+ ct_hooks,
+ ct_hooks_lock,
ct_logs,
ct_make,
ct_master,
@@ -45,7 +47,9 @@
ct_config,
ct_config_plain,
ct_config_xml,
- ct_slave
+ ct_slave,
+ cth_log_redirect,
+ cth_surefire
]},
{registered, [ct_logs,
ct_util_server,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 0a77527b2f..3c6e68101d 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -64,7 +64,7 @@
print/1, print/2, print/3,
pal/1, pal/2, pal/3,
capture_start/0, capture_stop/0, capture_get/0, capture_get/1,
- fail/1, fail/2, comment/1, comment/2,
+ fail/1, fail/2, comment/1, comment/2, make_priv_dir/0,
testcases/2, userdata/2, userdata/3,
timetrap/1, get_timetrap_info/0, sleep/1]).
@@ -146,7 +146,8 @@ run(TestDirs) ->
%%% {silent_connections,Conns} | {stylesheet,CSSFile} |
%%% {cover,CoverSpecFile} | {step,StepOpts} |
%%% {event_handler,EventHandlers} | {include,InclDirs} |
-%%% {auto_compile,Bool} | {multiply_timetraps,M} | {scale_timetraps,Bool} |
+%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} |
+%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
%%% {refresh_logs,LogDir} | {logopts,LogOpts} | {basic_html,Bool} |
@@ -171,6 +172,7 @@ run(TestDirs) ->
%%% EH = atom() | {atom(),InitArgs} | {[atom()],InitArgs}
%%% InitArgs = [term()]
%%% InclDirs = [string()] | string()
+%%% CreatePrivDir = auto_per_run | auto_per_tc | manual_per_tc
%%% M = integer()
%%% N = integer()
%%% DurTime = string(HHMMSS)
@@ -673,6 +675,15 @@ send_html_comment(Comment) ->
ct_util:set_testdata({comment,Html}),
test_server:comment(Html).
+%%%-----------------------------------------------------------------
+%%% @spec make_priv_dir() -> ok | {error,Reason}
+%%% Reason = term()
+%%% @doc If the test has been started with the create_priv_dir
+%%% option set to manual_per_tc, in order for the test case to use
+%%% the private directory, it must first create it by calling
+%%% this function.
+make_priv_dir() ->
+ test_server:make_priv_dir().
%%%-----------------------------------------------------------------
%%% @spec get_target_name(Handle) -> {ok,TargetName} | {error,Reason}
@@ -984,13 +995,21 @@ remove_config(Callback, Config) ->
%%%-----------------------------------------------------------------
%%% @spec timetrap(Time) -> ok
-%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity
+%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity | Func
%%% Hours = integer()
%%% Mins = integer()
%%% Secs = integer()
%%% Millisecs = integer() | float()
-%%%
-%%% @doc <p>Use this function to set a new timetrap for the running test case.</p>
+%%% Func = {M,F,A} | fun()
+%%% M = atom()
+%%% F = atom()
+%%% A = list()
+%%%
+%%% @doc <p>Use this function to set a new timetrap for the running test case.
+%%% If the argument is <code>Func</code>, the timetrap will be triggered
+%%% when this function returns. <code>Func</code> may also return a new
+%%% <code>Time</code> value, which in that case will be the value for the
+%%% new timetrap.</p>
timetrap(Time) ->
test_server:timetrap_cancel(),
test_server:timetrap(Time).
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index cdd8a6a596..e4fbf95fdd 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -29,7 +29,8 @@
-export([get_logopts/0, format_comment/1, get_html_wrapper/3]).
--export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]).
+-export([error_in_suite/1, init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2]).
-export([make_all_conf/3, make_conf/5]).
@@ -54,6 +55,7 @@
init_tc(Mod,Func,Config) ->
%% in case Mod == ct_framework, lookup the suite name
Suite = get_suite_name(Mod, Config),
+
%% check if previous testcase was interpreted and has left
%% a "dead" trace window behind - if so, kill it
case ct_util:get_testdata(interpret) of
@@ -63,27 +65,17 @@ init_tc(Mod,Func,Config) ->
_ ->
ok
end,
- %% check if we need to add defaults explicitly because
- %% there's no init_per_suite exported from Mod
- {InitFailed,DoInit} =
- case ct_util:get_testdata(curr_tc) of
- {Suite,{suite0_failed,_}=Failure} ->
- {Failure,false};
- {?MODULE,_} -> % should not really happen
- {false,false};
- {Suite,_} -> % Func is not 1st case in suite
- {false,false};
- _ when Func == init_per_suite -> % defaults will be added anyway
- {false,false};
- _ -> % first case in suite
- {false,true}
- end,
- case InitFailed of
- false ->
+
+ case ct_util:get_testdata(curr_tc) of
+ {Suite,{suite0_failed,{require,Reason}}} ->
+ {skip,{require_failed_in_suite0,Reason}};
+ {Suite,{suite0_failed,_}=Failure} ->
+ {skip,Failure};
+ _ ->
ct_util:set_testdata({curr_tc,{Suite,Func}}),
case ct_util:read_suite_data({seq,Suite,Func}) of
undefined ->
- init_tc1(Mod,Func,Config,DoInit);
+ init_tc1(Mod,Suite,Func,Config);
Seq when is_atom(Seq) ->
case ct_util:read_suite_data({seq,Suite,Seq}) of
[Func|TCs] -> % this is the 1st case in Seq
@@ -96,17 +88,13 @@ init_tc(Mod,Func,Config) ->
_ ->
ok
end,
- init_tc1(Mod,Func,Config,DoInit);
+ init_tc1(Mod,Suite,Func,Config);
{failed,Seq,BadFunc} ->
{skip,{sequence_failed,Seq,BadFunc}}
- end;
- {_,{require,Reason}} ->
- {skip,{require_failed_in_suite0,Reason}};
- _ ->
- {skip,InitFailed}
+ end
end.
-init_tc1(?MODULE,error_in_suite,[Config0],_) when is_list(Config0) ->
+init_tc1(?MODULE,_,error_in_suite,[Config0]) when is_list(Config0) ->
ct_logs:init_tc(false),
ct_event:notify(#event{name=tc_start,
node=node(),
@@ -117,14 +105,16 @@ init_tc1(?MODULE,error_in_suite,[Config0],_) when is_list(Config0) ->
Reason ->
{skip,Reason}
end;
-init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
+
+init_tc1(Mod,Suite,Func,[Config0]) when is_list(Config0) ->
Config1 =
case ct_util:read_suite_data(last_saved_config) of
- {{Mod,LastFunc},SavedConfig} -> % last testcase
+ {{Suite,LastFunc},SavedConfig} -> % last testcase
[{saved_config,{LastFunc,SavedConfig}} |
lists:keydelete(saved_config,1,Config0)];
- {{LastSuite,InitOrEnd},SavedConfig} when InitOrEnd == init_per_suite ;
- InitOrEnd == end_per_suite ->
+ {{LastSuite,InitOrEnd},
+ SavedConfig} when InitOrEnd == init_per_suite ;
+ InitOrEnd == end_per_suite ->
%% last suite
[{saved_config,{LastSuite,SavedConfig}} |
lists:keydelete(saved_config,1,Config0)];
@@ -133,13 +123,14 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
end,
ct_util:delete_suite_data(last_saved_config),
Config = lists:keydelete(watchdog,1,Config1),
- if Func /= init_per_suite, DoInit /= true ->
- ok;
- true ->
+
+ if Func == init_per_suite ->
%% delete all default values used in previous suite
ct_config:delete_default_config(suite),
%% release all name -> key bindings (once per suite)
- ct_config:release_allocated()
+ ct_config:release_allocated();
+ Func /= init_per_suite ->
+ ok
end,
GroupPath = ?val(tc_group_path, Config, []),
@@ -154,9 +145,7 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
true ->
ct_config:delete_default_config(testcase)
end,
- %% in case Mod == ct_framework, lookup the suite name
- Suite = get_suite_name(Mod, Config),
- case add_defaults(Mod,Func,AllGroups,DoInit) of
+ case add_defaults(Mod,Func,AllGroups) of
Error = {suite0_failed,_} ->
ct_logs:init_tc(false),
ct_event:notify(#event{name=tc_start,
@@ -166,35 +155,25 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
{error,Error};
{SuiteInfo,MergeResult} ->
case MergeResult of
- {error,Reason} when DoInit == false ->
+ {error,Reason} ->
ct_logs:init_tc(false),
ct_event:notify(#event{name=tc_start,
node=node(),
data={Mod,FuncSpec}}),
{skip,Reason};
_ ->
- init_tc2(Mod,Func,SuiteInfo,MergeResult,
- Config,DoInit)
+ init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config)
end
end;
-init_tc1(_Mod,_Func,Args,_DoInit) ->
- {ok,Args}.
-init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
- %% if first testcase fails when there's no init_per_suite
- %% we must do suite/0 configurations before skipping test
- MergedInfo =
- case MergeResult of
- {error,_} when DoInit == true ->
- SuiteInfo;
- _ ->
- MergeResult
- end,
+init_tc1(_Mod,_Suite,_Func,Args) ->
+ {ok,Args}.
+init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) ->
%% timetrap must be handled before require
- MergedInfo1 = timetrap_first(MergedInfo, [], []),
+ MergedInfo = timetrap_first(MergeResult, [], []),
%% tell logger to use specified style sheet
- case lists:keysearch(stylesheet,1,MergedInfo++Config) of
+ case lists:keysearch(stylesheet,1,MergeResult++Config) of
{value,{stylesheet,SSFile}} ->
ct_logs:set_stylesheet(Func,add_data_dir(SSFile,Config));
_ ->
@@ -209,7 +188,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
%% list of {Type,Bool} tuples, e.g. {telnet,true}),
case ct_util:get_overridden_silenced_connections() of
undefined ->
- case lists:keysearch(silent_connections,1,MergedInfo++Config) of
+ case lists:keysearch(silent_connections,1,MergeResult++Config) of
{value,{silent_connections,Conns}} ->
ct_util:silence_connections(Conns);
_ ->
@@ -218,18 +197,14 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
Conns ->
ct_util:silence_connections(Conns)
end,
- if Func /= init_per_suite, DoInit /= true ->
- ct_logs:init_tc(false);
- true ->
- ct_logs:init_tc(true)
- end,
+ ct_logs:init_tc(Func == init_per_suite),
FuncSpec = group_or_func(Func,Config),
ct_event:notify(#event{name=tc_start,
node=node(),
data={Mod,FuncSpec}}),
- case catch configure(MergedInfo1,MergedInfo1,SuiteInfo,
- {FuncSpec,DoInit},Config) of
+ case catch configure(MergedInfo,MergedInfo,SuiteInfo,
+ FuncSpec,Config) of
{suite0_failed,Reason} ->
ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}),
{skip,{require_failed_in_suite0,Reason}};
@@ -237,42 +212,35 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
{auto_skip,{require_failed,Reason}};
{'EXIT',Reason} ->
{auto_skip,Reason};
- {ok,FinalConfig} ->
- case MergeResult of
- {error,Reason} ->
- %% suite0 configure finished now, report that
- %% first test case actually failed
- {skip,Reason};
- _ ->
- case get('$test_server_framework_test') of
- undefined ->
- ct_suite_init(Mod, FuncSpec, FinalConfig);
- Fun ->
- case Fun(init_tc, FinalConfig) of
- NewConfig when is_list(NewConfig) ->
- {ok,NewConfig};
- Else ->
- Else
- end
+ {ok,Config1} ->
+ case get('$test_server_framework_test') of
+ undefined ->
+ ct_suite_init(Suite, FuncSpec, Config1);
+ Fun ->
+ case Fun(init_tc, Config1) of
+ NewConfig when is_list(NewConfig) ->
+ {ok,NewConfig};
+ Else ->
+ Else
end
end
end.
-ct_suite_init(Mod, Func, [Config]) when is_list(Config) ->
- case ct_hooks:init_tc( Mod, Func, Config) of
+ct_suite_init(Suite, Func, [Config]) when is_list(Config) ->
+ case ct_hooks:init_tc(Suite, Func, Config) of
NewConfig when is_list(NewConfig) ->
{ok, [NewConfig]};
Else ->
Else
end.
-add_defaults(Mod,Func, GroupPath, DoInit) ->
+add_defaults(Mod,Func, GroupPath) ->
Suite = get_suite_name(Mod, GroupPath),
case (catch Suite:suite()) of
{'EXIT',{undef,_}} ->
SuiteInfo = merge_with_suite_defaults(Suite,[]),
SuiteInfoNoCTH = [I || I <- SuiteInfo, element(1,I) =/= ct_hooks],
- case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH, DoInit) of
+ case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH) of
Error = {error,_} -> {SuiteInfo,Error};
MergedInfo -> {SuiteInfo,MergedInfo}
end;
@@ -292,7 +260,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) ->
SuiteInfoNoCTH = [I || I <- SuiteInfo1,
element(1,I) =/= ct_hooks],
case add_defaults1(Mod,Func, GroupPath,
- SuiteInfoNoCTH, DoInit) of
+ SuiteInfoNoCTH) of
Error = {error,_} -> {SuiteInfo1,Error};
MergedInfo -> {SuiteInfo1,MergedInfo}
end;
@@ -313,7 +281,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) ->
{suite0_failed,bad_return_value}
end.
-add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) ->
+add_defaults1(Mod,Func, GroupPath, SuiteInfo) ->
Suite = get_suite_name(Mod, GroupPath),
%% GroupPathInfo (for subgroup on level X) =
%% [LevelXGroupInfo, LevelX-1GroupInfo, ..., TopLevelGroupInfo]
@@ -325,8 +293,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) ->
_ -> []
end
end, GroupPath),
- Args = if Func == init_per_group; Func == ct_init_per_group;
- Func == end_per_group; Func == ct_end_per_group ->
+ Args = if Func == init_per_group ; Func == end_per_group ->
[?val(name, hd(GroupPath))];
true ->
[]
@@ -347,7 +314,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) ->
(default_config == element(1,SDDef)))],
case check_for_clashes(TestCaseInfo, GroupPathInfo, SuiteReqs) of
[] ->
- add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs, DoInit);
+ add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs);
Clashes ->
{error,{config_name_already_in_use,Clashes}}
end.
@@ -372,16 +339,12 @@ get_suite_name(Mod, _) ->
Mod.
%% Check that alias names are not already in use
-check_for_clashes(TCInfo, GrPathInfo, SuiteInfo) ->
- {CurrGrInfo,SearchIn} = case GrPathInfo of
- [] -> {[],[SuiteInfo]};
- [Curr|Path] -> {Curr,[SuiteInfo|Path]}
- end,
+check_for_clashes(TCInfo, [CurrGrInfo|Path], SuiteInfo) ->
ReqNames = fun(Info) -> [element(2,R) || R <- Info,
size(R) == 3,
require == element(1,R)]
end,
- ExistingNames = lists:flatten([ReqNames(L) || L <- SearchIn]),
+ ExistingNames = lists:flatten([ReqNames(L) || L <- [SuiteInfo|Path]]),
CurrGrReqNs = ReqNames(CurrGrInfo),
GrClashes = [Name || Name <- CurrGrReqNs,
true == lists:member(Name, ExistingNames)],
@@ -421,34 +384,25 @@ keysmember([Key,Pos|Next], List) ->
keysmember([], _) -> true.
-add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, false) ->
- add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, true);
+add_defaults2(_Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs) ->
+ Info = lists:flatten([IPSInfo, SuiteReqs]),
+ lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]);
-add_defaults2(_Mod,IPG, IPGAndGroupInfo, SuiteInfo,SuiteReqs, DoInit) when
- IPG == init_per_group ; IPG == ct_init_per_group ->
- %% If DoInit == true, we have to process the suite() list, otherwise
- %% it has already been handled (see clause for init_per_suite)
- case DoInit of
- true ->
- %% note: we know for sure this is a top level group
- Info = lists:flatten([IPGAndGroupInfo, SuiteReqs]),
- Info ++ remove_info_in_prev(Info, [SuiteInfo]);
- false ->
- SuiteInfo1 =
- remove_info_in_prev(lists:flatten([IPGAndGroupInfo,
- SuiteReqs]), [SuiteInfo]),
- %% don't require terms in prev groups (already processed)
- case IPGAndGroupInfo of
- [IPGInfo] ->
- lists:flatten([IPGInfo,SuiteInfo1]);
- [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] ->
- PrevGroupInfo1 = delete_require_terms(PrevGroupInfo),
- lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1,
- SuiteInfo1])
- end
+add_defaults2(_Mod,init_per_group, IPGAndGroupInfo, SuiteInfo,SuiteReqs) ->
+ SuiteInfo1 =
+ remove_info_in_prev(lists:flatten([IPGAndGroupInfo,
+ SuiteReqs]), [SuiteInfo]),
+ %% don't require terms in prev groups (already processed)
+ case IPGAndGroupInfo of
+ [IPGInfo] ->
+ lists:flatten([IPGInfo,SuiteInfo1]);
+ [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] ->
+ PrevGroupInfo1 = delete_require_terms(PrevGroupInfo),
+ lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1,
+ SuiteInfo1])
end;
-add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) ->
+add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs) ->
%% Include require elements from test case info and current group,
%% but not from previous groups or suite/0 (since we've already required
%% those vars). Let test case info elements override group and suite
@@ -463,14 +417,7 @@ add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) ->
PrevGroupInfo1 = delete_require_terms(PrevGroupInfo),
lists:flatten([TCInfo,CurrGroupInfo,PrevGroupInfo1,
SuiteInfo1])
- end;
-
-add_defaults2(_Mod,_Func, TCInfo, SuiteInfo,SuiteReqs, true) ->
- %% Here we have to process the suite info list also (no call to
- %% init_per_suite before this first test case). This TC can't belong
- %% to a group, or the clause for (ct_)init_per_group would've caught this.
- Info = lists:flatten([TCInfo, SuiteReqs]),
- lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]).
+ end.
delete_require_terms([Info | Prev]) ->
Info1 = [T || T <- Info,
@@ -558,18 +505,9 @@ configure([],_,_,_,Config) ->
%% function and be scoped 'group', or come from the testcase
%% info function and then be scoped 'testcase'
-required_default(Name,Key,Info,SuiteInfo,{FuncSpec,true}) ->
- case try_set_default(Name,Key,SuiteInfo,suite) of
- ok ->
- ok;
- _ ->
- required_default(Name,Key,Info,[],{FuncSpec,false})
- end;
-required_default(Name,Key,Info,_,{init_per_suite,_}) ->
+required_default(Name,Key,Info,_,init_per_suite) ->
try_set_default(Name,Key,Info,suite);
-required_default(Name,Key,Info,_,{{init_per_group,GrName,_},_}) ->
- try_set_default(Name,Key,Info,{group,GrName});
-required_default(Name,Key,Info,_,{{ct_init_per_group,GrName,_},_}) ->
+required_default(Name,Key,Info,_,{init_per_group,GrName,_}) ->
try_set_default(Name,Key,Info,{group,GrName});
required_default(Name,Key,Info,_,_FuncSpec) ->
try_set_default(Name,Key,Info,testcase).
@@ -621,6 +559,9 @@ end_tc(Mod,Func,{Result,[Args]}, Return) ->
end_tc(Mod,Func,self(),Result,Args,Return).
end_tc(Mod,Func,TCPid,Result,Args,Return) ->
+ %% in case Mod == ct_framework, lookup the suite name
+ Suite = get_suite_name(Mod, Args),
+
case lists:keysearch(watchdog,1,Args) of
{value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog);
false -> ok
@@ -636,60 +577,62 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
end,
ct_util:delete_testdata(comment),
ct_util:delete_suite_data(last_saved_config),
- FuncSpec =
- case group_or_func(Func,Args) of
- {_,GroupName,_Props} = Group ->
- if Func == end_per_group; Func == ct_end_per_group ->
- ct_config:delete_default_config({group,GroupName});
- true -> ok
- end,
- case lists:keysearch(save_config,1,Args) of
- {value,{save_config,SaveConfig}} ->
- ct_util:save_suite_data(
- last_saved_config,
- {Mod,{group,GroupName}},
- SaveConfig),
- Group;
- false ->
- Group
- end;
- _ ->
- case lists:keysearch(save_config,1,Args) of
- {value,{save_config,SaveConfig}} ->
- ct_util:save_suite_data(last_saved_config,
- {Mod,Func},SaveConfig),
- Func;
- false ->
- Func
- end
- end,
- ct_util:reset_silent_connections(),
+
+ FuncSpec = case group_or_func(Func,Args) of
+ {_,_GroupName,_} = Group -> Group;
+ _ -> Func
+ end,
case get('$test_server_framework_test') of
undefined ->
{FinalResult,FinalNotify} =
case ct_hooks:end_tc(
- Mod, FuncSpec, Args, Result, Return) of
+ Suite, FuncSpec, Args, Result, Return) of
'$ct_no_change' ->
{ok,Result};
FinalResult1 ->
{FinalResult1,FinalResult1}
end,
- % send sync notification so that event handlers may print
- % in the log file before it gets closed
+ %% send sync notification so that event handlers may print
+ %% in the log file before it gets closed
ct_event:sync_notify(#event{name=tc_done,
node=node(),
data={Mod,FuncSpec,
tag_cth(FinalNotify)}});
Fun ->
- % send sync notification so that event handlers may print
- % in the log file before it gets closed
+ %% send sync notification so that event handlers may print
+ %% in the log file before it gets closed
ct_event:sync_notify(#event{name=tc_done,
node=node(),
data={Mod,FuncSpec,tag(Result)}}),
FinalResult = Fun(end_tc, Return)
+ end,
+
+ case FuncSpec of
+ {_,GroupName,_Props} ->
+ if Func == end_per_group ->
+ ct_config:delete_default_config({group,GroupName});
+ true -> ok
+ end,
+ case lists:keysearch(save_config,1,Args) of
+ {value,{save_config,SaveConfig}} ->
+ ct_util:save_suite_data(last_saved_config,
+ {Suite,{group,GroupName}},
+ SaveConfig);
+ false ->
+ ok
+ end;
+ _ ->
+ case lists:keysearch(save_config,1,Args) of
+ {value,{save_config,SaveConfig}} ->
+ ct_util:save_suite_data(last_saved_config,
+ {Suite,Func},SaveConfig);
+ false ->
+ ok
+ end
end,
+ ct_util:reset_silent_connections(),
case FinalResult of
{skip,{sequence_failed,_,_}} ->
@@ -706,7 +649,7 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) ->
end,
case Func of
end_per_suite ->
- ct_util:match_delete_suite_data({seq,Mod,'_'});
+ ct_util:match_delete_suite_data({seq,Suite,'_'});
_ ->
ok
end,
@@ -864,9 +807,7 @@ mark_as_failed1(_,_,_,[]) ->
ok.
group_or_func(Func, Config) when Func == init_per_group;
- Func == end_per_group;
- Func == ct_init_per_group;
- Func == ct_end_per_group ->
+ Func == end_per_group ->
case ?val(tc_group_properties, Config) of
undefined ->
{Func,unknown,[]};
@@ -1209,8 +1150,8 @@ make_conf(Mod, Name, Props, TestSpec) ->
"end_per_group/2 missing for group "
"~p in ~p, using default.",
[Name,Mod]),
- {{?MODULE,ct_init_per_group},
- {?MODULE,ct_end_per_group},
+ {{?MODULE,init_per_group},
+ {?MODULE,end_per_group},
[{suite,Mod}]}
end,
{conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}.
@@ -1470,22 +1411,31 @@ error_in_suite(Config) ->
Reason = test_server:lookup_config(error,Config),
exit(Reason).
+%% if init_per_suite and end_per_suite are missing in the suite,
+%% these will be called instead (without any trace of them in the
+%% log files), only so that it's possible to call hook functions
+%% for configuration
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
%% if the group config functions are missing in the suite,
%% use these instead
-ct_init_per_group(GroupName, Config) ->
+init_per_group(GroupName, Config) ->
ct:comment(io_lib:format("start of ~p", [GroupName])),
ct_logs:log("TEST INFO", "init_per_group/2 for ~w missing "
"in suite, using default.",
[GroupName]),
Config.
-ct_end_per_group(GroupName, _) ->
+end_per_group(GroupName, _) ->
ct:comment(io_lib:format("end of ~p", [GroupName])),
ct_logs:log("TEST INFO", "end_per_group/2 for ~w missing "
"in suite, using default.",
[GroupName]),
ok.
-
%%%-----------------------------------------------------------------
%%% @spec report(What,Data) -> ok
@@ -1562,10 +1512,6 @@ report(What,Data) ->
ok;
{end_per_group,_} ->
ok;
- {ct_init_per_group,_} ->
- ok;
- {ct_end_per_group,_} ->
- ok;
{_,ok} ->
add_to_stats(ok);
{_,{skipped,{failed,{_,init_per_testcase,_}}}} ->
@@ -1602,8 +1548,7 @@ report(What,Data) ->
data=Data}),
ct_hooks:on_tc_skip(What, Data),
if Case /= end_per_suite,
- Case /= end_per_group,
- Case /= ct_end_per_group ->
+ Case /= end_per_group ->
add_to_stats(auto_skipped);
true ->
ok
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index c42adbbdd9..2a23cd992b 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -70,8 +70,7 @@ terminate(Hooks) ->
{skip, Reason :: term()} |
{auto_skip, Reason :: term()} |
{fail, Reason :: term()}.
-init_tc(ct_framework, _Func, Args) ->
- Args;
+
init_tc(Mod, init_per_suite, Config) ->
Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of
List when is_list(List) ->
@@ -104,27 +103,21 @@ init_tc(_Mod, TC, Config) ->
{auto_skip, Reason :: term()} |
{fail, Reason :: term()} |
ok | '$ct_no_change'.
-end_tc(ct_framework, _Func, _Args, Result, _Return) ->
- Result;
end_tc(Mod, init_per_suite, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config],
'$ct_no_change');
-
end_tc(Mod, end_per_suite, Config, Result, _Return) ->
call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config],
'$ct_no_change');
-
end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config],
'$ct_no_change');
-
end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) ->
Res = call(fun call_generic/3, Result,
[post_end_per_group, GroupName, Config], '$ct_no_change'),
maybe_stop_locker(Mod, GroupName,Opts),
Res;
-
end_tc(_Mod, TC, Config, Result, _Return) ->
call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
'$ct_no_change').
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index b2f669fefe..012f947fdd 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -36,6 +36,7 @@
-export([make_last_run_index/0]).
-export([make_all_suites_index/1,make_all_runs_index/1]).
-export([get_ts_html_wrapper/3]).
+-export([xhtml/2, locate_default_css_file/0, make_relative/1]).
%% Logging stuff directly from testcase
-export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3,
@@ -920,33 +921,48 @@ insert_dir(D,[D1|Ds]) ->
insert_dir(D,[]) ->
[D].
-make_last_run_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, Missing) ->
- case last_test(Name) of
+make_last_run_index([Name|Rest], Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Missing) ->
+ case get_run_dirs(Name) of
false ->
%% Silently skip.
- make_last_run_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, Missing);
- LastLogDir ->
+ make_last_run_index(Rest, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Missing);
+ LogDirs ->
SuiteName = filename:rootname(filename:basename(Name)),
- case make_one_index_entry(SuiteName, LastLogDir, "-", false, Missing) of
- {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
- %% for backwards compatibility
- AutoSkip1 = case catch AutoSkip+ASkip of
- {'EXIT',_} -> undefined;
- Res -> Res
- end,
- make_last_run_index(Rest, [Result|Result1], TotSucc+Succ,
- TotFail+Fail, UserSkip+USkip, AutoSkip1,
- TotNotBuilt+NotBuilt, Missing);
- error ->
- make_last_run_index(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, Missing)
- end
+ {Result1,TotSucc1,TotFail1,UserSkip1,AutoSkip1,TotNotBuilt1} =
+ make_last_run_index1(SuiteName, LogDirs, Result,
+ TotSucc, TotFail,
+ UserSkip, AutoSkip,
+ TotNotBuilt, Missing),
+ make_last_run_index(Rest, Result1, TotSucc1, TotFail1,
+ UserSkip1, AutoSkip1,
+ TotNotBuilt1, Missing)
end;
+
make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, _) ->
{ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)],
{TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}.
+
+make_last_run_index1(SuiteName, [LogDir | LogDirs], Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Missing) ->
+ case make_one_index_entry(SuiteName, LogDir, "-", false, Missing) of
+ {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
+ %% for backwards compatibility
+ AutoSkip1 = case catch AutoSkip+ASkip of
+ {'EXIT',_} -> undefined;
+ Res -> Res
+ end,
+ make_last_run_index1(SuiteName, LogDirs, [Result|Result1], TotSucc+Succ,
+ TotFail+Fail, UserSkip+USkip, AutoSkip1,
+ TotNotBuilt+NotBuilt, Missing);
+ error ->
+ make_last_run_index1(SuiteName, LogDirs, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Missing)
+ end;
+make_last_run_index1(_, [], Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, _) ->
+ {Result,TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}.
make_one_index_entry(SuiteName, LogDir, Label, All, Missing) ->
case count_cases(LogDir) of
@@ -1231,18 +1247,18 @@ header1(Title, SubTitle) ->
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n",
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]),
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n",
- "<head>\n",
- "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n",
- "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
- xhtml("",
- ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
- "</head>\n",
- body_tag(),
- "<center>\n",
- "<h1>" ++ Title ++ "</h1>\n",
- "</center>\n",
- SubTitleHTML,"\n"].
+ "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n",
+ "<head>\n",
+ "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n",
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ xhtml("",
+ ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ "</head>\n",
+ body_tag(),
+ "<center>\n",
+ "<h1>" ++ Title ++ "</h1>\n",
+ "</center>\n",
+ SubTitleHTML,"\n"].
index_footer() ->
["</table>\n"
@@ -1698,8 +1714,8 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) ->
sort_logdirs([Dir|Dirs],Groups) ->
TestName = filename:rootname(filename:basename(Dir)),
case filelib:wildcard(filename:join(Dir,"run.*")) of
- [RunDir] ->
- Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups),
+ RunDirs = [_|_] ->
+ Groups1 = sort_logdirs1(TestName,RunDirs,Groups),
sort_logdirs(Dirs,Groups1);
_ -> % ignore missing run directory
sort_logdirs(Dirs,Groups)
@@ -1707,6 +1723,12 @@ sort_logdirs([Dir|Dirs],Groups) ->
sort_logdirs([],Groups) ->
lists:keysort(1,sort_each_group(Groups)).
+sort_logdirs1(TestName,[RunDir|RunDirs],Groups) ->
+ Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups),
+ sort_logdirs1(TestName,RunDirs,Groups1);
+sort_logdirs1(_,[],Groups) ->
+ Groups.
+
insert_test(Test,IxDir,[{Test,IxDirs}|Groups]) ->
[{Test,[IxDir|IxDirs]}|Groups];
insert_test(Test,IxDir,[]) ->
@@ -1998,21 +2020,17 @@ notify_and_unlock_file(File) ->
end.
%%%-----------------------------------------------------------------
-%%% @spec last_test(Dir) -> string() | false
+%%% @spec get_run_dirs(Dir) -> [string()] | false
%%%
%%% @doc
%%%
-last_test(Dir) ->
- last_test(filelib:wildcard(filename:join(Dir, "run.[1-2]*")), false).
-
-last_test([Run|Rest], false) ->
- last_test(Rest, Run);
-last_test([Run|Rest], Latest) when Run > Latest ->
- last_test(Rest, Run);
-last_test([_|Rest], Latest) ->
- last_test(Rest, Latest);
-last_test([], Latest) ->
- Latest.
+get_run_dirs(Dir) ->
+ case filelib:wildcard(filename:join(Dir, "run.[1-2]*")) of
+ [] ->
+ false;
+ RunDirs ->
+ lists:sort(RunDirs)
+ end.
%%%-----------------------------------------------------------------
%%% @spec xhtml(HTML, XHTML) -> HTML | XHTML
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 2ea2ba106a..0d32bb0072 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -25,6 +25,7 @@
-export([run/1,run/3,run/4]).
-export([run_on_node/2,run_on_node/3]).
-export([run_test/1,run_test/2]).
+-export([basic_html/1]).
-export([abort/0,abort/1,progress/0]).
@@ -277,7 +278,17 @@ abort(Node) when is_atom(Node) ->
progress() ->
call(progress).
-
+%%%-----------------------------------------------------------------
+%%% @spec basic_html(Bool) -> ok
+%%% Bool = true | false
+%%%
+%%% @doc If set to true, the ct_master logs will be written on a
+%%% primitive html format, not using the Common Test CSS style
+%%% sheet.
+basic_html(Bool) ->
+ application:set_env(common_test_master, basic_html, Bool),
+ ok.
+
%%%-----------------------------------------------------------------
%%% MASTER, runs on central controlling node.
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 244faace06..8fd346670f 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -23,7 +23,8 @@
%%% node.</p>
-module(ct_master_logs).
--export([start/2, make_all_runs_index/0, log/3, nodedir/2, stop/0]).
+-export([start/2, make_all_runs_index/0, log/3, nodedir/2,
+ stop/0]).
-record(state, {log_fd, start_time, logdir, rundir,
nodedir_ix_fd, nodes, nodedirs=[]}).
@@ -32,6 +33,7 @@
-define(all_runs_name, "master_runs.html").
-define(nodedir_index_name, "index.html").
-define(details_file_name,"details.info").
+-define(css_default, "ct_default.css").
-define(table_color,"lightblue").
%%%--------------------------------------------------------------------
@@ -87,6 +89,40 @@ init(Parent,LogDir,Nodes) ->
RunDirAbs = filename:join(LogDir,RunDir),
file:make_dir(RunDirAbs),
write_details_file(RunDirAbs,{node(),Nodes}),
+
+ case basic_html() of
+ true ->
+ put(basic_html, true);
+ BasicHtml ->
+ put(basic_html, BasicHtml),
+ %% copy stylesheet to log dir (both top dir and test run
+ %% dir) so logs are independent of Common Test installation
+ CTPath = code:lib_dir(common_test),
+ CSSFileSrc = filename:join(filename:join(CTPath, "priv"),
+ ?css_default),
+ CSSFileDestTop = filename:join(LogDir, ?css_default),
+ CSSFileDestRun = filename:join(RunDirAbs, ?css_default),
+ case file:copy(CSSFileSrc, CSSFileDestTop) of
+ {error,Reason0} ->
+ io:format(user, "ERROR! "++
+ "CSS file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [CSSFileSrc,CSSFileDestTop,Reason0]),
+ exit({css_file_error,CSSFileDestTop});
+ _ ->
+ case file:copy(CSSFileSrc, CSSFileDestRun) of
+ {error,Reason1} ->
+ io:format(user, "ERROR! "++
+ "CSS file ~p could not be copied to ~p. "++
+ "Reason: ~p~n",
+ [CSSFileSrc,CSSFileDestRun,Reason1]),
+ exit({css_file_error,CSSFileDestRun});
+ _ ->
+ ok
+ end
+ end
+ end,
+
make_all_runs_index(LogDir),
CtLogFd = open_ct_master_log(RunDirAbs),
NodeStr =
@@ -164,8 +200,9 @@ open_ct_master_log(Dir) ->
"</style>\n",
[]),
io:format(Fd,
- "<br><h2>Progress Log</h2>\n"
- "<pre>\n",[]),
+ xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
+ "<br /><h2>Progress Log</h2>\n<pre>\n"),
+ []),
Fd.
close_ct_master_log(Fd) ->
@@ -178,18 +215,10 @@ config_table(Vars) ->
config_table_header() ->
["<h2>Configuration</h2>\n",
- "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color,
- "\"\n",
+ xhtml(["<table border=\"3\" cellpadding=\"5\" "
+ "bgcolor=\"",?table_color,"\"\n"], "<table>\n"),
"<tr><th>Key</th><th>Value</th></tr>\n"].
-%%
-%% keep for possible later use
-%%
-%%config_table1([{Key,Value}|Vars]) ->
-%% ["<tr><td>", atom_to_list(Key), "</td>\n",
-%% "<td><pre>",io_lib:format("~p",[Value]),"</pre></td></tr>\n" |
-%% config_table1(Vars)];
-
config_table1([]) ->
["</table>\n"].
@@ -210,10 +239,10 @@ open_nodedir_index(Dir,StartTime) ->
print_nodedir(Node,RunDir,Fd) ->
Index = filename:join(RunDir,"index.html"),
io:format(Fd,
- ["<TR>\n"
- "<TD ALIGN=center>",atom_to_list(Node),"</TD>\n",
- "<TD ALIGN=left><A HREF=\"",Index,"\">",Index,"</A></TD>\n",
- "</TR>\n"],[]),
+ ["<tr>\n"
+ "<td align=center>",atom_to_list(Node),"</td>\n",
+ "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n",
+ "</tr>\n"],[]),
ok.
close_nodedir_index(Fd) ->
@@ -222,12 +251,12 @@ close_nodedir_index(Fd) ->
nodedir_index_header(StartTime) ->
[header("Log Files " ++ format_time(StartTime)) |
- ["<CENTER>\n",
- "<P><A HREF=\"",?ct_master_log_name,"\">Common Test Master Log</A></P>",
- "<TABLE border=\"3\" cellpadding=\"5\" ",
- "BGCOLOR=\"",?table_color,"\">\n",
- "<th><B>Node</B></th>\n",
- "<th><B>Log</B></th>\n",
+ ["<center>\n",
+ "<p><a href=\"",?ct_master_log_name,"\">Common Test Master Log</a></p>",
+ xhtml(["<table border=\"3\" cellpadding=\"5\" "
+ "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "<th><b>Node</b></th>\n",
+ "<th><b>Log</b></th>\n",
"\n"]].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -279,20 +308,20 @@ runentry(Dir) ->
{"unknown",""}
end,
Index = filename:join(Dir,?nodedir_index_name),
- ["<TR>\n"
- "<TD ALIGN=center><A HREF=\"",Index,"\">",timestamp(Dir),"</A></TD>\n",
- "<TD ALIGN=center>",MasterStr,"</TD>\n",
- "<TD ALIGN=center>",NodesStr,"</TD>\n",
- "</TR>\n"].
+ ["<tr>\n"
+ "<td align=center><a href=\"",Index,"\">",timestamp(Dir),"</a></td>\n",
+ "<td align=center>",MasterStr,"</td>\n",
+ "<td align=center>",NodesStr,"</td>\n",
+ "</tr>\n"].
all_runs_header() ->
[header("Master Test Runs") |
- ["<CENTER>\n",
- "<TABLE border=\"3\" cellpadding=\"5\" "
- "BGCOLOR=\"",?table_color,"\">\n"
- "<th><B>History</B></th>\n"
- "<th><B>Master Host</B></th>\n"
- "<th><B>Test Nodes</B></th>\n"
+ ["<center>\n",
+ xhtml(["<table border=\"3\" cellpadding=\"5\" "
+ "bgcolor=\"",?table_color,"\">\n"], "<table>\n"),
+ "<th><b>History</b></th>\n"
+ "<th><b>Master Host</b></th>\n"
+ "<th><b>Test Nodes</b></th>\n"
"\n"]].
timestamp(Dir) ->
@@ -318,44 +347,46 @@ read_details_file(Dir) ->
%%%--------------------------------------------------------------------
header(Title) ->
- ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<HTML>\n",
- "<HEAD>\n",
-
- "<TITLE>" ++ Title ++ "</TITLE>\n",
- "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">\n",
-
- "</HEAD>\n",
-
+ CSSFile = xhtml(fun() -> "" end,
+ fun() -> make_relative(locate_default_css_file()) end),
+ [xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
+ "<html>\n"],
+ ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
+ "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n",
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]),
+ "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n",
+ "<head>\n",
+ "<title>" ++ Title ++ "</title>\n",
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ xhtml("",
+ ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]),
+ "</head>\n",
body_tag(),
-
- "<!-- ---- DOCUMENT TITLE ---- -->\n",
-
- "<CENTER>\n",
- "<H1>" ++ Title ++ "</H1>\n",
- "</CENTER>\n",
-
- "<!-- ---- CONTENT ---- -->\n"].
+ "<center>\n",
+ "<h1>" ++ Title ++ "</h1>\n",
+ "</center>\n"].
index_footer() ->
- ["</TABLE>\n"
- "</CENTER>\n" | footer()].
+ ["</table>\n"
+ "</center>\n" | footer()].
footer() ->
- ["<P><CENTER>\n"
- "<HR>\n"
- "<P><FONT SIZE=-1>\n"
+ ["<center>\n",
+ xhtml("<br><hr>\n", "<br />\n"),
+ xhtml("<p><font size=\"-1\">\n", "<div class=\"copyright\">"),
"Copyright &copy; ", year(),
- " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n"
- "Updated: <!date>", current_time(), "<!/date><BR>\n"
- "</FONT>\n"
- "</CENTER>\n"
+ " <a href=\"http://www.erlang.org\">Open Telecom Platform</a>",
+ xhtml("<br>\n", "<br />\n"),
+ "Updated: <!date>", current_time(), "<!/date>",
+ xhtml("<br>\n", "<br />\n"),
+ xhtml("</font></p>\n", "</div>\n"),
+ "</center>\n"
"</body>\n"].
body_tag() ->
- "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\""
- "vlink=\"#800080\" alink=\"#FF0000\">\n".
+ xhtml("<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" "
+ "vlink=\"#800080\" alink=\"#FF0000\">\n",
+ "<body>\n").
current_time() ->
format_time(calendar:local_time()).
@@ -404,6 +435,23 @@ log_timestamp(Now) ->
lists:flatten(io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w",
[H,M,S])).
+basic_html() ->
+ case application:get_env(common_test_master, basic_html) of
+ {ok,true} ->
+ true;
+ _ ->
+ false
+ end.
+
+xhtml(HTML, XHTML) ->
+ ct_logs:xhtml(HTML, XHTML).
+
+locate_default_css_file() ->
+ ct_logs:locate_default_css_file().
+
+make_relative(Dir) ->
+ ct_logs:make_relative(Dir).
+
force_write_file(Name,Contents) ->
force_delete(Name),
file:write_file(Name,Contents).
@@ -452,3 +500,4 @@ cast(Msg) ->
_Pid ->
?MODULE ! Msg
end.
+
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index be3c485b75..e6eb135ae8 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -116,7 +116,7 @@ spawn_tester(script,Ctrl,Args) ->
spawn_tester(func,Ctrl,Opts) ->
Tester = fun() ->
- case catch ct_run:run_test1(Opts) of
+ case catch ct_run:run_test2(Opts) of
{'EXIT',Reason} ->
exit(Reason);
Result ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 05b10bca32..717154667f 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -37,7 +37,7 @@
%% Misc internal functions
--export([variables_file_name/1,script_start1/2,run_test1/1]).
+-export([variables_file_name/1,script_start1/2,run_test2/1]).
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -57,12 +57,13 @@
config = [],
event_handlers = [],
ct_hooks = [],
- enable_builtin_hooks = true,
+ enable_builtin_hooks,
include = [],
silent_connections,
stylesheet,
multiply_timetraps = 1,
scale_timetraps = false,
+ create_priv_dir,
testspecs = [],
tests}).
@@ -178,12 +179,16 @@ script_start1(Parent, Args) ->
fun([CT]) -> list_to_atom(CT);
([]) -> true
end, false, Args),
+ CreatePrivDir = get_start_opt(create_priv_dir,
+ fun([PD]) -> list_to_atom(PD);
+ ([]) -> auto_per_tc
+ end, Args),
EvHandlers = event_handler_args2opts(Args),
CTHooks = ct_hooks_args2opts(Args),
EnableBuiltinHooks = get_start_opt(enable_builtin_hooks,
fun([CT]) -> list_to_atom(CT);
- ([]) -> true
- end, true, Args),
+ ([]) -> undefined
+ end, undefined, Args),
%% check flags and set corresponding application env variables
@@ -255,7 +260,8 @@ script_start1(Parent, Args) ->
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultTT,
- scale_timetraps = ScaleTT},
+ scale_timetraps = ScaleTT,
+ create_priv_dir = CreatePrivDir},
%% check if log files should be refreshed or go on to run tests...
Result = run_or_refresh(StartOpts, Args),
@@ -322,12 +328,21 @@ script_start2(StartOpts = #opts{vts = undefined,
Cover = choose_val(StartOpts#opts.cover,
SpecStartOpts#opts.cover),
- MultTT = choose_val(StartOpts#opts.multiply_timetraps,
- SpecStartOpts#opts.multiply_timetraps),
- ScaleTT = choose_val(StartOpts#opts.scale_timetraps,
- SpecStartOpts#opts.scale_timetraps),
- AllEvHs = merge_vals([StartOpts#opts.event_handlers,
- SpecStartOpts#opts.event_handlers]),
+ MultTT =
+ choose_val(StartOpts#opts.multiply_timetraps,
+ SpecStartOpts#opts.multiply_timetraps),
+ ScaleTT =
+ choose_val(StartOpts#opts.scale_timetraps,
+ SpecStartOpts#opts.scale_timetraps),
+
+ CreatePrivDir =
+ choose_val(StartOpts#opts.create_priv_dir,
+ SpecStartOpts#opts.create_priv_dir),
+
+ AllEvHs =
+ merge_vals([StartOpts#opts.event_handlers,
+ SpecStartOpts#opts.event_handlers]),
+
AllCTHooks = merge_vals(
[StartOpts#opts.ct_hooks,
SpecStartOpts#opts.ct_hooks]),
@@ -354,7 +369,8 @@ script_start2(StartOpts = #opts{vts = undefined,
EnableBuiltinHooks,
include = AllInclude,
multiply_timetraps = MultTT,
- scale_timetraps = ScaleTT}}
+ scale_timetraps = ScaleTT,
+ create_priv_dir = CreatePrivDir}}
end;
_ ->
{undefined,StartOpts}
@@ -567,6 +583,7 @@ script_usage() ->
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
+ "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t[-basic_html]\n\n"),
io:format("Run tests from command line:\n\n"
"\tct_run [-dir TestDir1 TestDir2 .. TestDirN] |"
@@ -586,6 +603,7 @@ script_usage() ->
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
+ "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t[-basic_html]"
"\n\t[-repeat N [-force_stop]] |"
"\n\t[-duration HHMMSS [-force_stop]] |"
@@ -606,6 +624,7 @@ script_usage() ->
"\n\t[-no_auto_compile]"
"\n\t[-multiply_timetraps N]"
"\n\t[-scale_timetraps]"
+ "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t[-basic_html]"
"\n\t[-repeat N [-force_stop]] |"
"\n\t[-duration HHMMSS [-force_stop]] |"
@@ -763,7 +782,7 @@ run_test2(StartOpts) ->
fun(EBH) when EBH == true;
EBH == false ->
EBH
- end, true, StartOpts),
+ end, undefined, StartOpts),
%% silent connections
SilentConns = get_start_opt(silent_connections,
@@ -782,6 +801,9 @@ run_test2(StartOpts) ->
MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts),
ScaleTT = get_start_opt(scale_timetraps, value, false, StartOpts),
+ %% create unique priv dir names
+ CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts),
+
%% auto compile & include files
Include =
case proplists:get_value(auto_compile, StartOpts) of
@@ -842,7 +864,8 @@ run_test2(StartOpts) ->
silent_connections = SilentConns,
stylesheet = Stylesheet,
multiply_timetraps = MultiplyTT,
- scale_timetraps = ScaleTT},
+ scale_timetraps = ScaleTT,
+ create_priv_dir = CreatePrivDir},
%% test specification
case proplists:get_value(spec, StartOpts) of
@@ -889,6 +912,8 @@ run_spec_file(Relaxed,
SpecOpts#opts.multiply_timetraps),
ScaleTT = choose_val(Opts#opts.scale_timetraps,
SpecOpts#opts.scale_timetraps),
+ CreatePrivDir = choose_val(Opts#opts.create_priv_dir,
+ SpecOpts#opts.create_priv_dir),
AllEvHs = merge_vals([Opts#opts.event_handlers,
SpecOpts#opts.event_handlers]),
AllInclude = merge_vals([Opts#opts.include,
@@ -912,6 +937,7 @@ run_spec_file(Relaxed,
testspecs = AbsSpecs,
multiply_timetraps = MultTT,
scale_timetraps = ScaleTT,
+ create_priv_dir = CreatePrivDir,
ct_hooks = AllCTHooks,
enable_builtin_hooks = EnableBuiltinHooks
},
@@ -1170,7 +1196,8 @@ get_data_for_node(#testspec{label = Labels,
enable_builtin_hooks = EnableBuiltinHooks,
include = Incl,
multiply_timetraps = MTs,
- scale_timetraps = STs}, Node) ->
+ scale_timetraps = STs,
+ create_priv_dir = PDs}, Node) ->
Label = proplists:get_value(Node, Labels),
Profile = proplists:get_value(Node, Profiles),
LogDir = case proplists:get_value(Node, LogDirs) of
@@ -1184,6 +1211,7 @@ get_data_for_node(#testspec{label = Labels,
Cover = proplists:get_value(Node, CoverFs),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
+ CreatePrivDir = proplists:get_value(Node, PDs),
ConfigFiles = [{?ct_config_txt,F} || {N,F} <- Cfgs, N==Node] ++
[CBF || {N,CBF} <- UsrCfgs, N==Node],
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
@@ -1200,7 +1228,8 @@ get_data_for_node(#testspec{label = Labels,
enable_builtin_hooks = EnableBuiltinHooks,
include = Include,
multiply_timetraps = MT,
- scale_timetraps = ST}.
+ scale_timetraps = ST,
+ create_priv_dir = CreatePrivDir}.
refresh_logs(LogDir) ->
{ok,Cwd} = file:get_cwd(),
@@ -1384,7 +1413,8 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
%% which framework it runs under.
case os:getenv("TEST_SERVER_FRAMEWORK") of
false ->
- os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework");
+ os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework"),
+ os:putenv("TEST_SERVER_FRAMEWORK_NAME", "common_test");
"ct_framework" ->
ok;
Other ->
@@ -1746,25 +1776,31 @@ set_group_leader_same_as_shell() ->
false
end.
-check_and_add([{TestDir0,M,_} | Tests], Added) ->
+check_and_add([{TestDir0,M,_} | Tests], Added, PA) ->
case locate_test_dir(TestDir0, M) of
{ok,TestDir} ->
case lists:member(TestDir, Added) of
true ->
- check_and_add(Tests, Added);
+ check_and_add(Tests, Added, PA);
false ->
- true = code:add_patha(TestDir),
- check_and_add(Tests, [TestDir|Added])
+ case lists:member(rm_trailing_slash(TestDir),
+ code:get_path()) of
+ false ->
+ true = code:add_patha(TestDir),
+ check_and_add(Tests, [TestDir|Added], [TestDir|PA]);
+ true ->
+ check_and_add(Tests, [TestDir|Added], PA)
+ end
end;
{error,_} ->
{error,{invalid_directory,TestDir0}}
end;
-check_and_add([], _) ->
- ok.
+check_and_add([], _, PA) ->
+ {ok,PA}.
do_run_test(Tests, Skip, Opts) ->
- case check_and_add(Tests, []) of
- ok ->
+ case check_and_add(Tests, [], []) of
+ {ok,AddedToPath} ->
ct_util:set_testdata({stats,{0,0,{0,0}}}),
ct_util:set_testdata({cover,undefined}),
test_server_ctrl:start_link(local),
@@ -1842,6 +1878,8 @@ do_run_test(Tests, Skip, Opts) ->
test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps),
test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps),
+ test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir,
+ auto_per_run)),
ct_event:notify(#event{name=start_info,
node=node(),
data={NoOfTests,NoOfSuites,NoOfCases}}),
@@ -1858,7 +1896,9 @@ do_run_test(Tests, Skip, Opts) ->
end,
lists:foreach(fun(Suite) ->
maybe_cleanup_interpret(Suite, Opts#opts.step)
- end, CleanUp);
+ end, CleanUp),
+ [code:del_path(Dir) || Dir <- AddedToPath],
+ ok;
Error ->
Error
end.
@@ -2289,7 +2329,7 @@ ct_hooks_args2opts(Args) ->
Acc
end,[],Args).
-ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) ->
+ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) when Arg /= "and" ->
ct_hooks_args2opts(Rest,[{list_to_atom(CTH),
parse_cth_args(Arg),
parse_cth_args(Prio)}|Acc]);
@@ -2347,31 +2387,38 @@ event_handler_init_args2opts([]) ->
%% relative dirs "post run_test erl_args" is not kept!
rel_to_abs(CtArgs) ->
{PA,PZ} = get_pa_pz(CtArgs, [], []),
- io:format(user, "~n", []),
[begin
- code:del_path(filename:basename(D)),
- Abs = filename:absname(D),
- code:add_pathz(Abs),
- if D /= Abs ->
+ Dir = rm_trailing_slash(D),
+ Abs = make_abs(Dir),
+ if Dir /= Abs ->
+ code:del_path(Dir),
+ code:del_path(Abs),
io:format(user, "Converting ~p to ~p and re-inserting "
"with add_pathz/1~n",
- [D, Abs]);
+ [Dir, Abs]);
true ->
- ok
- end
+ code:del_path(Dir)
+ end,
+ code:add_pathz(Abs)
end || D <- PZ],
[begin
- code:del_path(filename:basename(D)),
- Abs = filename:absname(D),
- code:add_patha(Abs),
- if D /= Abs ->
+ Dir = rm_trailing_slash(D),
+ Abs = make_abs(Dir),
+ if Dir /= Abs ->
+ code:del_path(Dir),
+ code:del_path(Abs),
io:format(user, "Converting ~p to ~p and re-inserting "
"with add_patha/1~n",
- [D, Abs]);
- true ->ok
- end
+ [Dir, Abs]);
+ true ->
+ code:del_path(Dir)
+ end,
+ code:add_patha(Abs)
end || D <- PA],
- io:format(user, "~n", []).
+ io:format(user, "~n", []).
+
+rm_trailing_slash(Dir) ->
+ filename:join(filename:split(Dir)).
get_pa_pz([{pa,Dirs} | Args], PA, PZ) ->
get_pa_pz(Args, PA ++ Dirs, PZ);
@@ -2382,6 +2429,19 @@ get_pa_pz([_ | Args], PA, PZ) ->
get_pa_pz([], PA, PZ) ->
{PA,PZ}.
+make_abs(RelDir) ->
+ Tokens = filename:split(filename:absname(RelDir)),
+ filename:join(lists:reverse(make_abs1(Tokens, []))).
+
+make_abs1([".."|Dirs], [_Dir|Path]) ->
+ make_abs1(Dirs, Path);
+make_abs1(["."|Dirs], Path) ->
+ make_abs1(Dirs, Path);
+make_abs1([Dir|Dirs], Path) ->
+ make_abs1(Dirs, [Dir|Path]);
+make_abs1([], Path) ->
+ Path.
+
%% This function translates ct:run_test/1 start options
%% to ct_run start arguments (on the init arguments format) -
%% this is useful mainly for testing the ct_run start functions.
@@ -2419,6 +2479,10 @@ opts2args(EnvStartOpts) ->
[{scale_timetraps,[]}];
({scale_timetraps,false}) ->
[];
+ ({create_priv_dir,auto_per_run}) ->
+ [];
+ ({create_priv_dir,PD}) when is_atom(PD) ->
+ [{create_priv_dir,[atom_to_list(PD)]}];
({force_stop,true}) ->
[{force_stop,[]}];
({force_stop,false}) ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index b68cbd3aa1..5b197c0c81 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -568,6 +568,21 @@ add_tests([{scale_timetraps,Node,ST}|Ts],Spec) ->
add_tests([{scale_timetraps,ST}|Ts],Spec) ->
add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec);
+%% --- create_priv_dir ---
+add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) ->
+ Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)),
+ add_tests(Tests++Ts,Spec);
+add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{create_priv_dir,Node,PD}|Ts],Spec) ->
+ PDs = Spec#testspec.create_priv_dir,
+ PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} |
+ lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)],
+ add_tests(Ts,Spec#testspec{create_priv_dir=PDs1});
+add_tests([{create_priv_dir,PD}|Ts],Spec) ->
+ add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec);
+
%% --- config ---
add_tests([{config,all_nodes,Files}|Ts],Spec) ->
Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)),
@@ -1158,7 +1173,8 @@ valid_terms() ->
{skip_groups,6},
{skip_groups,7},
{skip_cases,5},
- {skip_cases,6}
+ {skip_cases,6},
+ {create_priv_dir,2}
].
%% this function "guesses" if the user has misspelled a term name
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index bde832811a..082599a9c6 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -43,9 +43,10 @@
include=[],
multiply_timetraps=[],
scale_timetraps=[],
+ create_priv_dir=[],
alias=[],
tests=[],
- merge_tests = true }).
+ merge_tests=true}).
-record(cover, {app=none,
level=details,
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
new file mode 100644
index 0000000000..c42f956b3a
--- /dev/null
+++ b/lib/common_test/src/cth_surefire.erl
@@ -0,0 +1,199 @@
+%%% @doc Common Test Framework functions handling test specifications.
+%%%
+%%% <p>This module creates a junit report of the test run if plugged in
+%%% as a suite_callback.</p>
+
+-module(cth_surefire).
+
+%% Suite Callbacks
+-export([id/1, init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-record(state, { filepath, axis, properties, package, hostname,
+ curr_suite, curr_suite_ts, curr_group = [], curr_tc,
+ curr_log_dir, timer, tc_log,
+ test_cases = [],
+ test_suites = [] }).
+
+-record(testcase, { log, group, classname, name, time, failure, timestamp }).
+-record(testsuite, { errors, failures, hostname, name, tests,
+ time, timestamp, id, package,
+ properties, testcases }).
+
+id(Opts) ->
+ filename:absname(proplists:get_value(path, Opts, "junit_report.xml")).
+
+init(Path, Opts) ->
+ {ok, Host} = inet:gethostname(),
+ #state{ filepath = Path,
+ hostname = proplists:get_value(hostname,Opts,Host),
+ package = proplists:get_value(package,Opts),
+ axis = proplists:get_value(axis,Opts,[]),
+ properties = proplists:get_value(properties,Opts,[]),
+ timer = now() }.
+
+pre_init_per_suite(Suite,Config,State) ->
+ {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() },
+ Config) }.
+
+post_init_per_suite(_Suite,Config, Result, State) ->
+ {Result, end_tc(init_per_suite,Config,Result,State)}.
+
+pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}.
+
+post_end_per_suite(_Suite,Config,Result,State) ->
+ NewState = end_tc(end_per_suite,Config,Result,State),
+ TCs = NewState#state.test_cases,
+ Suite = get_suite(NewState, TCs),
+ {Result, State#state{ test_cases = [],
+ test_suites = [Suite | State#state.test_suites]}}.
+
+pre_init_per_group(Group,Config,State) ->
+ {Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]},
+ Config)}.
+
+post_init_per_group(_Group,Config,Result,State) ->
+ {Result, end_tc(init_per_group,Config,Result,State)}.
+
+pre_end_per_group(_Group,Config,State) -> {Config, init_tc(State, Config)}.
+
+post_end_per_group(_Group,Config,Result,State) ->
+ NewState = end_tc(end_per_group, Config, Result, State),
+ {Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}.
+
+pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}.
+
+post_end_per_testcase(TC,Config,Result,State) ->
+ {Result, end_tc(TC,Config, Result,State)}.
+
+on_tc_fail(_TC, Res, State) ->
+ TCs = State#state.test_cases,
+ TC = hd(State#state.test_cases),
+ NewTC = TC#testcase{ failure =
+ {fail,lists:flatten(io_lib:format("~p",[Res]))} },
+ State#state{ test_cases = [NewTC | tl(TCs)]}.
+
+on_tc_skip(_Tc, Res, State) ->
+ TCs = State#state.test_cases,
+ TC = hd(State#state.test_cases),
+ NewTC = TC#testcase{
+ failure =
+ {skipped,lists:flatten(io_lib:format("~p",[Res]))} },
+ State#state{ test_cases = [NewTC | tl(TCs)]}.
+
+init_tc(State, Config) ->
+ State#state{ timer = now(),
+ tc_log = proplists:get_value(tc_logfile, Config)}.
+
+end_tc(Func, Config, Res, State) when is_atom(Func) ->
+ end_tc(atom_to_list(Func), Config, Res, State);
+end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite,
+ curr_group = Groups,
+ timer = TS, tc_log = Log } ) ->
+ ClassName = atom_to_list(Suite),
+ PGroup = string:join([ atom_to_list(Group)||
+ Group <- lists:reverse(Groups)],"."),
+ TimeTakes = io_lib:format("~f",[timer:now_diff(now(),TS) / 1000000]),
+ State#state{ test_cases = [#testcase{ log = Log,
+ timestamp = now_to_string(TS),
+ classname = ClassName,
+ group = PGroup,
+ name = Name,
+ time = TimeTakes,
+ failure = passed }| State#state.test_cases]}.
+
+get_suite(State, TCs) ->
+ Total = length(TCs),
+ Succ = length(lists:filter(fun(#testcase{ failure = F }) ->
+ F == passed
+ end,TCs)),
+ Fail = Total - Succ,
+ TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000,
+ #testsuite{ name = atom_to_list(State#state.curr_suite),
+ package = State#state.package,
+ time = io_lib:format("~f",[TimeTaken]),
+ timestamp = now_to_string(State#state.curr_suite_ts),
+ errors = Fail, tests = Total, testcases = lists:reverse(TCs) }.
+
+terminate(State) ->
+ {ok,D} = file:open(State#state.filepath,[write]),
+ io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []),
+ io:format(D, to_xml(State), []),
+ catch file:sync(D),
+ catch file:close(D).
+
+to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) ->
+ ["<testcase ",
+ [["group=\"",Group,"\""]||Group /= ""]," "
+ "name=\"",N,"\" "
+ "time=\"",T,"\" "
+ "timestamp=\"",TS,"\" "
+ "log=\"",L,"\">",
+ case F of
+ passed ->
+ [];
+ {skipped,Reason} ->
+ ["<skipped type=\"skip\" message=\"Test ",N," in ",CL,
+ " skipped!\">", sanitize(Reason),"</skipped>"];
+ {fail,Reason} ->
+ ["<failure message=\"Test ",N," in ",CL," failed!\" type=\"crash\">",
+ sanitize(Reason),"</failure>"]
+ end,"</testcase>"];
+to_xml(#testsuite{ package = P, hostname = H, errors = E, time = Time,
+ timestamp = TS, tests = T, name = N, testcases = Cases }) ->
+ ["<testsuite ",
+ [["package=\"",P,"\" "]||P /= undefined],
+ [["hostname=\"",P,"\" "]||H /= undefined],
+ [["name=\"",N,"\" "]||N /= undefined],
+ [["time=\"",Time,"\" "]||Time /= undefined],
+ [["timestamp=\"",TS,"\" "]||TS /= undefined],
+ "errors=\"",integer_to_list(E),"\" "
+ "tests=\"",integer_to_list(T),"\">",
+ [to_xml(Case) || Case <- Cases],
+ "</testsuite>"];
+to_xml(#state{ test_suites = TestSuites, axis = Axis, properties = Props }) ->
+ ["<testsuites>",properties_to_xml(Axis,Props),
+ [to_xml(TestSuite) || TestSuite <- TestSuites],"</testsuites>"].
+
+properties_to_xml(Axis,Props) ->
+ ["<properties>",
+ [["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis],
+ [["<property name=\"",Name,"\" value=\"",Value,"\" />"] || {Name,Value} <- Props],
+ "</properties>"
+ ].
+
+sanitize([$>|T]) ->
+ "&gt;" ++ sanitize(T);
+sanitize([$<|T]) ->
+ "&lt;" ++ sanitize(T);
+sanitize([$"|T]) ->
+ "&quot;" ++ sanitize(T);
+sanitize([$'|T]) ->
+ "&apos;" ++ sanitize(T);
+sanitize([$&|T]) ->
+ "&amp;" ++ sanitize(T);
+sanitize([H|T]) ->
+ [H|sanitize(T)];
+sanitize([]) ->
+ [].
+
+now_to_string(Now) ->
+ {{YY,MM,DD},{HH,Mi,SS}} = calendar:now_to_local_time(Now),
+ io_lib:format("~p-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]).
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index cc8a932887..9dfb0bd6b8 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -766,10 +766,6 @@ report1(tc_done,{_Suite,init_per_group,_},State) ->
State;
report1(tc_done,{_Suite,end_per_group,_},State) ->
State;
-report1(tc_done,{_Suite,ct_init_per_group,_},State) ->
- State;
-report1(tc_done,{_Suite,ct_end_per_group,_},State) ->
- State;
report1(tc_done,{_Suite,_Case,ok},State) ->
State#state{ok=State#state.ok+1};
report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) ->
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 284612b8f7..332c444145 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -29,6 +29,7 @@ MODULES= \
ct_test_support_eh \
ct_userconfig_callback \
ct_smoke_test_SUITE \
+ ct_priv_dir_SUITE \
ct_event_handler_SUITE \
ct_config_info_SUITE \
ct_groups_test_1_SUITE \
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 053edba846..8c56d9ffde 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -700,7 +700,7 @@ test_events(timetrap_end_conf) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,8}},
+ {?eh,start_info,{1,1,9}},
{?eh,tc_start,{timetrap_1_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_1_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_1_SUITE,tc1}},
@@ -735,6 +735,10 @@ test_events(timetrap_end_conf) ->
{?eh,tc_done,
{timetrap_1_SUITE,tc8,{failed,{timetrap_timeout,1000}}}},
{?eh,test_stats,{0,8,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc9}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc9,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,9,{0,0}}},
{?eh,tc_start,{timetrap_1_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_1_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
@@ -844,77 +848,124 @@ test_events(timetrap_parallel) ->
test_events(timetrap_fun) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
- {?eh,start_info,{4,4,17}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{4,4,24}},
+ {?eh,tc_start,{timetrap_4_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_4_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_4_SUITE,tc0}},
- {?eh,tc_done,
- {timetrap_4_SUITE,tc0,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_4_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
{?eh,tc_start,{timetrap_4_SUITE,tc1}},
- {?eh,tc_done,
- {timetrap_4_SUITE,tc1,{failed,{timetrap_timeout,2000}}}},
+ {?eh,tc_done,{timetrap_4_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
{?eh,tc_start,{timetrap_4_SUITE,tc2}},
- {?eh,tc_done,
- {timetrap_4_SUITE,tc2,{failed,{timetrap_timeout,500}}}},
+ {?eh,tc_done,{timetrap_4_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
{?eh,tc_start,{timetrap_4_SUITE,tc3}},
- {?eh,tc_done,
- {timetrap_4_SUITE,tc3,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_4_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{timetrap_4_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_4_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{timetrap_5_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_5_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_5_SUITE,tc0}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc0,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
{?eh,test_stats,{0,5,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc1}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc1,{skipped,{timetrap_error,kaboom}}}},
+ {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {kaboom,'_'}}}},
+ {?eh,test_stats,{0,6,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc2}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc2,{skipped,{timetrap_error,kaboom}}}},
+ {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {kaboom,'_'}}}},
+ {?eh,test_stats,{0,7,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc3}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc3,
- {skipped,{invalid_time_format,{timetrap_utils,timetrap_val,[5000]}}}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,8,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc4}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc4,{skipped,{invalid_time_format,'_'}}}},
- {?eh,test_stats,{0,5,{0,4}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc4,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,9,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc5}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc5,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc5,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,10,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc6}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc6,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc6,
+ {failed,{timetrap_timeout,{'$approx',41000}}}}},
+ {?eh,test_stats,{0,11,{0,0}}},
{?eh,tc_start,{timetrap_5_SUITE,tc7}},
- {?eh,tc_done,
- {timetrap_5_SUITE,tc7,{failed,{timetrap_timeout,1000}}}},
- {?eh,test_stats,{0,8,{0,4}}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc7,
+ {failed,{timetrap_timeout,{'$approx',3000}}}}},
+ {?eh,test_stats,{0,12,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc8}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc8,
+ {failed,{timetrap_timeout,{'$approx',7000}}}}},
+ {?eh,test_stats,{0,13,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc9}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc9,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,14,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc10}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc10,
+ {failed,{timetrap_timeout,{'$approx',1500}}}}},
+ {?eh,test_stats,{0,15,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc11}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc11,
+ {failed,{timetrap_timeout,{'$approx',1500}}}}},
+ {?eh,test_stats,{0,16,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc12}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc12,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,17,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc13}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc13,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,18,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,tc14}},
+ {?eh,tc_done,{timetrap_5_SUITE,tc14,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,19,{0,0}}},
+ {?eh,tc_start,{timetrap_5_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_5_SUITE,end_per_suite,ok}},
{?eh,tc_start,{timetrap_6_SUITE,init_per_suite}},
- {?eh,tc_done,
- {timetrap_6_SUITE,init_per_suite,{skipped,{timetrap_error,kaboom}}}},
- {?eh,tc_auto_skip,
- {timetrap_6_SUITE,tc0,{timetrap_error,kaboom}}},
- {?eh,test_stats,{0,8,{0,5}}},
- {?eh,tc_auto_skip,
- {timetrap_6_SUITE,end_per_suite,{timetrap_error,kaboom}}},
-
+ {?eh,tc_done,{undefined,undefined,{user_timetrap_error,
+ {kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_6_SUITE,tc0,
+ {failed,{timetrap_6_SUITE,init_per_suite,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,19,{0,1}}},
+ {?eh,tc_auto_skip,{timetrap_6_SUITE,end_per_suite,
+ {failed,{timetrap_6_SUITE,init_per_suite,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+
+ {?eh,tc_start,{timetrap_7_SUITE,init_per_suite}},
{?eh,tc_done,{timetrap_7_SUITE,init_per_suite,ok}},
{?eh,tc_start,{timetrap_7_SUITE,tc0}},
- {?eh,tc_done,
- {timetrap_7_SUITE,tc0,{failed,{timetrap_timeout,1000}}}},
+ {?eh,tc_done,{timetrap_7_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',7000}}}}},
+ {?eh,test_stats,{0,20,{0,1}}},
{?eh,tc_start,{timetrap_7_SUITE,tc1}},
- {?eh,tc_done,
- {timetrap_7_SUITE,tc1,{failed,{timetrap_timeout,2000}}}},
+ {?eh,tc_done,{timetrap_7_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,21,{0,1}}},
{?eh,tc_start,{timetrap_7_SUITE,tc2}},
- {?eh,tc_done,
- {timetrap_7_SUITE,tc2,{failed,{timetrap_timeout,500}}}},
+ {?eh,tc_done,{timetrap_7_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,22,{0,1}}},
{?eh,tc_start,{timetrap_7_SUITE,tc3}},
- {?eh,tc_done,
- {timetrap_7_SUITE,tc3,{failed,{timetrap_timeout,1000}}}},
- {?eh,test_stats,{0,12,{0,5}}},
+ {?eh,tc_done,{timetrap_7_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',7000}}}}},
+ {?eh,test_stats,{0,23,{0,1}}},
+ {?eh,tc_start,{timetrap_7_SUITE,end_per_suite}},
{?eh,tc_done,{timetrap_7_SUITE,end_per_suite,ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl
index faa0a7305c..a44ff6d0bc 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl
@@ -145,8 +145,17 @@ end_per_testcase1(tc8, Config) ->
ct:pal("end_per_testcase(tc8): ~p", [Config]),
tc8 = ?config(tc, Config),
{failed,timetrap_timeout} = ?config(tc_status, Config),
+ ok;
+
+end_per_testcase1(tc9, Config) ->
+ ct:pal("end_per_testcase(tc9): ~p", [Config]),
+ tc9 = ?config(tc, Config),
+ %% check that it's possible to send and receive synchronously
+ %% with the group leader process for end_per_testcase
+ test_server:stop_node(dummy@somehost),
ok.
+
%%--------------------------------------------------------------------
%% Function: groups() -> [Group]
%% Group = {GroupName,Properties,GroupsAndTestCases}
@@ -170,7 +179,7 @@ groups() ->
%% Reason = term()
%%--------------------------------------------------------------------
all() ->
- [tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8].
+ [tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8, tc9].
tc1(_) ->
timer:sleep(2000),
@@ -205,6 +214,10 @@ tc8(_) ->
timetrap_helper:sleep(2000),
ok.
+tc9(_) ->
+ sleep(2000),
+ ok.
+
%%%-----------------------------------------------------------------
sleep(T) ->
timer:sleep(T),
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl
index c5d4b5062e..58042c04fc 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl
@@ -108,7 +108,8 @@ groups() ->
%% Reason = term()
%%--------------------------------------------------------------------
all() ->
- [tc0,tc1,tc2,tc3,tc4,tc5,tc6,tc7].
+ [tc0,tc1,tc2,tc3,tc4,tc5,tc6,tc7,tc8,tc9,
+ tc10,tc11,tc12,tc13,tc14].
tc0(_) ->
ct:comment(io_lib:format("TO after ~w sec", [?TO])),
@@ -126,30 +127,89 @@ tc2(_) ->
exit(this_should_not_execute).
tc3() ->
- [{timetrap,{timetrap_utils,timetrap_err_mfa,[]}}].
-tc3(_) ->
- exit(this_should_not_execute).
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,2}]}}].
+tc3(_) ->
+ ct:comment("TO after ~2 sec"),
+ ct:sleep({seconds,10}),
+ ok.
tc4() ->
- [{timetrap,fun() -> timetrap_utils:timetrap_err_fun() end}].
-tc4(_) ->
- exit(this_should_not_execute).
+ [{timetrap,fun() -> 500 end}].
+tc4(_) ->
+ ct:comment("TO after 500 ms"),
+ ct:sleep({seconds,10}),
+ ok.
tc5() ->
+ [{timetrap,{timetrap_utils,timetrap_timeout,[1000,ok]}}].
+tc5(_) ->
+ ct:comment("TO after ~1 sec"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc6() ->
[{timetrap,{timetrap_utils,timetrap_timeout,[{seconds,40},
{seconds,1}]}}].
-tc5(_) ->
+tc6(_) ->
ct:comment("TO after 40+1 sec"),
ct:sleep({seconds,42}),
ok.
-tc6() ->
+tc7() ->
+ [{timetrap,{timetrap_utils,timetrap_timeout,[1000,2000]}}].
+tc7(_) ->
+ ct:comment("TO after ~3 sec"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc8() ->
[{timetrap,fun() -> ct:sleep(6000), 1000 end}].
-tc6(_) ->
+tc8(_) ->
ct:comment("TO after 6+1 sec"),
- ct:sleep({seconds,10}).
+ ct:sleep({seconds,10}),
+ ok.
-tc7(_) ->
+tc9() ->
+ [{timetrap,{timetrap_utils,timetrap_timeout,
+ [500,fun() -> {seconds,2} end]}}].
+tc9(_) ->
+ ct:comment("TO after ~2 sec (2.5 sec in reality)"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc10() ->
+ [{timetrap,500}].
+tc10(_) ->
+ ct:timetrap({timetrap_utils,timetrap_val,[1500]}),
+ ct:comment("TO after ~1.5 sec"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc11() ->
+ [{timetrap,2000}].
+tc11(_) ->
+ ct:timetrap(fun() -> 1500 end),
+ ct:comment("TO after ~1.5 sec"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc12() ->
+ [{timetrap,500}].
+tc12(_) ->
+ ct:timetrap({timetrap_utils,timetrap_timeout,[1000,ok]}),
+ ct:comment("TO after ~1 sec"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc13() ->
+ [{timetrap,2000}].
+tc13(_) ->
+ ct:timetrap(fun() -> ct:sleep(500), ok end),
+ ct:comment("TO after ~500 ms"),
+ ct:sleep({seconds,10}),
+ ok.
+
+tc14(_) ->
ct:comment(io_lib:format("TO after ~w sec", [?TO])),
ct:sleep({seconds,5}),
ok.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl
index b25b7770a7..62de959458 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl
@@ -114,7 +114,7 @@ all() ->
tc0(_) ->
ct:comment(io_lib:format("TO after ~w+~w sec", [?HANG,?TO])),
- ct:sleep({seconds,5}),
+ ct:sleep({seconds,10}),
ok.
tc1() ->
@@ -133,5 +133,5 @@ tc2(_) ->
tc3(_) ->
ct:comment(io_lib:format("TO after ~w+~w sec", [?HANG,?TO])),
- ct:sleep({seconds,5}),
+ ct:sleep({seconds,10}),
ok.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl
index fcde6cd701..016014b03a 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl
@@ -20,24 +20,15 @@
-module(timetrap_utils).
-export([timetrap_val/1,
- timetrap_err_fun/0,
- timetrap_err_mfa/0,
timetrap_exit/1,
timetrap_timeout/2]).
timetrap_val(Val) ->
Val.
-timetrap_err_fun() ->
- fun() -> 5000 end.
-
-timetrap_err_mfa() ->
- {?MODULE,timetrap_val,[5000]}.
-
timetrap_exit(Reason) ->
exit(Reason).
timetrap_timeout(Sleep, Val) ->
ct:sleep(Sleep),
Val.
-
diff --git a/lib/common_test/test/ct_group_info_SUITE.erl b/lib/common_test/test/ct_group_info_SUITE.erl
index 2da8219196..18016c4979 100644
--- a/lib/common_test/test/ct_group_info_SUITE.erl
+++ b/lib/common_test/test/ct_group_info_SUITE.erl
@@ -440,72 +440,72 @@ test_events(timetrap_all_no_ipg) ->
{?eh,tc_done,{group_timetrap_3_SUITE,t1,{failed,{timetrap_timeout,1000}}}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t11,{failed,{timetrap_timeout,500}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t21,{failed,{timetrap_timeout,1500}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}],
{?eh,tc_done,{group_timetrap_3_SUITE,t2,{failed,{timetrap_timeout,1000}}}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t41,{failed,{timetrap_timeout,250}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}],
{?eh,tc_done,{group_timetrap_3_SUITE,t31,{failed,{timetrap_timeout,500}}}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t51,{failed,{timetrap_timeout,1500}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}],
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}],
{?eh,tc_done,{group_timetrap_3_SUITE,t3,{failed,{timetrap_timeout,250}}}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t61,{failed,{timetrap_timeout,500}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t81,{failed,{timetrap_timeout,750}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}],
{?eh,tc_done,{group_timetrap_3_SUITE,t71,{failed,{timetrap_timeout,500}}}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t91,{failed,{timetrap_timeout,250}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}],
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t101,{failed,{timetrap_timeout,1000}}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}},
{?eh,tc_done,{group_timetrap_3_SUITE,t111,{failed,{timetrap_timeout,1000}}}},
{?eh,test_stats,{0,14,{0,0}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}],
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
@@ -779,78 +779,78 @@ test_events(require_no_ipg) ->
{?eh,start_info,{1,1,13}},
{?eh,tc_done,{group_require_3_SUITE,t1,ok}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t11,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t21,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t31,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]},
{skipped,{require_failed,{name_in_use,common2_alias,common2}}}}},
{?eh,tc_auto_skip,{group_require_3_SUITE,t41,
{require_failed,{name_in_use,common2_alias,common2}}}},
{?eh,test_stats,{4,0,{0,1}}},
- {?eh,tc_auto_skip,{ct_framework,ct_end_per_group,
+ {?eh,tc_auto_skip,{ct_framework,end_per_group,
{require_failed,{name_in_use,common2_alias,common2}}}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t61,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}],
{?eh,tc_done,{group_require_3_SUITE,t51,ok}},
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t71,ok}},
{?eh,tc_done,{group_require_3_SUITE,t72,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}],
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]},
{skipped,{require_failed,{not_available,non_existing}}}}},
{?eh,tc_auto_skip,{group_require_3_SUITE,t81,
{require_failed,{not_available,non_existing}}}},
{?eh,test_stats,{8,0,{0,2}}},
- {?eh,tc_auto_skip,{ct_framework,ct_end_per_group,
+ {?eh,tc_auto_skip,{ct_framework,end_per_group,
{require_failed,{not_available,non_existing}}}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t91,
{skipped,{require_failed,{not_available,non_existing}}}}},
{?eh,test_stats,{8,0,{0,3}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t101,ok}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}],
- [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}},
{?eh,tc_done,{group_require_3_SUITE,t111,ok}},
{?eh,test_stats,{10,0,{0,3}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}],
+ {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}],
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl
index 2392b0b850..c3601ba0ce 100644
--- a/lib/common_test/test/ct_groups_test_2_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl
@@ -171,16 +171,16 @@ test_events(missing_conf) ->
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
{?eh,start_info,{1,1,2}},
- {?eh,tc_start,{ct_framework,{ct_init_per_group,group1,[]}}},
- {?eh,tc_done,{ct_framework,{ct_init_per_group,group1,[]},ok}},
+ {?eh,tc_start,{ct_framework,{init_per_group,group1,[]}}},
+ {?eh,tc_done,{ct_framework,{init_per_group,group1,[]},ok}},
{?eh,tc_start,{missing_conf_SUITE,tc1}},
{?eh,tc_done,{missing_conf_SUITE,tc1,ok}},
{?eh,test_stats,{1,0,{0,0}}},
{?eh,tc_start,{missing_conf_SUITE,tc2}},
{?eh,tc_done,{missing_conf_SUITE,tc2,ok}},
{?eh,test_stats,{2,0,{0,0}}},
- {?eh,tc_start,{ct_framework,{ct_end_per_group,group1,[]}}},
- {?eh,tc_done,{ct_framework,{ct_end_per_group,group1,[]},ok}},
+ {?eh,tc_start,{ct_framework,{end_per_group,group1,[]}}},
+ {?eh,tc_done,{ct_framework,{end_per_group,group1,[]},ok}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
index 2c519f08b5..27fb7a4ef3 100644
--- a/lib/common_test/test/ct_hooks_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -83,10 +83,10 @@ all(suite) ->
fail_post_suite_cth, skip_pre_suite_cth,
skip_post_suite_cth, recover_post_suite_cth, update_config_cth,
state_update_cth, options_cth, same_id_cth,
- fail_n_skip_with_minimal_cth, prio_cth
+ fail_n_skip_with_minimal_cth, prio_cth, no_config,
+ data_dir
]
- )
- .
+ ).
%%--------------------------------------------------------------------
@@ -214,6 +214,16 @@ prio_cth(Config) when is_list(Config) ->
[{empty_cth,[1000],1000},{empty_cth,[900],900},
{prio_cth,[1100,100],100},{prio_cth,[1100]}],Config).
+no_config(Config) when is_list(Config) ->
+ do_test(no_config, "ct_no_config_SUITE.erl",
+ [verify_config_cth],Config).
+
+data_dir(Config) when is_list(Config) ->
+ do_test(data_dir, "ct_data_dir_SUITE.erl",
+ [verify_data_dir_cth],Config).
+
+
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -1078,6 +1088,105 @@ test_events(prio_cth) ->
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}];
+test_events(no_config) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,[verify_config_cth,[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_no_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_no_config_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{ct_no_config_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [test_case_1,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [test_case_1,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_config_SUITE,test_case_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [test_group,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_framework,
+ {init_per_group,test_group,'$proplist'},ok}},
+ {?eh,tc_start,{ct_no_config_SUITE,test_case_2}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [test_case_2,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [test_case_2,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_no_config_SUITE,test_case_2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_end_per_group,
+ [test_group,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_group,
+ [test_group,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}],
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_no_config_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [ct_no_config_SUITE,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(data_dir) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,cth,{empty_cth,init,[verify_data_dir_cth,[]]}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_data_dir_SUITE,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_data_dir_SUITE,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
+ {?eh,tc_start,{ct_data_dir_SUITE,test_case_1}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [test_case_1,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [test_case_1,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_data_dir_SUITE,test_case_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_init_per_group,
+ [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_init_per_group,
+ [test_group,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_framework,
+ {init_per_group,test_group,'$proplist'},ok}},
+ {?eh,tc_start,{ct_data_dir_SUITE,test_case_2}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,
+ [test_case_2,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,
+ [test_case_2,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_data_dir_SUITE,test_case_2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}},
+ {?eh,cth,{empty_cth,pre_end_per_group,
+ [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_end_per_group,
+ [test_group,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}],
+ {?eh,tc_start,{ct_framework,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_data_dir_SUITE,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,
+ [ct_data_dir_SUITE,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}},
+ {?eh,tc_done,{ct_framework,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(ok) ->
ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl
new file mode 100644
index 0000000000..b4a9079939
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl
@@ -0,0 +1,67 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_data_dir_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+-define(data_dir_name, atom_to_list(?MODULE)++"_data").
+
+suite() ->
+ [{timetrap, {seconds,1}},
+ {ct_hooks, [verify_data_dir_cth]}].
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+init_per_testcase(_, Config) ->
+ check_dirs(Config),
+ Config.
+
+end_per_testcase(_, Config) ->
+ check_dirs(Config),
+ ok.
+
+test_case_1(Config) ->
+ check_dirs(Config),
+ ok.
+
+test_case_2(Config) ->
+ check_dirs(Config),
+ ok.
+
+check_dirs(Config) ->
+ %% check priv_dir
+ PrivDir = proplists:get_value(priv_dir, Config),
+ "log_private" = filename:basename(PrivDir),
+ {ok,_} = file:list_dir(PrivDir),
+
+ %% check data_dir
+ DataDir = proplists:get_value(data_dir, Config),
+ DataDirName = ?data_dir_name,
+ DataDirName = filename:basename(DataDir),
+ ok.
+
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl
new file mode 100644
index 0000000000..2ad80aa1e7
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_no_config_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify 2 things:
+%%%
+%%% 1) All hook pre/post functions get called, even if no init/end
+%%% config functions exist in the suite (new from ver 1.6.1, R15B01).
+%%%
+%%% 2) The hook functions can read Config list elements, as well as
+%%% required config variables, even if no init/end config
+%%% functions exist.
+
+suite() ->
+ [{timetrap, {seconds,1}},
+ {ct_hooks, [verify_config_cth]},
+ {require,suite_cfg},
+ {default_config,suite_cfg,?MODULE}].
+
+group(test_group) ->
+ [{require,group_cfg},
+ {default_config,group_cfg,test_group}].
+
+test_case_1() ->
+ [{require,test_case_1_cfg},
+ {default_config,test_case_1_cfg,test_case_1}].
+
+test_case_2() ->
+ [{require,test_case_2_cfg},
+ {default_config,test_case_2_cfg,test_case_2}].
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+test_case_1(Config) ->
+ ok.
+
+test_case_2(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
index 7befcfa57c..2529e806ea 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -1,277 +1,277 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% @doc Common Test Example Suite Callback module.
-%%%
-%%% <p>This module gives an example of a common test CTH (Common Test Hook).
-%%% There are many ways to add a CTH to a test run, you can do it either in
-%%% the command line using -ct_hook, in a test spec using
-%%% {ct_hook,M} or in the suite it self by returning ct_hook
-%%% from either suite/0, init_per_suite/1, init_per_group/2 and
-%%% init_per_testcase/2. The scope of the CTH is determined by where is it
-%%% started. If it is started in the command line or test spec then it will
-%%% be stopped at the end of all tests. If it is started in init_per_suite,
-%%% it will be stopped after end_per_suite and so on. See terminate
-%%% documentation for a table describing the scoping machanics.
-%%%
-%%% All of callbacks except init/1 in a CTH are optional.</p>
-
--module(empty_cth).
-
-%% CT Hooks
--export([id/1]).
--export([init/2]).
-
--export([pre_init_per_suite/3]).
--export([post_init_per_suite/4]).
--export([pre_end_per_suite/3]).
--export([post_end_per_suite/4]).
-
--export([pre_init_per_group/3]).
--export([post_init_per_group/4]).
--export([pre_end_per_group/3]).
--export([post_end_per_group/4]).
-
--export([pre_init_per_testcase/3]).
--export([post_end_per_testcase/4]).
-
--export([on_tc_fail/3]).
--export([on_tc_skip/3]).
-
--export([terminate/1]).
-
--include_lib("common_test/src/ct_util.hrl").
--include_lib("common_test/include/ct_event.hrl").
-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Example Suite Callback module.
+%%%
+%%% <p>This module gives an example of a common test CTH (Common Test Hook).
+%%% There are many ways to add a CTH to a test run, you can do it either in
+%%% the command line using -ct_hook, in a test spec using
+%%% {ct_hook,M} or in the suite it self by returning ct_hook
+%%% from either suite/0, init_per_suite/1, init_per_group/2 and
+%%% init_per_testcase/2. The scope of the CTH is determined by where is it
+%%% started. If it is started in the command line or test spec then it will
+%%% be stopped at the end of all tests. If it is started in init_per_suite,
+%%% it will be stopped after end_per_suite and so on. See terminate
+%%% documentation for a table describing the scoping machanics.
+%%%
+%%% All of callbacks except init/1 in a CTH are optional.</p>
+
+-module(empty_cth).
+
+%% CT Hooks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
-type config() :: proplists:proplist().
--type reason() :: term().
--type skip_or_fail() :: {skip, reason()} |
- {auto_skip, reason()} |
- {fail, reason()} |
- {'EXIT',reason()}.
-
--record(state, { id = ?MODULE :: term()}).
-
-%% @doc Always called before any other callback function. Use this to initiate
-%% any common state. It should return an state for this CTH.
+-type reason() :: term().
+-type skip_or_fail() :: {skip, reason()} |
+ {auto_skip, reason()} |
+ {fail, reason()} |
+ {'EXIT',reason()}.
+
+-record(state, { id = ?MODULE :: term()}).
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state. It should return an state for this CTH.
-spec init(Id :: term(), Opts :: proplists:proplist()) ->
- {ok, State :: #state{}}.
-init(Id, Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, init, [Id, Opts]}}),
- {ok,Opts}.
-
-%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
-%% return the same ID the seconds CTH is ignored. This function should NOT
-%% have any side effects as it might be called multiple times by common test.
+ {ok, State :: #state{}}.
+init(Id, Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, init, [Id, Opts]}}),
+ {ok,Opts}.
+
+%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
+%% return the same ID the seconds CTH is ignored. This function should NOT
+%% have any side effects as it might be called multiple times by common test.
-spec id(Opts :: proplists:proplist()) ->
- Id :: term().
-id(Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, id, [Opts]}}),
- now().
-
-%% @doc Called before init_per_suite is called. Note that this callback is
-%% only called if the CTH is added before init_per_suite is run (eg. in a test
-%% specification, suite/0 function etc).
-%% You can change the config in the this function.
--spec pre_init_per_suite(Suite :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after init_per_suite.
-%% you can change the return value in this function.
--spec post_init_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before end_per_suite. The config/state can be changed here,
-%% though it will only affect the *end_per_suite function.
--spec pre_end_per_suite(Suite :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after end_per_suite. Note that the config cannot be
-%% changed here, only the status of the suite.
--spec post_end_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each init_per_group.
-%% You can change the config in this function.
--spec pre_init_per_group(Group :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each init_per_group.
-%% You can change the return value in this function.
--spec post_init_per_group(Group :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after each end_per_group. The config/state can be changed here,
-%% though it will only affect the *end_per_group functions.
--spec pre_end_per_group(Group :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each end_per_group. Note that the config cannot be
-%% changed here, only the status of the group.
--spec post_end_per_group(Group :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each test case.
-%% You can change the config in this function.
--spec pre_init_per_testcase(TC :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_testcase(TC,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_testcase,
- [TC,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each test case. Note that the config cannot be
-%% changed here, only the status of the test case.
--spec post_end_per_testcase(TC :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_testcase(TC,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_testcase,
- [TC,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
-%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
-%% This function should be used for extra cleanup which might be needed.
-%% It is not possible to modify the config or the status of the test run.
--spec on_tc_fail(TC :: init_per_suite | end_per_suite |
- init_per_group | end_per_group | atom(),
- Reason :: term(), State :: #state{}) ->
- NewState :: #state{}.
-on_tc_fail(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_fail,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when a test case is skipped by either user action
-%% or due to an init function failing. Test case can be
-%% end_per_suite, init_per_group, end_per_group and the actual test cases.
--spec on_tc_skip(TC :: end_per_suite |
- init_per_group | end_per_group | atom(),
- {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
- {tc_user_skip, {skipped, Reason :: term()}},
- State :: #state{}) ->
- NewState :: #state{}.
-on_tc_skip(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_skip,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when the scope of the CTH is done, this depends on
-%% when the CTH was specified. This translation table describes when this
-%% function is called.
-%%
-%% | Started in | terminate called |
-%% |---------------------|-------------------------|
-%% | command_line | after all tests are run |
-%% | test spec | after all tests are run |
-%% | suite/0 | after SUITE is done |
-%% | init_per_suite/1 | after SUITE is done |
-%% | init_per_group/2 | after group is done |
-%% |-----------------------------------------------|
-%%
--spec terminate(State :: #state{}) ->
- term().
-terminate(State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, terminate, [State]}}),
- ok.
+ Id :: term().
+id(Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, id, [Opts]}}),
+ now().
+
+%% @doc Called before init_per_suite is called. Note that this callback is
+%% only called if the CTH is added before init_per_suite is run (eg. in a test
+%% specification, suite/0 function etc).
+%% You can change the config in the this function.
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after init_per_suite.
+%% you can change the return value in this function.
+-spec post_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before end_per_suite. The config/state can be changed here,
+%% though it will only affect the *end_per_suite function.
+-spec pre_end_per_suite(Suite :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after end_per_suite. Note that the config cannot be
+%% changed here, only the status of the suite.
+-spec post_end_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+%% You can change the config in this function.
+-spec pre_init_per_group(Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+%% You can change the return value in this function.
+-spec post_init_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after each end_per_group. The config/state can be changed here,
+%% though it will only affect the *end_per_group functions.
+-spec pre_end_per_group(Group :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each end_per_group. Note that the config cannot be
+%% changed here, only the status of the group.
+-spec post_end_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each test case.
+%% You can change the config in this function.
+-spec pre_init_per_testcase(TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_testcase(TC,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_testcase,
+ [TC,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each test case. Note that the config cannot be
+%% changed here, only the status of the test case.
+-spec post_end_per_testcase(TC :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_testcase(TC,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_testcase,
+ [TC,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
+%% This function should be used for extra cleanup which might be needed.
+%% It is not possible to modify the config or the status of the test run.
+-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom(),
+ Reason :: term(), State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_fail(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_fail,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. Test case can be
+%% end_per_suite, init_per_group, end_per_group and the actual test cases.
+-spec on_tc_skip(TC :: end_per_suite |
+ init_per_group | end_per_group | atom(),
+ {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_skip(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_skip,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when the scope of the CTH is done, this depends on
+%% when the CTH was specified. This translation table describes when this
+%% function is called.
+%%
+%% | Started in | terminate called |
+%% |---------------------|-------------------------|
+%% | command_line | after all tests are run |
+%% | test spec | after all tests are run |
+%% | suite/0 | after SUITE is done |
+%% | init_per_suite/1 | after SUITE is done |
+%% | init_per_group/2 | after group is done |
+%% |-----------------------------------------------|
+%%
+-spec terminate(State :: #state{}) ->
+ term().
+terminate(State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, terminate, [State]}}),
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl
new file mode 100644
index 0000000000..99ea261e14
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl
@@ -0,0 +1,130 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(verify_config_cth).
+
+-include_lib("common_test/src/ct_util.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+-define(val(K, L), proplists:get_value(K, L)).
+
+id(Opts) ->
+ ?MODULE.
+
+init(Id, Opts) ->
+ {ok, State} = empty_cth:init(Id, Opts),
+ {ok, State}.
+
+pre_init_per_suite(Suite, Config, State) ->
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_init_per_suite(Suite,
+ [{pre_init_per_suite,true} | Config],
+ State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_init_per_suite, Return),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_init_per_suite(Suite,
+ Config,
+ [{post_init_per_suite,true} | Return],
+ State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_end_per_suite(Suite,
+ [{pre_end_per_suite,true} | Config],
+ State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_end_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_init_per_group(Group,
+ [{pre_init_per_group,true} | Config],
+ State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_init_per_group, Return),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_init_per_group(Group,
+ Config,
+ [{post_init_per_group,true} | Return],
+ State).
+
+pre_end_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_end_per_group(Group,
+ [{pre_end_per_group,true} | Config],
+ State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_end_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:pre_init_per_testcase(TC,
+ [{pre_init_per_testcase,true} | Config],
+ State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ true = ?val(post_init_per_suite, Config),
+ true = ?val(pre_init_per_testcase, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl
new file mode 100644
index 0000000000..28cbc94c90
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl
@@ -0,0 +1,95 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(verify_data_dir_cth).
+
+-include_lib("common_test/src/ct_util.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+-define(val(K, L), proplists:get_value(K, L)).
+
+check_dirs(State,Config) ->
+ DataDirName = ?val(data_dir_name, State),
+ %% check priv_dir
+ PrivDir = proplists:get_value(priv_dir, Config),
+ "log_private" = filename:basename(PrivDir),
+ {ok,_} = file:list_dir(PrivDir),
+
+ %% check data_dir
+ DataDir = proplists:get_value(data_dir, Config),
+ DataDirName = filename:basename(DataDir),
+ ok.
+
+id(_Opts) ->
+ ?MODULE.
+
+init(Id, _Opts) ->
+ {ok, _State} = empty_cth:init(Id, []),
+ {ok, [{data_dir_name,"ct_data_dir_SUITE_data"}]}.
+
+pre_init_per_suite(Suite,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ check_dirs(State,Return),
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ check_dirs(State,Return),
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index 1471cc1e0c..d8cb6318c1 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -98,7 +98,7 @@ end_per_group(_GroupName, Config) ->
%%--------------------------------------------------------------------
%% TEST CASES
%%--------------------------------------------------------------------
-ct_master_test(Config) when is_list(Config)->
+ct_master_test(Config) when is_list(Config) ->
NodeNames = proplists:get_value(node_names, Config),
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
@@ -106,19 +106,14 @@ ct_master_test(Config) when is_list(Config)->
FileName = filename:join(PrivDir, "ct_master_spec.spec"),
Suites = [master_SUITE],
TSFile = make_spec(DataDir, FileName, NodeNames, Suites, Config),
+
ERPid = ct_test_support:start_event_receiver(Config),
- spawn(ct@ancalagon,
- fun() ->
- dbg:tracer(),dbg:p(all,c),
- dbg:tpl(erlang, spawn_link, 4,x),
- receive ok -> ok end
- end),
- [{TSFile, ok}] = run_test(ct_master_test, FileName, Config),
+ [{TSFile,ok}] = run_test(ct_master_test, FileName, Config),
Events = ct_test_support:get_events(ERPid, Config),
- ct_test_support:log_events(groups_suite_1,
+ ct_test_support:log_events(ct_master_test,
reformat(Events, ?eh),
PrivDir, []),
@@ -134,48 +129,59 @@ ct_master_test(Config) when is_list(Config)->
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
-make_spec(DataDir, FileName, NodeNames, Suites, Config)->
- {ok, HostName} = inet:gethostname(),
+make_spec(DataDir, FileName, NodeNames, Suites, Config) ->
+ {ok,HostName} = inet:gethostname(),
- N = lists:map(fun(NodeName)->
+ N = lists:map(fun(NodeName) ->
{node, NodeName, list_to_atom(atom_to_list(NodeName)++"@"++HostName)}
end,
NodeNames),
- C = lists:map(fun(NodeName)->
- Rnd = random:uniform(2),
- if Rnd == 1->
- {config, NodeName, filename:join(DataDir, "master/config.txt")};
- true->
- {userconfig, NodeName, {ct_config_xml, filename:join(DataDir, "master/config.xml")}}
- end
- end,
- NodeNames),
-
- NS = lists:map(fun(NodeName)->
- {init, NodeName, [
- {node_start, [{startup_functions, []}, {monitor_master, true}]},
- {eval, {erlang, nodes, []}}
- ]
- }
- end,
- NodeNames),
-
+ C = lists:map(
+ fun(NodeName) ->
+ Rnd = random:uniform(2),
+ if Rnd == 1->
+ {config,NodeName,filename:join(DataDir,
+ "master/config.txt")};
+ true ->
+ {userconfig,NodeName,
+ {ct_config_xml,filename:join(DataDir,
+ "master/config.xml")}}
+ end
+ end,
+ NodeNames),
+
+ CM = [{config,master,filename:join(DataDir,"master/config.txt")}],
+
+ NS = lists:map(
+ fun(NodeName) ->
+ {init,NodeName,[
+ {node_start,[{startup_functions,[]},
+ {monitor_master,true}]},
+ {eval,{erlang,nodes,[]}}
+ ]
+ }
+ end,
+ NodeNames),
+
S = [{suites, NodeNames, filename:join(DataDir, "master"), Suites}],
-
+
PrivDir = ?config(priv_dir, Config),
- LD = lists:map(fun(NodeName)->
- {logdir, NodeName, get_log_dir(os:type(),PrivDir, NodeName)}
- end,
- NodeNames) ++ [{logdir, master, PrivDir}],
+
+ LD = lists:map(
+ fun(NodeName) ->
+ {logdir,NodeName,get_log_dir(os:type(),PrivDir, NodeName)}
+ end,
+ NodeNames) ++ [{logdir,master,PrivDir}],
+
EvHArgs = [{cbm,ct_test_support},{trace_level,?config(trace_level,Config)}],
EH = [{event_handler,master,[?eh],EvHArgs}],
-
+
Include = [{include,filename:join([DataDir,"master/include"])}],
+
+ ct_test_support:write_testspec(N++Include++EH++C++CM++S++LD++NS, FileName).
- ct_test_support:write_testspec(N++Include++EH++C++S++LD++NS, FileName).
-
-get_log_dir({win32,_}, _PrivDir, NodeName)->
+get_log_dir({win32,_}, _PrivDir, NodeName) ->
case filelib:is_dir(?TEMP_DIR) of
false ->
file:make_dir(?TEMP_DIR);
@@ -188,8 +194,15 @@ get_log_dir(_,PrivDir,NodeName) ->
file:make_dir(LogDir),
LogDir.
-run_test(_Name, FileName, Config)->
- [{FileName, ok}] = ct_test_support:run(ct_master, run, [FileName], Config).
+run_test(_Name, FileName, Config) ->
+ %% run the test twice, using different html versions
+ [{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]},
+ [{ct_master,basic_html,[true]}],
+ Config),
+ timer:sleep(5000),
+ [{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]},
+ [{ct_master,basic_html,[false]}],
+ Config).
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
@@ -220,5 +233,5 @@ add_host(NodeName) ->
{ok, HostName} = inet:gethostname(),
list_to_atom(atom_to_list(NodeName)++"@"++HostName).
-expected_events(_)->
+expected_events(_) ->
[].
diff --git a/lib/common_test/test/ct_priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE.erl
new file mode 100644
index 0000000000..f6942d59bf
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE.erl
@@ -0,0 +1,277 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_priv_dir_SUITE
+%%%
+%%% Description:
+%%% Test that it works to use the create_priv_dir option.
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_priv_dir_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).
+
+%%--------------------------------------------------------------------
+%% 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) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+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() ->
+ [
+ default,
+ auto_per_run,
+ auto_per_tc,
+ manual_per_tc,
+ spec_default,
+ spec_auto_per_run,
+ spec_auto_per_run,
+ spec_manual_per_tc
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "priv_dir_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,default},
+ {label,default}], Config),
+ ok = execute(default, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+auto_per_run(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "priv_dir_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,default},
+ {label,auto_per_run},
+ {create_priv_dir,auto_per_run}], Config),
+ ok = execute(auto_per_run, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+auto_per_tc(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "priv_dir_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,auto_per_tc},
+ {label,auto_per_tc},
+ {create_priv_dir,auto_per_tc}], Config),
+ ok = execute(auto_per_tc, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+manual_per_tc(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "priv_dir_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,manual_per_tc},
+ {label,manual_per_tc},
+ {create_priv_dir,manual_per_tc}], Config),
+ ok = execute(manual_per_tc, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+spec_default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Spec = filename:join(DataDir, "default.spec"),
+ {Opts,ERPid} = setup([{spec,Spec},
+ {label,spec_default}], Config),
+ ok = execute(spec_default, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+spec_auto_per_run(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Spec = filename:join(DataDir, "auto_per_run.spec"),
+ {Opts,ERPid} = setup([{spec,Spec},
+ {label,spec_auto_per_run}], Config),
+ ok = execute(spec_auto_per_run, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+spec_auto_per_tc(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Spec = filename:join(DataDir, "auto_per_tc.spec"),
+ {Opts,ERPid} = setup([{spec,Spec},
+ {label,spec_auto_per_tc}], Config),
+ ok = execute(spec_auto_per_tc, Opts, ERPid, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+spec_manual_per_tc(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Spec = filename:join(DataDir, "manual_per_tc.spec"),
+ {Opts,ERPid} = setup([{spec,Spec},
+ {label,spec_manual_per_tc}], Config),
+ ok = execute(spec_manual_per_tc, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+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),
+
+ TestEvents = events_to_check(Name),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+
+test_events(DEF) when DEF == default ; DEF == auto_per_run ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,default}},
+ {?eh,tc_done,{priv_dir_SUITE,default,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(auto_per_tc) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,auto_per_tc}},
+ {?eh,tc_done,{priv_dir_SUITE,auto_per_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(manual_per_tc) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,manual_per_tc}},
+ {?eh,tc_done,{priv_dir_SUITE,manual_per_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(SPECDEF) when SPECDEF == spec_default ;
+ SPECDEF == spec_auto_per_run ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,default}},
+ {?eh,tc_done,{priv_dir_SUITE,default,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(spec_auto_per_tc) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,auto_per_tc}},
+ {?eh,tc_done,{priv_dir_SUITE,auto_per_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(spec_manual_per_tc) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,1}},
+ {?eh,tc_start,{priv_dir_SUITE,init_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{priv_dir_SUITE,manual_per_tc}},
+ {?eh,tc_done,{priv_dir_SUITE,manual_per_tc,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{priv_dir_SUITE,end_per_suite}},
+ {?eh,tc_done,{priv_dir_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}].
+
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec
new file mode 100644
index 0000000000..4dde0ed1f4
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_run.spec
@@ -0,0 +1,5 @@
+{create_priv_dir, auto_per_run}.
+
+{alias, curr, "./"}.
+
+{cases, curr, priv_dir_SUITE, default}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec
new file mode 100644
index 0000000000..c265500865
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/auto_per_tc.spec
@@ -0,0 +1,5 @@
+{create_priv_dir, auto_per_tc}.
+
+{alias, curr, "./"}.
+
+{cases, curr, priv_dir_SUITE, auto_per_tc}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec
new file mode 100644
index 0000000000..2f053e792f
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/default.spec
@@ -0,0 +1,3 @@
+{alias, curr, "./"}.
+
+{cases, curr, priv_dir_SUITE, default}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec b/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec
new file mode 100644
index 0000000000..4f98734d5f
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/manual_per_tc.spec
@@ -0,0 +1,5 @@
+{create_priv_dir, manual_per_tc}.
+
+{alias, curr, "./"}.
+
+{cases, curr, priv_dir_SUITE, manual_per_tc}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
new file mode 100644
index 0000000000..423cb2999b
--- /dev/null
+++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl
@@ -0,0 +1,127 @@
+%%%-------------------------------------------------------------------
+%%% @author Peter Andersson <[email protected]>
+%%% @copyright (C) 2012, Peter Andersson
+%%% @doc
+%%%
+%%% @end
+%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]>
+%%%-------------------------------------------------------------------
+-module(priv_dir_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [].
+
+default(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ "log_private" = filename:basename(PrivDir),
+ {ok,_} = file:list_dir(PrivDir).
+
+auto_per_tc(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ["log_private",_] = string:tokens(filename:basename(PrivDir), "."),
+ {ok,_} = file:list_dir(PrivDir).
+
+manual_per_tc(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ["log_private",_] = string:tokens(filename:basename(PrivDir), "."),
+ {error,_} = file:list_dir(PrivDir),
+ ok = ct:make_priv_dir(),
+ {ok,_} = file:list_dir(PrivDir).
+
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index efc0309781..8825d84884 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -242,25 +242,28 @@ test_events(ts_if_1) ->
{?eh,tc_auto_skip,{ts_if_5_SUITE,end_per_suite,
{require_failed_in_suite0,{not_available,undef_variable}}}},
- {?eh,tc_start,{ts_if_6_SUITE,tc1}},
- {?eh,tc_done,{ts_if_6_SUITE,tc1,{failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}},
- {?eh,test_stats,{3,5,{6,8}}},
+ {?eh,tc_start,{ct_framework,init_per_suite}},
+ {?eh,tc_done,{ct_framework,init_per_suite,
+ {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}},
+ {?eh,tc_auto_skip,{ts_if_6_SUITE,tc1,
+ {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}},
+ {?eh,test_stats,{3,5,{5,9}}},
{?eh,tc_start,{ts_if_7_SUITE,tc1}},
{?eh,tc_done,{ts_if_7_SUITE,tc1,ok}},
- {?eh,test_stats,{4,5,{6,8}}},
+ {?eh,test_stats,{4,5,{5,9}}},
{?eh,tc_start,{ts_if_8_SUITE,tc1}},
{?eh,tc_done,{ts_if_8_SUITE,tc1,{failed,{error,failed_on_purpose}}}},
- {?eh,test_stats,{4,6,{6,8}}},
+ {?eh,test_stats,{4,6,{5,9}}},
{?eh,tc_user_skip,{skipped_by_spec_1_SUITE,all,"should be skipped"}},
- {?eh,test_stats,{4,6,{7,8}}},
+ {?eh,test_stats,{4,6,{6,9}}},
{?eh,tc_start,{skipped_by_spec_2_SUITE,init_per_suite}},
{?eh,tc_done,{skipped_by_spec_2_SUITE,init_per_suite,ok}},
{?eh,tc_user_skip,{skipped_by_spec_2_SUITE,tc1,"should be skipped"}},
- {?eh,test_stats,{4,6,{8,8}}},
+ {?eh,test_stats,{4,6,{7,9}}},
{?eh,tc_start,{skipped_by_spec_2_SUITE,end_per_suite}},
{?eh,tc_done,{skipped_by_spec_2_SUITE,end_per_suite,ok}},
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 6df02d12b7..a712ab6cb9 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -29,7 +29,7 @@
-export([init_per_suite/1, init_per_suite/2, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2,
write_testspec/2, write_testspec/3,
- run/2, run/4, get_opts/1, wait_for_ct_stop/1]).
+ run/2, run/3, run/4, get_opts/1, wait_for_ct_stop/1]).
-export([handle_event/2, start_event_receiver/1, get_events/2,
verify_events/3, reformat/2, log_events/4,
@@ -223,7 +223,7 @@ get_opts(Config) ->
%%%-----------------------------------------------------------------
%%%
-run(Opts, Config) ->
+run(Opts, Config) when is_list(Opts) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
%% use ct interface
@@ -256,9 +256,19 @@ run(Opts, Config) ->
end.
run(M, F, A, Config) ->
+ run({M,F,A}, [], Config).
+
+run({M,F,A}, InitCalls, Config) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
- test_server:format(Level, "~nCalling ~w:~w(~p) on ~p~n",
+ lists:foreach(
+ fun({IM,IF,IA}) ->
+ test_server:format(Level, "~nInit call ~w:~w(~p) on ~p...~n",
+ [IM, IF, IA, CTNode]),
+ Result = rpc:call(CTNode, IM, IF, IA),
+ test_server:format(Level, "~n...with result: ~p~n", [Result])
+ end, InitCalls),
+ test_server:format(Level, "~nStarting test with ~w:~w(~p) on ~p~n",
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
@@ -1001,6 +1011,12 @@ result_match({SkipOrFail,{ErrorInd,{Why,'_'}}},
result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}},
{SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,_Stack}}}}) ->
true;
+result_match({failed,{timetrap_timeout,{'$approx',Num}}},
+ {failed,{timetrap_timeout,Value}}) ->
+ if Value >= trunc(Num-0.02*Num),
+ Value =< trunc(Num+0.02*Num) -> true;
+ true -> false
+ end;
result_match(Result, Result) ->
true;
result_match(_, _) ->