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/common_test_app.xml11
-rw-r--r--lib/common_test/doc/src/cover_chapter.xml112
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml43
-rw-r--r--lib/common_test/doc/src/ct_run.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml50
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml11
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml52
-rw-r--r--lib/common_test/src/ct.erl5
-rw-r--r--lib/common_test/src/ct_cover.erl32
-rw-r--r--lib/common_test/src/ct_framework.erl6
-rw-r--r--lib/common_test/src/ct_master.erl7
-rw-r--r--lib/common_test/src/ct_master_logs.erl41
-rw-r--r--lib/common_test/src/ct_netconfc.erl8
-rw-r--r--lib/common_test/src/ct_run.erl53
-rw-r--r--lib/common_test/src/ct_slave.erl67
-rw-r--r--lib/common_test/src/ct_snmp.erl335
-rw-r--r--lib/common_test/src/ct_testspec.erl4
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_conn_log.erl2
-rw-r--r--lib/common_test/src/cth_log_redirect.erl2
-rw-r--r--lib/common_test/src/cth_surefire.erl183
-rw-r--r--lib/common_test/test/Makefile9
-rw-r--r--lib/common_test/test/common_test.cover10
-rw-r--r--lib/common_test/test/ct_config_info_SUITE.erl14
-rw-r--r--lib/common_test/test/ct_cover_SUITE.erl312
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl156
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore0
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl4
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl446
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl12
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl258
-rw-r--r--lib/common_test/test/ct_group_leader_SUITE.erl181
-rw-r--r--lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl252
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl57
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl9
-rw-r--r--lib/common_test/test/ct_snmp_SUITE.erl141
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg44
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl395
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf7
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf2
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf1
-rw-r--r--lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf6
-rw-r--r--lib/common_test/test/ct_surefire_SUITE.erl351
-rw-r--r--lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl92
-rw-r--r--lib/common_test/test/ct_system_error_SUITE.erl132
-rw-r--r--lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl122
-rw-r--r--lib/common_test/test/ct_test_support.erl94
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl108
52 files changed, 3842 insertions, 404 deletions
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index b6d4a633cb..151159ad69 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -170,7 +170,9 @@
<v> UserData = term()</v>
<v> Conns = [atom()]</v>
<v> CSSFile = string()</v>
- <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]</v>
+ <v> CTHs = [CTHModule |</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs} |</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs, CTHPriority}]</v>
<v> CTHModule = atom()</v>
<v> CTHInitArgs = term()</v>
</type>
@@ -297,8 +299,9 @@
<v> UserData = term()</v>
<v> Conns = [atom()]</v>
<v> CSSFile = string()</v>
- <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} |
- {CTHModule, CTHInitArgs, CTHPriority}]</v>
+ <v> CTHs = [CTHModule |</v>
+ <v> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs} |</v>
+ <v> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{CTHModule, CTHInitArgs, CTHPriority}]</v>
<v> CTHModule = atom()</v>
<v> CTHInitArgs = term()</v>
</type>
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml
index 803a71de07..4fa92d5583 100644
--- a/lib/common_test/doc/src/cover_chapter.xml
+++ b/lib/common_test/doc/src/cover_chapter.xml
@@ -108,6 +108,33 @@
specifications</seealso>).</p>
</section>
+ <marker id="cover_stop"></marker>
+ <section>
+ <title>Stopping the cover tool when tests are completed</title>
+ <p>By default the Cover tool is automatically stopped when the
+ tests are completed. This causes the original (non cover
+ compiled) modules to be loaded back in to the test node. If a
+ process at this point is still running old code of any of the
+ modules that are cover compiled, meaning that it has not done
+ any fully qualified function call after the cover compilation,
+ the process will now be killed. To avoid this it is possible to
+ set the value of the <c>cover_stop</c> option to
+ <c>false</c>. This means that the modules will stay cover
+ compiled, and it is therefore only recommended if the erlang
+ node(s) under test is terminated after the test is completed
+ or if cover can be manually stopped.</p>
+
+ <p>The option can be set by using the <c>-cover_stop</c> flag with
+ <c>ct_run</c>, by adding <c>{cover_stop,true|false}</c> to the
+ Opts argument to <c><seealso
+ marker="ct#run_test-1">ct:run_test/1</seealso></c>, or by adding
+ a <c>cover_stop</c> term in your test specification (see chapter
+ about <seealso
+ marker="run_test_chapter#test_specifications">test
+ specifications</seealso>).</p>
+
+ </section>
+
<section>
<title>The cover specification file</title>
<p>These are the terms allowed in a cover specification file:</p>
@@ -148,6 +175,11 @@
%% Specific modules to exclude in cover.
{excl_mods, Mods}.
+
+ %% Cross cover compilation
+ %% Tag = atom(), an identifier for a test run
+ %% Mod = [atom()], modules to compile for accumulated analysis
+ {cross,[{Tag,Mods}]}.
</pre>
<p>The <c>incl_dirs_r</c> and <c>excl_dirs_r</c> terms tell Common
@@ -163,6 +195,81 @@
specification file for Common Test).</p>
</section>
+ <marker id="cross_cover"/>
+ <section>
+ <title>Cross cover analysis</title>
+ <p>The cross cover mechanism allows cover analysis of modules
+ across multiple tests. It is useful if some code, e.g. a library
+ module, is used by many different tests and the accumulated cover
+ result is desirable.</p>
+
+ <p>This can of course also be achieved in a more customized way by
+ using the <c>export</c> parameter in the cover specification and
+ analysing the result off line, but the cross cover mechanism is a
+ build in solution which also provides the logging.</p>
+
+ <p>The mechanism is easiest explained via an example:</p>
+
+ <p>Let's say that there are two systems, <c>s1</c> and <c>s2</c>,
+ which are tested in separate test runs. System <c>s1</c> contains
+ a library module <c>m1</c> which is tested by the <c>s1</c> test
+ run and is included in <c>s1</c>'s cover specification:</p>
+
+<code type="none">
+s1.cover:
+ {incl_mods,[m1]}.</code>
+
+ <p>When analysing code coverage, the result for <c>m1</c> can be
+ seen in the cover log in the <c>s1</c> test result.</p>
+
+ <p>Now, let's imagine that since <c>m1</c> is a library module, it
+ is also used quite a bit by system <c>s2</c>. The <c>s2</c> test
+ run does not specifically test <c>m1</c>, but it might still be
+ interesting to see which parts of <c>m1</c> is actually covered by
+ the <c>s2</c> tests. To do this, <c>m1</c> could be included also
+ in <c>s2</c>'s cover specification:</p>
+
+<code type="none">
+s2.cover:
+ {incl_mods,[m1]}.</code>
+
+ <p>This would give an entry for <c>m1</c> also in the cover log
+ for the <c>s2</c> test run. The problem is that this would only
+ reflect the coverage by <c>s2</c> tests, not the accumulated
+ result over <c>s1</c> and <c>s2</c>. And this is where the cross
+ cover mechanism comes in handy.</p>
+
+ <p>If instead the cover specification for <c>s2</c> was like
+ this:</p>
+
+<code type="none">
+s2.cover:
+ {cross,[{s1,[m1]}]}.</code>
+
+ <p>then <c>m1</c> would be cover compiled in the <c>s2</c> test
+ run, but not shown in the coverage log. Instead, if
+ <c>ct_cover:cross_cover_analyse/2</c> is called after both
+ <c>s1</c> and <c>s2</c> test runs are completed, the accumulated
+ result for <c>m1</c> would be available in the cross cover log for
+ the <c>s1</c> test run.</p>
+
+ <p>The call to the analyse function must be like this:</p>
+
+<code type="none">
+ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).</code>
+
+ <p>where <c>S1LogDir</c> and <c>S2LogDir</c> are the directories
+ named <c>&lt;TestName&gt;.logs</c> for each test respectively.</p>
+
+ <p>Note the tags <c>s1</c> and <c>s2</c> which are used in the
+ cover specification file and in the call to
+ <c>ct_cover:cross_cover_analyse/2</c>. The point of these are only
+ to map the modules specified in the cover specification to the log
+ directory specified in the call to the analyse function. The name
+ of the tag has no meaning beyond this.</p>
+
+ </section>
+
<section>
<title>Logging</title>
<p>To view the result of a code coverage test, follow the
@@ -170,6 +277,11 @@
takes you to the code coverage overview page. If you have
successfully performed a detailed coverage analysis, you
find links to each individual module coverage page here.</p>
+
+ <p>If cross cover analysis has been performed, and there are
+ accumulated coverage results for the current test, then the -
+ "Coverdata collected over all tests" link will take you to these
+ results.</p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index 86237f5fc1..fe871eb516 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2012</year>
+ <year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -439,14 +439,14 @@ terminate(State) ->
<table>
<row>
- <cell><em>CTH Name</em></cell>
- <cell><em>Is Built-in</em></cell>
- <cell><em>Description</em></cell>
+ <cell align="left"><em>CTH Name</em></cell>
+ <cell align="left"><em>Is Built-in</em></cell>
+ <cell align="left"><em>Description</em></cell>
</row>
<row>
- <cell>cth_log_redirect</cell>
- <cell>yes</cell>
- <cell>Captures all error_logger and SASL logging events and prints them
+ <cell align="left">cth_log_redirect</cell>
+ <cell align="left">yes</cell>
+ <cell align="left">Captures all error_logger and SASL logging events and prints them
to the current test case log. If an event can not be associated with a
testcase it will be printed in the common test framework log. This will
happen for testcases which are run in parallel and events which occur
@@ -455,14 +455,29 @@ terminate(State) ->
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.
+ <cell align="left">cth_surefire</cell>
+ <cell align="left">no</cell>
+ <cell align="left"><p>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 file name can be changed by
+ setting the <c>path</c> option for this hook, e.g.</p>
+
<code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code>
- Surefire XML can forinstance be used by Jenkins to display test
- results.</cell>
+
+ <p>If the <c>url_base</c> option is set, an additional
+ attribute named <c>url</c> will be added to each
+ <c>testsuite</c> and <c>testcase</c> XML element. The value will
+ be constructed from the <c>url_base</c> and a relative path
+ to the test suite or test case log respectively, e.g.</p>
+
+ <code>-ct_hooks cth_surefire [{url_base, "http://myserver.com/"}]</code>
+ <p>will give a url attribute value similar to</p>
+
+ <code>"http://myserver.com/[email protected]_11.19.39/
+x86_64-unknown-linux-gnu.my_test.logs/run.2012-12-12_11.19.39/suite.log.html"</code>
+
+ <p>Surefire XML can for instance be used by Jenkins to display test
+ results.</p></cell>
</row>
</table>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index c6749d6960..0750f560b3 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -104,6 +104,7 @@
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
[-cover CoverCfgFile]
+ [-cover_stop Bool]
[-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
[-event_handler_init EvHandler1 InitArg1 and
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
@@ -138,6 +139,7 @@
[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
[-stylesheet CSSFile]
[-cover CoverCfgFile]
+ [-cover_stop Bool]
[-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
[-event_handler_init EvHandler1 InitArg1 and
EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 7e33b71de1..8c3b13951d 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -32,6 +32,56 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.6.3.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ The following corrections/changes are done in the
+ cth_surefire hook:</p>
+ <p>
+ <list> <item> Earlier there would always be a
+ 'properties' element under the 'testsuites' element. This
+ would exist even if there were no 'property' element
+ inside it. This has been changed so if there are no
+ 'property' elements to display, then there will not be a
+ 'properties' element either. </item> <item> The XML file
+ will now (unless other is specified) be stored in the top
+ log directory. Earlier, the default directory would be
+ the current working directory for the erlang node, which
+ would mostly, but not always, be the top log directory.
+ </item> <item> The 'hostname' attribute in the
+ 'testsuite' element would earlier never have the correct
+ value. This has been corrected. </item> <item> The
+ 'errors' attribute in the 'testsuite' element would
+ earlier display the number of failed testcases. This has
+ been changed and will now always have the value 0, while
+ the 'failures' attribute will show the number of failed
+ testcases. </item> <item> A new attribute 'skipped' is
+ added to the 'testsuite' element. This will display the
+ number of skipped testcases. These would earlier be
+ included in the number of failed test cases. </item>
+ <item> The total number of tests displayed by the 'tests'
+ attribute in the 'testsuite' element would earlier
+ include init/end_per_suite and init/end_per_group. This
+ is no longer the case. The 'tests' attribute will now
+ only count "real" test cases. </item> <item> Earlier,
+ auto skipped test cases would have no value in the 'log'
+ attribute. This is now corrected. </item> <item> A new
+ attributes 'log' is added to the 'testsuite' element.
+ </item> <item> A new option named 'url_base' is added for
+ this hook. If this option is used, a new attribute named
+ 'url' will be added to the 'testcase' and 'testsuite'
+ elements. </item> </list></p>
+ <p>
+ Own Id: OTP-10589</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.6.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index a0b2c96006..d5f5d89e05 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -155,6 +155,8 @@
<item><c><![CDATA[-stylesheet <css_file>]]></c>, points out a user HTML style sheet (see below).</item>
<item><c><![CDATA[-cover <cover_cfg_file>]]></c>, to perform code coverage test (see
<seealso marker="cover_chapter#cover">Code Coverage Analysis</seealso>).</item>
+ <item><c><![CDATA[-cover_stop <bool>]]></c>, to specify if the cover tool shall be stopped after the test is completed (see
+ <seealso marker="cover_chapter#cover_stop">Code Coverage Analysis</seealso>).</item>
<item><c><![CDATA[-event_handler <event_handlers>]]></c>, to install
<seealso marker="event_handler_chapter#event_handling">event handlers</seealso>.</item>
<item><c><![CDATA[-event_handler_init <event_handlers>]]></c>, to install
@@ -658,6 +660,9 @@
{cover, CoverSpecFile}.
{cover, NodeRefs, CoverSpecFile}.
+ {cover_stop, Bool}.
+ {cover_stop, NodeRefs, Bool}.
+
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
@@ -747,7 +752,9 @@
PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
EventHandlers = atom() | [atom()]
InitArgs = [term()]
- CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
+ CTHModules = [CTHModule |
+ {CTHModule, CTHInitArgs} |
+ {CTHModule, CTHInitArgs, CTHPriority}]
CTHModule = atom()
CTHInitArgs = term()
Dir = string()
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 248d7de8b6..cc8d913994 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2012</year>
+ <year>2003</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -982,38 +982,36 @@
<p>Example:</p>
<pre>
+ Some printouts during test case execution:
- Some printouts during test case execution:
+ io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
+ ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
+ ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
+ ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
+ ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
+ ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),
- io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]),
- ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]),
- ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]),
- ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]),
- ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]),
- ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]),
+ If starting the test without specifying any verbosity levels:
- If starting the test without specifying any verbosity levels:
+ $ ct_run ...
- $ ct_run ...
+ the following gets printed:
- the following gets printed:
-
- 1. Standard IO, importance = 50
- 2. Uncategorized, importance = 50
- 3. Categorized info, importance = 50
- 5. Categorized error, importance = 75
- 6. Categorized error, importance = 99
-
- If starting the test with:
-
- $ ct_run -verbosity 1 and info 75
-
- the following gets printed:
+ 1. Standard IO, importance = 50
+ 2. Uncategorized, importance = 50
+ 3. Categorized info, importance = 50
+ 5. Categorized error, importance = 75
+ 6. Categorized error, importance = 99
+
+ If starting the test with:
+
+ $ ct_run -verbosity 1 and info 75
+
+ the following gets printed:
- 3. Categorized info, importance = 50
- 4. Categorized info, importance = 25
- 6. Categorized error, importance = 99
- </pre>
+ 3. Categorized info, importance = 50
+ 4. Categorized info, importance = 25
+ 6. Categorized error, importance = 99</pre>
<p>How categories can be mapped to CSS tags is documented in the
<seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index ad9bf4e2d6..8eafdff29f 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -148,7 +148,7 @@ run(TestDirs) ->
%%% {config,CfgFiles} | {userconfig, UserConfig} |
%%% {allow_user_terms,Bool} | {logdir,LogDir} |
%%% {silent_connections,Conns} | {stylesheet,CSSFile} |
-%%% {cover,CoverSpecFile} | {step,StepOpts} |
+%%% {cover,CoverSpecFile} | {cover_stop,Bool} | {step,StepOpts} |
%%% {event_handler,EventHandlers} | {include,InclDirs} |
%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} |
%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
@@ -988,8 +988,9 @@ get_testdata(Key) ->
end.
%%%-----------------------------------------------------------------
-%%% @spec abort_current_testcase(Reason) -> ok | {error,no_testcase_running}
+%%% @spec abort_current_testcase(Reason) -> ok | {error,ErrorReason}
%%% Reason = term()
+%%% ErrorReason = no_testcase_running | parallel_group
%%%
%%% @doc <p>When calling this function, the currently executing test case will be aborted.
%%% It is the user's responsibility to know for sure which test case is currently
diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl
index d39f50ba00..ae671c750a 100644
--- a/lib/common_test/src/ct_cover.erl
+++ b/lib/common_test/src/ct_cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2012. 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
@@ -24,7 +24,7 @@
-module(ct_cover).
--export([get_spec/1, add_nodes/1, remove_nodes/1]).
+-export([get_spec/1, add_nodes/1, remove_nodes/1, cross_cover_analyse/2]).
-include("ct_util.hrl").
@@ -100,6 +100,22 @@ remove_nodes(Nodes) ->
%%%-----------------------------------------------------------------
+%%% @spec cross_cover_analyse(Level,Tests) -> ok
+%%% Level = overview | details
+%%% Tests = [{Tag,Dir}]
+%%% Tag = atom()
+%%% Dir = string()
+%%%
+%%% @doc Accumulate cover results over multiple tests.
+%%% See the chapter about <seealso
+%%% marker="cover_chapter#cross_cover">cross cover
+%%% analysis</seealso> in the users's guide.
+%%%
+cross_cover_analyse(Level,Tests) ->
+ test_server_ctrl:cross_cover_analyse(Level,Tests).
+
+
+%%%-----------------------------------------------------------------
%%% @hidden
%% Read cover specification file and return the parsed info.
@@ -249,9 +265,11 @@ get_app_info(App=#cover{app=Name}, [{excl_mods,Name,Mods1}|Terms]) ->
Mods = App#cover.excl_mods,
get_app_info(App#cover{excl_mods=Mods++Mods1},Terms);
-get_app_info(App=#cover{app=Name}, [{cross_apps,Name,AppMods1}|Terms]) ->
- AppMods = App#cover.cross,
- get_app_info(App#cover{cross=AppMods++AppMods1},Terms);
+get_app_info(App=#cover{app=none}, [{cross,Cross}|Terms]) ->
+ get_app_info(App, [{cross,none,Cross}|Terms]);
+get_app_info(App=#cover{app=Name}, [{cross,Name,Cross1}|Terms]) ->
+ Cross = App#cover.cross,
+ get_app_info(App#cover{cross=Cross++Cross1},Terms);
get_app_info(App=#cover{app=none}, [{src_dirs,Dirs}|Terms]) ->
get_app_info(App, [{src_dirs,none,Dirs}|Terms]);
@@ -354,10 +372,10 @@ remove_excludes_and_dups(CoverData=#cover{excl_mods=Excl,incl_mods=Incl}) ->
files2mods(Info=#cover{excl_mods=ExclFs,
incl_mods=InclFs,
- cross=CrossFs}) ->
+ cross=Cross}) ->
Info#cover{excl_mods=files2mods1(ExclFs),
incl_mods=files2mods1(InclFs),
- cross=files2mods1(CrossFs)}.
+ cross=[{Tag,files2mods1(Fs)} || {Tag,Fs} <- Cross]}.
files2mods1([M|Fs]) when is_atom(M) ->
[M|files2mods1(Fs)];
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 403eab66cb..c1abf27e9f 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1191,6 +1191,12 @@ report(What,Data) ->
end;
tests_done ->
ok;
+ severe_error ->
+ ct_event:sync_notify(#event{name=What,
+ node=node(),
+ data=Data}),
+ ct_util:set_testdata({What,Data}),
+ ok;
tc_start ->
%% Data = {{Suite,Func},LogFileName}
ct_event:sync_notify(#event{name=tc_logfile,
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 042c5ba267..f29eba605c 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -51,7 +51,7 @@
%%% {testcase,Cases} | {spec,TestSpecs} | {allow_user_terms,Bool} |
%%% {logdir,LogDir} | {event_handler,EventHandlers} |
%%% {silent_connections,Conns} | {cover,CoverSpecFile} |
-%%% {userconfig, UserCfgFiles}
+%%% {cover_stop,Bool} | {userconfig, UserCfgFiles}
%%% CfgFiles = string() | [string()]
%%% TestDirs = string() | [string()]
%%% Suites = atom() | [atom()]
@@ -696,8 +696,9 @@ status(MasterPid,Event) ->
log(To,Heading,Str,Args) ->
if To == all ; To == tty ->
- Str1 = ["=== ",Heading," ===\n",io_lib:format(Str,Args),"\n"],
- io:format(Str1,[]);
+ Chars = ["=== ",Heading," ===\n",
+ io_lib:format(Str,Args),"\n"],
+ io:put_chars(Chars);
true ->
ok
end,
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 9e61d5b16f..84f175c0a9 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -134,7 +134,7 @@ init(Parent,LogDir,Nodes) ->
io:format(CtLogFd,int_header(),[log_timestamp(now()),"Test Nodes\n"]),
io:format(CtLogFd,"~s\n",[NodeStr]),
- io:format(CtLogFd,int_footer()++"\n",[]),
+ io:put_chars(CtLogFd,[int_footer(),"\n"]),
NodeDirIxFd = open_nodedir_index(RunDirAbs,Time),
Parent ! {started,self(),{Time,RunDirAbs}},
@@ -202,24 +202,21 @@ loop(State) ->
open_ct_master_log(Dir) ->
FullName = filename:join(Dir,?ct_master_log_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,header("Common Test Master Log", {[],[1,2],[]}),[]),
+ io:put_chars(Fd,header("Common Test Master Log", {[],[1,2],[]})),
%% maybe add config info here later
- io:format(Fd, config_table([]), []),
- io:format(Fd,
- "<style>\n"
- "div.ct_internal { background:lightgrey; color:black }\n"
- "div.default { background:lightgreen; color:black }\n"
- "</style>\n",
- []),
- io:format(Fd,
- xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
- "<br /><h2>Progress Log</h2>\n<pre>\n"),
- []),
+ io:put_chars(Fd,config_table([])),
+ io:put_chars(Fd,
+ "<style>\n"
+ "div.ct_internal { background:lightgrey; color:black }\n"
+ "div.default { background:lightgreen; color:black }\n"
+ "</style>\n"),
+ io:put_chars(Fd,
+ xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
+ "<br /><h2>Progress Log</h2>\n<pre>\n")),
Fd.
close_ct_master_log(Fd) ->
- io:format(Fd,"</pre>",[]),
- io:format(Fd,footer(),[]),
+ io:put_chars(Fd,["</pre>",footer()]),
file:close(Fd).
config_table(Vars) ->
@@ -248,20 +245,20 @@ int_footer() ->
open_nodedir_index(Dir,StartTime) ->
FullName = filename:join(Dir,?nodedir_index_name),
{ok,Fd} = file:open(FullName,[write]),
- io:format(Fd,nodedir_index_header(StartTime),[]),
+ io:put_chars(Fd,nodedir_index_header(StartTime)),
Fd.
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"],[]),
+ io:put_chars(Fd,
+ ["<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) ->
- io:format(Fd,index_footer(),[]),
+ io:put_chars(Fd,index_footer()),
file:close(Fd).
nodedir_index_header(StartTime) ->
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 294b82bff6..1ccbc86d8f 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -307,7 +307,7 @@
-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
{password,string()} | {user_dir,string()} |
{timeout,timeout()}.
--type host() :: inet:host_name() | inet:ip_address().
+-type host() :: inet:hostname() | inet:ip_address().
-type notification() :: {notification, xml_attributes(), notification_content()}.
-type notification_content() :: [event_time()|simple_xml()].
@@ -1073,7 +1073,8 @@ handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
SimpleXml = encode_rpc_operation(get,[Filter]),
do_send_rpc(Op, SimpleXml, Timeout, From, State).
-handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, State) ->
+handle_msg({ssh_cm, CM, {data, Ch, _Type, Data}}, State) ->
+ ssh_connection:adjust_window(CM,Ch,size(Data)),
handle_data(Data, State);
handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
%% _SshCloseMsg can probably be one of
@@ -1805,7 +1806,8 @@ get_tag([]) ->
%%% SSH stuff
ssh_receive_data() ->
receive
- {ssh_cm, _CM, {data, _Ch, _Type, Data}} ->
+ {ssh_cm, CM, {data, Ch, _Type, Data}} ->
+ ssh_connection:adjust_window(CM,Ch,size(Data)),
{ok, Data};
{ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof ->
{error,X};
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 96b2934382..eb05c90ba8 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -58,6 +58,7 @@
vts,
shell,
cover,
+ cover_stop,
coverspec,
step,
logdir,
@@ -245,6 +246,7 @@ script_start1(Parent, Args) ->
Vts = get_start_opt(vts, true, Args),
Shell = get_start_opt(shell, true, Args),
Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
+ CoverStop = get_start_opt(cover_stop, fun([CS]) -> list_to_atom(CS) end, Args),
LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end,
[], Args),
@@ -329,7 +331,8 @@ script_start1(Parent, Args) ->
end,
StartOpts = #opts{label = Label, profile = Profile,
- vts = Vts, shell = Shell, cover = Cover,
+ vts = Vts, shell = Shell,
+ cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
basic_html = BasicHtml,
verbosity = Verbosity,
@@ -416,6 +419,9 @@ script_start2(StartOpts = #opts{vts = undefined,
Cover =
choose_val(StartOpts#opts.cover,
SpecStartOpts#opts.cover),
+ CoverStop =
+ choose_val(StartOpts#opts.cover_stop,
+ SpecStartOpts#opts.cover_stop),
MultTT =
choose_val(StartOpts#opts.multiply_timetraps,
SpecStartOpts#opts.multiply_timetraps),
@@ -475,6 +481,7 @@ script_start2(StartOpts = #opts{vts = undefined,
profile = Profile,
testspecs = Specs,
cover = Cover,
+ cover_stop = CoverStop,
logdir = LogDir,
logopts = AllLogOpts,
basic_html = BasicHtml,
@@ -723,6 +730,7 @@ script_usage() ->
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
+ "\n\t[-cover_stop Bool]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
@@ -745,6 +753,7 @@ script_usage() ->
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
+ "\n\t[-cover_stop Bool]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
"\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
"\n\t[-include InclDir1 InclDir2 .. InclDirN]"
@@ -938,6 +947,7 @@ run_test2(StartOpts) ->
%% code coverage
Cover = get_start_opt(cover,
fun(CoverFile) -> ?abs(CoverFile) end, StartOpts),
+ CoverStop = get_start_opt(cover_stop, value, StartOpts),
%% timetrap manipulation
MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts),
@@ -1000,7 +1010,8 @@ run_test2(StartOpts) ->
Step = get_start_opt(step, value, StartOpts),
Opts = #opts{label = Label, profile = Profile,
- cover = Cover, step = Step, logdir = LogDir,
+ cover = Cover, cover_stop = CoverStop,
+ step = Step, logdir = LogDir,
logopts = LogOpts, basic_html = BasicHtml,
config = CfgFiles,
verbosity = Verbosity,
@@ -1063,6 +1074,8 @@ run_spec_file(Relaxed,
AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
Cover = choose_val(Opts#opts.cover,
SpecOpts#opts.cover),
+ CoverStop = choose_val(Opts#opts.cover_stop,
+ SpecOpts#opts.cover_stop),
MultTT = choose_val(Opts#opts.multiply_timetraps,
SpecOpts#opts.multiply_timetraps),
ScaleTT = choose_val(Opts#opts.scale_timetraps,
@@ -1103,6 +1116,7 @@ run_spec_file(Relaxed,
Opts1 = Opts#opts{label = Label,
profile = Profile,
cover = Cover,
+ cover_stop = CoverStop,
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
stylesheet = Stylesheet,
@@ -1376,6 +1390,7 @@ get_data_for_node(#testspec{label = Labels,
verbosity = VLvls,
silent_connections = SilentConnsList,
cover = CoverFs,
+ cover_stop = CoverStops,
config = Cfgs,
userconfig = UsrCfgs,
event_handler = EvHs,
@@ -1407,6 +1422,7 @@ get_data_for_node(#testspec{label = Labels,
SCs -> SCs
end,
Cover = proplists:get_value(Node, CoverFs),
+ CoverStop = proplists:get_value(Node, CoverStops),
MT = proplists:get_value(Node, MTs),
ST = proplists:get_value(Node, STs),
CreatePrivDir = proplists:get_value(Node, PDs),
@@ -1425,6 +1441,7 @@ get_data_for_node(#testspec{label = Labels,
verbosity = Verbosity,
silent_connections = SilentConns,
cover = Cover,
+ cover_stop = CoverStop,
config = ConfigFiles,
event_handlers = EvHandlers,
ct_hooks = FiltCTHooks,
@@ -1597,14 +1614,7 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc),
StepOpts ->
#opts{step = StepOpts}
end,
- Opts1 =
- case proplists:get_value(cover, Misc) of
- undefined ->
- Opts;
- CoverFile ->
- Opts#opts{cover = CoverFile}
- end,
- do_run(Tests, [], Opts1#opts{logdir = LogDir}, []);
+ do_run(Tests, [], Opts#opts{logdir = LogDir}, []);
do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
#opts{label = Label, profile = Profile, cover = Cover,
@@ -1638,7 +1648,13 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) ->
{error,Reason} ->
exit({error,Reason});
CoverSpec ->
- Opts#opts{coverspec = CoverSpec}
+ CoverStop =
+ case Opts#opts.cover_stop of
+ undefined -> true;
+ Stop -> Stop
+ end,
+ Opts#opts{coverspec = CoverSpec,
+ cover_stop = CoverStop}
end
end,
%% This env variable is used by test_server to determine
@@ -2144,7 +2160,8 @@ do_run_test(Tests, Skip, Opts) ->
%% tell test_server which modules should be cover compiled
%% note that actual compilation is done when tests start
test_server_ctrl:cover(CovApp, CovFile, CovExcl, CovIncl,
- CovCross, CovExport, CovLevel),
+ CovCross, CovExport, CovLevel,
+ Opts#opts.cover_stop),
%% save cover data (used e.g. to add nodes dynamically)
ct_util:set_testdata({cover,CovData}),
%% start cover on specified nodes
@@ -2216,6 +2233,15 @@ do_run_test(Tests, Skip, Opts) ->
end, CleanUp),
[code:del_path(Dir) || Dir <- AddedToPath],
+ %% If a severe error has occurred in the test_server,
+ %% we will generate an exception here.
+ case ct_util:get_testdata(severe_error) of
+ undefined -> ok;
+ SevereError ->
+ ct_logs:log("SEVERE ERROR", "~p\n", [SevereError]),
+ exit(SevereError)
+ end,
+
case ct_util:get_testdata(stats) of
Stats = {_Ok,_Failed,{_UserSkipped,_AutoSkipped}} ->
Stats;
@@ -2618,6 +2644,9 @@ merge_arguments([LogDir={logdir,_}|Args], Merged) ->
merge_arguments([CoverFile={cover,_}|Args], Merged) ->
merge_arguments(Args, handle_arg(replace, CoverFile, Merged));
+merge_arguments([CoverStop={cover_stop,_}|Args], Merged) ->
+ merge_arguments(Args, handle_arg(replace, CoverStop, Merged));
+
merge_arguments([{'case',TC}|Args], Merged) ->
merge_arguments(Args, handle_arg(merge, {testcase,TC}, Merged));
diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl
index aa3413fa89..1fd8c04f8b 100644
--- a/lib/common_test/src/ct_slave.erl
+++ b/lib/common_test/src/ct_slave.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2012. 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
@@ -37,7 +37,7 @@
-record(options, {username, password, boot_timeout, init_timeout,
startup_timeout, startup_functions, monitor_master,
- kill_if_fail, erl_flags}).
+ kill_if_fail, erl_flags, env}).
%%%-----------------------------------------------------------------
%%% @spec start(Node) -> Result
@@ -85,7 +85,8 @@ start(Host, Node) ->
%%% {startup_functions, StartupFunctions} |
%%% {monitor_master, Monitor} |
%%% {kill_if_fail, KillIfFail} |
-%%% {erl_flags, ErlangFlags}
+%%% {erl_flags, ErlangFlags} |
+%%% {env, [{EnvVar,Value}]}
%%% Username = string()
%%% Password = string()
%%% BootTimeout = integer()
@@ -99,6 +100,8 @@ start(Host, Node) ->
%%% Monitor = bool()
%%% KillIfFail = bool()
%%% ErlangFlags = string()
+%%% EnvVar = string()
+%%% Value = string()
%%% Result = {ok, NodeName} | {error, already_started, NodeName} |
%%% {error, started_not_connected, NodeName} |
%%% {error, boot_timeout, NodeName} |
@@ -152,6 +155,9 @@ start(Host, Node) ->
%%% <p>Option <code>erlang_flags</code> specifies, which flags will be added
%%% to the parameters of the <code>erl</code> executable.</p>
%%%
+%%% <p>Option <code>env</code> specifies a list of environment variables
+%%% that will extended the environment.</p>
+%%%
%%% <p>Special return values are:
%%% <list>
%%% <item><code>{error, already_started, NodeName}</code> - if the node with
@@ -233,10 +239,12 @@ fetch_options(Options) ->
Monitor = get_option_value(monitor_master, Options, false),
KillIfFail = get_option_value(kill_if_fail, Options, true),
ErlFlags = get_option_value(erl_flags, Options, []),
+ EnvVars = get_option_value(env, Options, []),
#options{username=UserName, password=Password,
boot_timeout=BootTimeout, init_timeout=InitTimeout,
startup_timeout=StartupTimeout, startup_functions=StartupFunctions,
- monitor_master=Monitor, kill_if_fail=KillIfFail, erl_flags=ErlFlags}.
+ monitor_master=Monitor, kill_if_fail=KillIfFail,
+ erl_flags=ErlFlags, env=EnvVars}.
% send a message when slave node is started
% @hidden
@@ -306,11 +314,19 @@ do_start(Host, Node, Options) ->
true->
spawn_remote_node(Host, Node, Options)
end,
+
BootTimeout = Options#options.boot_timeout,
InitTimeout = Options#options.init_timeout,
StartupTimeout = Options#options.startup_timeout,
Result = case wait_for_node_alive(ENode, BootTimeout) of
pong->
+ case test_server:is_cover() of
+ true ->
+ MainCoverNode = cover:get_main_node(),
+ rpc:call(MainCoverNode,cover,start,[ENode]);
+ false ->
+ ok
+ end,
call_functions(ENode, Functions2),
receive
{node_started, ENode}->
@@ -365,9 +381,9 @@ get_cmd(Node, Flags) ->
% spawn node locally
spawn_local_node(Node, Options) ->
- ErlFlags = Options#options.erl_flags,
+ #options{env=Env,erl_flags=ErlFlags} = Options,
Cmd = get_cmd(Node, ErlFlags),
- open_port({spawn, Cmd}, [stream]).
+ open_port({spawn, Cmd}, [stream,{env,Env}]).
% start crypto and ssh if not yet started
check_for_ssh_running() ->
@@ -386,9 +402,10 @@ check_for_ssh_running() ->
% spawn node remotely
spawn_remote_node(Host, Node, Options) ->
- Username = Options#options.username,
- Password = Options#options.password,
- ErlFlags = Options#options.erl_flags,
+ #options{username=Username,
+ password=Password,
+ erl_flags=ErlFlags,
+ env=Env} = Options,
SSHOptions = case {Username, Password} of
{[], []}->
[];
@@ -400,8 +417,17 @@ spawn_remote_node(Host, Node, Options) ->
check_for_ssh_running(),
{ok, SSHConnRef} = ssh:connect(atom_to_list(Host), 22, SSHOptions),
{ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity),
+ ssh_setenv(SSHConnRef, SSHChannelId, Env),
ssh_connection:exec(SSHConnRef, SSHChannelId, get_cmd(Node, ErlFlags), infinity).
+
+ssh_setenv(SSHConnRef, SSHChannelId, [{Var, Value} | Vars])
+ when is_list(Var), is_list(Value) ->
+ success = ssh_connection:setenv(SSHConnRef, SSHChannelId,
+ Var, Value, infinity),
+ ssh_setenv(SSHConnRef, SSHChannelId, Vars);
+ssh_setenv(_SSHConnRef, _SSHChannelId, []) -> ok.
+
% call functions on a remote Erlang node
call_functions(_Node, []) ->
ok;
@@ -423,8 +449,29 @@ wait_for_node_alive(Node, N) ->
% call init:stop on a remote node
do_stop(ENode) ->
+ {Cover,MainCoverNode} =
+ case test_server:is_cover() of
+ true ->
+ Main = cover:get_main_node(),
+ rpc:call(Main,cover,flush,[ENode]),
+ {true,Main};
+ false ->
+ {false,undefined}
+ end,
spawn(ENode, init, stop, []),
- wait_for_node_dead(ENode, 5).
+ case wait_for_node_dead(ENode, 5) of
+ {ok,ENode} ->
+ if Cover ->
+ %% To avoid that cover is started again if a node
+ %% with the same name is started later.
+ rpc:call(MainCoverNode,cover,stop,[ENode]);
+ true ->
+ ok
+ end,
+ {ok,ENode};
+ Error ->
+ Error
+ end.
% wait N seconds until node is disconnected
wait_for_node_dead(Node, 0) ->
diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl
index 8fe63e8ed1..71038bd4f4 100644
--- a/lib/common_test/src/ct_snmp.erl
+++ b/lib/common_test/src/ct_snmp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2012. 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
@@ -39,7 +39,7 @@
%%% %%% Manager config
%%% [{start_manager, boolean()} % Optional - default is true
%%% {users, [{user_name(), [call_back_module(), user_data()]}]}, %% Optional
-%%% {usm_users, [{usm_user_name(), usm_config()}]},%% Optional - snmp v3 only
+%%% {usm_users, [{usm_user_name(), [usm_config()]}]},%% Optional - snmp v3 only
%%% % managed_agents is optional
%%% {managed_agents,[{agent_name(), [user_name(), agent_ip(), agent_port(), [agent_config()]]}]},
%%% {max_msg_size, integer()}, % Optional - default is 484
@@ -130,7 +130,7 @@
%%% @type agent_config() = {Item, Value}
%%% @type user_name() = atom()
%%% @type usm_user_name() = string()
-%%% @type usm_config() = string()
+%%% @type usm_config() = {Item, Value}
%%% @type call_back_module() = atom()
%%% @type user_data() = term()
%%% @type oids() = [oid()]
@@ -157,8 +157,9 @@
%%% API
-export([start/2, start/3, stop/1, get_values/3, get_next_values/3, set_values/4,
set_info/1, register_users/2, register_agents/2, register_usm_users/2,
- unregister_users/1, unregister_agents/1, update_usm_users/2,
- load_mibs/1]).
+ unregister_users/1, unregister_users/2, unregister_agents/1,
+ unregister_agents/2, unregister_usm_users/1, unregister_usm_users/2,
+ load_mibs/1, unload_mibs/1]).
%% Manager values
-define(CT_SNMP_LOG_FILE, "ct_snmp_set.log").
@@ -250,10 +251,8 @@ stop(Config) ->
%%%
%%% @doc Issues a synchronous snmp get request.
get_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec get_next_values(Agent, Oids, MgrAgentConfName) -> SnmpReply
@@ -265,10 +264,8 @@ get_values(Agent, Oids, MgrAgentConfName) ->
%%%
%%% @doc Issues a synchronous snmp get next request.
get_next_values(Agent, Oids, MgrAgentConfName) ->
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
- {ok, SnmpReply, _} =
- snmpm:gn(Uid, AgentIp, AgentUdpPort, Oids),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
+ {ok, SnmpReply, _} = snmpm:sync_get_next2(Uid, target_name(Agent), Oids),
SnmpReply.
%%% @spec set_values(Agent, VarsAndVals, MgrAgentConfName, Config) -> SnmpReply
@@ -282,13 +279,11 @@ get_next_values(Agent, Oids, MgrAgentConfName) ->
%%% @doc Issues a synchronous snmp set request.
set_values(Agent, VarsAndVals, MgrAgentConfName, Config) ->
PrivDir = ?config(priv_dir, Config),
- [Uid, AgentIp, AgentUdpPort | _] =
- agent_conf(Agent, MgrAgentConfName),
+ [Uid | _] = agent_conf(Agent, MgrAgentConfName),
Oids = lists:map(fun({Oid, _, _}) -> Oid end, VarsAndVals),
- {ok, SnmpGetReply, _} =
- snmpm:g(Uid, AgentIp, AgentUdpPort, Oids),
- {ok, SnmpSetReply, _} =
- snmpm:s(Uid, AgentIp, AgentUdpPort, VarsAndVals),
+ TargetName = target_name(Agent),
+ {ok, SnmpGetReply, _} = snmpm:sync_get2(Uid, TargetName, Oids),
+ {ok, SnmpSetReply, _} = snmpm:sync_set2(Uid, TargetName, VarsAndVals),
case SnmpSetReply of
{noError, 0, _} when PrivDir /= false ->
log(PrivDir, Agent, SnmpGetReply, VarsAndVals);
@@ -328,12 +323,23 @@ set_info(Config) ->
%%% Reason = term()
%%%
%%% @doc Register the manager entity (=user) responsible for specific agent(s).
-%%% Corresponds to making an entry in users.conf
+%%% Corresponds to making an entry in users.conf.
+%%%
+%%% This function will try to register the given users, without
+%%% checking if any of them already exist. In order to change an
+%%% already registered user, the user must first be unregistered.
register_users(MgrAgentConfName, Users) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, Users}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- setup_users(Users).
+ case setup_users(Users) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldUsers = ct:get_config({MgrAgentConfName,users},[]),
+ NewSnmpVals = lists:keystore(users, 1, SnmpVals,
+ {users, Users ++ OldUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
%%% @spec register_agents(MgrAgentConfName, ManagedAgents) -> ok | {error, Reason}
%%%
@@ -343,12 +349,24 @@ register_users(MgrAgentConfName, Users) ->
%%%
%%% @doc Explicitly instruct the manager to handle this agent.
%%% Corresponds to making an entry in agents.conf
+%%%
+%%% This function will try to register the given managed agents,
+%%% without checking if any of them already exist. In order to change
+%%% an already registered managed agent, the agent must first be
+%%% unregistered.
register_agents(MgrAgentConfName, ManagedAgents) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
- {managed_agents, ManagedAgents}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- setup_managed_agents(ManagedAgents).
+ case setup_managed_agents(MgrAgentConfName,ManagedAgents) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
+ NewSnmpVals = lists:keystore(managed_agents, 1, SnmpVals,
+ {managed_agents,
+ ManagedAgents ++ OldAgents}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
%%% @spec register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
%%%
@@ -358,60 +376,115 @@ register_agents(MgrAgentConfName, ManagedAgents) ->
%%%
%%% @doc Explicitly instruct the manager to handle this USM user.
%%% Corresponds to making an entry in usm.conf
+%%%
+%%% This function will try to register the given users, without
+%%% checking if any of them already exist. In order to change an
+%%% already registered user, the user must first be unregistered.
register_usm_users(MgrAgentConfName, UsmUsers) ->
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {usm_users, UsmUsers}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
- setup_usm_users(UsmUsers, EngineID).
+ case setup_usm_users(UsmUsers, EngineID) of
+ ok ->
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ OldUsmUsers = ct:get_config({MgrAgentConfName,usm_users},[]),
+ NewSnmpVals = lists:keystore(usm_users, 1, SnmpVals,
+ {usm_users, UsmUsers ++ OldUsmUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok;
+ Error ->
+ Error
+ end.
-%%% @spec unregister_users(MgrAgentConfName) -> ok | {error, Reason}
+%%% @spec unregister_users(MgrAgentConfName) -> ok
%%%
%%% MgrAgentConfName = atom()
%%% Reason = term()
%%%
-%%% @doc Removes information added when calling register_users/2.
+%%% @doc Unregister all users.
unregister_users(MgrAgentConfName) ->
- Users = lists:map(fun({UserName, _}) -> UserName end,
- ct:get_config({MgrAgentConfName, users})),
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, []}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- takedown_users(Users).
+ Users = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, users},[])],
+ unregister_users(MgrAgentConfName,Users).
-%%% @spec unregister_agents(MgrAgentConfName) -> ok | {error, Reason}
+%%% @spec unregister_users(MgrAgentConfName,Users) -> ok
%%%
%%% MgrAgentConfName = atom()
+%%% Users = [user_name()]
%%% Reason = term()
%%%
-%%% @doc Removes information added when calling register_agents/2.
+%%% @doc Unregister the given users.
+unregister_users(MgrAgentConfName,Users) ->
+ takedown_users(Users),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllUsers = ct:get_config({MgrAgentConfName, users},[]),
+ RemainingUsers = lists:filter(fun({Id,_}) ->
+ not lists:member(Id,Users)
+ end,
+ AllUsers),
+ NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users,RemainingUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
+
+%%% @spec unregister_agents(MgrAgentConfName) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% Reason = term()
+%%%
+%%% @doc Unregister all managed agents.
unregister_agents(MgrAgentConfName) ->
- ManagedAgents = lists:map(fun({_, [Uid, AgentIP, AgentPort, _]}) ->
- {Uid, AgentIP, AgentPort}
- end,
- ct:get_config({MgrAgentConfName, managed_agents})),
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
- {managed_agents, []}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
- takedown_managed_agents(ManagedAgents).
+ ManagedAgents = [AgentName ||
+ {AgentName, _} <-
+ ct:get_config({MgrAgentConfName,managed_agents},[])],
+ unregister_agents(MgrAgentConfName,ManagedAgents).
+%%% @spec unregister_agents(MgrAgentConfName,ManagedAgents) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% ManagedAgents = [agent_name()]
+%%% Reason = term()
+%%%
+%%% @doc Unregister the given managed agents.
+unregister_agents(MgrAgentConfName,ManagedAgents) ->
+ takedown_managed_agents(MgrAgentConfName, ManagedAgents),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllAgents = ct:get_config({MgrAgentConfName,managed_agents},[]),
+ RemainingAgents = lists:filter(fun({Name,_}) ->
+ not lists:member(Name,ManagedAgents)
+ end,
+ AllAgents),
+ NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
+ {managed_agents,RemainingAgents}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
-%%% @spec update_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
+%%% @spec unregister_usm_users(MgrAgentConfName) -> ok
%%%
%%% MgrAgentConfName = atom()
-%%% UsmUsers = usm_users()
%%% Reason = term()
%%%
-%%% @doc Alters information added when calling register_usm_users/2.
-update_usm_users(MgrAgentConfName, UsmUsers) ->
-
- {snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
- NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
- {usm_users, UsmUsers}),
- ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+%%% @doc Unregister all usm users.
+unregister_usm_users(MgrAgentConfName) ->
+ UsmUsers = [Id || {Id,_} <- ct:get_config({MgrAgentConfName, usm_users},[])],
+ unregister_usm_users(MgrAgentConfName,UsmUsers).
+
+%%% @spec unregister_usm_users(MgrAgentConfName,UsmUsers) -> ok
+%%%
+%%% MgrAgentConfName = atom()
+%%% UsmUsers = [usm_user_name()]
+%%% Reason = term()
+%%%
+%%% @doc Unregister the given usm users.
+unregister_usm_users(MgrAgentConfName,UsmUsers) ->
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
- do_update_usm_users(UsmUsers, EngineID).
+ takedown_usm_users(UsmUsers,EngineID),
+ SnmpVals = ct:get_config(MgrAgentConfName),
+ AllUsmUsers = ct:get_config({MgrAgentConfName, usm_users},[]),
+ RemainingUsmUsers = lists:filter(fun({Id,_}) ->
+ not lists:member(Id,UsmUsers)
+ end,
+ AllUsmUsers),
+ NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
+ {usm_users,RemainingUsmUsers}),
+ ct_config:update_config(MgrAgentConfName, NewSnmpVals),
+ ok.
%%% @spec load_mibs(Mibs) -> ok | {error, Reason}
%%%
@@ -423,6 +496,15 @@ update_usm_users(MgrAgentConfName, UsmUsers) ->
load_mibs(Mibs) ->
snmpa:load_mibs(snmp_master_agent, Mibs).
+%%% @spec unload_mibs(Mibs) -> ok | {error, Reason}
+%%%
+%%% Mibs = [MibName]
+%%% MibName = string()
+%%% Reason = term()
+%%%
+%%% @doc Unload the mibs from the agent 'snmp_master_agent'.
+unload_mibs(Mibs) ->
+ snmpa:unload_mibs(snmp_master_agent, Mibs).
%%%========================================================================
%%% Internal functions
@@ -486,9 +568,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
file:make_dir(DbDir),
snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp,
AgentIP, AgentUdp, SysName,
- atom_to_list(NotifType),
- SecType, Passwd, AgentEngineID,
- AgentMaxMsgSize),
+ NotifType, SecType, Passwd,
+ AgentEngineID, AgentMaxMsgSize),
override_default_configuration(Config, AgentConfName),
@@ -497,7 +578,8 @@ setup_agent(true, AgentConfName, SnmpConfName,
{verbosity, trace}]},
{agent_type, master},
{agent_verbosity, trace},
- {net_if, [{verbosity, trace}]}],
+ {net_if, [{verbosity, trace}]},
+ {versions, Vsns}],
ct:get_config({SnmpConfName,agent})),
application:set_env(snmp, agent, SnmpEnv).
%%%---------------------------------------------------------------------------
@@ -535,65 +617,61 @@ manager_register(true, MgrAgentConfName) ->
setup_usm_users(UsmUsers, EngineID),
setup_users(Users),
- setup_managed_agents(Agents).
+ setup_managed_agents(MgrAgentConfName,Agents).
%%%---------------------------------------------------------------------------
setup_users(Users) ->
- lists:foreach(fun({Id, [Module, Data]}) ->
- snmpm:register_user(Id, Module, Data)
- end, Users).
+ while_ok(fun({Id, [Module, Data]}) ->
+ snmpm:register_user(Id, Module, Data)
+ end, Users).
%%%---------------------------------------------------------------------------
-setup_managed_agents([]) ->
- ok;
-
-setup_managed_agents([{_, [Uid, AgentIp, AgentUdpPort, AgentConf]} |
- Rest]) ->
- NewAgentIp = case AgentIp of
- IpTuple when is_tuple(IpTuple) ->
- IpTuple;
- HostName when is_list(HostName) ->
- {ok,Hostent} = inet:gethostbyname(HostName),
- [IpTuple|_] = Hostent#hostent.h_addr_list,
- IpTuple
- end,
- ok = snmpm:register_agent(Uid, NewAgentIp, AgentUdpPort),
- lists:foreach(fun({Item, Val}) ->
- snmpm:update_agent_info(Uid, NewAgentIp,
- AgentUdpPort, Item, Val)
- end, AgentConf),
- setup_managed_agents(Rest).
+setup_managed_agents(AgentConfName,Agents) ->
+ Fun =
+ fun({AgentName, [Uid, AgentIp, AgentUdpPort, AgentConf0]}) ->
+ NewAgentIp = case AgentIp of
+ IpTuple when is_tuple(IpTuple) ->
+ IpTuple;
+ HostName when is_list(HostName) ->
+ {ok,Hostent} = inet:gethostbyname(HostName),
+ [IpTuple|_] = Hostent#hostent.h_addr_list,
+ IpTuple
+ end,
+ AgentConf =
+ case lists:keymember(engine_id,1,AgentConf0) of
+ true ->
+ AgentConf0;
+ false ->
+ DefaultEngineID =
+ ct:get_config({AgentConfName,agent_engine_id},
+ ?AGENT_ENGINE_ID),
+ [{engine_id,DefaultEngineID}|AgentConf0]
+ end,
+ snmpm:register_agent(Uid, target_name(AgentName),
+ [{address,NewAgentIp},{port,AgentUdpPort} |
+ AgentConf])
+ end,
+ while_ok(Fun,Agents).
%%%---------------------------------------------------------------------------
setup_usm_users(UsmUsers, EngineID)->
- lists:foreach(fun({UsmUser, Conf}) ->
- snmpm:register_usm_user(EngineID, UsmUser, Conf)
- end, UsmUsers).
+ while_ok(fun({UsmUser, Conf}) ->
+ snmpm:register_usm_user(EngineID, UsmUser, Conf)
+ end, UsmUsers).
%%%---------------------------------------------------------------------------
takedown_users(Users) ->
- lists:foreach(fun({Id}) ->
+ lists:foreach(fun(Id) ->
snmpm:unregister_user(Id)
end, Users).
%%%---------------------------------------------------------------------------
-takedown_managed_agents([{Uid, AgentIp, AgentUdpPort} |
- Rest]) ->
- NewAgentIp = case AgentIp of
- IpTuple when is_tuple(IpTuple) ->
- IpTuple;
- HostName when is_list(HostName) ->
- {ok,Hostent} = inet:gethostbyname(HostName),
- [IpTuple|_] = Hostent#hostent.h_addr_list,
- IpTuple
- end,
- ok = snmpm:unregister_agent(Uid, NewAgentIp, AgentUdpPort),
- takedown_managed_agents(Rest);
-
-takedown_managed_agents([]) ->
- ok.
+takedown_managed_agents(MgrAgentConfName,ManagedAgents) ->
+ lists:foreach(fun(AgentName) ->
+ [Uid | _] = agent_conf(AgentName, MgrAgentConfName),
+ snmpm:unregister_agent(Uid, target_name(AgentName))
+ end, ManagedAgents).
%%%---------------------------------------------------------------------------
-do_update_usm_users(UsmUsers, EngineID) ->
- lists:foreach(fun({UsmUser, {Item, Val}}) ->
- snmpm:update_usm_user_info(EngineID, UsmUser,
- Item, Val)
- end, UsmUsers).
+takedown_usm_users(UsmUsers, EngineID) ->
+ lists:foreach(fun(Id) ->
+ snmpm:unregister_usm_user(EngineID, Id)
+ end, UsmUsers).
%%%---------------------------------------------------------------------------
log(PrivDir, Agent, {_, _, Varbinds}, NewVarsAndVals) ->
@@ -657,7 +735,7 @@ override_contexts(Config, {data_dir_file, File}) ->
override_contexts(Config, ContextInfo);
override_contexts(Config, Contexts) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"context.conf"),
file:delete(File),
snmp_config:write_agent_context_config(Dir, "", Contexts).
@@ -673,7 +751,7 @@ override_sysinfo(Config, {data_dir_file, File}) ->
override_sysinfo(Config, SysInfo);
override_sysinfo(Config, SysInfo) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"standard.conf"),
file:delete(File),
snmp_config:write_agent_standard_config(Dir, "", SysInfo).
@@ -688,7 +766,7 @@ override_target_address(Config, {data_dir_file, File}) ->
override_target_address(Config, TargetAddressConf);
override_target_address(Config, TargetAddressConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_addr.conf"),
file:delete(File),
snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf).
@@ -704,7 +782,7 @@ override_target_params(Config, {data_dir_file, File}) ->
override_target_params(Config, TargetParamsConf);
override_target_params(Config, TargetParamsConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"target_params.conf"),
file:delete(File),
snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf).
@@ -719,7 +797,7 @@ override_notify(Config, {data_dir_file, File}) ->
override_notify(Config, NotifyConf);
override_notify(Config, NotifyConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"notify.conf"),
file:delete(File),
snmp_config:write_agent_notify_config(Dir, "", NotifyConf).
@@ -734,7 +812,7 @@ override_usm(Config, {data_dir_file, File}) ->
override_usm(Config, UsmConf);
override_usm(Config, UsmConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"usm.conf"),
file:delete(File),
snmp_config:write_agent_usm_config(Dir, "", UsmConf).
@@ -749,7 +827,7 @@ override_community(Config, {data_dir_file, File}) ->
override_community(Config, CommunityConf);
override_community(Config, CommunityConf) ->
- Dir = ?config(priv_dir, Config),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
File = filename:join(Dir,"community.conf"),
file:delete(File),
snmp_config:write_agent_community_config(Dir, "", CommunityConf).
@@ -765,7 +843,20 @@ override_vacm(Config, {data_dir_file, File}) ->
override_vacm(Config, VacmConf);
override_vacm(Config, VacmConf) ->
- Dir = ?config(priv_dir, Config),
- File = filename:join(Dir,"vacm.conf"),
+ Dir = filename:join(?config(priv_dir, Config),"conf"),
+ File = filename:join(Dir,"vacm.conf"),
file:delete(File),
snmp_config:write_agent_vacm_config(Dir, "", VacmConf).
+
+%%%---------------------------------------------------------------------------
+
+target_name(Agent) ->
+ atom_to_list(Agent).
+
+while_ok(Fun,[H|T]) ->
+ case Fun(H) of
+ ok -> while_ok(Fun,T);
+ Error -> Error
+ end;
+while_ok(_Fun,[]) ->
+ ok.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 5ce095e38e..202d8f9373 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -903,6 +903,8 @@ handle_data(logdir,Node,Dir,Spec) ->
[{Node,ref2dir(Dir,Spec)}];
handle_data(cover,Node,File,Spec) ->
[{Node,get_absfile(File,Spec)}];
+handle_data(cover_stop,Node,Stop,_Spec) ->
+ [{Node,Stop}];
handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) ->
[{Node,ref2dir(Dir,Spec)} || Dir <- Dirs];
handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) ->
@@ -1262,6 +1264,8 @@ valid_terms() ->
{node,3},
{cover,2},
{cover,3},
+ {cover_stop,2},
+ {cover_stop,3},
{config,2},
{config,3},
{config,4},
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 196b5e46d0..c9c6514fa4 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -38,6 +38,7 @@
verbosity=[],
silent_connections=[],
cover=[],
+ cover_stop=[],
config=[],
userconfig=[],
event_handler=[],
diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl
index 3af89db3a5..255f3ec78a 100644
--- a/lib/common_test/src/cth_conn_log.erl
+++ b/lib/common_test/src/cth_conn_log.erl
@@ -58,7 +58,7 @@
-spec init(Id, HookOpts) -> Result when
Id :: term(),
- HookOpts :: ct:hook_options(),
+ HookOpts :: ct_netconfc:hook_options(),
Result :: {ok,[{ct_netconfc:conn_mod(),
{ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}.
init(_Id, HookOpts) ->
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 77f57c6195..78ae70f37e 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -54,7 +54,7 @@ post_init_per_group(_Group, _Config, Result, State) ->
post_end_per_testcase(_TC, _Config, Result, State) ->
%% Make sure that the event queue is flushed
%% before ending this test case.
- gen_event:call(error_logger, ?MODULE, flush),
+ gen_event:call(error_logger, ?MODULE, flush, 300000),
{Result, State}.
pre_end_per_group(Group, Config, {ct_log, Group}) ->
diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl
index 76b0f0b5ea..e6eaad8d48 100644
--- a/lib/common_test/src/cth_surefire.erl
+++ b/lib/common_test/src/cth_surefire.erl
@@ -1,3 +1,22 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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 Framework functions handling test specifications.
%%%
%%% <p>This module creates a junit report of the test run if plugged in
@@ -27,18 +46,28 @@
-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,
+ curr_suite, curr_suite_ts, curr_group = [],
+ curr_log_dir, timer, tc_log, url_base,
test_cases = [],
test_suites = [] }).
--record(testcase, { log, group, classname, name, time, failure, timestamp }).
--record(testsuite, { errors, failures, hostname, name, tests,
+-record(testcase, { log, url, group, classname, name, time, result, timestamp }).
+-record(testsuite, { errors, failures, skipped, hostname, name, tests,
time, timestamp, id, package,
- properties, testcases }).
+ properties, testcases, log, url }).
+
+-define(default_report,"junit_report.xml").
+-define(suite_log,"suite.log.html").
+
+%% Number of dirs from log root to testcase log file.
+%% ct_run.<node>.<timestamp>/<test_name>/run.<timestamp>/<tc_log>.html
+-define(log_depth,3).
id(Opts) ->
- filename:absname(proplists:get_value(path, Opts, "junit_report.xml")).
+ case proplists:get_value(path, Opts) of
+ undefined -> ?default_report;
+ Path -> filename:absname(Path)
+ end.
init(Path, Opts) ->
{ok, Host} = inet:gethostname(),
@@ -47,10 +76,24 @@ init(Path, Opts) ->
package = proplists:get_value(package,Opts),
axis = proplists:get_value(axis,Opts,[]),
properties = proplists:get_value(properties,Opts,[]),
+ url_base = proplists:get_value(url_base,Opts),
timer = now() }.
pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) ->
- {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() },
+ TcLog = proplists:get_value(tc_logfile,Config),
+ CurrLogDir = filename:dirname(TcLog),
+ Path =
+ case State#state.filepath of
+ ?default_report ->
+ RootDir = get_test_root(TcLog),
+ filename:join(RootDir,?default_report);
+ P ->
+ P
+ end,
+ {Config, init_tc(State#state{ filepath = Path,
+ curr_suite = Suite,
+ curr_suite_ts = now(),
+ curr_log_dir = CurrLogDir},
Config) };
pre_init_per_suite(Suite,Config,State) ->
%% Have to close the previous suite
@@ -59,7 +102,8 @@ pre_init_per_suite(Suite,Config,State) ->
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)}.
+pre_end_per_suite(_Suite,Config,State) ->
+ {Config, init_tc(State, Config)}.
post_end_per_suite(_Suite,Config,Result,State) ->
{Result, end_tc(end_per_suite,Config,Result,State)}.
@@ -71,13 +115,15 @@ pre_init_per_group(Group,Config,State) ->
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)}.
+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)}.
+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)}.
@@ -88,11 +134,19 @@ on_tc_fail(_TC, Res, State) ->
TCs = State#state.test_cases,
TC = hd(TCs),
NewTC = TC#testcase{
- failure =
+ result =
{fail,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
-on_tc_skip(Tc,{Type,_Reason} = Res, State) when Type == tc_auto_skip ->
+on_tc_skip(Tc,{Type,_Reason} = Res, State0) when Type == tc_auto_skip ->
+ TcStr = atom_to_list(Tc),
+ State =
+ case State0#state.test_cases of
+ [#testcase{name=TcStr}|TCs] ->
+ State0#state{test_cases=TCs};
+ _ ->
+ State0
+ end,
do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[])));
on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) ->
State;
@@ -103,7 +157,7 @@ do_tc_skip(Res, State) ->
TCs = State#state.test_cases,
TC = hd(TCs),
NewTC = TC#testcase{
- failure =
+ result =
{skipped,lists:flatten(io_lib:format("~p",[Res]))} },
State#state{ test_cases = [NewTC | tl(TCs)]}.
@@ -117,33 +171,52 @@ 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 } ) ->
+ curr_log_dir = CurrLogDir,
+ timer = TS,
+ tc_log = Log0,
+ url_base = UrlBase } ) ->
+ Log =
+ case Log0 of
+ "" ->
+ LowerSuiteName = string:to_lower(atom_to_list(Suite)),
+ filename:join(CurrLogDir,LowerSuiteName++"."++Name++".html");
+ _ ->
+ Log0
+ end,
+ Url = make_url(UrlBase,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,
+ url = Url,
timestamp = now_to_string(TS),
classname = ClassName,
group = PGroup,
name = Name,
time = TimeTakes,
- failure = passed }| State#state.test_cases]}.
+ result = passed }|
+ State#state.test_cases],
+ tc_log = ""}. % so old tc_log is not set if next is on_tc_skip
close_suite(#state{ test_cases = [] } = State) ->
State;
-close_suite(#state{ test_cases = TCs } = State) ->
- Total = length(TCs),
- Succ = length(lists:filter(fun(#testcase{ failure = F }) ->
- F == passed
- end,TCs)),
- Fail = Total - Succ,
+close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) ->
+ {Total,Fail,Skip} = count_tcs(TCs,0,0,0),
TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000,
+ SuiteLog = filename:join(State#state.curr_log_dir,?suite_log),
+ SuiteUrl = make_url(UrlBase,SuiteLog),
Suite = #testsuite{ name = atom_to_list(State#state.curr_suite),
package = State#state.package,
+ hostname = State#state.hostname,
time = io_lib:format("~f",[TimeTaken]),
timestamp = now_to_string(State#state.curr_suite_ts),
- errors = Fail, tests = Total,
- testcases = lists:reverse(TCs) },
+ errors = 0,
+ failures = Fail,
+ skipped = Skip,
+ tests = Total,
+ testcases = lists:reverse(TCs),
+ log = SuiteLog,
+ url = SuiteUrl},
State#state{ test_cases = [],
test_suites = [Suite | State#state.test_suites]}.
@@ -159,14 +232,15 @@ terminate(State) ->
-to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) ->
+to_xml(#testcase{ group = Group, classname = CL, log = L, url = U, name = N, time = T, timestamp = TS, result = R}) ->
["<testcase ",
- [["group=\"",Group,"\""]||Group /= ""]," "
+ [["group=\"",Group,"\" "]||Group /= ""],
"name=\"",N,"\" "
"time=\"",T,"\" "
- "timestamp=\"",TS,"\" "
+ "timestamp=\"",TS,"\" ",
+ [["url=\"",U,"\" "]||U /= undefined],
"log=\"",L,"\">",
- case F of
+ case R of
passed ->
[];
{skipped,Reason} ->
@@ -176,22 +250,29 @@ to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, ti
["<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 }) ->
+to_xml(#testsuite{ package = P, hostname = H, errors = E, failures = F,
+ skipped = S, time = Time, timestamp = TS, tests = T, name = N,
+ testcases = Cases, log = Log, url = Url }) ->
["<testsuite ",
[["package=\"",P,"\" "]||P /= undefined],
- [["hostname=\"",P,"\" "]||H /= undefined],
- [["name=\"",N,"\" "]||N /= undefined],
- [["time=\"",Time,"\" "]||Time /= undefined],
- [["timestamp=\"",TS,"\" "]||TS /= undefined],
+ "hostname=\"",H,"\" "
+ "name=\"",N,"\" "
+ "time=\"",Time,"\" "
+ "timestamp=\"",TS,"\" "
"errors=\"",integer_to_list(E),"\" "
- "tests=\"",integer_to_list(T),"\">",
+ "failures=\"",integer_to_list(F),"\" "
+ "skipped=\"",integer_to_list(S),"\" "
+ "tests=\"",integer_to_list(T),"\" ",
+ [["url=\"",Url,"\" "]||Url /= undefined],
+ "log=\"",Log,"\">",
[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([],[]) ->
+ [];
properties_to_xml(Axis,Props) ->
["<properties>",
[["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis],
@@ -217,3 +298,37 @@ 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]).
+
+make_url(undefined,_) ->
+ undefined;
+make_url(_,[]) ->
+ undefined;
+make_url(UrlBase0,Log) ->
+ UrlBase = string:strip(UrlBase0,right,$/),
+ RelativeLog = get_relative_log_url(Log),
+ string:join([UrlBase,RelativeLog],"/").
+
+get_test_root(Log) ->
+ LogParts = filename:split(Log),
+ filename:join(lists:sublist(LogParts,1,length(LogParts)-?log_depth)).
+
+get_relative_log_url(Log) ->
+ LogParts = filename:split(Log),
+ Start = length(LogParts)-?log_depth,
+ Length = ?log_depth+1,
+ string:join(lists:sublist(LogParts,Start,Length),"/").
+
+count_tcs([#testcase{name=ConfCase}|TCs],Ok,F,S)
+ when ConfCase=="init_per_suite";
+ ConfCase=="end_per_suite";
+ ConfCase=="init_per_group";
+ ConfCase=="end_per_group" ->
+ count_tcs(TCs,Ok,F,S);
+count_tcs([#testcase{result=passed}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok+1,F,S);
+count_tcs([#testcase{result={fail,_}}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok,F+1,S);
+count_tcs([#testcase{result={skipped,_}}|TCs],Ok,F,S) ->
+ count_tcs(TCs,Ok,F,S+1);
+count_tcs([],Ok,F,S) ->
+ {Ok+F+S,F,S}.
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 374fd8a824..d469d03e04 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -52,7 +52,12 @@ MODULES= \
ct_auto_compile_SUITE \
ct_verbosity_SUITE \
ct_shell_SUITE \
- ct_groups_search_SUITE
+ ct_system_error_SUITE \
+ ct_snmp_SUITE \
+ ct_group_leader_SUITE \
+ ct_cover_SUITE \
+ ct_groups_search_SUITE \
+ ct_surefire_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -106,7 +111,7 @@ release_spec: opt
release_tests_spec:
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)"
- $(INSTALL_DATA) common_test.spec "$(RELSYSDIR)"
+ $(INSTALL_DATA) common_test.spec common_test.cover "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/common_test/test/common_test.cover b/lib/common_test/test/common_test.cover
new file mode 100644
index 0000000000..3aa49623e7
--- /dev/null
+++ b/lib/common_test/test/common_test.cover
@@ -0,0 +1,10 @@
+%% -*- erlang -*-
+{incl_app,common_test,details}.
+{cross,common_test,[{test_server,[erl2html2,
+ test_server,
+ test_server_ctrl,
+ test_server_gl,
+ test_server_h,
+ test_server_io,
+ test_server_node,
+ test_server_sup]}]}.
diff --git a/lib/common_test/test/ct_config_info_SUITE.erl b/lib/common_test/test/ct_config_info_SUITE.erl
index 40da377ee5..10fe8286dd 100644
--- a/lib/common_test/test/ct_config_info_SUITE.erl
+++ b/lib/common_test/test/ct_config_info_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2012. 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
@@ -123,8 +123,7 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,init_per_suite,ok}},
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g1,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{init_per_group,g1,[]},
{failed,{timetrap_timeout,350}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,t11,
{failed,{config_info_1_SUITE,init_per_group,{timetrap_timeout,350}}}}},
@@ -136,14 +135,12 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g2,[]},ok}},
{?eh,tc_done,{config_info_1_SUITE,t21,ok}},
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g2,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {end_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{end_per_group,g2,[]},
{failed,{timetrap_timeout,450}}}}],
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g3,[]},ok}},
[{?eh,tc_start,{config_info_1_SUITE,{init_per_group,g4,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{init_per_group,g4,[]},
{failed,{timetrap_timeout,400}}}},
{?eh,tc_auto_skip,{config_info_1_SUITE,t41,
{failed,{config_info_1_SUITE,init_per_group,
@@ -164,8 +161,7 @@ test_events(config_info) ->
{?eh,tc_done,{config_info_1_SUITE,{init_per_group,g5,[]},ok}},
{?eh,tc_done,{config_info_1_SUITE,t51,ok}},
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g5,[]}}},
- {?eh,tc_done,{config_info_1_SUITE,
- {end_per_group,unknown,[]},
+ {?eh,tc_done,{config_info_1_SUITE,{end_per_group,g5,[]},
{failed,{timetrap_timeout,400}}}}],
{?eh,tc_start,{config_info_1_SUITE,{end_per_group,g3,[]}}},
{?eh,tc_done,{config_info_1_SUITE,{end_per_group,g3,[]},ok}}],
diff --git a/lib/common_test/test/ct_cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE.erl
new file mode 100644
index 0000000000..cb49dc423f
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE.erl
@@ -0,0 +1,312 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_cover_SUITE
+%%%
+%%% Description:
+%%% Test code cover analysis support
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_cover_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+-define(suite, cover_SUITE).
+-define(mod, cover_test_mod).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ case test_server:is_cover() of
+ true ->
+ {skip,"Test server is running cover already - skipping"};
+ false ->
+ ct_test_support:init_per_suite(Config)
+ end.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ Node = fullname(existing_node),
+ case lists:member(Node,nodes()) of
+ true -> rpc:call(Node,erlang,halt,[]);
+ false -> ok
+ end,
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ default,
+ cover_stop_true,
+ cover_stop_false,
+ slave,
+ slave_start_slave,
+ cover_node_option,
+ ct_cover_add_remove_nodes,
+ otp_9956,
+ cross
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%% Check that cover is collected from test node
+%% Also check that cover is by default stopped after test is completed
+default(Config) ->
+ {ok,Events} = run_test(default,Config),
+ false = check_cover(Config),
+ check_calls(Events,1),
+ ok.
+
+%% Check that cover is stopped when cover_stop option is set to true
+cover_stop_true(Config) ->
+ {ok,_Events} = run_test(cover_stop_true,[{cover_stop,true}],Config),
+ false = check_cover(Config).
+
+%% Check that cover is not stopped when cover_stop option is set to false
+cover_stop_false(Config) ->
+ {ok,_Events} = run_test(cover_stop_false,[{cover_stop,false}],Config),
+ {true,[],[?mod]} = check_cover(Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ ok = rpc:call(CTNode,cover,stop,[]),
+ false = check_cover(Config),
+ ok.
+
+%% Let test node start a slave node - check that cover is collected
+%% from both nodes
+slave(Config) ->
+ {ok,Events} = run_test(slave,slave,[],Config),
+ check_calls(Events,2),
+ ok.
+
+%% Let test node start a slave node which in turn starts another slave
+%% node - check that cover is collected from all three nodes
+slave_start_slave(Config) ->
+ {ok,Events} = run_test(slave_start_slave,slave_start_slave,[],Config),
+ check_calls(Events,3),
+ ok.
+
+%% Start a slave node before test starts - the node is listed in cover
+%% spec file.
+%% Check that cover is collected from test node and slave node.
+cover_node_option(Config) ->
+ {ok, HostStr}=inet:gethostname(),
+ Host = list_to_atom(HostStr),
+ DataDir = ?config(data_dir,Config),
+ {ok,Node} = ct_slave:start(Host,existing_node,
+ [{erl_flags,"-pa " ++ DataDir}]),
+ false = check_cover(Node),
+ CoverSpec = default_cover_file_content() ++ [{nodes,[Node]}],
+ CoverFile = create_cover_file(cover_node_option,CoverSpec,Config),
+ {ok,Events} = run_test(cover_node_option,cover_node_option,
+ [{cover,CoverFile}],Config),
+ check_calls(Events,2),
+ {ok,Node} = ct_slave:stop(existing_node),
+ ok.
+
+%% Test ct_cover:add_nodes/1 and ct_cover:remove_nodes/1
+%% Check that cover is collected from added node
+ct_cover_add_remove_nodes(Config) ->
+ {ok, HostStr}=inet:gethostname(),
+ Host = list_to_atom(HostStr),
+ DataDir = ?config(data_dir,Config),
+ {ok,Node} = ct_slave:start(Host,existing_node,
+ [{erl_flags,"-pa " ++ DataDir}]),
+ false = check_cover(Node),
+ {ok,Events} = run_test(ct_cover_add_remove_nodes,ct_cover_add_remove_nodes,
+ [],Config),
+ check_calls(Events,2),
+ {ok,Node} = ct_slave:stop(existing_node),
+ ok.
+
+%% Test that the test suite itself can be cover compiled and that
+%% data_dir is set correctly (OTP-9956)
+otp_9956(Config) ->
+ CoverFile = create_cover_file(otp_9956,[{incl_mods,[?suite]}],Config),
+ {ok,Events} = run_test(otp_9956,otp_9956,[{cover,CoverFile}],Config),
+ check_calls(Events,{?suite,otp_9956,1},1),
+ ok.
+
+%% Test cross cover mechanism
+cross(Config) ->
+ {ok,Events1} = run_test(cross1,Config),
+ check_calls(Events1,1),
+
+ CoverFile2 = create_cover_file(cross1,[{cross,[{cross1,[?mod]}]}],Config),
+ {ok,Events2} = run_test(cross2,[{cover,CoverFile2}],Config),
+ check_calls(Events2,1),
+
+ %% Get the log dirs for each test and run cross cover analyse
+ [D11,D12] = lists:sort(get_run_dirs(Events1)),
+ [D21,D22] = lists:sort(get_run_dirs(Events2)),
+
+ ct_cover:cross_cover_analyse(details,[{cross1,D11},{cross2,D21}]),
+ ct_cover:cross_cover_analyse(details,[{cross1,D12},{cross2,D22}]),
+
+ %% Get the cross cover logs and read for each test
+ [C11,C12,C21,C22] =
+ [filename:join(D,"cross_cover.html") || D <- [D11,D12,D21,D22]],
+
+ {ok,CrossData} = file:read_file(C11),
+ {ok,CrossData} = file:read_file(C12),
+
+ {ok,Def} = file:read_file(C21),
+ {ok,Def} = file:read_file(C22),
+
+ %% A simple test: just check that the test module exists in the
+ %% log from cross1 test, and that it does not exist in the log
+ %% from cross2 test.
+ TestMod = list_to_binary(atom_to_list(?mod)),
+ {_,_} = binary:match(CrossData,TestMod),
+ nomatch = binary:match(Def,TestMod),
+ {_,_} = binary:match(Def,
+ <<"No cross cover modules exist for this application">>),
+
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+run_test(Label,Config) ->
+ run_test(Label,[],Config).
+run_test(Label,ExtraOpts,Config) ->
+ run_test(Label,default,ExtraOpts,Config).
+run_test(Label,Testcase,ExtraOpts,Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, ?suite),
+ CoverFile =
+ case proplists:get_value(cover,ExtraOpts) of
+ undefined ->
+ create_default_cover_file(Label,Config);
+ CF ->
+ CF
+ end,
+ RestOpts = lists:keydelete(cover,1,ExtraOpts),
+ {Opts,ERPid} = setup([{suite,Suite},{testcase,Testcase},
+ {cover,CoverFile},{label,Label}] ++ RestOpts, Config),
+ execute(Label, Testcase, Opts, ERPid, Config).
+
+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, Testcase, 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(Testcase),
+ R = ct_test_support:verify_events(TestEvents, Events, Config),
+ {R,Events}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+events_to_check(Testcase) ->
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{?suite,Testcase,ok}}] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
+
+check_cover(Config) when is_list(Config) ->
+ CTNode = proplists:get_value(ct_node, Config),
+ check_cover(CTNode);
+check_cover(Node) when is_atom(Node) ->
+ case rpc:call(Node,test_server,is_cover,[]) of
+ true ->
+ {true,
+ rpc:call(Node,cover,which_nodes,[]),
+ rpc:call(Node,cover,modules,[])};
+ false ->
+ false
+ end.
+
+%% Get the log dir "run.<timestamp>" for all (both!) tests
+get_run_dirs(Events) ->
+ [filename:dirname(TCLog) ||
+ {ct_test_support_eh,
+ {event,tc_logfile,_Node,
+ {{?suite,init_per_suite},TCLog}}} <- Events].
+
+%% Check that each coverlog includes N calls to ?mod:foo/0
+check_calls(Events,N) ->
+ check_calls(Events,{?mod,foo,0},N).
+check_calls(Events,MFA,N) ->
+ CoverLogs = [filename:join(D,"all.coverdata") || D <- get_run_dirs(Events)],
+ do_check_logs(CoverLogs,MFA,N).
+
+do_check_logs([CoverLog|CoverLogs],{Mod,_,_} = MFA,N) ->
+ {ok,_} = cover:start(),
+ ok = cover:import(CoverLog),
+ {ok,Calls} = cover:analyse(Mod,calls,function),
+ ok = cover:stop(),
+ {MFA,N} = lists:keyfind(MFA,1,Calls),
+ do_check_logs(CoverLogs,MFA,N);
+do_check_logs([],_,_) ->
+ ok.
+
+fullname(Name) ->
+ {ok,Host} = inet:gethostname(),
+ list_to_atom(atom_to_list(Name) ++ "@" ++ Host).
+
+default_cover_file_content() ->
+ [{incl_mods,[?mod]}].
+
+create_default_cover_file(Filename,Config) ->
+ create_cover_file(Filename,default_cover_file_content(),Config).
+
+create_cover_file(Filename,Terms,Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ File = filename:join(PrivDir,Filename) ++ ".cover",
+ {ok,Fd} = file:open(File,[write]),
+ lists:foreach(fun(Term) ->
+ file:write(Fd,io_lib:format("~p.~n",[Term]))
+ end,Terms),
+ ok = file:close(Fd),
+ File.
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
new file mode 100644
index 0000000000..fdc3323f0a
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
@@ -0,0 +1,156 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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: cover_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the code coverage support
+%%
+%% @author Support
+%% @doc Test of code coverage support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(cover_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+suite() ->
+ [].
+
+all() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case, Config) ->
+ %% try apply(?MODULE,Case,[cleanup,Config])
+ %% catch error:undef -> ok
+ %% end,
+
+ kill_slaves(Case,nodes()),
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+default(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ ok.
+
+slave(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ N1 = nodename(slave,1),
+ {ok,Node} = ct_slave:start(N1),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ {ok,Node} = ct_slave:stop(N1),
+ ok.
+
+slave_start_slave(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ N1 = nodename(slave_start_slave,1),
+ N2 = nodename(slave_start_slave,2),
+ {ok,Node} = ct_slave:start(N1),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ {ok,Node2} = rpc:call(Node,ct_slave,start,[N2]),
+ rpc:call(Node2,cover_test_mod,foo,[]),
+ {ok,Node2} = rpc:call(Node,ct_slave,stop,[N2]),
+ {ok,Node} = ct_slave:stop(N1),
+ ok.
+
+cover_node_option(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ Node = fullname(existing_node),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]),
+ ok.
+
+ct_cover_add_remove_nodes(Config) ->
+ cover_compiled = code:which(cover_test_mod),
+ cover_test_mod:foo(),
+ Node = fullname(existing_node),
+ Beam = rpc:call(Node,code,which,[cover_test_mod]),
+ false = (Beam == cover_compiled),
+
+ rpc:call(Node,cover_test_mod,foo,[]), % should not be collected
+ {ok,[Node]} = ct_cover:add_nodes([Node]),
+ cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
+ rpc:call(Node,cover_test_mod,foo,[]), % should be collected
+ ok = ct_cover:remove_nodes([Node]),
+ rpc:call(Node,cover_test_mod,foo,[]), % should not be collected
+
+ Beam = rpc:call(Node,code,which,[cover_test_mod]),
+
+ ok.
+
+otp_9956(Config) ->
+ cover_compiled = code:which(?MODULE),
+ DataDir = ?config(data_dir,Config),
+ absolute = filename:pathtype(DataDir),
+ true = filelib:is_dir(DataDir),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% Internal
+nodename(Case,N) ->
+ list_to_atom(nodeprefix(Case) ++ integer_to_list(N)).
+
+nodeprefix(Case) ->
+ atom_to_list(?MODULE) ++ "_" ++ atom_to_list(Case) ++ "_node".
+
+
+fullname(Name) ->
+ {ok,Host} = inet:gethostname(),
+ list_to_atom(atom_to_list(Name) ++ "@" ++ Host).
+
+kill_slaves(Case, [Node|Nodes]) ->
+ Prefix = nodeprefix(Case),
+ case lists:prefix(Prefix,atom_to_list(Node)) of
+ true ->
+ rpc:call(Node,erlang,halt,[]);
+ _ ->
+ ok
+ end,
+ kill_slaves(Case,Nodes);
+kill_slaves(_,[]) ->
+ ok.
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE_data/.gitignore
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl
new file mode 100644
index 0000000000..d4f69452c3
--- /dev/null
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_test_mod.erl
@@ -0,0 +1,4 @@
+-module(cover_test_mod).
+-compile(export_all).
+foo() ->
+ ok.
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index 338e76264e..6d90b29f41 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -61,7 +61,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[cfg_error, lib_error, no_compile, timetrap_end_conf,
timetrap_normal, timetrap_extended, timetrap_parallel,
- timetrap_fun, misc_errors].
+ timetrap_fun, timetrap_fun_group, misc_errors].
groups() ->
[].
@@ -251,6 +251,24 @@ timetrap_fun(Config) when is_list(Config) ->
%%%-----------------------------------------------------------------
%%%
+timetrap_fun_group(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
+ Suites = [Join(DataDir, "timetrap_8_SUITE")],
+ {Opts,ERPid} = setup([{suite,Suites}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(timetrap_fun_group,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(timetrap_fun_group),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
misc_errors(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
@@ -429,8 +447,7 @@ test_events(cfg_error) ->
{'EXIT',{init_per_group_fails,g1}}}}}}],
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g2,[]}}},
- {?eh,tc_done,{cfg_error_8_SUITE,
- {init_per_group,unknown,[]},
+ {?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g2,[]},
{failed,{timetrap_timeout,2000}}}},
{?eh,tc_auto_skip,{cfg_error_8_SUITE,tc1,
{failed,{cfg_error_8_SUITE,init_per_group,
@@ -500,7 +517,7 @@ test_events(cfg_error) ->
{?eh,tc_done,{cfg_error_8_SUITE,tc1,ok}},
{?eh,test_stats,{9,0,{0,14}}},
{?eh,tc_start,{cfg_error_8_SUITE,{end_per_group,g12,[]}}},
- {?eh,tc_done,{cfg_error_8_SUITE,{end_per_group,unknown,[]},
+ {?eh,tc_done,{cfg_error_8_SUITE,{end_per_group,g12,[]},
{failed,{timetrap_timeout,2000}}}}],
{?eh,tc_start,{cfg_error_8_SUITE,end_per_suite}},
@@ -971,11 +988,423 @@ test_events(timetrap_fun) ->
{?eh,stop_logging,[]}
];
+test_events(timetrap_fun_group) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,58}},
+ {?eh,tc_start,{timetrap_8_SUITE,init_per_suite}},
+ {?eh,tc_done,{timetrap_8_SUITE,init_per_suite,ok}},
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g0,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g0,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g0,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g0,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g1,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,5,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,6,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g2,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g3,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g3,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc4}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc4,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{0,7,{0,0}}},
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g1,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,8,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,9,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g1,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g1,[]},ok}}],
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g2,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{0,10,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{0,11,{0,0}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g2,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g2,[]},ok}}],
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g3,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g3,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g4,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g4,[]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,1}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,2}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g5,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g5,[]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,3}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{0,11,{0,4}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g6,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g6,[]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{0,11,{0,5}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{0,11,{0,6}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g7,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g7,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{1,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g7,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g7,[]},
+ {user_timetrap_error,{kaboom,'_'}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g8,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g8,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{2,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g8,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g8,[]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g9,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g9,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{3,11,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{3,12,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g9,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g9,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g10,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g10,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{3,13,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{4,13,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g10,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g10,[]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,g11,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,g11,[]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',4000}}}}},
+ {?eh,test_stats,{4,14,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,15,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,g11,[]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,g11,[]},ok}}],
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg0,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg0,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,16,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,17,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg0,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg0,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,18,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,19,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,20,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,21,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg3,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg3,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc4}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc4,
+ {failed,{timetrap_timeout,{'$approx',2000}}}}},
+ {?eh,test_stats,{4,22,{0,6}}},
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg1,[parallel]},
+ ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,23,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,24,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg1,[parallel]},
+ ok}}]},
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg2,[parallel]},
+ ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc1}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{4,25,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{4,26,{0,6}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg2,[parallel]},
+ ok}}]},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg3,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg3,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg4,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,7}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,8}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg5,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,9}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}},
+ {?eh,test_stats,{4,26,{0,10}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {user_timetrap_error,{kaboom,'_'}}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg6,[parallel]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{4,26,{0,11}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}},
+ {?eh,test_stats,{4,26,{0,12}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,end_per_group,
+ {failed,{timetrap_8_SUITE,init_per_group,
+ {timetrap_timeout,'_'}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg7,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg7,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{5,26,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg7,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg7,[parallel]},
+ {user_timetrap_error,{kaboom,'_'}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg8,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg8,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{6,26,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg8,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg8,[parallel]},
+ {failed,{timetrap_timeout,{'$approx',500}}}}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg9,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg9,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ %% Due to parallelism only checking final test stat in group
+ {?eh,test_stats,{7,27,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg9,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg9,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg10,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg10,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ %% Due to parallelism only checking final test stat in group
+ {?eh,test_stats,{8,28,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg10,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg10,[parallel]},ok}}]},
+
+ {parallel,
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,pg11,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,pg11,[parallel]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc3}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc3,
+ {failed,{timetrap_timeout,{'$approx',4000}}}}},
+ {?eh,test_stats,{8,29,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc2}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_timeout,{'$approx',500}}}}},
+ {?eh,test_stats,{8,30,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,pg11,[parallel]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,pg11,[parallel]},ok}}]},
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,sg1,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,sg1,[sequence]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{9,30,{0,12}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {user_timetrap_error,{kaboom,'_'}}}},
+ {?eh,test_stats,{9,31,{0,12}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{9,31,{0,13}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{9,31,{0,14}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,sg1,[sequence]},ok}}],
+
+ [{?eh,tc_start,{timetrap_8_SUITE,{init_per_group,sg2,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{init_per_group,sg2,[sequence]},ok}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc5}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc5,ok}},
+ {?eh,test_stats,{10,31,{0,14}}},
+ {?eh,tc_start,{timetrap_8_SUITE,tc0}},
+ {?eh,tc_done,{timetrap_8_SUITE,tc0,
+ {failed,{timetrap_timeout,{'$approx',1000}}}}},
+ {?eh,test_stats,{10,32,{0,14}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc1,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{10,32,{0,15}}},
+ {?eh,tc_auto_skip,{timetrap_8_SUITE,tc2,
+ {failed,{timetrap_8_SUITE,tc0}}}},
+ {?eh,test_stats,{10,32,{0,16}}},
+ {?eh,tc_start,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]}}},
+ {?eh,tc_done,{timetrap_8_SUITE,{end_per_group,sg2,[sequence]},ok}}],
+
+ {?eh,tc_start,{timetrap_8_SUITE,end_per_suite}},
+ {?eh,tc_done,{timetrap_8_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
test_events(misc_errors) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{1,1,7}},
+ {?eh,start_info,{1,1,9}},
{?eh,tc_start,{misc_error_1_SUITE,ct_fail_1}},
{?eh,tc_done,{misc_error_1_SUITE,ct_fail_1,
{failed,{error,{test_case_failed,{error,this_is_expected}}}}}},
@@ -1002,7 +1431,12 @@ test_events(misc_errors) ->
{?eh,tc_start,{misc_error_1_SUITE,killed_by_signal_2}},
{?eh,tc_done,{misc_error_1_SUITE,killed_by_signal_2,
{failed,testcase_aborted_or_killed}}},
- {?eh,test_stats,{0,7,{0,0}}},
+ {parallel,
+ [{?eh,tc_start,{misc_error_1_SUITE,p1}},
+ {?eh,tc_done,{misc_error_1_SUITE,p1,ok}},
+ {?eh,tc_start,{misc_error_1_SUITE,p2}},
+ {?eh,tc_done,{misc_error_1_SUITE,p2,ok}}]},
+ {?eh,test_stats,{2,7,{0,0}}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
index 99c3ed05ec..61f3fa7e59 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl
@@ -96,7 +96,7 @@ end_per_testcase(_TestCase, _Config) ->
%% N = integer() | forever
%%--------------------------------------------------------------------
groups() ->
- [].
+ [{p,[parallel],[p1,p2]}].
%%--------------------------------------------------------------------
%% Function: all() -> GroupsAndTestCases | {skip,Reason}
@@ -107,7 +107,8 @@ groups() ->
%%--------------------------------------------------------------------
all() ->
[ct_fail_1, ct_fail_2, ct_fail_3, ts_fail_1, ts_fail_2,
- killed_by_signal_1, killed_by_signal_2].
+ killed_by_signal_1, killed_by_signal_2,
+ {group,p}].
ct_fail_1(_) ->
ct:fail({error,this_is_expected}),
@@ -152,3 +153,10 @@ killed_by_signal_2(_) ->
end),
ct:sleep(1000),
exit(this_should_not_be_seen).
+
+p1(_) ->
+ {error,parallel_group} = ct:abort_current_testcase(aborted),
+ ok.
+
+p2(_) ->
+ receive after 1000 -> ok end.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl
new file mode 100644
index 0000000000..ff138f38b5
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_8_SUITE.erl
@@ -0,0 +1,258 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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(timetrap_8_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(TO, 4).
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,?TO}]}}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(G6, Config) when G6==g6; G6==pg6 ->
+ ct:sleep({seconds,1}),
+ Config;
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(G7or8, _Config) when G7or8==g7; G7or8==pg7; G7or8==g8; G7or8==pg8 ->
+ ct:sleep({seconds,5}),
+ ok;
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(_, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: 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
+%%--------------------------------------------------------------------
+groups() ->
+ [
+ {g0,[],[tc0,tc2]}, % group override suite and tc overrides group
+ {g1,[],[tc0,tc2]}, % group override suite and tc overrides group
+ {g2,[],[tc1,tc2]}, % tc override group
+ {g3,[],[tc4,{group,g1},{group,g2}]}, % subgroup override group
+ {g4,[],[tc0,tc2]}, % exit during init_per_group
+ {g5,[],[tc0,tc2]}, % exit during init_per_group
+ {g6,[],[tc0,tc2]}, % timeout during init_per_group
+ {g7,[],[tc5]}, % exit during end_per_group
+ {g8,[],[tc5]}, % timeout during end_per_group
+ {g9,[],[tc5,tc0]}, % exit during testcase
+ {g10,[],[tc0,tc5]}, % exit during testcase
+ {g11,[],[tc3,tc2]}, % suite is valid if nothing else is specified
+ {pg0,[parallel],[tc0,tc2]}, % group override suite and tc overrides group
+ {pg1,[parallel],[tc0,tc2]}, % group override suite and tc overrides group
+ {pg2,[parallel],[tc1,tc2]}, % tc override group
+ {pg3,[parallel],[tc4,{group,pg1},{group,pg2}]}, % subgroup override group
+ {pg4,[parallel],[tc0,tc2]}, % exit during init_per_group
+ {pg5,[parallel],[tc0,tc2]}, % exit during init_per_group
+ {pg6,[parallel],[tc0,tc2]}, % timeout during init_per_group
+ {pg7,[parallel],[tc5]}, % exit during end_per_group
+ {pg8,[parallel],[tc5]}, % timeout during end_per_group
+ {pg9,[parallel],[tc5,tc0]}, % exit during testcase
+ {pg10,[parallel],[tc0,tc5]},% exit during testcase
+ {pg11,[parallel],[tc3,tc2]},% suite is valid if nothing else is specified
+ {sg1,[sequence],[tc5,tc0,tc1,tc2]}, % exit during sequencial testcase
+ {sg2,[sequence],[tc5,tc0,tc1,tc2]}].% timeout during sequencial testcase
+
+group(g0) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}];
+group(g1) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(1000) end}];
+group(g2) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(3000) end}];
+group(g3) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(2000) end}];
+group(g4) ->
+ [{timetrap,{timetrap_utils,timetrap_exit,[kaboom]}}];
+group(g5) ->
+ [{timetrap,fun() -> exit(kaboom) end}];
+group(g6) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(g7) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g8) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(g9) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g10) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(g11) ->
+ [];
+group(pg0) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}];
+group(pg1) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(1000) end}];
+group(pg2) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(3000) end}];
+group(pg3) ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(2000) end}];
+group(pg4) ->
+ [{timetrap,{timetrap_utils,timetrap_exit,[kaboom]}}];
+group(pg5) ->
+ [{timetrap,fun() -> exit(kaboom) end}];
+group(pg6) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(pg7) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg8) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[500]}}];
+group(pg9) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg10) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(pg11) ->
+ [];
+group(sg1) ->
+ [{timetrap,fun() -> ct:sleep(1000),exit(kaboom) end}];
+group(sg2) ->
+ [{timetrap,{timetrap_utils,timetrap_val,[{seconds,1}]}}].
+
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group,g0},
+ {group,g1},
+ {group,g2},
+ {group,g3},
+ {group,g4},
+ {group,g5},
+ {group,g6},
+ {group,g7},
+ {group,g8},
+ {group,g9},
+ {group,g10},
+ {group,g11},
+ {group,pg0},
+ {group,pg1},
+ {group,pg2},
+ {group,pg3},
+ {group,pg4},
+ {group,pg5},
+ {group,pg6},
+ {group,pg7},
+ {group,pg8},
+ {group,pg9},
+ {group,pg10},
+ {group,pg11},
+ {group,sg1},
+ {group,sg2}].
+
+
+
+tc0(_) ->
+ ct:comment("TO set by group"),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc1() ->
+ [{timetrap,{timetrap_utils,timetrap_val,[1000]}}].
+tc1(_) ->
+ ct:comment("TO after 1 sec"),
+ ct:sleep({seconds,2}),
+ ok.
+
+tc2() ->
+ [{timetrap,fun() -> timetrap_utils:timetrap_val(500) end}].
+tc2(_) ->
+ ct:comment("TO after 0.5 sec"),
+ ct:sleep({seconds,2}),
+ ok.
+
+tc3(_) ->
+ ct:comment(io_lib:format("TO after ~w sec", [?TO])),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc4(_) ->
+ ct:comment("TO set by group"),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc5(_) ->
+ ct:comment("No TO in this testcase, maybe later"),
+ ok.
diff --git a/lib/common_test/test/ct_group_leader_SUITE.erl b/lib/common_test/test/ct_group_leader_SUITE.erl
new file mode 100644
index 0000000000..cde3061d6a
--- /dev/null
+++ b/lib/common_test/test/ct_group_leader_SUITE.erl
@@ -0,0 +1,181 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_system_error_SUITE
+%%%
+%%% Description:
+%%%
+%%% Test the group leader functionality in the test_server application.
+%%%-------------------------------------------------------------------
+-module(ct_group_leader_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() ->
+ [
+ basic
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+basic(Config) ->
+ TC = basic,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "group_leader_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ SuiteLog = execute(TC, Opts, ERPid, Config),
+ {ok,Data} = file:read_file(SuiteLog),
+ Lines = binary:split(Data, <<"\n">>, [global]),
+ {ok,RE} = re:compile("(\\S+):(\\S+)$"),
+ Cases0 = [begin
+ {match,[M,F]} = re:run(Case, RE, [{capture,all_but_first,list}]),
+ {list_to_atom(M),list_to_atom(F)}
+ end || <<"=case ",Case/binary>> <- Lines],
+ Cases = [MF || {_,F}=MF <- Cases0,
+ F =/= init_per_suite,
+ F =/= end_per_suite,
+ F =/= init_per_group,
+ F =/= end_per_group],
+ io:format("~p\n", [Cases]),
+ [] = verify_cases(events_to_check(TC), Cases, false),
+ ok.
+
+verify_cases([{parallel,P}|Ts], Cases0, Par) ->
+ Cases = verify_cases(P, Cases0, true),
+ verify_cases(Ts, Cases, Par);
+verify_cases([{?eh,tc_done,{M,F,_}}|Ts], Cases0, false) ->
+ [{M,F}|Cases] = Cases0,
+ verify_cases(Ts, Cases, false);
+verify_cases([{?eh,tc_done,{M,F,_}}|Ts], Cases0, true) ->
+ case lists:member({M,F}, Cases0) of
+ true ->
+ Cases = Cases0 -- [{M,F}],
+ verify_cases(Ts, Cases, true);
+ false ->
+ io:format("~p not found\n", [{M,F}]),
+ ?t:fail()
+ end;
+verify_cases([{?eh,_,_}|Ts], Cases, Par) ->
+ verify_cases(Ts, Cases, Par);
+verify_cases([], Cases, _) ->
+ Cases;
+verify_cases([List|Ts], Cases0, Par) when is_list(List) ->
+ Cases = verify_cases(List, Cases0, false),
+ verify_cases(Ts, Cases, Par).
+
+%%%-----------------------------------------------------------------
+%%% 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),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config),
+ {event,tc_logfile,_,{_,File}} =
+ lists:keyfind(tc_logfile, 2, [Ev || {?eh,Ev} <- Events]),
+ LogDir = filename:dirname(File),
+ filename:join(LogDir, "suite.log").
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+events_to_check(_Test) ->
+ [{?eh,tc_done,{group_leader_SUITE,tc1,ok}},
+ {parallel,[{?eh,tc_start,{group_leader_SUITE,p1}},
+ {?eh,tc_done,{group_leader_SUITE,p1,ok}},
+ {?eh,tc_start,{group_leader_SUITE,p2}},
+ {?eh,tc_done,{group_leader_SUITE,p2,ok}}]},
+ {?eh,tc_done,{group_leader_SUITE,p_restart_my_io_server,ok}},
+ {?eh,tc_done,{group_leader_SUITE,p3,ok}},
+ {parallel,[
+ {?eh,tc_start,{group_leader_SUITE,p10}},
+ {?eh,tc_start,{group_leader_SUITE,p11}},
+ {?eh,tc_done,{group_leader_SUITE,p10,ok}},
+ {?eh,tc_done,{group_leader_SUITE,p11,ok}},
+ [{?eh,tc_done,{group_leader_SUITE,s1,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s2,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s3,ok}}],
+ {?eh,tc_start,{group_leader_SUITE,p12}},
+ {?eh,tc_done,{group_leader_SUITE,p12,ok}},
+ [{?eh,tc_done,{group_leader_SUITE,s4,ok}},
+ {?eh,tc_done,{group_leader_SUITE,s5,ok}}],
+ {?eh,tc_start,{group_leader_SUITE,p13}},
+ {?eh,tc_done,{group_leader_SUITE,p13,ok}} ]},
+ {?eh,tc_done,{group_leader_SUITE,cap1,ok}},
+ {?eh,tc_done,{group_leader_SUITE,cap2,ok}},
+ {parallel,[{?eh,tc_start,{group_leader_SUITE,cap1}},
+ {?eh,tc_done,{group_leader_SUITE,cap1,ok}},
+ {?eh,tc_start,{group_leader_SUITE,cap2}},
+ {?eh,tc_done,{group_leader_SUITE,cap2,ok}}]},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl
new file mode 100644
index 0000000000..3f1844b4ae
--- /dev/null
+++ b/lib/common_test/test/ct_group_leader_SUITE_data/group_leader_SUITE.erl
@@ -0,0 +1,252 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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(group_leader_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ start_my_io_server(),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ my_io_server ! die,
+ 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() ->
+ [{p,[parallel],[p1,p2]},
+ {p_restart,[parallel],[p_restart_my_io_server]},
+ {seq,[],[s1,s2,s3]},
+ {seq2,[],[s4,s5]},
+ {seq_in_par,[parallel],[p10,p11,{group,seq},p12,{group,seq2},p13]},
+ {capture_io,[parallel],[cap1,cap2]}].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,{group,p},{group,p_restart},p3,
+ {group,seq_in_par},
+ cap1,cap2,
+ {group,capture_io}].
+
+tc1(_C) ->
+ ok.
+
+p1(_) ->
+ %% OTP-10101:
+ %%
+ %% External apps/processes started by init_per_suite (common operation),
+ %% will inherit the group leader of the init_per_suite process, i.e. the
+ %% test_server test case control process (executing run_test_case_msgloop/7).
+ %% If, later, a parallel test case triggers the external app to print with
+ %% e.g. io:format() (also common operation), the calling process will hang!
+ %% The reason for this is that a parallel test case has a dedicated IO
+ %% server process, other than the central test case control process. The
+ %% latter process is not executing run_test_case_msgloop/7 and will not
+ %% respond to IO messages. The process is still group leader for the
+ %% external app, however, which is wrong. It's the IO process for the
+ %% parallel test case that should be group leader - but only for the
+ %% particular invokation, since other parallel test cases could be
+ %% invoking the external app too.
+ print("hej\n").
+
+p2(_) ->
+ print("hopp\n").
+
+p_restart_my_io_server(_) ->
+ %% Restart the IO server and change its group leader. This used
+ %% to set to the group leader to a process that would soon die.
+ Ref = erlang:monitor(process, my_io_server),
+ my_io_server ! die,
+ receive
+ {'DOWN',Ref,_,_,_} ->
+ start_my_io_server()
+ end.
+
+p3(_) ->
+ %% OTP-10125. This would crash since the group leader process
+ %% for the my_io_server had died.
+ print("hoppsan\n").
+
+print(String) ->
+ my_io_server ! {print,self(),String},
+ receive
+ {printed,String} ->
+ ok
+ end.
+
+start_my_io_server() ->
+ Parent = self(),
+ Pid = spawn(fun() -> my_io_server(Parent) end),
+ receive
+ {Pid,started} ->
+ io:format("~p\n", [process_info(Pid)]),
+ ok
+ end.
+
+my_io_server(Parent) ->
+ register(my_io_server, self()),
+ Parent ! {self(),started},
+ my_io_server_loop().
+
+my_io_server_loop() ->
+ receive
+ {print,From,String} ->
+ io:put_chars(String),
+ From ! {printed,String},
+ my_io_server_loop();
+ die ->
+ ok
+ end.
+
+p10(_) ->
+ receive after 1 -> ok end.
+
+p11(_) ->
+ ok.
+
+p12(_) ->
+ ok.
+
+p13(_) ->
+ ok.
+
+s1(_) ->
+ ok.
+
+s2(_) ->
+ ok.
+
+s3(_) ->
+ ok.
+
+s4(_) ->
+ ok.
+
+s5(_) ->
+ ok.
+
+cap1(_) ->
+ ct:capture_start(),
+ IO = gen_io(cap1, 10, []),
+ ct:capture_stop(),
+ IO = ct:capture_get(),
+ ok.
+
+cap2(_) ->
+ ct:capture_start(),
+ {Pid,Ref} = spawn_monitor(fun() ->
+ exit(gen_io(cap2, 42, []))
+ end),
+ receive
+ {'DOWN',Ref,process,Pid,IO} ->
+ ct:capture_stop(),
+ IO = ct:capture_get(),
+ ok
+ end.
+
+gen_io(_, 0, Acc) ->
+ lists:reverse(Acc);
+gen_io(Label, N, Acc) ->
+ S = lists:flatten(io_lib:format("~s: ~p\n", [Label,N])),
+ io:put_chars(S),
+ gen_io(Label, N-1, [S|Acc]).
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
index 27243a0067..56a343a96f 100644
--- a/lib/common_test/test/ct_master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -117,14 +117,8 @@ ct_master_test(Config) when is_list(Config) ->
reformat(Events, ?eh),
PrivDir, []),
- find_events(NodeNames, [{tc_start,{master_SUITE,init_per_suite}},
- {tc_start,{master_SUITE,first_testcase}},
- {tc_start,{master_SUITE,second_testcase}},
- {tc_start,{master_SUITE,third_testcase}},
- {tc_start,{master_SUITE,end_per_suite}}],
- Events),
-
- ok.
+ TestEvents = events_to_check(ct_master_test),
+ ok = find_events(NodeNames, TestEvents, Events, Config).
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
@@ -153,13 +147,15 @@ make_spec(DataDir, FileName, NodeNames, Suites, Config) ->
CM = [{config,master,filename:join(DataDir,"master/config.txt")}],
+ Env = [{"THIS_MUST_BE_SET","yes"},
+ {"SO_MUST_THIS","value"}],
NS = lists:map(
fun(NodeName) ->
{init,NodeName,[
{node_start,[{startup_functions,[]},
- {monitor_master,true}]},
- {eval,{erlang,nodes,[]}}
- ]
+ {monitor_master,true},
+ {env,Env}]},
+ {eval,{erlang,nodes,[]}}]
}
end,
NodeNames),
@@ -199,7 +195,6 @@ run_test(_Name, FileName, Config) ->
[{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).
@@ -210,28 +205,26 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
-find_events([], _CheckEvents, _) ->
- ok;
-find_events([NodeName|NodeNames],CheckEvents,AllEvents) ->
- find_events(NodeNames, CheckEvents,
- remove_events(add_host(NodeName),CheckEvents, AllEvents, [])).
-
-remove_events(Node,[{Name,Data} | RestChecks],
- [{?eh,#event{ name = Name, node = Node, data = Data }}|RestEvs],
- Acc) ->
- remove_events(Node, RestChecks, RestEvs, Acc);
-remove_events(Node, Checks, [Event|RestEvs], Acc) ->
- remove_events(Node, Checks, RestEvs, [Event | Acc]);
-remove_events(_Node, [], [], Acc) ->
- lists:reverse(Acc);
-remove_events(Node, Events, [], Acc) ->
- test_server:format("Could not find events: ~p in ~p for node ~p",
- [Events, lists:reverse(Acc), Node]),
- exit(event_not_found).
+
+find_events(NodeNames, TestEvents, Events, Config) ->
+ [begin
+ Node = add_host(Node0),
+ io:format("Searching for events for node: ~s", [Node]),
+ ok = ct_test_support:verify_events(TestEvents, Events, Node, Config),
+ io:nl()
+ end || Node0 <- NodeNames],
+ ok.
add_host(NodeName) ->
{ok, HostName} = inet:gethostname(),
list_to_atom(atom_to_list(NodeName)++"@"++HostName).
-expected_events(_) ->
- [].
+events_to_check(_) ->
+ [{?eh,tc_start,{master_SUITE,first_testcase}},
+ {?eh,tc_done,{master_SUITE,first_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,second_testcase}},
+ {?eh,tc_done,{master_SUITE,second_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,third_testcase}},
+ {?eh,tc_done,{master_SUITE,third_testcase,ok}},
+ {?eh,tc_start,{master_SUITE,env_vars}},
+ {?eh,tc_done,{master_SUITE,env_vars,ok}}].
diff --git a/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl b/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl
index 032d69ad9f..8a5009ad62 100644
--- a/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl
+++ b/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl
@@ -39,7 +39,8 @@ init_per_suite(Config) ->
end_per_suite(_) ->
ok.
-all() -> [first_testcase, second_testcase, third_testcase].
+all() -> [first_testcase, second_testcase, third_testcase,
+ env_vars].
init_per_testcase(_, Config) ->
Config.
@@ -56,3 +57,9 @@ second_testcase(_)->
third_testcase(_)->
A = 4,
A = 2*2.
+
+env_vars(_) ->
+ io:format("~p\n", [os:getenv()]),
+ "yes" = os:getenv("THIS_MUST_BE_SET"),
+ "value" = os:getenv("SO_MUST_THIS"),
+ ok.
diff --git a/lib/common_test/test/ct_snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE.erl
new file mode 100644
index 0000000000..f8b4543770
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE.erl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_snmp_SUITE
+%%%
+%%% Description:
+%%% Test ct_snmp module
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_snmp_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
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "snmp_SUITE"),
+ CfgFile = filename:join(DataDir, "snmp.cfg"),
+ {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
+ {label,default}], Config),
+
+ ok = execute(default, 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,Config),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(_TestName,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
+ snmp_SUITE)),
+ TCs = get_tcs(),
+ code:purge(snmp_SUITE),
+ code:delete(snmp_SUITE),
+
+ OneTest =
+ [{?eh,start_logging,{'DEF','RUNDIR'}}] ++
+ [{?eh,tc_done,{snmp_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,stop_logging,[]}],
+
+ %% 2 tests (ct:run_test + script_start) is default
+ OneTest ++ OneTest.
+
+
+get_tcs() ->
+ All = snmp_SUITE:all(),
+ Groups =
+ try snmp_SUITE:groups()
+ catch error:undef -> []
+ end,
+ flatten_tcs(All,Groups).
+
+flatten_tcs([H|T],Groups) when is_atom(H) ->
+ [H|flatten_tcs(T,Groups)];
+flatten_tcs([{group,Group}|T],Groups) ->
+ TCs = proplists:get_value(Group,Groups),
+ flatten_tcs(TCs ++ T,Groups);
+flatten_tcs([],_) ->
+ [].
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
new file mode 100644
index 0000000000..895e097de6
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp.cfg
@@ -0,0 +1,44 @@
+%% -*- erlang -*-
+{snmp1, [{start_agent,true},
+ {users,[{user_name,[snmpm_user_default,[]]}]},
+ {managed_agents,[{agent_name, [user_name, {127,0,0,1}, 4000,
+ [{engine_id,"ct_snmp-test-engine"},
+ {version,v2}]]}]},
+ {engine_id,"ct_snmp-test-engine"},
+ {agent_vsns,[v2]}
+ ]}.
+{snmp2, [{start_agent,true},
+ {engine_id,"ct_snmp-test-engine"}
+ ]}.
+{snmp3, [{start_agent,true},
+ {engine_id,"ct_snmp-test-engine"},
+ {agent_vsns,[v1,v2,v3]},
+ {agent_contexts,{data_dir_file,"context.conf"}},
+ {agent_usm,{data_dir_file,"usm.conf"}},
+ {agent_community,{data_dir_file,"community.conf"}},
+ {agent_notify_def,{data_dir_file,"notify.conf"}},
+ {agent_sysinfo,{data_dir_file,"standard.conf"}},
+ {agent_target_address_def,{data_dir_file,"target_addr.conf"}},
+ {agent_target_param_def,{data_dir_file,"target_params.conf"}},
+ {agent_vacm,{data_dir_file,"vacm.conf"}}]}.
+{snmp_app1,[{manager, [{config, [{verbosity, silence}]},
+ {server,[{verbosity,silence}]},
+ {net_if,[{verbosity,silence}]},
+ {versions,[v2]}
+ ]},
+ {agent, [{config, [{verbosity, silence}]},
+ {net_if,[{verbosity,silence}]},
+ {mib_server,[{verbosity,silence}]},
+ {local_db,[{verbosity,silence}]},
+ {agent_verbosity,silence}
+ ]}]}.
+{snmp_app2,[{manager, [{config, [{verbosity, silence}]},
+ {server,[{verbosity,silence}]},
+ {net_if,[{verbosity,silence}]}
+ ]},
+ {agent, [{config, [{verbosity, silence}]},
+ {net_if,[{verbosity,silence}]},
+ {mib_server,[{verbosity,silence}]},
+ {local_db,[{verbosity,silence}]},
+ {agent_verbosity,silence}
+ ]}]}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl
new file mode 100644
index 0000000000..16b2b5690c
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE.erl
@@ -0,0 +1,395 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_snmp_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for the ct_snmp API.
+%%
+%% @author Support
+%% @doc Test of SNMP support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(snmp_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("snmp/include/STANDARD-MIB.hrl").
+-include_lib("snmp/include/SNMP-USER-BASED-SM-MIB.hrl").
+-include_lib("snmp/include/snmp_types.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+-define(AGENT_UDP, 4000).
+
+suite() ->
+ [
+ {require, snmp1, snmp1},
+ {require, snmp_app1, snmp_app1},
+ {require, snmp2, snmp2},
+ {require, snmp_app2, snmp_app2},
+ {require, snmp3, snmp3}
+ ].
+
+all() ->
+ [start_stop,
+ {group,get_set},
+ {group,register},
+ {group,override},
+ set_info].
+
+
+groups() ->
+ [{get_set,[get_values,
+ get_next_values,
+ set_values,
+ load_mibs]},
+ {register,[register_users,
+ register_users_fail,
+ register_agents,
+ register_agents_fail,
+ register_usm_users,
+ register_usm_users_fail]},
+ {override,[override_usm,
+ override_standard,
+ override_context,
+ override_community,
+ override_notify,
+ override_target_addr,
+ override_target_params,
+ override_vacm]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(get_set, Config) ->
+ ok = ct_snmp:start(Config,snmp1,snmp_app1),
+ Config;
+init_per_group(register, Config) ->
+ ok = ct_snmp:start(Config,snmp2,snmp_app2),
+ Config;
+init_per_group(_, Config) ->
+ ok = ct_snmp:start(Config,snmp3,snmp_app2),
+ Config.
+
+end_per_group(_Group, Config) ->
+ catch ct_snmp:stop(Config),
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case, Config) ->
+ try apply(?MODULE,Case,[cleanup,Config])
+ catch error:undef -> ok
+ end,
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+start_stop(Config) ->
+ ok = ct_snmp:start(Config,snmp1,snmp_app1),
+ timer:sleep(1000),
+ {snmp,_,_} = lists:keyfind(snmp,1,application:which_applications()),
+ [_|_] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+
+ ok = ct_snmp:stop(Config),
+ timer:sleep(1000),
+ false = lists:keyfind(snmp,1,application:which_applications()),
+ [] = filelib:wildcard("*/*.conf",?config(priv_dir,Config)),
+ ok.
+
+get_values(_Config) ->
+ Oids1 = [?sysDescr_instance, ?sysName_instance],
+ {noError,_,V1} = ct_snmp:get_values(agent_name,Oids1,snmp1),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"},
+ #varbind{oid=?sysName_instance,value="ct_test"}] = V1,
+ ok.
+
+get_next_values(_Config) ->
+ Oids2 = [?system],
+ {noError,_,V2} = ct_snmp:get_next_values(agent_name,Oids2,snmp1),
+ [#varbind{oid=?sysDescr_instance,value="Erlang SNMP agent"}] = V2,
+ ok.
+
+set_values(Config) ->
+ Oid3 = ?sysName_instance,
+ NewName = "ct_test changed by " ++ atom_to_list(?MODULE),
+ VarsAndVals = [{Oid3,s,NewName}],
+ {noError,_,_} =
+ ct_snmp:set_values(agent_name,VarsAndVals,snmp1,Config),
+
+ Oids4 = [?sysName_instance],
+ {noError,_,V4} = ct_snmp:get_values(agent_name,Oids4,snmp1),
+ [#varbind{oid=?sysName_instance,value=NewName}] = V4,
+
+ ok.
+
+load_mibs(_Config) ->
+ [{'SNMPv2-MIB',_}=SnmpV2Mib] = snmpa:which_mibs(),
+ Mib = filename:join([code:priv_dir(snmp),"mibs","SNMP-USER-BASED-SM-MIB"]),
+ ok = ct_snmp:load_mibs([Mib]),
+ TwoMibs = [_,_] = snmpa:which_mibs(),
+ [{'SNMP-USER-BASED-SM-MIB',_}] = lists:delete(SnmpV2Mib,TwoMibs),
+ ok = ct_snmp:unload_mibs([Mib]),
+ [{'SNMPv2-MIB',_}] = snmpa:which_mibs(),
+ ok.
+
+
+register_users(_Config) ->
+ [] = snmpm:which_users(),
+ ok = ct_snmp:register_users(snmp2,[{reg_user1,[snmpm_user_default,[]]}]),
+ [_] = snmpm:which_users(),
+ [_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:register_users(snmp2,[{reg_user2,[snmpm_user_default,[]]}]),
+ [_,_] = snmpm:which_users(),
+ [_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:register_users(snmp2,[{reg_user3,[snmpm_user_default,[]]}]),
+ [_,_,_] = snmpm:which_users(),
+ [_,_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:unregister_users(snmp2,[reg_user3]),
+ [_,_] = snmpm:which_users(),
+ [_,_] = ct:get_config({snmp2,users}),
+ ok = ct_snmp:unregister_users(snmp2),
+ [] = snmpm:which_users(),
+ [] = ct:get_config({snmp2,users}),
+ ok.
+register_users(cleanup,_Config) ->
+ ct_snmp:unregister_users(snmp2).
+
+register_users_fail(_Config) ->
+ [] = snmpm:which_users(),
+ {error,_} = ct_snmp:register_users(snmp2,[{reg_user3,[unknown_module,[]]}]),
+ [] = snmpm:which_users(),
+ ok.
+register_users_fail(cleanup,_Config) ->
+ ct_snmp:unregister_users(snmp2).
+
+register_agents(_Config) ->
+ {ok, HostName} = inet:gethostname(),
+ {ok, Addr} = inet:getaddr(HostName, inet),
+
+ [] = snmpm:which_agents(),
+ ok = ct_snmp:register_users(snmp2,[{reg_user1,[snmpm_user_default,[]]}]),
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent1,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_] = snmpm:which_agents(),
+ [_] = ct:get_config({snmp2,managed_agents}),
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent2,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_,_] = snmpm:which_agents(),
+ [_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:register_agents(snmp2,[{reg_agent3,
+ [reg_user1,Addr,?AGENT_UDP,[]]}]),
+ [_,_,_] = snmpm:which_agents(),
+ [_,_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:unregister_agents(snmp2,[reg_agent3]),
+ [_,_] = snmpm:which_agents(),
+ [_,_] = ct:get_config({snmp2,managed_agents}),
+
+ ok = ct_snmp:unregister_agents(snmp2),
+ ok = ct_snmp:unregister_users(snmp2),
+ [] = snmpm:which_agents(),
+ [] = ct:get_config({snmp2,managed_agents}),
+ ok.
+register_agents(cleanup,_Config) ->
+ ct_snmp:unregister_agents(snmp2),
+ ct_snmp:unregister_users(snmp2).
+
+register_agents_fail(_Config) ->
+ {ok, HostName} = inet:gethostname(),
+ {ok, Addr} = inet:getaddr(HostName, inet),
+
+ [] = snmpm:which_agents(),
+ {error,_}
+ = ct_snmp:register_agents(snmp2,[{reg_agent3,
+ [unknown_user,Addr,?AGENT_UDP,[]]}]),
+ [] = snmpm:which_agents(),
+ ok.
+register_agents_fail(cleanup,_Config) ->
+ ct_snmp:unregister_agents(snmp2).
+
+register_usm_users(_Config) ->
+ [] = snmpm:which_usm_users(),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user1",[]}]),
+ [_] = snmpm:which_usm_users(),
+ [_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user2",[]}]),
+ [_,_] = snmpm:which_usm_users(),
+ [_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user3",[]}]),
+ [_,_,_] = snmpm:which_usm_users(),
+ [_,_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:unregister_usm_users(snmp2,["reg_usm_user3"]),
+ [_,_] = snmpm:which_usm_users(),
+ [_,_] = ct:get_config({snmp2,usm_users}),
+ ok = ct_snmp:unregister_usm_users(snmp2),
+ [] = snmpm:which_usm_users(),
+ [] = ct:get_config({snmp2,usm_users}),
+ ok.
+register_usm_users(cleanup,_Config) ->
+ ct_snmp:unregister_usm_users(snmp2).
+
+register_usm_users_fail(_Config) ->
+ [] = snmpm:which_usm_users(),
+ {error,_}
+ = ct_snmp:register_usm_users(snmp2,[{"reg_usm_user3",
+ [{sec_name,invalid_data_type}]}]),
+ [] = snmpm:which_usm_users(),
+ ok.
+register_usm_users_fail(cleanup,_Config) ->
+ ct_snmp:unregister_usm_users(snmp2).
+
+%% Test that functionality for overriding default configuration file
+%% works - i.e. that the files are written and that the configuration
+%% is actually used.
+%%
+%% Note that the config files used in this test case do not
+%% necessarily make up a reasonable configuration for the snmp
+%% application...
+override_usm(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ Mib = filename:join([code:priv_dir(snmp),"mibs","SNMP-USER-BASED-SM-MIB"]),
+ ok = ct_snmp:load_mibs([Mib]),
+
+ %% Check that usm.conf is overwritten
+ {ok,MyUsm} = snmpa_conf:read_usm_config(DataDir),
+ {ok,UsedUsm} = snmpa_conf:read_usm_config(ConfDir),
+ true = (MyUsm == UsedUsm),
+
+ %% Check that the usm user is actually configured...
+ [{Index,"secname"}] =
+ snmp_user_based_sm_mib:usmUserTable(get_next,?usmUserEntry,[3]),
+ true = lists:suffix("usm_user_name",Index),
+ ok.
+
+override_standard(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that standard.conf is overwritten
+ {ok,MyStandard} = snmpa_conf:read_standard_config(DataDir),
+ {ok,UsedStandard} = snmpa_conf:read_standard_config(ConfDir),
+ true = (MyStandard == UsedStandard),
+
+ %% Check that the values from standard.conf is actually configured...
+ {value,"name for override test"} = snmp_standard_mib:sysName(get),
+ {value,"agent for ct_snmp override test"} = snmp_standard_mib:sysDescr(get),
+ ok.
+
+override_context(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that context.conf is overwritten
+ {ok,MyContext} = snmpa_conf:read_context_config(DataDir),
+ {ok,UsedContext} = snmpa_conf:read_context_config(ConfDir),
+ true = (MyContext == UsedContext),
+ ok.
+
+override_community(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that community.conf is overwritten
+ {ok,MyCommunity} = snmpa_conf:read_community_config(DataDir),
+ {ok,UsedCommunity} = snmpa_conf:read_community_config(ConfDir),
+ true = (MyCommunity == UsedCommunity),
+ ok.
+
+override_notify(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that notify.conf is overwritten
+ {ok,MyNotify} = snmpa_conf:read_notify_config(DataDir),
+ {ok,UsedNotify} = snmpa_conf:read_notify_config(ConfDir),
+ true = (MyNotify == UsedNotify),
+ ok.
+
+override_target_addr(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that target_addr.conf is overwritten
+ {ok,MyTargetAddr} = snmpa_conf:read_target_addr_config(DataDir),
+ {ok,UsedTargetAddr} = snmpa_conf:read_target_addr_config(ConfDir),
+ true = (MyTargetAddr == UsedTargetAddr),
+ ok.
+
+override_target_params(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that target_params.conf is overwritten
+ {ok,MyTargetParams} = snmpa_conf:read_target_params_config(DataDir),
+ {ok,UsedTargetParams} = snmpa_conf:read_target_params_config(ConfDir),
+ true = (MyTargetParams == UsedTargetParams),
+ ok.
+
+override_vacm(Config) ->
+ DataDir = ?config(data_dir,Config),
+ PrivDir = ?config(priv_dir,Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+
+ %% Check that vacm.conf is overwritten
+ {ok,MyVacm} = snmpa_conf:read_vacm_config(DataDir),
+ {ok,UsedVacm} = snmpa_conf:read_vacm_config(ConfDir),
+ true = (MyVacm == UsedVacm),
+ ok.
+
+
+
+
+%% NOTE!! This test must always be executed last in the suite, and
+%% should match all set requests performed in the suite. I.e. if you
+%% add a set request, you must add an entry in the return value of
+%% ct_snmp:set_info/1 below.
+set_info(Config) ->
+ %% From test case set_values/1:
+ Oid1 = ?sysName_instance,
+ NewValue1 = "ct_test changed by " ++ atom_to_list(?MODULE),
+
+ %% The test...
+ [{_AgentName,_,[{Oid1,_,NewValue1}]}]
+ = ct_snmp:set_info(Config),
+ ok.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf
new file mode 100644
index 0000000000..5a64df6605
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/community.conf
@@ -0,0 +1 @@
+{"public", "public", "initial", "", ""}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf
new file mode 100644
index 0000000000..feed5e1d11
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/context.conf
@@ -0,0 +1 @@
+"testcontext".
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf
new file mode 100644
index 0000000000..367ba3aa4b
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/notify.conf
@@ -0,0 +1 @@
+{"standard inform", "std_inform", inform}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf
new file mode 100644
index 0000000000..79908fb355
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/standard.conf
@@ -0,0 +1,7 @@
+{sysDescr, "agent for ct_snmp override test"}.
+{sysObjectID, [1,2,3]}.
+{sysContact, "[email protected]"}.
+{sysLocation, "erlang"}.
+{sysServices, 72}.
+{snmpEnableAuthenTraps, enabled}.
+{sysName, "name for override test"}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf
new file mode 100644
index 0000000000..d02672a074
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_addr.conf
@@ -0,0 +1,2 @@
+{"target1", snmpUDPDomain, [147,214,122,73], 5000, 1500, 3, "std_trap", "target_v3", "", [], 2048}.
+{"target2", snmpUDPDomain, [147,214,122,73], 5000, 1500, 3, "std_inform", "target_v3", "", [], 2048}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf
new file mode 100644
index 0000000000..5a9a619422
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/target_params.conf
@@ -0,0 +1 @@
+{"target_v3", v3, usm, "initial", noAuthNoPriv}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf
new file mode 100644
index 0000000000..d6e245914e
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/usm.conf
@@ -0,0 +1 @@
+{"ct_snmp-test-engine","usm_user_name","secname",zeroDotZero,usmNoAuthProtocol,"","",usmNoPrivProtocol,"","","","",""}.
diff --git a/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf
new file mode 100644
index 0000000000..158fe02e3b
--- /dev/null
+++ b/lib/common_test/test/ct_snmp_SUITE_data/snmp_SUITE_data/vacm.conf
@@ -0,0 +1,6 @@
+{vacmSecurityToGroup, usm, "initial", "initial"}.
+{vacmAccess, "initial", "", any, noAuthNoPriv, exact, "restricted", "", "restricted"}.
+{vacmAccess, "initial", "", usm, authNoPriv, exact, "internet", "internet", "internet"}.
+{vacmAccess, "initial", "", usm, authPriv, exact, "internet", "internet", "internet"}.
+{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.
+{vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl
new file mode 100644
index 0000000000..69e98cef48
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE.erl
@@ -0,0 +1,351 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_surefire_SUITE
+%%%
+%%% Description:
+%%% Test cth_surefire hook
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_surefire_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-include_lib("xmerl/include/xmerl.hrl").
+
+-define(eh, ct_test_support_eh).
+
+-define(url_base,"http://my.host.com/").
+
+%%--------------------------------------------------------------------
+%% 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,
+ absolute_path,
+ relative_path,
+ url,
+ logdir
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+default(Config) when is_list(Config) ->
+ run(default,[cth_surefire],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*","junit_report.xml"]),
+ check_xml(default,XmlRe).
+
+absolute_path(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ Path = filename:join(PrivDir,"abspath.xml"),
+ run(absolute_path,[{cth_surefire,[{path,Path}]}],Config),
+ check_xml(absolute_path,Path).
+
+relative_path(Config) when is_list(Config) ->
+ Path = "relpath.xml",
+ run(relative_path,[{cth_surefire,[{path,Path}]}],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*",Path]),
+ check_xml(relative_path,XmlRe).
+
+url(Config) when is_list(Config) ->
+ Path = "url.xml",
+ run(url,[{cth_surefire,[{url_base,?url_base},
+ {path,Path}]}],Config),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([PrivDir,"*",Path]),
+ check_xml(url,XmlRe).
+
+logdir(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir,Config),
+ LogDir = filename:join(PrivDir,"specific_logdir"),
+ file:make_dir(LogDir),
+ Path = "logdir.xml",
+ run(logdir,[{cth_surefire,[{path,Path}]}],Config,[{logdir,LogDir}]),
+ PrivDir = ?config(priv_dir,Config),
+ XmlRe = filename:join([LogDir,"*",Path]),
+ check_xml(logdir,XmlRe).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+run(Case,CTHs,Config) ->
+ run(Case,CTHs,Config,[]).
+run(Case,CTHs,Config,ExtraOpts) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "surefire_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts],
+ Config),
+ ok = execute(Case, Opts, ERPid, Config).
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts1 =
+ case lists:keymember(logdir,1,Test) of
+ true -> lists:keydelete(logdir,1,Opts0);
+ false -> Opts0
+ end,
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts1 ++ [{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(_) ->
+ [{?eh,start_logging,'_'},
+ {?eh,start_info,{1,1,9}},
+ {?eh,tc_start,{surefire_SUITE,init_per_suite}},
+ {?eh,tc_done,{surefire_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_fail}},
+ {?eh,tc_done,{surefire_SUITE,tc_fail,
+ {failed,{error,{test_case_failed,"this test should fail"}}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_skip}},
+ {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
+ {?eh,test_stats,{1,1,{1,0}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{1,1,{1,1}}},
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+ {?eh,test_stats,{2,1,{1,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_fail}},
+ {?eh,tc_done,{surefire_SUITE,tc_fail,
+ {failed,{error,{test_case_failed,"this test should fail"}}}}},
+ {?eh,test_stats,{2,2,{1,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_skip}},
+ {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}},
+ {?eh,test_stats,{2,2,{2,1}}},
+ {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}},
+ {?eh,tc_done,{surefire_SUITE,tc_autoskip_require,
+ {skipped,{require_failed,'_'}}}},
+ {?eh,test_stats,{2,2,{2,2}}},
+ {?eh,tc_start,{surefire_SUITE,{end_per_group,g,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{end_per_group,g,[]},ok}}],
+ [{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}},
+ {?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]},
+ {failed,{error,all_cases_should_be_skipped}}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,tc_ok,
+ {failed,
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}},
+ {?eh,test_stats,{2,2,{2,3}}},
+ {?eh,tc_auto_skip,{surefire_SUITE,end_per_group,
+ {failed,
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}}],
+ {?eh,tc_start,{surefire_SUITE,end_per_suite}},
+ {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}},
+ {?eh,stop_logging,[]}].
+
+
+%%%-----------------------------------------------------------------
+%%% Check generated xml log files
+check_xml(Case,XmlRe) ->
+ case filelib:wildcard(XmlRe) of
+ [] ->
+ ct:fail("No xml files found with regexp ~p~n", [XmlRe]);
+ [_] = Xmls when Case==absolute_path ->
+ do_check_xml(Case,Xmls);
+ [_,_] = Xmls ->
+ do_check_xml(Case,Xmls)
+ end.
+
+%% Allowed structure:
+%% <testsuites>
+%% <testsuite>
+%% <properties>
+%% <property/>
+%% ...
+%% </properties>
+%% <testcase>
+%% [<failure/> | <error/> | <skipped/> ]
+%% </testcase>
+%% ...
+%% </testsuite>
+%% ...
+%% </testsuites>
+do_check_xml(Case,[Xml|Xmls]) ->
+ ct:log("Checking <a href=~p>~s</a>~n",[Xml,Xml]),
+ {E,_} = xmerl_scan:file(Xml),
+ Expected = events_to_result(lists:flatten(test_events(Case))),
+ ParseResult = testsuites(Case,E),
+ ct:log("Expecting: ~p~n",[[Expected]]),
+ ct:log("Actual : ~p~n",[ParseResult]),
+ [Expected] = ParseResult,
+ do_check_xml(Case,Xmls);
+do_check_xml(_,[]) ->
+ ok.
+
+%% Scanning the XML to get the same type of result as events_to_result/1
+testsuites(Case,#xmlElement{name=testsuites,content=TS}) ->
+ %% OTP-10589 - move properties element to <testsuite>
+ false = lists:keytake(properties,#xmlElement.name,TS),
+ testsuite(Case,TS).
+
+testsuite(Case,[#xmlElement{name=testsuite,content=TC,attributes=A}|TS]) ->
+ {ET,EF,ES} = events_to_numbers(lists:flatten(test_events(Case))),
+ {T,E,F,S} = get_numbers_from_attrs(A,false,false,false,false),
+ ct:log("Expecting total:~p, error:~p, failure:~p, skipped:~p~n",[ET,0,EF,ES]),
+ ct:log("Actual total:~p, error:~p, failure:~p, skipped:~p~n",[T,E,F,S]),
+ {ET,0,EF,ES} = {T,E,F,S},
+
+ %% properties should only be there if given a options to hook
+ false = lists:keytake(properties,#xmlElement.name,TC),
+ %% system-out and system-err is not used by common_test
+ false = lists:keytake('system-out',#xmlElement.name,TC),
+ false = lists:keytake('system-err',#xmlElement.name,TC),
+ R=testcase(Case,TC),
+ [R|testsuite(Case,TS)];
+testsuite(_Case,[]) ->
+ [].
+
+testcase(url=Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) ->
+ R = failed_or_skipped(C),
+ case R of
+ [s] ->
+ case lists:keyfind(url,#xmlAttribute.name,A) of
+ false -> ok;
+ #xmlAttribute{value=UrlAttr} ->
+ lists:keyfind(url,#xmlAttribute.name,A),
+ true = lists:prefix(?url_base,UrlAttr)
+ end;
+ _ ->
+ #xmlAttribute{value=UrlAttr} =
+ lists:keyfind(url,#xmlAttribute.name,A),
+ true = lists:prefix(?url_base,UrlAttr)
+ end,
+ [R|testcase(Case,TC)];
+testcase(Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) ->
+ false = lists:keyfind(url,#xmlAttribute.name,A),
+ R = failed_or_skipped(C),
+ [R|testcase(Case,TC)];
+testcase(_Case,[]) ->
+ [].
+
+failed_or_skipped([#xmlElement{name=failure}|E]) ->
+ [f|failed_or_skipped(E)];
+failed_or_skipped([#xmlElement{name=error}|E]) ->
+ [e|failed_or_skipped(E)];
+failed_or_skipped([#xmlElement{name=skipped}|E]) ->
+ [s|failed_or_skipped(E)];
+failed_or_skipped([]) ->
+ [].
+
+%% Using the expected events to produce the expected result of the XML scanning.
+%% The result is a list of test suites:
+%% Testsuites = [Testsuite]
+%% Testsuite = [Testcase]
+%% Testcase = [] | [f] | [s], indicating ok, failed and skipped respectively
+events_to_result([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
+ [result(R)|events_to_result(E)];
+events_to_result([{?eh,tc_auto_skip,_}|E]) ->
+ [[s]|events_to_result(E)];
+events_to_result([_|E]) ->
+ events_to_result(E);
+events_to_result([]) ->
+ [].
+
+result(ok) ->[];
+result({skipped,_}) -> [s];
+result({failed,_}) -> [f].
+
+%% Using the expected events' last test_stats element to produce the
+%% expected number of totla, errors, failed and skipped testcases.
+events_to_numbers(E) ->
+ RevE = lists:reverse(E),
+ {?eh,test_stats,{Ok,F,{US,AS}}} = lists:keyfind(test_stats,2,RevE),
+ {Ok+F+US+AS,F,US+AS}.
+
+get_numbers_from_attrs([#xmlAttribute{name=tests,value=X}|A],false,E,F,S) ->
+ get_numbers_from_attrs(A,list_to_integer(X),E,F,S);
+get_numbers_from_attrs([#xmlAttribute{name=errors,value=X}|A],T,false,F,S) ->
+ get_numbers_from_attrs(A,T,list_to_integer(X),F,S);
+get_numbers_from_attrs([#xmlAttribute{name=failures,value=X}|A],T,E,false,S) ->
+ get_numbers_from_attrs(A,T,E,list_to_integer(X),S);
+get_numbers_from_attrs([#xmlAttribute{name=skipped,value=X}|A],T,E,F,false) ->
+ get_numbers_from_attrs(A,T,E,F,list_to_integer(X));
+get_numbers_from_attrs([_|A],T,E,F,S) ->
+ get_numbers_from_attrs(A,T,E,F,S);
+get_numbers_from_attrs([],T,E,F,S) ->
+ {T,E,F,S}.
diff --git a/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl
new file mode 100644
index 0000000000..677aee46c5
--- /dev/null
+++ b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl
@@ -0,0 +1,92 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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: surefire_SUITE.erl
+%%
+%% Description:
+%% This file contains the test cases for cth_surefire.
+%%
+%% @author Support
+%% @doc Test of surefire support in common_test
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(surefire_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+all() ->
+ testcases() ++ [{group,g},{group,g_fail}].
+
+groups() ->
+ [{g,testcases()},
+ {g_fail,[tc_ok]}].
+
+testcases() ->
+ [tc_ok,
+ tc_fail,
+ tc_skip,
+ tc_autoskip_require].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(g_fail, _Config) ->
+ exit(all_cases_should_be_skipped);
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_Group, Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+break(_Config) ->
+ test_server:break(""),
+ ok.
+
+tc_ok(_Config) ->
+ ok.
+
+tc_fail(_Config) ->
+ ct:fail("this test should fail").
+
+tc_skip(_Config) ->
+ {skip,"this test is skipped"}.
+
+tc_autoskip_require() ->
+ [{require,whatever}].
+tc_autoskip_require(Config) ->
+ ct:fail("this test should never be executed - it should be autoskipped").
diff --git a/lib/common_test/test/ct_system_error_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE.erl
new file mode 100644
index 0000000000..f2d6ef4b1b
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE.erl
@@ -0,0 +1,132 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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_system_error_SUITE
+%%%
+%%% Description:
+%%%
+%%% Test that severe system errors (such as failure to write logs) are
+%%% noticed and handled.
+%%%-------------------------------------------------------------------
+-module(ct_system_error_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() ->
+ [
+ test_server_failing_logs
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+test_server_failing_logs(Config) ->
+ TC = test_server_failing_logs,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "a_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config),
+ crash_test_server(Config),
+ {error,{cannot_create_log_dir,__}} = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(TC,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(TC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+crash_test_server(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Root = proplists:get_value(logdir, ct_test_support:get_opts(Config)),
+ [$@|Host] = lists:dropwhile(fun(C) ->
+ C =/= $@
+ end, atom_to_list(node())),
+ Format = filename:join(Root,
+ "ct_run.ct@" ++ Host ++
+ ".~4..0w-~2..0w-~2..0w_"
+ "~2..0w.~2..0w.~2..0w"),
+ [C2,C1|_] = lists:reverse(filename:split(DataDir)),
+ LogDir = C1 ++ "." ++ C2 ++ ".a_SUITE.logs",
+ T = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ [begin
+ {{Y,Mon,D},{H,Min,S}} =
+ calendar:gregorian_seconds_to_datetime(T+Offset),
+ Dir0 = io_lib:format(Format, [Y,Mon,D,H,Min,S]),
+ Dir = lists:flatten(Dir0),
+ file:make_dir(Dir),
+ File = filename:join(Dir, LogDir),
+ file:write_file(File, "anything goes\n")
+ end || Offset <- lists:seq(0, 20)],
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% 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}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+
+events_to_check(_Test) ->
+ [{?eh,severe_error,{cannot_create_log_dir,{'_','_'}}}].
diff --git a/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
new file mode 100644
index 0000000000..c6e3ddfd5d
--- /dev/null
+++ b/lib/common_test/test/ct_system_error_SUITE_data/a_SUITE.erl
@@ -0,0 +1,122 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. 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(a_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,10}}].
+
+%%--------------------------------------------------------------------
+%% @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() ->
+ [tc1].
+
+tc1(_C) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 80cca4a1cc..fc572aa82f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -32,7 +32,7 @@
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,
+ verify_events/3, verify_events/4, reformat/2, log_events/4,
join_abs_dirs/2]).
-export([ct_test_halt/1]).
@@ -117,8 +117,7 @@ end_per_suite(Config) ->
CTNode = proplists:get_value(ct_node, Config),
PrivDir = proplists:get_value(priv_dir, Config),
true = rpc:call(CTNode, code, del_path, [filename:join(PrivDir,"")]),
- cover:stop(CTNode),
- slave:stop(CTNode),
+ slave_stop(CTNode),
ok.
%%%-----------------------------------------------------------------
@@ -149,8 +148,7 @@ end_per_testcase(_TestCase, Config) ->
case wait_for_ct_stop(CTNode) of
%% Common test was not stopped to we restart node.
false ->
- cover:stop(CTNode),
- slave:stop(CTNode),
+ slave_stop(CTNode),
start_slave(Config,proplists:get_value(trace_level,Config)),
{fail, "Could not stop common_test"};
true ->
@@ -364,6 +362,14 @@ verify_events(TEvs, Evs, Config) ->
ok
end.
+verify_events(TEvs, Evs, Node, Config) ->
+ case catch verify_events1(TEvs, Evs, Node, Config) of
+ {'EXIT',Reason} ->
+ Reason;
+ _ ->
+ ok
+ end.
+
verify_events1([TestEv|_], [{TEH,#event{name=stop_logging,node=Node,data=_}}|_], Node, _)
when element(1,TestEv) == TEH, element(2,TestEv) =/= stop_logging ->
test_server:format("Failed to find ~p in the list of events!~n", [TestEv]),
@@ -612,8 +618,11 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
fun({EH,#event{name=tc_auto_skip,
node=EvNode,
data={Mod,end_per_group,Reason}}}) when
- EH == TEH, EvNode == Node, Mod == M, Reason == R ->
- false;
+ EH == TEH, EvNode == Node, Mod == M ->
+ case match_data(R, Reason) of
+ match -> false;
+ _ -> true
+ end;
({EH,#event{name=stop_logging,
node=EvNode,data=_}}) when
EH == TEH, EvNode == Node ->
@@ -627,23 +636,12 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
[_AutoSkip | RemEvs2] ->
{Done,RemEvs2,length(RemEvs2)}
end;
- %% match other event than test case
- (TEv={TEH,N,D}, Acc) when D == '_' ->
- case [E || E={EH,#event{name=Name,
- node=EvNode,
- data=_}} <- Evs1,
- EH == TEH, EvNode == Node, Name == N] of
- [] ->
- exit({unmatched,TEv});
- _ ->
- test_server:format("Found ~p!", [TEv]),
- Acc
- end;
(TEv={TEH,N,D}, Acc) ->
case [E || E={EH,#event{name=Name,
node=EvNode,
data=Data}} <- Evs1,
- EH == TEH, EvNode == Node, Name == N, Data == D] of
+ EH == TEH, EvNode == Node, Name == N,
+ match == match_data(D,Data)] of
[] ->
exit({unmatched,TEv});
_ ->
@@ -1002,33 +1000,39 @@ locate({TEH,Name,Data}, Node, [{TEH,#event{name=Name,
data = EvData,
node = Node}}|Evs],
Config) ->
- try match_data(Data, EvData) of
+ case match_data(Data, EvData) of
match ->
- {Config,Evs}
- catch _:_ ->
+ {Config,Evs};
+ _ ->
nomatch
end;
locate({_TEH,_Name,_Data}, _Node, [_|_Evs], _Config) ->
nomatch.
-match_data(D,D) ->
+match_data(Data, EvData) ->
+ try do_match_data(Data, EvData)
+ catch _:_ ->
+ nomatch
+ end.
+
+do_match_data(D,D) ->
match;
-match_data('_',_) ->
+do_match_data('_',_) ->
match;
-match_data(Fun,Data) when is_function(Fun) ->
+do_match_data(Fun,Data) when is_function(Fun) ->
Fun(Data);
-match_data('$proplist',Proplist) ->
- match_data(
+do_match_data('$proplist',Proplist) ->
+ do_match_data(
fun(List) ->
lists:foreach(fun({_,_}) -> ok end,List)
end,Proplist);
-match_data([H1|MatchT],[H2|ValT]) ->
- match_data(H1,H2),
- match_data(MatchT,ValT);
-match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) ->
- match_data(tuple_to_list(Tuple1),tuple_to_list(Tuple2));
-match_data([],[]) ->
+do_match_data([H1|MatchT],[H2|ValT]) ->
+ do_match_data(H1,H2),
+ do_match_data(MatchT,ValT);
+do_match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) ->
+ do_match_data(tuple_to_list(Tuple1),tuple_to_list(Tuple2));
+do_match_data([],[]) ->
match.
result_match({SkipOrFail,{ErrorInd,{Why,'_'}}},
@@ -1043,6 +1047,9 @@ result_match({failed,{timetrap_timeout,{'$approx',Num}}},
Value =< trunc(Num+0.02*Num) -> true;
true -> false
end;
+result_match({user_timetrap_error,{Why,'_'}},
+ {user_timetrap_error,{Why,_Stack}}) ->
+ true;
result_match(Result, Result) ->
true;
result_match(_, _) ->
@@ -1259,3 +1266,22 @@ rm_files([F | Fs]) ->
rm_files([]) ->
ok.
+%%%-----------------------------------------------------------------
+%%%
+slave_stop(Node) ->
+ Cover = test_server:is_cover(),
+ if Cover-> cover:flush(Node);
+ true -> ok
+ end,
+ erlang:monitor_node(Node, true),
+ slave:stop(Node),
+ receive
+ {nodedown, Node} ->
+ if Cover -> cover:stop(Node);
+ true -> ok
+ end
+ after 5000 ->
+ erlang:monitor_node(Node, false),
+ receive {nodedown, Node} -> ok after 0 -> ok end %flush
+ end,
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
index b7e19f25dd..6a4a4acd80 100644
--- a/lib/common_test/test/ct_testspec_1_SUITE.erl
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -58,7 +58,7 @@ end_per_testcase(TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[all_suites, skip_all_suites, suite, skip_suite,
all_testcases, skip_all_testcases, testcase,
skip_testcase, all_groups, skip_all_groups, group,
@@ -67,23 +67,23 @@ all() ->
skip_group_testcase, topgroup, subgroup, skip_subgroup,
subgroup_all_testcases, skip_subgroup_all_testcases,
subgroup_testcase, skip_subgroup_testcase,
- sub_skipped_by_top, testcase_in_multiple_groups,
- order_of_tests_in_multiple_dirs_no_merge_tests,
- order_of_tests_in_multiple_suites_no_merge_tests,
- order_of_suites_in_multiple_dirs_no_merge_tests,
- order_of_groups_in_multiple_dirs_no_merge_tests,
- order_of_groups_in_multiple_suites_no_merge_tests,
- order_of_tests_in_multiple_dirs,
- order_of_tests_in_multiple_suites,
- order_of_suites_in_multiple_dirs,
- order_of_groups_in_multiple_dirs,
- order_of_groups_in_multiple_suites,
- order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
- order_of_tests_in_multiple_suites_with_skip,
+ sub_skipped_by_top, testcase_many_groups,
+ order_of_tests_many_dirs_no_merge_tests,
+ order_of_tests_many_suites_no_merge_tests,
+ order_of_suites_many_dirs_no_merge_tests,
+ order_of_groups_many_dirs_no_merge_tests,
+ order_of_groups_many_suites_no_merge_tests,
+ order_of_tests_many_dirs,
+ order_of_tests_many_suites,
+ order_of_suites_many_dirs,
+ order_of_groups_many_dirs,
+ order_of_groups_many_suites,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip,
all_plus_one_tc_no_merge_tests,
all_plus_one_tc].
-groups() ->
+groups() ->
[].
init_per_group(_GroupName, Config) ->
@@ -373,19 +373,19 @@ sub_skipped_by_top(Config) when is_list(Config) ->
%%%-----------------------------------------------------------------
%%%
-testcase_in_multiple_groups(Config) when is_list(Config) ->
+testcase_many_groups(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir = filename:join(DataDir, "groups_1"),
TestSpec = [{cases,TestDir,groups_12_SUITE,[testcase_1a,testcase_1b]},
{skip_cases,TestDir,groups_12_SUITE,[testcase_1b],"SKIPPED!"}],
- setup_and_execute(testcase_in_multiple_groups, TestSpec, Config).
+ setup_and_execute(testcase_many_groups, TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -395,13 +395,13 @@ order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{cases,TestDir2,groups_22_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_tests_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
+order_of_tests_many_suites_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -410,13 +410,13 @@ order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_suites_no_merge_tests,
+ setup_and_execute(order_of_tests_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_suites_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -426,13 +426,13 @@ order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_suites_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+order_of_groups_many_dirs_no_merge_tests(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -442,13 +442,13 @@ order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
{groups,TestDir2,groups_22_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_dirs_no_merge_tests,
+ setup_and_execute(order_of_groups_many_dirs_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites_no_merge_tests(Config)
+order_of_groups_many_suites_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -458,13 +458,13 @@ order_of_groups_in_multiple_suites_no_merge_tests(Config)
{groups,TestDir1,groups_11_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_suites_no_merge_tests,
+ setup_and_execute(order_of_groups_many_suites_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
+order_of_tests_many_suites_with_skip_no_merge_tests(Config)
when is_list(Config) ->
DataDir = ?config(data_dir, Config),
@@ -477,14 +477,14 @@ order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
{skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it"}],
setup_and_execute(
- order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
+ order_of_tests_many_suites_with_skip_no_merge_tests,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_tests_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -493,13 +493,13 @@ order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
{cases,TestDir2,groups_22_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_dirs,
+ setup_and_execute(order_of_tests_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
+order_of_tests_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -507,13 +507,13 @@ order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_1]},
{cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
- setup_and_execute(order_of_tests_in_multiple_suites,
+ setup_and_execute(order_of_tests_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_suites_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -522,13 +522,13 @@ order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
{suites,TestDir2,groups_22_SUITE},
{suites,TestDir1,groups_11_SUITE}],
- setup_and_execute(order_of_suites_in_multiple_dirs,
+ setup_and_execute(order_of_suites_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
+order_of_groups_many_dirs(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -537,13 +537,13 @@ order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
{groups,TestDir2,groups_22_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_dirs,
+ setup_and_execute(order_of_groups_many_dirs,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
+order_of_groups_many_suites(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -551,13 +551,13 @@ order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
{groups,TestDir1,groups_11_SUITE,test_group_1a},
{groups,TestDir1,groups_12_SUITE,test_group_1b}],
- setup_and_execute(order_of_groups_in_multiple_suites,
+ setup_and_execute(order_of_groups_many_suites,
TestSpec, Config).
%%%-----------------------------------------------------------------
%%%
-order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
+order_of_tests_many_suites_with_skip(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
TestDir1 = filename:join(DataDir, "groups_1"),
@@ -567,7 +567,7 @@ order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
{cases,TestDir1,groups_11_SUITE,[testcase_2]},
{skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it!"}],
- setup_and_execute(order_of_tests_in_multiple_suites_with_skip,
+ setup_and_execute(order_of_tests_many_suites_with_skip,
TestSpec, Config).
%%%-----------------------------------------------------------------
@@ -1204,10 +1204,10 @@ test_events(sub_skipped_by_top) ->
{negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
];
-test_events(testcase_in_multiple_groups) ->
+test_events(testcase_many_groups) ->
[];
-test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_tests_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done, {groups_12_SUITE,testcase_1a,
@@ -1219,7 +1219,7 @@ test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
{failed,{error,{test_case_failed,no_group_data}}}}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
+test_events(order_of_tests_many_suites_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1229,7 +1229,7 @@ test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
{?eh,tc_done,{groups_12_SUITE,testcase_1b,'_'}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_suites_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
@@ -1244,7 +1244,7 @@ test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
{?eh,tc_start,{groups_11_SUITE,end_per_suite}},
{?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
+test_events(order_of_groups_many_dirs_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1257,7 +1257,7 @@ test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
{?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
+test_events(order_of_groups_many_suites_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1270,7 +1270,7 @@ test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
{?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
+test_events(order_of_tests_many_suites_with_skip_no_merge_tests) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1282,7 +1282,7 @@ test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_dirs) ->
+test_events(order_of_tests_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,
@@ -1296,7 +1296,7 @@ test_events(order_of_tests_in_multiple_dirs) ->
{?eh,tc_done,{groups_22_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_tests_in_multiple_suites) ->
+test_events(order_of_tests_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
@@ -1308,7 +1308,7 @@ test_events(order_of_tests_in_multiple_suites) ->
{?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
{?eh,stop_logging,[]}
];
-test_events(order_of_suites_in_multiple_dirs) ->
+test_events(order_of_suites_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
{?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
@@ -1325,7 +1325,7 @@ test_events(order_of_suites_in_multiple_dirs) ->
{?eh,tc_start,{groups_22_SUITE,end_per_suite}},
{?eh,tc_done,{groups_22_SUITE,end_per_suite,'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_dirs) ->
+test_events(order_of_groups_many_dirs) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1338,7 +1338,7 @@ test_events(order_of_groups_in_multiple_dirs) ->
{?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
{?eh,stop_logging,[]}];
-test_events(order_of_groups_in_multiple_suites) ->
+test_events(order_of_groups_many_suites) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
@@ -1352,7 +1352,7 @@ test_events(order_of_groups_in_multiple_suites) ->
{?eh,stop_logging,[]}];
-test_events(order_of_tests_in_multiple_suites_with_skip) ->
+test_events(order_of_tests_many_suites_with_skip) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,tc_start,{groups_12_SUITE,testcase_1a}},
{?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},