aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test')
-rw-r--r--lib/common_test/doc/src/ct.xml13
-rw-r--r--lib/common_test/doc/src/ct_run.xml5
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml9
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml12
-rw-r--r--lib/common_test/src/ct.erl6
-rw-r--r--lib/common_test/src/ct_groups.erl2
-rw-r--r--lib/common_test/src/ct_hooks.erl6
-rw-r--r--lib/common_test/src/ct_logs.erl78
-rw-r--r--lib/common_test/src/ct_master.erl12
-rw-r--r--lib/common_test/src/ct_run.erl58
-rw-r--r--lib/common_test/src/ct_testspec.erl7
-rw-r--r--lib/common_test/src/ct_util.erl1
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/src/cth_log_redirect.erl18
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl96
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl2
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl2
-rw-r--r--lib/common_test/test/ct_pre_post_test_io_SUITE.erl126
-rw-r--r--lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl8
-rw-r--r--lib/common_test/test/ct_test_support.erl21
20 files changed, 342 insertions, 141 deletions
diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml
index a87be21d73..b6c862c233 100644
--- a/lib/common_test/doc/src/ct.xml
+++ b/lib/common_test/doc/src/ct.xml
@@ -601,18 +601,21 @@
</func>
<func>
- <name>get_timetrap_info() -&gt; {Time, Scale}</name>
+ <name>get_timetrap_info() -&gt; {Time, {Scaling,ScaleVal}}</name>
<fsummary>Reads information about the timetrap set for the current
test case.</fsummary>
<type>
<v>Time = integer() | infinity</v>
- <v>Scale = true | false</v>
+ <v>Scaling = true | false</v>
+ <v>ScaleVal = integer()</v>
</type>
<desc><marker id="get_timetrap_info-0"/>
<p>Reads information about the timetrap set for the current test
- case. <c>Scale</c> indicates if <c>Common Test</c> will attempt
+ case. <c>Scaling</c> indicates if <c>Common Test</c> will attempt
to compensate timetraps automatically for runtime delays
- introduced by, for example, tools like cover.</p>
+ introduced by, for example, tools like cover. <c>ScaleVal</c> is
+ the value of the current scaling multipler (always 1 if scaling is
+ disabled). Note the <c>Time</c> is not the scaled result.</p>
</desc>
</func>
@@ -1136,7 +1139,7 @@
Opts.</fsummary>
<type>
<v>Opts = [OptTuples]</v>
- <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
+ <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
<v>TestDirs = [string()] | string()</v>
<v>Suites = [string()] | [atom()] | string() | atom()</v>
<v>Cases = [atom()] | atom()</v>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index d0ecc38564..2552938346 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -124,6 +124,7 @@
[-duration HHMMSS [-force_stop [skip_rest]]] |
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
+ [-no_esc_chars]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]
@@ -162,6 +163,7 @@
[-duration HHMMSS [-force_stop [skip_rest]]] |
[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
+ [-no_esc_chars]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
[-exit_status ignore_config]</pre>
@@ -186,7 +188,8 @@
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
- [-basic_html]</pre>
+ [-basic_html]
+ [-no_esc_chars]</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index e04bb3e208..e5e217ca1d 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -266,6 +266,10 @@
<tag><c><![CDATA[-verbosity <levels>]]></c></tag>
<item><p>Sets <seealso marker="write_test_chapter#logging">verbosity levels
for printouts</seealso>.</p></item>
+
+ <tag><c><![CDATA[-no_esc_chars]]></c></tag>
+ <item><p>Disables automatic escaping of special HTML characters.
+ See the <seealso marker="write_test_chapter#logging">Logging chapter</seealso>.</p></item>
</taglist>
<note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note>
@@ -802,7 +806,7 @@
program</seealso> for an overview of available start flags
(as most flags have a corresponding configuration term)</item>
<item><seealso marker="write_test_chapter#logging">Logging</seealso>
- (for terms <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c>)</item>
+ (for terms <c>verbosity</c>, <c>stylesheet</c>, <c>basic_html</c> and <c>esc_chars</c>)</item>
<item><seealso marker="config_file_chapter#top">External Configuration Data</seealso>
(for terms <c>config</c> and <c>userconfig</c>)</item>
<item><seealso marker="event_handler_chapter#event_handling">Event
@@ -887,6 +891,9 @@
{basic_html, Bool}.
{basic_html, NodeRefs, Bool}.
+ {esc_chars, Bool}.
+ {esc_chars, NodeRefs, Bool}.
+
{release_shell, Bool}.</pre>
<p><em>Test terms:</em></p>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index e3811ec4cf..a7a652d506 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -1047,9 +1047,15 @@
<p>Common Test will escape special HTML characters (&lt;, &gt; and &amp;) in printouts
to the log file made with <c>ct:pal/4</c> and <c>io:format/2</c>. In order to print
- strings with HTML tags to the log, use the <c>ct:log/5</c> function. The character escaping
- feature is per default disabled for <c>ct:log/5</c>, but can be enabled with the
- <c>esc_chars</c> option.</p>
+ strings with HTML tags to the log, use the <c>ct:log/3,4,5</c> function. The character
+ escaping feature is per default disabled for <c>ct:log/3,4,5</c> but can be enabled with
+ the <c>esc_chars</c> option in the <c>Opts</c> list, see <seealso marker="ct#log-5">
+ <c>ct:log/3,4,5</c></seealso>.</p>
+
+ <p>If the character escaping feature needs to be disabled (typically for backwards
+ compatibility reasons), use the <c>ct_run</c> start flag <c>-no_esc_chars</c>, or the
+ <c>ct:run_test/1</c> start option <c>{esc_chars,Bool}</c> (this start option is also
+ supported in test specifications).</p>
<p>For more information about log files, see section
<seealso marker="run_test_chapter#log_files">Log Files</seealso>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 538be514d6..22941668f2 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -161,9 +161,9 @@ run(TestDirs) ->
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
%%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} |
%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
-%%% {verbosity,VLevels} | {basic_html,Bool} |
-%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
-%%% {release_shell,Bool}
+%%% {verbosity,VLevels} | {basic_html,Bool} |
+%%% {esc_chars,Bool} | {ct_hooks, CTHs} |
+%%% {enable_builtin_hooks,Bool} | {release_shell,Bool}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | [atom()] | string() | atom()
%%% Cases = [atom()] | atom()
diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl
index 92640cf323..899d2bfb5a 100644
--- a/lib/common_test/src/ct_groups.erl
+++ b/lib/common_test/src/ct_groups.erl
@@ -325,7 +325,7 @@ modify_tc_list1(GrSpecTs, TSCs) ->
true ->
{[TC|TSCs1],lists:delete(TC,GrSpecTs2)};
false ->
- case lists:keymember(TC, 2, GrSpecTs) of
+ case lists:keysearch(TC, 2, GrSpecTs) of
{value,Test} ->
{[Test|TSCs1],
lists:keydelete(TC, 2, GrSpecTs2)};
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
index 83ad33fdd8..90f36cbdc2 100644
--- a/lib/common_test/src/ct_hooks.erl
+++ b/lib/common_test/src/ct_hooks.erl
@@ -67,6 +67,8 @@ terminate(Hooks) ->
%% tests.
-spec init_tc(Mod :: atom(),
FuncSpec :: atom() |
+ {ConfigFunc :: init_per_testcase | end_per_testcase,
+ TestCase :: atom()} |
{ConfigFunc :: init_per_group | end_per_group,
GroupName :: atom(),
Properties :: list()},
@@ -103,7 +105,9 @@ init_tc(_Mod, TC = error_in_suite, Config) ->
%% @doc Called as each test case is completed. This includes all configuration
%% tests.
-spec end_tc(Mod :: atom(),
- FuncSpec :: atom() |
+ FuncSpec :: atom() |
+ {ConfigFunc :: init_per_testcase | end_per_testcase,
+ TestCase :: atom()} |
{ConfigFunc :: init_per_group | end_per_group,
GroupName :: atom(),
Properties :: list()},
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 4920383f39..a9ad571bfc 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -609,7 +609,8 @@ log_timestamp({MS,S,US}) ->
ct_log_fd,
tc_groupleaders,
stylesheet,
- async_print_jobs}).
+ async_print_jobs,
+ tc_esc_chars}).
logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
@@ -728,14 +729,18 @@ logger(Parent, Mode, Verbosity) ->
end
end || {Cat,VLvl} <- Verbosity],
io:nl(CtLogFd),
-
+ TcEscChars = case application:get_env(common_test, esc_chars) of
+ {ok,ECBool} -> ECBool;
+ _ -> true
+ end,
logger_loop(#logger_state{parent=Parent,
log_dir=AbsDir,
start_time=Time,
orig_GL=group_leader(),
ct_log_fd=CtLogFd,
tc_groupleaders=[],
- async_print_jobs=[]}).
+ async_print_jobs=[],
+ tc_esc_chars=TcEscChars}).
copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
case file:copy(SrcF, DestF) of
@@ -761,20 +766,21 @@ logger_loop(State) ->
end,
if Importance >= (100-VLvl) ->
CtLogFd = State#logger_state.ct_log_fd,
+ DoEscChars = State#logger_state.tc_esc_chars and EscChars,
case get_groupleader(Pid, GL, State) of
{tc_log,TCGL,TCGLs} ->
case erlang:is_process_alive(TCGL) of
true ->
State1 = print_to_log(SyncOrAsync, Pid,
Category, TCGL, Content,
- EscChars, State),
+ DoEscChars, State),
logger_loop(State1#logger_state{
tc_groupleaders = TCGLs});
false ->
%% Group leader is dead, so write to the
%% CtLog or unexpected_io log instead
unexpected_io(Pid, Category, Importance,
- Content, CtLogFd, EscChars),
+ Content, CtLogFd, DoEscChars),
logger_loop(State)
end;
@@ -783,7 +789,7 @@ logger_loop(State) ->
%% to ct_log, else write to unexpected_io
%% log
unexpected_io(Pid, Category, Importance, Content,
- CtLogFd, EscChars),
+ CtLogFd, DoEscChars),
logger_loop(State#logger_state{
tc_groupleaders = TCGLs})
end;
@@ -794,7 +800,7 @@ logger_loop(State) ->
%% make sure no IO for this test case from the
%% CT logger gets rejected
test_server:permit_io(GL, self()),
- print_style(GL, State#logger_state.stylesheet),
+ print_style(GL,GL,State#logger_state.stylesheet),
set_evmgr_gl(GL),
TCGLs = add_tc_gl(TCPid,GL,State),
if not RefreshLog ->
@@ -882,7 +888,7 @@ create_io_fun(FromPid, CtLogFd, EscChars) ->
{_HdOrFt,S,A} -> {false,S,A};
{S,A} -> {true,S,A}
end,
- try io_lib:format(Str,Args) of
+ try io_lib:format(Str, Args) of
IoStr when Escapable, EscChars, IoList == [] ->
escape_chars(IoStr);
IoStr when Escapable, EscChars ->
@@ -925,7 +931,12 @@ print_to_log(sync, FromPid, Category, TCGL, Content, EscChars, State) ->
if FromPid /= TCGL ->
IoFun = create_io_fun(FromPid, CtLogFd, EscChars),
IoList = lists:foldl(IoFun, [], Content),
- io:format(TCGL,["$tc_html","~ts"], [IoList]);
+ try io:format(TCGL,["$tc_html","~ts"], [IoList]) of
+ ok -> ok
+ catch
+ _:_ ->
+ io:format(TCGL,"~ts", [IoList])
+ end;
true ->
unexpected_io(FromPid, Category, ?MAX_IMPORTANCE, Content,
CtLogFd, EscChars)
@@ -958,7 +969,10 @@ print_to_log(async, FromPid, Category, TCGL, Content, EscChars, State) ->
_:terminated ->
unexpected_io(FromPid, Category,
?MAX_IMPORTANCE,
- Content, CtLogFd, EscChars)
+ Content, CtLogFd, EscChars);
+ _:_ ->
+ io:format(TCGL, "~ts",
+ [lists:foldl(IoFun,[],Content)])
end;
false ->
unexpected_io(FromPid, Category,
@@ -1099,26 +1113,27 @@ open_ctlog(MiscIoName) ->
"View I/O logged after the test run</a></li>\n</ul>\n",
[MiscIoName,MiscIoName]),
- print_style(Fd,undefined),
+ print_style(Fd,group_leader(),undefined),
io:format(Fd,
xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
"<br />\n<h4>PROGRESS LOG</h4>\n<pre>\n"), []),
Fd.
-print_style(Fd,undefined) ->
+print_style(Fd,GL,undefined) ->
case basic_html() of
true ->
- io:format(Fd,
- "<style>\n"
- "div.ct_internal { background:lightgrey; color:black; }\n"
- "div.default { background:lightgreen; color:black; }\n"
- "</style>\n",
- []);
+ Style = "<style>\n
+ div.ct_internal { background:lightgrey; color:black; }\n
+ div.default { background:lightgreen; color:black; }\n
+ </style>\n",
+ if Fd == GL -> io:format(["$tc_html",Style], []);
+ true -> io:format(Fd, Style, [])
+ end;
_ ->
ok
end;
-print_style(Fd,StyleSheet) ->
+print_style(Fd,GL,StyleSheet) ->
case file:read_file(StyleSheet) of
{ok,Bin} ->
Str = b2s(Bin,encoding(StyleSheet)),
@@ -1131,23 +1146,30 @@ print_style(Fd,StyleSheet) ->
N1 -> N1
end,
if (Pos0 == 0) and (Pos1 /= 0) ->
- print_style_error(Fd,StyleSheet,missing_style_start_tag);
+ print_style_error(Fd,GL,StyleSheet,missing_style_start_tag);
(Pos0 /= 0) and (Pos1 == 0) ->
- print_style_error(Fd,StyleSheet,missing_style_end_tag);
+ print_style_error(Fd,GL,StyleSheet,missing_style_end_tag);
Pos0 /= 0 ->
Style = string:sub_string(Str,Pos0,Pos1+7),
- io:format(Fd,"~ts\n",[Style]);
+ if Fd == GL -> io:format(Fd,["$tc_html","~ts\n"],[Style]);
+ true -> io:format(Fd,"~ts\n",[Style])
+ end;
Pos0 == 0 ->
- io:format(Fd,"<style>~ts</style>\n",[Str])
+ if Fd == GL -> io:format(Fd,["$tc_html","<style>\n~ts</style>\n"],[Str]);
+ true -> io:format(Fd,"<style>\n~ts</style>\n",[Str])
+ end
end;
{error,Reason} ->
- print_style_error(Fd,StyleSheet,Reason)
+ print_style_error(Fd,GL,StyleSheet,Reason)
end.
-print_style_error(Fd,StyleSheet,Reason) ->
- io:format(Fd,"\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
- [StyleSheet,Reason]),
- print_style(Fd,undefined).
+print_style_error(Fd,GL,StyleSheet,Reason) ->
+ IO = io_lib:format("\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
+ [StyleSheet,Reason]),
+ if Fd == GL -> io:format(Fd,["$tc_html",IO],[]);
+ true -> io:format(Fd,IO,[])
+ end,
+ print_style(Fd,GL,undefined).
close_ctlog(Fd) ->
io:format(Fd, "\n</pre>\n", []),
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 228daf459b..d24edad2eb 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -27,7 +27,7 @@
-export([run_on_node/2,run_on_node/3]).
-export([run_test/1,run_test/2]).
-export([get_event_mgr_ref/0]).
--export([basic_html/1]).
+-export([basic_html/1,esc_chars/1]).
-export([abort/0,abort/1,progress/0]).
@@ -317,6 +317,16 @@ basic_html(Bool) ->
ok.
%%%-----------------------------------------------------------------
+%%% @spec esc_chars(Bool) -> ok
+%%% Bool = true | false
+%%%
+%%% @doc If set to false, the ct_master logs will be written without
+%%% special characters being escaped in the HTML logs.
+esc_chars(Bool) ->
+ application:set_env(common_test_master, esc_chars, Bool),
+ ok.
+
+%%%-----------------------------------------------------------------
%%% MASTER, runs on central controlling node.
%%%-----------------------------------------------------------------
start_master(NodeOptsList) ->
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index ceb94ceee5..a0f9f47b41 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -65,6 +65,7 @@
logdir,
logopts = [],
basic_html,
+ esc_chars = true,
verbosity = [],
config = [],
event_handlers = [],
@@ -346,6 +347,15 @@ script_start1(Parent, Args) ->
application:set_env(common_test, basic_html, true),
true
end,
+ %% esc_chars - used by ct_logs
+ EscChars = case proplists:get_value(no_esc_chars, Args) of
+ undefined ->
+ application:set_env(common_test, esc_chars, true),
+ undefined;
+ _ ->
+ application:set_env(common_test, esc_chars, false),
+ false
+ end,
%% disable_log_cache - used by ct_logs
case proplists:get_value(disable_log_cache, Args) of
undefined ->
@@ -359,6 +369,7 @@ script_start1(Parent, Args) ->
cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
verbosity = Verbosity,
event_handlers = EvHandlers,
ct_hooks = CTHooks,
@@ -587,6 +598,17 @@ combine_test_opts(TS, Specs, Opts) ->
BHBool
end,
+ EscChars =
+ case choose_val(Opts#opts.esc_chars,
+ TSOpts#opts.esc_chars) of
+ undefined ->
+ true;
+ ECBool ->
+ application:set_env(common_test, esc_chars,
+ ECBool),
+ ECBool
+ end,
+
Opts#opts{label = Label,
profile = Profile,
testspec_files = Specs,
@@ -595,6 +617,7 @@ combine_test_opts(TS, Specs, Opts) ->
logdir = which(logdir, LogDir),
logopts = AllLogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
verbosity = AllVerbosity,
silent_connections = AllSilentConns,
config = TSOpts#opts.config,
@@ -795,6 +818,7 @@ script_usage() ->
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]"
"\n\t [-repeat N] |"
"\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
"\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]"
@@ -822,6 +846,7 @@ script_usage() ->
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]"
"\n\t [-repeat N] |"
"\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
"\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"),
@@ -847,7 +872,8 @@ script_usage() ->
"\n\t [-multiply_timetraps N]"
"\n\t [-scale_timetraps]"
"\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
- "\n\t [-basic_html]\n\n").
+ "\n\t [-basic_html]"
+ "\n\t [-no_esc_chars]\n\n").
%%%-----------------------------------------------------------------
%%% @hidden
@@ -1089,7 +1115,17 @@ run_test2(StartOpts) ->
application:set_env(common_test, basic_html, BasicHtmlBool),
BasicHtmlBool
end,
-
+ %% esc_chars - used by ct_logs
+ EscChars =
+ case proplists:get_value(esc_chars, StartOpts) of
+ undefined ->
+ application:set_env(common_test, esc_chars, true),
+ undefined;
+ EscCharsBool ->
+ application:set_env(common_test, esc_chars, EscCharsBool),
+ EscCharsBool
+ end,
+ %% disable_log_cache - used by ct_logs
case proplists:get_value(disable_log_cache, StartOpts) of
undefined ->
application:set_env(common_test, disable_log_cache, false);
@@ -1104,6 +1140,7 @@ run_test2(StartOpts) ->
cover = Cover, cover_stop = CoverStop,
step = Step, logdir = LogDir,
logopts = LogOpts, basic_html = BasicHtml,
+ esc_chars = EscChars,
config = CfgFiles,
verbosity = Verbosity,
event_handlers = EvHandlers,
@@ -1445,6 +1482,7 @@ get_data_for_node(#testspec{label = Labels,
logdir = LogDirs,
logopts = LogOptsList,
basic_html = BHs,
+ esc_chars = EscChs,
stylesheet = SSs,
verbosity = VLvls,
silent_connections = SilentConnsList,
@@ -1472,6 +1510,7 @@ get_data_for_node(#testspec{label = Labels,
LOs -> LOs
end,
BasicHtml = proplists:get_value(Node, BHs),
+ EscChars = proplists:get_value(Node, EscChs),
Stylesheet = proplists:get_value(Node, SSs),
Verbosity = case proplists:get_value(Node, VLvls) of
undefined -> [];
@@ -1498,6 +1537,7 @@ get_data_for_node(#testspec{label = Labels,
logdir = LogDir,
logopts = LogOpts,
basic_html = BasicHtml,
+ esc_chars = EscChars,
stylesheet = Stylesheet,
verbosity = Verbosity,
silent_connections = SilentConns,
@@ -2182,10 +2222,18 @@ do_run_test(Tests, Skip, Opts0) ->
%% test_server needs to know the include path too
InclPath = case application:get_env(common_test, include) of
{ok,Incls} -> Incls;
- _ -> []
+ _ -> []
end,
application:set_env(test_server, include, InclPath),
+ %% copy the escape characters setting to test_server
+ EscChars =
+ case application:get_env(common_test, esc_chars) of
+ {ok,ECBool} -> ECBool;
+ _ -> true
+ end,
+ application:set_env(test_server, esc_chars, EscChars),
+
test_server_ctrl:start_link(local),
%% let test_server expand the test tuples and count no of cases
@@ -3071,6 +3119,10 @@ opts2args(EnvStartOpts) ->
[{basic_html,[]}];
({basic_html,false}) ->
[];
+ ({esc_chars,false}) ->
+ [{no_esc_chars,[]}];
+ ({esc_chars,true}) ->
+ [];
({event_handler,EH}) when is_atom(EH) ->
[{event_handler,[atom_to_list(EH)]}];
({event_handler,EHs}) when is_list(EHs) ->
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 5cd52bd042..61d8f49dcc 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1146,8 +1146,9 @@ should_be_added(Tag,Node,_Data,Spec) ->
if
%% list terms *without* possible duplicates here
Tag == logdir; Tag == logopts;
- Tag == basic_html; Tag == label;
- Tag == auto_compile; Tag == abort_if_missing_suites;
+ Tag == basic_html; Tag == esc_chars;
+ Tag == label; Tag == auto_compile;
+ Tag == abort_if_missing_suites;
Tag == stylesheet; Tag == verbosity;
Tag == silent_connections ->
lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
@@ -1544,6 +1545,8 @@ valid_terms() ->
{logopts,3},
{basic_html,2},
{basic_html,3},
+ {esc_chars,2},
+ {esc_chars,3},
{verbosity,2},
{verbosity,3},
{silent_connections,2},
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index b7fa7947e2..3561a0a2d3 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -459,6 +459,7 @@ loop(Mode,TestData,StartDir) ->
error:badarg -> []
end,
ct_hooks:terminate(Callbacks),
+
close_connections(ets:tab2list(?conn_table)),
ets:delete(?conn_table),
ets:delete(?board_table),
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index 2c1954c2b3..bdfe2041a5 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -37,6 +37,7 @@
logdir=["."],
logopts=[],
basic_html=[],
+ esc_chars=[],
verbosity=[],
silent_connections=[],
cover=[],
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index a8c4a455e1..780fbea79a 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -130,7 +130,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
tag_event(Event)),
if is_list(SReport) ->
SaslHeader = format_header(State),
- ct_logs:LogFunc(sasl, ?STD_IMPORTANCE, SaslHeader, SReport, []);
+ case LogFunc of
+ tc_log ->
+ ct_logs:tc_log(sasl, ?STD_IMPORTANCE,
+ SaslHeader, SReport, [], []);
+ tc_log_async ->
+ ct_logs:tc_log_async(sasl, ?STD_IMPORTANCE,
+ SaslHeader, SReport, [])
+ end;
true -> %% Report is an atom if no logging is to be done
ignore
end
@@ -139,7 +146,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
tag_event(Event),io_lib),
if is_list(EReport) ->
ErrHeader = format_header(State),
- ct_logs:LogFunc(error_logger, ?STD_IMPORTANCE, ErrHeader, EReport, []);
+ case LogFunc of
+ tc_log ->
+ ct_logs:tc_log(error_logger, ?STD_IMPORTANCE,
+ ErrHeader, EReport, [], []);
+ tc_log_async ->
+ ct_logs:tc_log_async(error_logger, ?STD_IMPORTANCE,
+ ErrHeader, EReport, [])
+ end;
true -> %% Report is an atom if no logging is to be done
ignore
end,
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
index 9a924a66ac..5e4df0b3c2 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
@@ -1,8 +1,8 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
-%%
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,46 +14,46 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(ct_update_config_SUITE).
-
--suite_defaults([{timetrap, {minutes, 10}}]).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include("ct.hrl").
-
--define(now, os:timestamp()).
-
-%% Test server callback functions
-init_per_suite(Config) ->
- [{init_per_suite,?now}|Config].
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_testcase(_TestCase, Config) ->
- [{init_per_testcase,?now}|Config].
-
-end_per_testcase(_TestCase, _Config) ->
- ok.
-
-init_per_group(GroupName, Config) ->
- [{init_per_group,?now}|Config].
-
-end_per_group(GroupName, Config) ->
- ok.
-
-all() ->
- [{group,group1}].
-
-groups() ->
- [{group1,[],[test_case]}].
-
-%% Test cases starts here.
-test_case(Config) when is_list(Config) ->
- ok.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_update_config_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+-define(now, ct_test_support:unique_timestamp()).
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{init_per_suite,?now}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ [{init_per_testcase,?now}|Config].
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{init_per_group,?now}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
index e5bb4f3ef6..8b64ff8060 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -90,7 +90,7 @@ id(Opts) ->
gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
data = {?MODULE, id, [Opts]}}),
ct:log("~w:id called", [?MODULE]),
- os:timestamp().
+ ct_test_support:unique_timestamp().
%% @doc Called before init_per_suite is called. Note that this callback is
%% only called if the CTH is added before init_per_suite is run (eg. in a test
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
index 5503bf85ae..928bedfed1 100644
--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
@@ -25,7 +25,7 @@
-include_lib("common_test/src/ct_util.hrl").
-include_lib("common_test/include/ct_event.hrl").
--define(now, os:timestamp()).
+-define(now, ct_test_support:unique_timestamp()).
%% CT Hooks
-compile(export_all).
diff --git a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
index 2994ce4a96..bf3eeee328 100644
--- a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
+++ b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
@@ -44,13 +44,29 @@
%% instance, the tests need to be performed on a separate node (or
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,120}}].
+
+all() ->
+ [
+ pre_post_io
+ ].
+
init_per_suite(Config) ->
- DataDir = ?config(data_dir, Config),
- CTH = filename:join(DataDir, "cth_ctrl.erl"),
- ct:pal("Compiling ~p: ~p",
- [CTH,compile:file(CTH,[{outdir,DataDir},debug_info])]),
- ct_test_support:init_per_suite([{path_dirs,[DataDir]},
- {start_sasl,true} | Config]).
+ TTInfo = {_T,{_Scaled,ScaleVal}} = ct:get_timetrap_info(),
+ ct:pal("Timetrap info = ~w", [TTInfo]),
+ if ScaleVal > 1 ->
+ {skip,"Skip on systems running e.g. cover or debug!"};
+ ScaleVal =< 1 ->
+ DataDir = ?config(data_dir, Config),
+ CTH = filename:join(DataDir, "cth_ctrl.erl"),
+ ct:pal("Compiling ~p: ~p",
+ [CTH,compile:file(CTH,[{outdir,DataDir},
+ debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]},
+ {start_sasl,true} | Config])
+ end.
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -61,13 +77,6 @@ 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() ->
- [
- pre_post_io
- ].
-
%%--------------------------------------------------------------------
%% TEST CASES
%%--------------------------------------------------------------------
@@ -90,31 +99,50 @@ pre_post_io(Config) ->
%%!--------------------------------------------------------------------
spawn(fun() ->
- ct:pal("CONTROLLER: Started!", []),
+ ct:pal("CONTROLLER: Starting test run #1...", []),
%% --- test run 1 ---
- ct:sleep(3000),
- ct:pal("CONTROLLER: Handle remote events = true", []),
- ok = ct_test_support:ct_rpc({cth_log_redirect,
- handle_remote_events,
- [true]}, Config),
- ct:sleep(2000),
- ct:pal("CONTROLLER: Proceeding with test run #1!", []),
+ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
+ handle_remote_events,
+ [true]}, Config], 3000),
+ CTLoggerPid1 = ct_test_support:ct_rpc({erlang,whereis,
+ [ct_logs]}, Config),
+ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
+ [CTLoggerPid1]),
+ ct:sleep(5000),
+ ct:pal("CONTROLLER: Proceeding with test run #1...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
ct:sleep(6000),
- ct:pal("CONTROLLER: Proceeding with shutdown #1!", []),
+ ct:pal("CONTROLLER: Proceeding with shutdown #1...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ try_loop(fun() ->
+ false = ct_test_support:ct_rpc({erlang,
+ is_process_alive,
+ [CTLoggerPid1]},
+ Config)
+ end, 3000),
+ ct:pal("CONTROLLER: Shutdown #1 complete!", []),
+ ct:pal("CONTROLLER: Starting test run #2...", []),
%% --- test run 2 ---
- ct:sleep(3000),
- ct:pal("CONTROLLER: Handle remote events = true", []),
- ok = ct_test_support:ct_rpc({cth_log_redirect,
- handle_remote_events,
- [true]}, Config),
- ct:sleep(2000),
- ct:pal("CONTROLLER: Proceeding with test run #2!", []),
+ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
+ handle_remote_events,
+ [true]}, Config], 3000),
+ CTLoggerPid2 = ct_test_support:ct_rpc({erlang,whereis,
+ [ct_logs]}, Config),
+ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
+ [CTLoggerPid2]),
+ ct:sleep(5000),
+ ct:pal("CONTROLLER: Proceeding with test run #2...", []),
ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
ct:sleep(6000),
- ct:pal("CONTROLLER: Proceeding with shutdown #2!", []),
- ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config)
+ ct:pal("CONTROLLER: Proceeding with shutdown #2...", []),
+ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ try_loop(fun() ->
+ false = ct_test_support:ct_rpc({erlang,
+ is_process_alive,
+ [CTLoggerPid2]},
+ Config)
+ end, 3000),
+ ct:pal("CONTROLLER: Shutdown #2 complete!", [])
end),
ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -157,7 +185,7 @@ pre_post_io(Config) ->
Counters
end, {pre,0,0,0,0}, Ts),
[_|Counters] = tuple_to_list(PrePostIOEntries),
- ct:log("Entries in the Pre/Post Test IO Log: ~p", [Counters]),
+ ct:pal("Entries in the Pre/Post Test IO Log: ~w", [Counters]),
case [C || C <- Counters, C < 2] of
[] ->
ok;
@@ -183,7 +211,7 @@ pre_post_io(Config) ->
[LogN,ErrN+1];
(_, Counters) -> Counters
end, [0,0], Ts),
- ct:log("Entries in the Unexpected IO Log: ~p", [UnexpIOEntries]),
+ ct:log("Entries in the Unexpected IO Log: ~w", [UnexpIOEntries]),
case [N || N <- UnexpIOEntries, N < 2] of
[] ->
ok;
@@ -208,6 +236,38 @@ setup(Test, Config) ->
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
+try_loop(_Fun, 0) ->
+ ct:pal("WARNING! Fun never succeeded!", []),
+ gave_up;
+try_loop(Fun, N) ->
+ try Fun() of
+ {error,_} ->
+ timer:sleep(10),
+ try_loop(Fun, N-1);
+ Result ->
+ Result
+ catch
+ _:_What ->
+ timer:sleep(10),
+ try_loop(Fun, N-1)
+ end.
+
+try_loop(M, F, _A, 0) ->
+ ct:pal("WARNING! ~w:~w never succeeded!", [M,F]),
+ gave_up;
+try_loop(M, F, A, N) ->
+ try apply(M, F, A) of
+ {error,_} ->
+ timer:sleep(10),
+ try_loop(M, F, A, N-1);
+ Result ->
+ Result
+ catch
+ _:_ ->
+ timer:sleep(10),
+ try_loop(M, F, A, N-1)
+ end.
+
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl b/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
index b8595b40b9..66a950c178 100644
--- a/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
+++ b/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
@@ -53,8 +53,7 @@ init(_Id, _Opts) ->
receive
{?MODULE,proceed} -> ok
after
- 10000 ->
- ok
+ 10000 -> ok
end,
{ok,[],ct_last}.
@@ -66,8 +65,7 @@ terminate(_State) ->
receive
{?MODULE,proceed} -> ok
after
- 10000 ->
- ok
+ 10000 -> ok
end,
stop_external_logger(cth_logger),
stop_dispatcher(),
@@ -94,7 +92,7 @@ init_logger(Name) ->
logger_loop(N) ->
ct:log("Logger iteration: ~p", [N]),
error_logger:error_report(N),
- timer:sleep(250),
+ timer:sleep(100),
logger_loop(N+1).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 8e7ac9395c..493fa82c79 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -43,6 +43,8 @@
-export([random_error/1]).
+-export([unique_timestamp/0]).
+
-include_lib("kernel/include/file.hrl").
%%%-----------------------------------------------------------------
@@ -110,7 +112,8 @@ start_slave(NodeName, Config, Level) ->
undefined -> [];
Ds -> Ds
end,
- PathDirs = [PrivDir,TSDir | AddPathDirs],
+ TestSupDir = filename:dirname(code:which(?MODULE)),
+ PathDirs = [PrivDir,TSDir,TestSupDir | AddPathDirs],
[true = rpc:call(CTNode, code, add_patha, [D]) || D <- PathDirs],
test_server:format(Level, "Dirs added to code path (on ~w):~n",
[CTNode]),
@@ -1430,7 +1433,21 @@ rm_files([F | Fs]) ->
end;
rm_files([]) ->
ok.
-
+
+unique_timestamp() ->
+ unique_timestamp(os:timestamp(), 100000).
+
+unique_timestamp(TS, 0) ->
+ TS;
+unique_timestamp(TS0, N) ->
+ case os:timestamp() of
+ TS0 ->
+ timer:sleep(1),
+ unique_timestamp(TS0, N-1);
+ TS1 ->
+ TS1
+ end.
+
%%%-----------------------------------------------------------------
%%%
slave_stop(Node) ->