diff options
Diffstat (limited to 'lib/common_test')
39 files changed, 943 insertions, 254 deletions
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index 8c3b13951d..030de2efa9 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2012</year> + <year>2004</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,6 +32,122 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Severe errors detected by <c>test_server</c> (e.g. if log + files directories cannot be created) will now be reported + to <c>common_test</c> and noted in the <c>common_test</c> + logs.</p> + <p> + Own Id: OTP-9769 Aux Id: kunagi-202 [113] </p> + </item> + <item> + <p> + If a busy test case generated lots of error messages, + cth_log_redirect:post_end_per_testcase would crash with a + timeout while waiting for the error logger to finish + handling all error reports. The default timer was 5 + seconds. This has now been extended to 5 minutes.</p> + <p> + Own Id: OTP-10040 Aux Id: kunagi-173 [84] </p> + </item> + <item> + <p> + Some bugfixes in <c>ct_snmp:</c></p> + <p> + <list> <item> ct_snmp will now use the value of the + 'agent_vsns' config variable when setting the 'variables' + parameter to snmp application agent configuration. + Earlier this had to be done separately - i.e. the + supported versions had to be specified twice. </item> + <item> Snmp application failed to write notify.conf since + ct_snmp gave the notify type as a string instead of an + atom. This has been corrected. </item> </list></p> + <p> + Own Id: OTP-10432</p> + </item> + <item> + <p> + Some bugfixes in <c>ct_snmp</c>:</p> + <p> + <list> <item> Functions <c>register_users/2</c>, + <c>register_agents/2</c> and <c>register_usm_users/2</c>, + and the corresponding <c>unregister_*/1</c> functions + were not executable. These are corrected/rewritten. + </item> <item> Function <c>update_usm_users/2</c> is + removed, and an unregister function is added instead. + Update can now be done with unregister_usm_users and then + register_usm_users. </item> <item> Functions + <c>unregister_*/2</c> are added, so specific + users/agents/usm users can be unregistered. </item> + <item> Function <c>unload_mibs/1</c> is added for + completeness. </item> <item> Overriding configuration + files did not work, since the files were written in + priv_dir instead of in the configuration dir + (priv_dir/conf). This has been corrected. </item> <item> + Arguments to <c>register_usm_users/2</c> were faulty + documented. This has been corrected. </item> </list></p> + <p> + Own Id: OTP-10434 Aux Id: kunagi-264 [175] </p> + </item> + <item> + <p> + Faulty exported specs in common test has been corrected + to <c>ct_netconfc:hook_options/0</c> and + <c>inet:hostname/0</c></p> + <p> + Own Id: OTP-10601</p> + </item> + <item> + <p> + The netconf client in common_test did not adjust the + window after receiving data. Due to this, the client + stopped receiving data after a while. This has been + corrected.</p> + <p> + Own Id: OTP-10646</p> + </item> + </list> + </section> + + + <section><title>Known Bugs and Problems</title> + <list> + <item> + <p> + The earlier undocumented cross cover feature for + accumulating cover data over multiple tests has now been + fixed and documented.</p> + <p> + Own Id: OTP-9870 Aux Id: kunagi-206 [117] </p> + </item> + <item> + <p> + CT drops error reason when groups/0 crashes.</p> + <p> + Own Id: OTP-10631 Aux Id: kunagi-345 [256] </p> + </item> + <item> + <p> + Problem opening sftp connection with ct_ssh.</p> + <p> + Own Id: OTP-10632 Aux Id: kunagi-346 [257] </p> + </item> + <item> + <p> + Event handler on a ct_master node causes hanging.</p> + <p> + Own Id: OTP-10634 Aux Id: kunagi-347 [258] </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.6.3.1</title> <section><title>Known Bugs and Problems</title> diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in index 4372ab124e..5a9fabbe45 100644 --- a/lib/common_test/priv/Makefile.in +++ b/lib/common_test/priv/Makefile.in @@ -68,15 +68,15 @@ JS = jquery-latest.js jquery.tablesorter.min.js include ../../test_server/vsn.mk debug opt: - sed -e 's;@CT_VSN@;$(VSN);' \ + $(V_at)sed -e 's;@CT_VSN@;$(VSN);' \ -e 's;@TS_VSN@;$(TEST_SERVER_VSN);' \ ../install.sh.in > install.sh - chmod 775 install.sh + $(V_at)chmod 775 install.sh docs: clean: - rm -f $(SCRIPTS) + $(V_at)rm -f $(SCRIPTS) # ---------------------------------------------------- diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index dd2923ece9..4600c0ad78 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2012. All Rights Reserved. +# Copyright Ericsson AB 2003-2013. 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 @@ -97,7 +97,7 @@ DTD_FILES = \ # ---------------------------------------------------- ERL_COMPILE_FLAGS += -pa ../ebin -I../include -I $(ERL_TOP)/lib/snmp/include/ \ -I../../test_server/include -I../../xmerl/inc/ \ - -I $(ERL_TOP)/lib/kernel/include + -I $(ERL_TOP)/lib/kernel/include -Werror # ---------------------------------------------------- # Targets @@ -127,10 +127,10 @@ clean: # Special Build Targets # ---------------------------------------------------- $(APP_TARGET): $(APP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ # ---------------------------------------------------- # Release Target diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 853a1582cf..04a95a53fa 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -735,7 +735,7 @@ fail(Format, Args) -> %%% overwrites the string set by this function.</p> comment(Comment) when is_list(Comment) -> Formatted = - case (catch io_lib:format("~s",[Comment])) of + case (catch io_lib:format("~ts",[Comment])) of {'EXIT',_} -> % it's a list not a string io_lib:format("~p",[Comment]); String -> diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index ac4ffbb236..c35cbd3c08 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -1,7 +1,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 @@ -610,7 +610,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> EncBin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin2), case file:write_file(EncryptFileName, EncBin) of ok -> - io:format("~s --(encrypt)--> ~s~n", + io:format("~ts --(encrypt)--> ~ts~n", [SrcFileName,EncryptFileName]), ok; {error,Reason} -> @@ -652,7 +652,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) -> _ -> case file:write_file(TargetFileName, SrcBin) of ok -> - io:format("~s --(decrypt)--> ~s~n", + io:format("~ts --(decrypt)--> ~ts~n", [EncryptFileName,TargetFileName]), ok; {error,Reason} -> @@ -703,7 +703,7 @@ get_crypt_key_from_file() -> _ -> case catch string:tokens(binary_to_list(Result), [$\n,$\r]) of [Key] -> - io:format("~nCrypt key file: ~s~n", [FullName]), + io:format("~nCrypt key file: ~ts~n", [FullName]), Key; _ -> {error,{bad_crypt_file,FullName}} diff --git a/lib/common_test/src/ct_config_plain.erl b/lib/common_test/src/ct_config_plain.erl index 237df5c8f3..c6547f0a40 100644 --- a/lib/common_test/src/ct_config_plain.erl +++ b/lib/common_test/src/ct_config_plain.erl @@ -1,7 +1,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 @@ -47,7 +47,7 @@ read_config(ConfigFile) -> {error,no_crypt_file} -> {error,{config_file_error, lists:flatten( - io_lib:format("~s",[file:format_error(Reason)]))}}; + io_lib:format("~ts",[file:format_error(Reason)]))}}; {error,CryptError} -> {error,{decrypt_file_error,CryptError}}; _ when is_list(Key) -> diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl index d7bd18606b..ac08a3e0ad 100644 --- a/lib/common_test/src/ct_conn_log_h.erl +++ b/lib/common_test/src/ct_conn_log_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -52,7 +52,7 @@ open_files([],State) -> do_open_files([{Tag,File}|Logs],Acc) -> - case file:open(File, [write]) of + case file:open(File, [write,{encoding,utf8}]) of {ok,Fd} -> do_open_files(Logs,[{Tag,Fd}|Acc]); {error,Reason} -> @@ -98,9 +98,9 @@ terminate(_,#state{logs=Logs}) -> %%% Writing reports write_report(Time,#conn_log{module=ConnMod}=Info,Data,State) -> {LogType,Fd} = get_log(Info,State), - io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time), - format_title(LogType,Info), - format_data(ConnMod,LogType,Data)]). + io:format(Fd,"~n~ts~ts~ts",[format_head(ConnMod,LogType,Time), + format_title(LogType,Info), + format_data(ConnMod,LogType,Data)]). write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) -> case get_log(Info,State) of @@ -109,9 +109,10 @@ write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) -> %% sasl error handler, so don't write it again. ok; {LogType,Fd} -> - io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time," ERROR"), - format_title(LogType,Info), - format_error(LogType,Report)]) + io:format(Fd,"~n~ts~ts~ts", + [format_head(ConnMod,LogType,Time," ERROR"), + format_title(LogType,Info), + format_error(LogType,Report)]) end. get_log(Info,State) -> @@ -140,16 +141,16 @@ format_head(ConnMod,LogType,Time) -> format_head(ConnMod,LogType,Time,""). format_head(ConnMod,raw,Time,Text) -> - io_lib:format("~n~p, ~p~s, ",[now_to_time(Time),ConnMod,Text]); + io_lib:format("~n~w, ~w~ts, ",[now_to_time(Time),ConnMod,Text]); format_head(ConnMod,_,Time,Text) -> Head = pad_char_end(?WIDTH,pretty_head(now_to_time(Time),ConnMod,Text),$=), - io_lib:format("~n~s",[Head]). + io_lib:format("~n~ts",[Head]). format_title(raw,#conn_log{client=Client}=Info) -> - io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]); + io_lib:format("Client ~w ~s ~ts",[Client,actionstr(Info),serverstr(Info)]); format_title(_,Info) -> Title = pad_char_end(?WIDTH,pretty_title(Info),$=), - io_lib:format("~n~s", [Title]). + io_lib:format("~n~ts", [Title]). format_data(_,_,NoData) when NoData == ""; NoData == <<>> -> ""; @@ -162,8 +163,6 @@ format_error(pretty,Report) -> [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report]. - - %%%----------------------------------------------------------------- %%% Helpers conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) -> @@ -187,12 +186,12 @@ now_to_time({_,_,MicroS}=Now) -> pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) -> Text = string:to_upper(atom_to_list(ConnMod) ++ Text0), - io_lib:format("= ~s ==== ~s-~s-~p::~s:~s:~s,~s ", + io_lib:format("= ~s ==== ~s-~s-~w::~s:~s:~s,~s ", [Text,t(D),month(Mo),Y,t(H),t(Mi),t(S), micro2milli(MicroS)]). pretty_title(#conn_log{client=Client}=Info) -> - io_lib:format("= Client ~p ~s Server ~s ", + io_lib:format("= Client ~w ~s Server ~ts ", [Client,actionstr(Info),serverstr(Info)]). actionstr(#conn_log{action=send}) -> "----->"; @@ -204,7 +203,7 @@ actionstr(_) -> "<---->". serverstr(#conn_log{name=undefined,address=Address}) -> io_lib:format("~p",[Address]); serverstr(#conn_log{name=Alias,address=Address}) -> - io_lib:format("~p(~p)",[Alias,Address]). + io_lib:format("~w(~p)",[Alias,Address]). month(1) -> "Jan"; month(2) -> "Feb"; diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl index 49e0635d79..c1c1d943b9 100644 --- a/lib/common_test/src/ct_event.erl +++ b/lib/common_test/src/ct_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -150,7 +150,7 @@ init(RecvPids) -> %%-------------------------------------------------------------------- handle_event(Event,State=#state{receivers=RecvPids}) -> print("~n=== ~w ===~n", [?MODULE]), - print("~p: ~p~n", [Event#event.name,Event#event.data]), + print("~w: ~w~n", [Event#event.name,Event#event.data]), lists:foreach(fun(Recv) -> report_event(Recv,Event) end, RecvPids), {ok,State}. diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index c1abf27e9f..5fe4eaf511 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -750,12 +750,12 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> Descr1 = lists:flatten(io_lib:format("~P",[Descr,10])), if length(Descr1) > 50 -> Descr2 = string:substr(Descr1,1,50), - io_lib:format("{badmatch,~s...}",[Descr2]); + io_lib:format("{badmatch,~ts...}",[Descr2]); true -> - io_lib:format("{badmatch,~s}",[Descr1]) + io_lib:format("{badmatch,~ts}",[Descr1]) end; {test_case_failed,Reason} -> - case (catch io_lib:format("{test_case_failed,~s}", [Reason])) of + case (catch io_lib:format("{test_case_failed,~ts}", [Reason])) of {'EXIT',_} -> io_lib:format("{test_case_failed,~p}", [Reason]); Result -> Result @@ -788,7 +788,7 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> "<font color=\"green\">" ++ "(" ++ "</font>" ++ Comment ++ "<font color=\"green\">" ++ ")" ++ "</font>", - Str = io_lib:format("~s ~s", [ErrorHtml,CommentHtml]), + Str = io_lib:format("~ts ~ts", [ErrorHtml,CommentHtml]), test_server:comment(Str) end end, @@ -803,24 +803,24 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> end, case Loc of [{?MODULE,error_in_suite}] -> - PrintErr("Error in suite detected: ~s", [ErrStr]); + PrintErr("Error in suite detected: ~ts", [ErrStr]); R when R == unknown; R == undefined -> - PrintErr("Error detected: ~s", [ErrStr]); + PrintErr("Error detected: ~ts", [ErrStr]); %% if a function specified by all/0 does not exist, we %% pick up undef here [{LastMod,LastFunc}|_] when ErrStr == "undef" -> - PrintErr("~w:~w could not be executed~nReason: ~s", + PrintErr("~w:~w could not be executed~nReason: ~ts", [LastMod,LastFunc,ErrStr]); [{LastMod,LastFunc}|_] -> - PrintErr("~w:~w failed~nReason: ~s", [LastMod,LastFunc,ErrStr]); + PrintErr("~w:~w failed~nReason: ~ts", [LastMod,LastFunc,ErrStr]); [{LastMod,LastFunc,LastLine}|_] -> %% print error to console, we are only %% interested in the last executed expression - PrintErr("~w:~w failed on line ~w~nReason: ~s", + PrintErr("~w:~w failed on line ~w~nReason: ~ts", [LastMod,LastFunc,LastLine,ErrStr]), case ct_util:read_suite_data({seq,Mod,Func}) of @@ -1184,7 +1184,7 @@ report(What,Data) -> ok; {error,Reason} -> ct_logs:log("COVER INFO", - "Importing cover data from: ~s fails! " + "Importing cover data from: ~ts fails! " "Reason: ~p", [Imp,Reason]) end end, Imps) @@ -1349,4 +1349,7 @@ format_comment(Comment) -> %%%----------------------------------------------------------------- %%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> - ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols). + get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, utf8). + +get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) -> + ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding). diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl index 1f01d84601..2d4b1d1f52 100644 --- a/lib/common_test/src/ct_gen_conn.erl +++ b/lib/common_test/src/ct_gen_conn.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -272,7 +272,7 @@ call(Pid, Msg, Timeout) -> after Timeout -> erlang:demonitor(MRef, [flush]), log("ct_gen_conn", - "Connection process ~p not responding. Killing now!", + "Connection process ~w not responding. Killing now!", [Pid]), exit(Pid, kill), {error,{process_down,Pid,forced_termination}} diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl index 74ab5e5439..14a8aab881 100644 --- a/lib/common_test/src/ct_groups.erl +++ b/lib/common_test/src/ct_groups.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -462,7 +462,7 @@ make_conf(Mod, Name, Props, TestSpec) -> false -> ct_logs:log("TEST INFO", "init_per_group/2 and " "end_per_group/2 missing for group " - "~p in ~p, using default.", + "~w in ~w, using default.", [Name,Mod]), {{ct_framework,init_per_group}, {ct_framework,end_per_group}, diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 1bcc63738e..3d87a82e24 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -362,11 +362,11 @@ catch_apply(M,F,A, Default) -> [{M,F,A,_}|_] when Reason == undef -> Default; Trace -> - ct_logs:log("Suite Hook","Call to CTH failed: ~p:~p", + ct_logs:log("Suite Hook","Call to CTH failed: ~w:~p", [error,{Reason,Trace}]), throw({error_in_cth_call, lists:flatten( - io_lib:format("~p:~p/~p CTH call failed", + io_lib:format("~w:~w/~w CTH call failed", [M,F,length(A)]))}) end end. diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 0b7a8bb075..5924930072 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -35,9 +35,10 @@ -export([add_external_logs/1, add_link/3]). -export([make_last_run_index/0]). -export([make_all_suites_index/1,make_all_runs_index/1]). --export([get_ts_html_wrapper/4]). +-export([get_ts_html_wrapper/5]). -export([xhtml/2, locate_priv_file/1, make_relative/1]). -export([insert_javascript/1]). +-export([uri/1]). %% Logging stuff directly from testcase -export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4, @@ -307,8 +308,8 @@ end_log() -> %%% calling test suite.</p> add_external_logs(Logs) -> start_log("External Logs"), - [cont_log("<a href=~p>~s</a>\n", - [filename:join("log_private",Log),Log]) || Log <- Logs], + [cont_log("<a href=\"~ts\">~ts</a>\n", + [uri(filename:join("log_private",Log)),Log]) || Log <- Logs], end_log(). %%%----------------------------------------------------------------- @@ -320,8 +321,8 @@ add_external_logs(Logs) -> %%% @doc Print a link to a given file stored in the priv_dir of the %%% calling test suite. add_link(Heading,File,Type) -> - log(Heading,"<a href=~p type=~p>~s</a>\n", - [filename:join("log_private",File),Type,File]). + log(Heading,"<a href=\"~ts\" type=~p>~ts</a>\n", + [uri(filename:join("log_private",File)),Type,File]). %%%----------------------------------------------------------------- @@ -469,7 +470,7 @@ ct_log(Category,Format,Args) -> %%%================================================================= %%% Internal functions int_header() -> - "<div class=\"ct_internal\"><b>*** CT ~s *** ~s</b>". + "<div class=\"ct_internal\"><b>*** CT ~s *** ~ts</b>". int_footer() -> "</div>". @@ -692,7 +693,7 @@ logger_loop(State) -> logger_loop(State); {set_stylesheet,TC,SSFile} -> Fd = State#logger_state.ct_log_fd, - io:format(Fd, "~p loading external style sheet: ~s~n", + io:format(Fd, "~p loading external style sheet: ~ts~n", [TC,SSFile]), logger_loop(State#logger_state{stylesheet = SSFile}); {clear_stylesheet,_} when State#logger_state.stylesheet == undefined -> @@ -752,7 +753,7 @@ print_to_log(sync, FromPid, TCGL, List, State) -> IoProc = if FromPid /= TCGL -> TCGL; true -> State#logger_state.ct_log_fd end, - io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]), + io:format(IoProc, "~ts", [lists:foldl(IoFun, [], List)]), State; print_to_log(async, FromPid, TCGL, List, State) -> @@ -764,7 +765,7 @@ print_to_log(async, FromPid, TCGL, List, State) -> end, Printer = fun() -> test_server:permit_io(IoProc, self()), - io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]) + io:format(IoProc, "~ts", [lists:foldl(IoFun, [], List)]) end, case State#logger_state.async_print_jobs of [] -> @@ -868,7 +869,7 @@ set_evmgr_gl(GL) -> end. open_ctlog() -> - {ok,Fd} = file:open(?ct_log_name,[write]), + {ok,Fd} = file:open(?ct_log_name,[write,{encoding,utf8}]), io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []), case file:consult(ct_run:variables_file_name("../")) of {ok,Vars} -> @@ -878,7 +879,7 @@ open_ctlog() -> Dir = filename:dirname(Cwd), Variables = ct_run:variables_file_name(Dir), io:format(Fd, - "Can not read the file \'~s\' Reason: ~w\n" + "Can not read the file \'~ts\' Reason: ~w\n" "No configuration found for test!!\n", [Variables,Reason]) end, @@ -904,7 +905,7 @@ print_style(Fd,undefined) -> print_style(Fd,StyleSheet) -> case file:read_file(StyleSheet) of {ok,Bin} -> - Str = binary_to_list(Bin), + Str = b2s(Bin,encoding(StyleSheet)), Pos0 = case string:str(Str,"<style>") of 0 -> string:str(Str,"<STYLE>"); N0 -> N0 @@ -919,9 +920,9 @@ print_style(Fd,StyleSheet) -> print_style_error(Fd,StyleSheet,missing_style_end_tag); Pos0 /= 0 -> Style = string:sub_string(Str,Pos0,Pos1+7), - io:format(Fd,"~s\n",[Style]); + io:format(Fd,"~ts\n",[Style]); Pos0 == 0 -> - io:format(Fd,"<style>~s</style>\n",[Str]) + io:format(Fd,"<style>~ts</style>\n",[Str]) end; {error,Reason} -> print_style_error(Fd,StyleSheet,Reason) @@ -934,7 +935,7 @@ print_style(Fd,StyleSheet) -> %% [StyleSheet]). print_style_error(Fd,StyleSheet,Reason) -> - io:format(Fd,"\n<!-- Failed to load stylesheet ~s: ~p -->\n", + io:format(Fd,"\n<!-- Failed to load stylesheet ~ts: ~p -->\n", [StyleSheet,Reason]), print_style(Fd,undefined). @@ -963,7 +964,7 @@ make_last_run_index(StartTime) -> % io:put_chars("done\n"), ok; Err -> - io:format("Unknown internal error while updating ~s. " + io:format("Unknown internal error while updating ~ts. " "Please report.\n(Err: ~p, ID: 1)", [AbsIndexName,Err]), {error, Err} @@ -1001,7 +1002,7 @@ make_last_run_index1(StartTime,IndexName) -> %% write current Totals to file, later to be used in all_runs log write_totals_file(?totals_name,Label,Logs1,Totals), Index = [Index0|index_footer()], - case force_write_file(IndexName, Index) of + case force_write_file(IndexName, unicode:characters_to_binary(Index)) of ok -> ok; {error, Reason} -> @@ -1085,7 +1086,7 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, CrashDumpName = SuiteName ++ "_erl_crash.dump", case filelib:is_file(CrashDumpName) of true -> - [" <a href=\"", CrashDumpName, + [" <a href=\"", uri(CrashDumpName), "\">(CrashDump)</a>"]; false -> "" @@ -1115,10 +1116,10 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, [] -> "none"; _ -> "<a href=\""++?all_runs_name++"\">Old Runs</a>" end, - A = xhtml(["<td><font size=\"-1\"><a href=\"",CtLogFile, + A = xhtml(["<td><font size=\"-1\"><a href=\"",uri(CtLogFile), "\">CT Log</a></font></td>\n", "<td><font size=\"-1\">",OldRunsLink,"</font></td>\n"], - ["<td><a href=\"",CtLogFile,"\">CT Log</a></td>\n", + ["<td><a href=\"",uri(CtLogFile),"\">CT Log</a></td>\n", "<td>",OldRunsLink,"</td>\n"]), {L,T,N,A}; false -> @@ -1128,7 +1129,8 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, if NotBuilt == 0 -> ["<td align=right>",integer_to_list(NotBuilt),"</td>\n"]; true -> - ["<td align=right><a href=\"",filename:join(CtRunDir,?ct_log_name),"\">", + ["<td align=right><a href=\"", + uri(filename:join(CtRunDir,?ct_log_name)),"\">", integer_to_list(NotBuilt),"</a></td>\n"] end, FailStr = @@ -1151,7 +1153,7 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, [xhtml("<tr valign=top>\n", ["<tr class=\"",odd_or_even(),"\">\n"]), xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""), - LogFile,"\">",SuiteName,"</a>", CrashDumpLink, + uri(LogFile),"\">",SuiteName,"</a>", CrashDumpLink, xhtml("</font></td>\n", "</td>\n"), Lbl, Timestamp, "<td align=right>",integer_to_list(Success),"</td>\n", @@ -1364,8 +1366,9 @@ header1(Title, SubTitle, TableCols) -> "<head>\n", "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n", xhtml("", - ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]), + ["<link rel=\"stylesheet\" href=\"",uri(CSSFile),"\" type=\"text/css\">\n"]), xhtml("", ["<script type=\"text/javascript\" src=\"",JQueryFile, "\"></script>\n"]), @@ -1462,7 +1465,7 @@ count_cases(Dir) -> LogFile = filename:join(Dir, ?suitelog_name), case file:read_file(LogFile) of {ok, Bin} -> - case count_cases1(binary_to_list(Bin), + case count_cases1(b2s(Bin), {undefined,undefined,undefined,undefined}) of {error,not_complete} -> %% The test is not complete - dont write summary @@ -1557,7 +1560,7 @@ config_table1([{Key,Value}|Vars]) -> "<td><pre>",io_lib:format("~p",[Value]),"</pre></td></tr>\n"], ["<tr class=\"", odd_or_even(), "\">\n", "<td>", atom_to_list(Key), "</td>\n", - "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) | + "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) | config_table1(Vars)]; config_table1([]) -> ["</tbody>\n</table>\n"]. @@ -1574,8 +1577,9 @@ make_all_runs_index(When) -> DirsSorted = (catch sort_all_runs(Dirs)), Header = all_runs_header(), Index = [runentry(Dir) || Dir <- DirsSorted], - Result = file:write_file(AbsName,Header++Index++ - all_runs_index_footer()), + Result = file:write_file(AbsName, + unicode:characters_to_binary( + Header++Index++all_runs_index_footer())), if When == start -> ok; true -> io:put_chars("done\n") end, @@ -1602,10 +1606,27 @@ sort_all_runs(Dirs) -> interactive_link() -> [Dir|_] = lists:reverse(filelib:wildcard(logdir_prefix()++"*.*")), CtLog = filename:join(Dir,"ctlog.html"), - Body = ["Log from last interactive run: <a href=\"",CtLog,"\">", - timestamp(Dir),"</a>"], - file:write_file("last_interactive.html",Body), - io:format("~n~nUpdated ~s\n" + Body = + [xhtml( + ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", + "<html>\n"], + ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]), + "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n", + "<head>\n", + "<title>Last interactive run</title>\n", + "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n", + "</head>\n", + "<body>\n", + "Log from last interactive run: <a href=\"",uri(CtLog),"\">", + timestamp(Dir),"</a>", + "</body>\n", + "</html>\n" + ], + file:write_file("last_interactive.html",unicode:characters_to_binary(Body)), + io:format("~n~nUpdated ~ts\n" "Any CT activities will be logged here\n", [?abs("last_interactive.html")]). @@ -1655,7 +1676,7 @@ runentry(Dir) -> TestNames; true -> Trunc = Polish(string:substr(TestNames,1,?testname_width-3)), - lists:flatten(io_lib:format("~s...",[Trunc])) + lists:flatten(io_lib:format("~ts...",[Trunc])) end, Total = TotSucc+TotFail+AllSkip, A = xhtml(["<td align=center><font size=\"-1\">",Node, @@ -1695,7 +1716,7 @@ runentry(Dir) -> "<td align=right>?</td>\n"], A++B++C end, - Index = filename:join(Dir,?index_name), + Index = uri(filename:join(Dir,?index_name)), [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]), xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",timestamp(Dir),"</a>", TotalsStr,"</font></td>\n"], @@ -1836,7 +1857,7 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) -> ok -> ok; Err -> - io:format("Unknown internal error while updating ~s. " + io:format("Unknown internal error while updating ~ts. " "Please report.\n(Err: ~p, ID: 1)", [AbsIndexName,Err]), {error, Err} @@ -1900,7 +1921,7 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) -> ok end; Err -> - io:format("Unknown internal error while updating ~s. " + io:format("Unknown internal error while updating ~ts. " "Please report.\n(Err: ~p, ID: 1)", [AbsIndexName,Err]), {error, Err} @@ -1912,7 +1933,7 @@ make_all_suites_index2(IndexName, AllTestLogDirs) -> all_suites_index_header(), 0, 0, 0, 0, 0, [], []), Index = [Index0|index_footer()], - case force_write_file(IndexName, Index) of + case force_write_file(IndexName, unicode:characters_to_binary(Index)) of ok -> {ok,CacheData}; {error, Reason} -> @@ -1970,7 +1991,7 @@ make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) -> all_suites_index_header(IndexDir), 0, 0, 0, 0, 0), Index = [Index0|index_footer()], - case force_write_file(AbsIndexName, Index) of + case force_write_file(AbsIndexName, unicode:characters_to_binary(Index)) of ok -> ok; {error, Reason} -> @@ -2116,7 +2137,7 @@ simulate_logger_loop() -> receive {log,_,_,_,_,_,List} -> S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List], - io:format("~s",[S]), + io:format("~ts",[S]), simulate_logger_loop(); stop -> ok @@ -2273,11 +2294,12 @@ make_relative1(DirTs, CwdTs) -> Ups ++ DirTs. %%%----------------------------------------------------------------- -%%% @spec get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> {Mode,Header,Footer} +%%% @spec get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) +%%% -> {Mode,Header,Footer} %%% %%% @doc %%% -get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> +get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) -> TestName1 = if is_list(TestName) -> lists:flatten(TestName); true -> @@ -2319,14 +2341,16 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> "<html>\n", "<head><title>", TestName1, "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; charset=", + html_encoding(Encoding),"\">\n", "</head>\n", "<body", Bgr, " bgcolor=\"white\" text=\"black\" ", "link=\"blue\" vlink=\"purple\" alink=\"red\">\n", LabelStr, "\n"], ["<center>\n<br><hr><p>\n", - "<a href=\"", AllRuns, + "<a href=\"", uri(AllRuns), "\">Test run history\n</a> | ", - "<a href=\"", TestIndex, + "<a href=\"", uri(TestIndex), "\">Top level test index\n</a>\n</p>\n", Copyright,"</center>\n</body>\n</html>\n"]}; _ -> @@ -2363,14 +2387,15 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n", "<head>\n<title>", TestName1, "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", - "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n", + "<link rel=\"stylesheet\" href=\"", uri(CSSFile), "\" type=\"text/css\">\n", "<script type=\"text/javascript\" src=\"", JQueryFile, "\"></script>\n", "<script type=\"text/javascript\" src=\"", TableSorterFile, "\"></script>\n"] ++ TableSorterScript ++ ["</head>\n","<body>\n", LabelStr, "\n"], ["<center>\n<br /><hr /><p>\n", - "<a href=\"", AllRuns, + "<a href=\"", uri(AllRuns), "\">Test run history\n</a> | ", - "<a href=\"", TestIndex, + "<a href=\"", uri(TestIndex), "\">Top level test index\n</a>\n</p>\n", Copyright,"</center>\n</body>\n</html>\n"]} end. @@ -2460,3 +2485,31 @@ insert_javascript({tablesorter,TableName, " $(\"#",TableName,"\").trigger(\"update\");\n", " $(\"#",TableName,"\").trigger(\"appendCache\");\n", "});\n</script>\n"]. + +uri("") -> + ""; +uri(Href) -> + test_server_ctrl:uri_encode(Href). + +%% Read magic comment to get encoding of text file. +%% If no magic comment exists, assume default encoding +encoding(File) -> + case epp:read_encoding(File) of + none -> + epp:default_encoding(); + E -> + E + end. + +%% Convert binary to string using default encoding +b2s(Bin) -> + b2s(Bin,epp:default_encoding()). + +%% Convert binary to string using given encoding +b2s(Bin,Encoding) -> + unicode:characters_to_list(Bin,Encoding). + +html_encoding(latin1) -> + "iso-8859-1"; +html_encoding(utf8) -> + "utf-8". diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl index 8ddb91d355..d4bd81e78d 100644 --- a/lib/common_test/src/ct_make.erl +++ b/lib/common_test/src/ct_make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2013. 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 @@ -280,13 +280,13 @@ recompile(File, NoExec, Load, Opts) -> do_recompile(_File, true, _Load, _Opts) -> out_of_date; do_recompile(File, false, noload, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), compile:file(File, [report_errors, report_warnings, error_summary |Opts]); do_recompile(File, false, load, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), c:c(File, Opts); do_recompile(File, false, netload, Opts) -> - io:format("Recompile: ~s\n",[File]), + io:format("Recompile: ~ts\n",[File]), c:nc(File, Opts). exists(File) -> diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index d58395b692..e516f635d2 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -256,7 +256,7 @@ run_all([],AllLogDirs,_,AllEvHs,_AllIncludes, {value,{_,Dir}} -> Dir; false -> "." end, - log(tty,"Master Logdir","~s",[MasterLogDir]), + log(tty,"Master Logdir","~ts",[MasterLogDir]), start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir, LogDirs,InitOptions,Specs), ok. @@ -347,7 +347,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, SpecsStr = lists:map(fun(Name) -> Name ++ " " end,Specs), - ct_master_logs:log("Test Specification file(s)","~s", + ct_master_logs:log("Test Specification file(s)","~ts", [lists:flatten(SpecsStr)]) end, @@ -356,7 +356,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, ct_master_event:add_handler(), %% add user handlers for master event manager Add = fun({H,Args}) -> - log(all,"Adding Event Handler","~p",[H]), + log(all,"Adding Event Handler","~w",[H]), case gen_event:add_handler(?CT_MEVMGR_REF,H,Args) of ok -> ok; {'EXIT',Why} -> exit(Why); @@ -408,7 +408,7 @@ init_master2(Parent,NodeOptsList,LogDirs) -> SpawnAndMon = fun({Node,Opts}) -> monitor_node(Node,true), - log(all,"Test Info","Starting test(s) on ~p...",[Node]), + log(all,"Test Info","Starting test(s) on ~w...",[Node]), {spawn_link(Node,?MODULE,init_node_ctrl,[self(),Cookie,Opts]), Node} end, @@ -422,7 +422,7 @@ master_loop(#state{node_ctrl_pids=[], results=Finished}) -> Str = lists:map(fun({Node,Result}) -> - io_lib:format("~-40.40.*s~p\n", + io_lib:format("~-40.40.*ts~p\n", [$_,atom_to_list(Node),Result]) end,lists:reverse(Finished)), log(all,"TEST RESULTS",Str,[]), @@ -469,7 +469,7 @@ master_loop(State=#state{node_ctrl_pids=NodeCtrlPids, undefined -> %% ignore (but report) exit from master_logger etc log(all,"Test Info", - "Warning! Process ~p has terminated. Reason: ~p", + "Warning! Process ~w has terminated. Reason: ~p", [Pid,Reason]), master_loop(State) end; @@ -552,7 +552,7 @@ update_queue(take,Node,From,Lock={Op,Resource},Locks,Blocked) -> %% Blocked: [{{Operation,Resource},Node,WaitingPid},...] case lists:keysearch(Lock,1,Locks) of {value,{_Lock,Owner}} -> % other node has lock - log(html,"Lock Info","Node ~p blocked on ~w by ~w. Resource: ~p", + log(html,"Lock Info","Node ~w blocked on ~w by ~w. Resource: ~p", [Node,Op,Owner,Resource]), Blocked1 = Blocked ++ [{Lock,Node,From}], {Locks,Blocked1}; @@ -567,7 +567,7 @@ update_queue(release,Node,_From,Lock={Op,Resource},Locks,Blocked) -> case lists:keysearch(Lock,1,Blocked) of {value,E={Lock,SomeNode,WaitingPid}} -> Blocked1 = lists:delete(E,Blocked), - log(html,"Lock Info","Node ~p proceeds with ~w. Resource: ~p", + log(html,"Lock Info","Node ~w proceeds with ~w. Resource: ~p", [SomeNode,Op,Resource]), reply(ok,WaitingPid), % waiting process may start {Locks1,Blocked1}; @@ -646,7 +646,8 @@ refresh_logs([D|Dirs],Refreshed) -> refresh_logs([],Refreshed) -> Str = lists:map(fun({D,Result}) -> - io_lib:format("Refreshing logs in ~p... ~p",[D,Result]) + io_lib:format("Refreshing logs in ~p... ~p", + [D,Result]) end,Refreshed), log(all,"Info",Str,[]). @@ -659,7 +660,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> process_flag(trap_exit, true), MasterNode = node(MasterPid), group_leader(whereis(user),self()), - io:format("~n********** node_ctrl process ~p started on ~p **********~n", + io:format("~n********** node_ctrl process ~w started on ~w **********~n", [self(),node()]), %% initially this node must have the same cookie as the master node %% but now we set it explicitly for the connection so that test suites @@ -692,7 +693,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> pong -> MasterPid ! {self(),{result,Result}}; pang -> - io:format("Warning! Connection to master node ~p is lost. " + io:format("Warning! Connection to master node ~w is lost. " "Can't report result!~n~n", [MasterNode]) end. @@ -773,24 +774,24 @@ start_nodes(InitOptions)-> IsAlive = lists:member(NodeName, nodes()), case {HasNodeStart, IsAlive} of {false, false}-> - io:format("WARNING: Node ~p is not alive but has no " + io:format("WARNING: Node ~w is not alive but has no " "node_start option~n", [NodeName]); {false, true}-> - io:format("Node ~p is alive~n", [NodeName]); + io:format("Node ~w is alive~n", [NodeName]); {true, false}-> {node_start, NodeStart} = lists:keyfind(node_start, 1, Options), {value, {callback_module, Callback}, NodeStart2}= lists:keytake(callback_module, 1, NodeStart), case Callback:start(Host, Node, NodeStart2) of {ok, NodeName} -> - io:format("Node ~p started successfully " - "with callback ~p~n", [NodeName,Callback]); + io:format("Node ~w started successfully " + "with callback ~w~n", [NodeName,Callback]); {error, Reason, _NodeName} -> - io:format("Failed to start node ~p with callback ~p! " + io:format("Failed to start node ~w with callback ~w! " "Reason: ~p~n", [NodeName, Callback, Reason]) end; {true, true}-> - io:format("WARNING: Node ~p is alive but has node_start " + io:format("WARNING: Node ~w is alive but has node_start " "option~n", [NodeName]) end end, @@ -804,8 +805,8 @@ eval_on_nodes(InitOptions)-> {false,_}-> ok; {true,false}-> - io:format("WARNING: Node ~p is not alive but has eval " - "option ~n", [NodeName]); + io:format("WARNING: Node ~w is not alive but has eval " + "option~n", [NodeName]); {true,true}-> {eval, MFAs} = lists:keyfind(eval, 1, Options), evaluate(NodeName, MFAs) @@ -816,10 +817,10 @@ eval_on_nodes(InitOptions)-> evaluate(Node, [{M,F,A}|MFAs])-> case rpc:call(Node, M, F, A) of {badrpc,Reason}-> - io:format("WARNING: Failed to call ~p:~p/~p on node ~p " + io:format("WARNING: Failed to call ~w:~w/~w on node ~w " "due to ~p~n", [M,F,length(A),Node,Reason]); Result-> - io:format("Called ~p:~p/~p on node ~p, result: ~p~n", + io:format("Called ~w:~w/~w on node ~w, result: ~p~n", [M,F,length(A),Node,Result]) end, evaluate(Node, MFAs); diff --git a/lib/common_test/src/ct_master_event.erl b/lib/common_test/src/ct_master_event.erl index a70baefaaf..5877b7c6f2 100644 --- a/lib/common_test/src/ct_master_event.erl +++ b/lib/common_test/src/ct_master_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -114,13 +114,13 @@ init(_) -> %% each installed event handler to handle the event. %%-------------------------------------------------------------------- handle_event(#event{name=start_logging,node=Node,data=RunDir},State) -> - ct_master_logs:log("CT Master Event Handler","Got ~s from ~p",[RunDir,Node]), + ct_master_logs:log("CT Master Event Handler","Got ~ts from ~w",[RunDir,Node]), ct_master_logs:nodedir(Node,RunDir), {ok,State}; handle_event(#event{name=Name,node=Node,data=Data},State) -> print("~n=== ~w ===~n", [?MODULE]), - print("~p on ~p: ~p~n", [Name,Node,Data]), + print("~w on ~w: ~p~n", [Name,Node,Data]), {ok,State}. %%-------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl index 84f175c0a9..5393097f57 100644 --- a/lib/common_test/src/ct_master_logs.erl +++ b/lib/common_test/src/ct_master_logs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -133,7 +133,7 @@ init(Parent,LogDir,Nodes) -> end,Nodes)), io:format(CtLogFd,int_header(),[log_timestamp(now()),"Test Nodes\n"]), - io:format(CtLogFd,"~s\n",[NodeStr]), + io:format(CtLogFd,"~ts\n",[NodeStr]), io:put_chars(CtLogFd,[int_footer(),"\n"]), NodeDirIxFd = open_nodedir_index(RunDirAbs,Time), @@ -201,7 +201,7 @@ loop(State) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%% open_ct_master_log(Dir) -> FullName = filename:join(Dir,?ct_master_log_name), - {ok,Fd} = file:open(FullName,[write]), + {ok,Fd} = file:open(FullName,[write,{encoding,utf8}]), io:put_chars(Fd,header("Common Test Master Log", {[],[1,2],[]})), %% maybe add config info here later io:put_chars(Fd,config_table([])), @@ -235,7 +235,7 @@ config_table1([]) -> ["</tbody>\n</table>\n"]. int_header() -> - "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>". + "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~ts</b>". int_footer() -> "</div>". @@ -244,7 +244,7 @@ int_footer() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% open_nodedir_index(Dir,StartTime) -> FullName = filename:join(Dir,?nodedir_index_name), - {ok,Fd} = file:open(FullName,[write]), + {ok,Fd} = file:open(FullName,[write,{encoding,utf8}]), io:put_chars(Fd,nodedir_index_header(StartTime)), Fd. @@ -253,7 +253,8 @@ print_nodedir(Node,RunDir,Fd) -> 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", + "<td align=left><a href=\"",ct_logs:uri(Index),"\">",Index, + "</a></td>\n", "</tr>\n"]), ok. @@ -283,7 +284,9 @@ make_all_runs_index(LogDir) -> DirsSorted = (catch sort_all_runs(Dirs)), Header = all_runs_header(), Index = [runentry(Dir) || Dir <- DirsSorted], - Result = file:write_file(FullName,Header++Index++index_footer()), + Result = file:write_file(FullName, + unicode:characters_to_binary( + Header++Index++index_footer())), Result. sort_all_runs(Dirs) -> @@ -323,7 +326,8 @@ runentry(Dir) -> end, Index = filename:join(Dir,?nodedir_index_name), ["<tr>\n" - "<td align=center><a href=\"",Index,"\">",timestamp(Dir),"</a></td>\n", + "<td align=center><a href=\"",ct_logs:uri(Index),"\">", + timestamp(Dir),"</a></td>\n", "<td align=center>",MasterStr,"</td>\n", "<td align=center>",NodesStr,"</td>\n", "</tr>\n"]. @@ -381,8 +385,10 @@ header(Title, TableCols) -> "<head>\n", "<title>" ++ Title ++ "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n", xhtml("", - ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]), + ["<link rel=\"stylesheet\" href=\"",ct_logs:uri(CSSFile), + "\" type=\"text/css\">"]), xhtml("", ["<script type=\"text/javascript\" src=\"",JQueryFile, "\"></script>\n"]), diff --git a/lib/common_test/src/ct_master_status.erl b/lib/common_test/src/ct_master_status.erl index 76060fb7bb..f9f511ecca 100644 --- a/lib/common_test/src/ct_master_status.erl +++ b/lib/common_test/src/ct_master_status.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -70,7 +70,7 @@ init(_) -> %% handle_event(#event{name=Name,node=Node,data=Data},State) -> print("~n=== ~w ===~n", [?MODULE]), - print("~p on ~p: ~p~n", [Name,Node,Data]), + print("~w on ~w: ~p~n", [Name,Node,Data]), {ok,State}. %%-------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 1ccbc86d8f..1339e53780 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -1,7 +1,7 @@ %%---------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -1281,10 +1281,11 @@ do_send(Connection, SimpleXml) -> to_xml_doc(Simple) -> Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", - Xml = list_to_binary(xmerl:export_simple([Simple], - xmerl_xml, - [#xmlAttribute{name=prolog, - value=Prolog}])), + Xml = unicode:characters_to_binary( + xmerl:export_simple([Simple], + xmerl_xml, + [#xmlAttribute{name=prolog, + value=Prolog}])), <<Xml/binary,?END_TAG/binary>>. %%%----------------------------------------------------------------- @@ -1688,18 +1689,27 @@ log(#connection{host=Host,port=Port,name=Name},Action,Data) -> %% Log callback - called from the error handler process -format_data(raw,Data) -> - io_lib:format("~n~s~n",[hide_password(Data)]); -format_data(pretty,Data) -> - io_lib:format("~n~s~n",[indent(Data)]); -format_data(html,Data) -> - io_lib:format("~n~s~n",[html_format(Data)]). +format_data(How,Data) -> + %% Assuming that the data is encoded as UTF-8. If it is not, then + %% the printout might be wrong, but the format function will not + %% crash! + %% FIXME: should probably read encoding from the data and do + %% unicode:characters_to_binary(Data,InEncoding,utf8) when calling + %% log/3 instead of assuming utf8 in as done here! + do_format_data(How,unicode:characters_to_binary(Data)). + +do_format_data(raw,Data) -> + io_lib:format("~n~ts~n",[hide_password(Data)]); +do_format_data(pretty,Data) -> + io_lib:format("~n~ts~n",[indent(Data)]); +do_format_data(html,Data) -> + io_lib:format("~n~ts~n",[html_format(Data)]). %%%----------------------------------------------------------------- %%% Hide password elements from XML data hide_password(Bin) -> re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>, - [global,{return,binary}]). + [global,{return,binary},unicode]). %%%----------------------------------------------------------------- %%% HTML formatting @@ -1717,13 +1727,13 @@ indent(Bin) -> Part -> indent1(lists:reverse(Part)++String,erase(indent)) end, - list_to_binary(IndentedString). + unicode:characters_to_binary(IndentedString). %% Normalizes the XML document by removing all space and newline %% between two XML tags. %% Returns a list, no matter if the input was a list or a binary. -normalize(Str) -> - re:replace(Str,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list}]). +normalize(Bin) -> + re:replace(Bin,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list},unicode]). indent1("<?"++Rest1,Indent1) -> diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 742e287493..c0bdbb2a09 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -148,7 +148,7 @@ script_start(Args) -> _ -> "" end end, - io:format("~nCommon Test~s starting (cwd is ~s)~n~n", + io:format("~nCommon Test~s starting (cwd is ~ts)~n~n", [CTVsn,Cwd]), Self = self(), Pid = spawn_link(fun() -> script_start1(Self, Args) end), @@ -378,7 +378,7 @@ run_or_refresh(Opts = #opts{logdir = LogDir}, Args) -> {error,{all_suites_index,ASReason}}; _ -> file:set_cwd(Cwd), - io:format("Logs in ~s refreshed!~n~n", + io:format("Logs in ~ts refreshed!~n~n", [LogDir1]), timer:sleep(500), % time to flush io before quitting ok @@ -881,7 +881,7 @@ run_test1(StartOpts) when is_list(StartOpts) -> undefined -> Tracing = start_trace(StartOpts), {ok,Cwd} = file:get_cwd(), - io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]), + io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]), Res = case ct_repeat:loop_test(func, StartOpts) of false -> @@ -1320,7 +1320,7 @@ run_testspec(TestSpec) -> run_testspec1(TestSpec) -> {ok,Cwd} = file:get_cwd(), - io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]), + io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]), case catch run_testspec2(TestSpec) of {'EXIT',Reason} -> file:set_cwd(Cwd), @@ -1457,7 +1457,7 @@ refresh_logs(LogDir) -> {error,{all_runs_index,ARReason}}; _ -> file:set_cwd(Cwd), - io:format("Logs in ~s refreshed!~n",[LogDir]), + io:format("Logs in ~ts refreshed!~n",[LogDir]), ok end end @@ -1780,7 +1780,7 @@ possibly_spawn(true, Tests, Skip, Opts) -> end, unlink(CTUtilSrv), SupPid = spawn(Supervisor), - io:format(user, "~nTest control handed over to process ~p~n~n", + io:format(user, "~nTest control handed over to process ~w~n~n", [SupPid]), SupPid. @@ -1868,7 +1868,7 @@ verify_suites(TestSuites) -> Suite)), io:format(user, "Suite ~w not found" - "in directory ~s~n", + "in directory ~ts~n", [Suite,TestDir]), {Found,[{DS,[Name]}|NotFound]} end @@ -1883,7 +1883,7 @@ verify_suites(TestSuites) -> ActualDir = filename:dirname(SuiteFile), {[{ActualDir,Suite}|Found],NotFound}; false -> - io:format(user, "Directory ~s is " + io:format(user, "Directory ~ts is " "invalid~n", [Dir]), Name = filename:join(Dir, atom_to_list(Suite)), {Found,[{DS,[Name]}|NotFound]} @@ -2123,7 +2123,7 @@ do_run_test(Tests, Skip, Opts) -> cross = CovCross, src = _CovSrc}} -> ct_logs:log("COVER INFO", - "Using cover specification file: ~s~n" + "Using cover specification file: ~ts~n" "App: ~w~n" "Cross cover: ~w~n" "Including ~w modules~n" @@ -2138,7 +2138,7 @@ do_run_test(Tests, Skip, Opts) -> DelResult = file:delete(CovExport), ct_logs:log("COVER INFO", "Warning! " - "Export file ~s already exists. " + "Export file ~ts already exists. " "Deleting with result: ~p", [CovExport,DelResult]); false -> @@ -2620,7 +2620,7 @@ log_ts_names(Specs) -> List = lists:map(fun(Name) -> Name ++ " " end, Specs), - ct_logs:log("Test Specification file(s)", "~s", + ct_logs:log("Test Specification file(s)", "~ts", [lists:flatten(List)]). merge_arguments(Args) -> @@ -2731,10 +2731,10 @@ event_handler_args2opts(Default, Args) -> event_handler_init_args2opts(EHs) end. event_handler_init_args2opts([EH, Arg, "and" | EHs]) -> - [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))} | + [{list_to_atom(EH),lists:flatten(io_lib:format("~ts",[Arg]))} | event_handler_init_args2opts(EHs)]; event_handler_init_args2opts([EH, Arg]) -> - [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))}]; + [{list_to_atom(EH),lists:flatten(io_lib:format("~ts",[Arg]))}]; event_handler_init_args2opts([]) -> []. @@ -3056,7 +3056,7 @@ start_trace(Args) -> false end; {_,Error} -> - io:format("Warning! Tracing not started. Reason: ~s~n~n", + io:format("Warning! Tracing not started. Reason: ~ts~n~n", [file:format_error(Error)]), false end; diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index b13c050e32..02186864a5 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -201,7 +201,7 @@ open(KeyOrName,ConnType,TargetMod,Extra) -> close(Connection) -> case get_handle(Connection) of {ok,Pid} -> - log("ct_telnet:close","Handle: ~p",[Pid]), + log("ct_telnet:close","Handle: ~w",[Pid]), case ct_gen_conn:stop(Pid) of {error,{process_down,Pid,noproc}} -> {error,already_closed}; @@ -558,7 +558,7 @@ reconnect(Ip,Port,N,State=#state{target_mod=TargetMod, Error when N==0 -> Error; _Error -> - log("Reconnect failed!","Retries left: ~p",[N]), + log("Reconnect failed!","Retries left: ~w",[N]), timer:sleep(ReconnInt), reconnect(Ip,Port,N-1,State) end. @@ -567,7 +567,7 @@ reconnect(Ip,Port,N,State=#state{target_mod=TargetMod, %% @hidden terminate(TelnPid,State) -> log(heading(terminate,State#state.name), - "Closing telnet connection.\nId: ~p", + "Closing telnet connection.\nId: ~w", [TelnPid]), ct_telnet_client:close(TelnPid). @@ -899,7 +899,7 @@ one_expect(Data,Pattern,EO) -> [Prompt] when Prompt==prompt; Prompt=={prompt,PromptType} -> %% Only searching for prompt log_lines(UptoPrompt), - try_cont_log("<b>PROMPT:</b> ~s", [PromptType]), + try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]), {match,{prompt,PromptType},Rest}; [{prompt,_OtherPromptType}] -> %% Only searching for one specific prompt, not thisone @@ -969,7 +969,7 @@ seq_expect1(Data,[prompt|Patterns],Acc,Rest,EO) -> {continue,[prompt|Patterns],Acc,LastLine}; PromptType -> log_lines(Data), - try_cont_log("<b>PROMPT:</b> ~s", [PromptType]), + try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]), seq_expect(Rest,Patterns,[{prompt,PromptType}|Acc],EO) end; seq_expect1(Data,[{prompt,PromptType}|Patterns],Acc,Rest,EO) -> @@ -980,7 +980,7 @@ seq_expect1(Data,[{prompt,PromptType}|Patterns],Acc,Rest,EO) -> {continue,[{prompt,PromptType}|Patterns],Acc,LastLine}; PromptType -> log_lines(Data), - try_cont_log("<b>PROMPT:</b> ~s", [PromptType]), + try_cont_log("<b>PROMPT:</b> ~ts", [PromptType]), seq_expect(Rest,Patterns,[{prompt,PromptType}|Acc],EO); _OtherPromptType -> log_lines(Data), @@ -1032,13 +1032,13 @@ match_line(Line,Patterns,FoundPrompt,EO) -> match_line(Line,[prompt|Patterns],false,EO,RetTag) -> match_line(Line,Patterns,false,EO,RetTag); match_line(Line,[prompt|_Patterns],FoundPrompt,_EO,RetTag) -> - try_cont_log(" ~s", [Line]), - try_cont_log("<b>PROMPT:</b> ~s", [FoundPrompt]), + try_cont_log(" ~ts", [Line]), + try_cont_log("<b>PROMPT:</b> ~ts", [FoundPrompt]), {RetTag,{prompt,FoundPrompt}}; match_line(Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_EO,RetTag) when PromptType==FoundPrompt -> - try_cont_log(" ~s", [Line]), - try_cont_log("<b>PROMPT:</b> ~s", [FoundPrompt]), + try_cont_log(" ~ts", [Line]), + try_cont_log("<b>PROMPT:</b> ~ts", [FoundPrompt]), {RetTag,{prompt,FoundPrompt}}; match_line(Line,[{prompt,PromptType}|Patterns],FoundPrompt,EO,RetTag) when PromptType=/=FoundPrompt -> @@ -1048,7 +1048,7 @@ match_line(Line,[{Tag,Pattern}|Patterns],FoundPrompt,EO,RetTag) -> nomatch -> match_line(Line,Patterns,FoundPrompt,EO,RetTag); {match,Match} -> - try_cont_log("<b>MATCH:</b> ~s", [Line]), + try_cont_log("<b>MATCH:</b> ~ts", [Line]), {RetTag,{Tag,Match}} end; match_line(Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) -> @@ -1056,13 +1056,13 @@ match_line(Line,[Pattern|Patterns],FoundPrompt,EO,RetTag) -> nomatch -> match_line(Line,Patterns,FoundPrompt,EO,RetTag); {match,Match} -> - try_cont_log("<b>MATCH:</b> ~s", [Line]), + try_cont_log("<b>MATCH:</b> ~ts", [Line]), {RetTag,Match} end; match_line(Line,[],FoundPrompt,EO,match) -> match_line(Line,EO#eo.haltpatterns,FoundPrompt,EO,halt); match_line(Line,[],_FoundPrompt,_EO,halt) -> - try_cont_log(" ~s", [Line]), + try_cont_log(" ~ts", [Line]), nomatch. one_line([$\n|Rest],Line) -> @@ -1086,7 +1086,7 @@ log_lines(String) -> [] -> ok; LastLine -> - try_cont_log(" ~s", [LastLine]) + try_cont_log(" ~ts", [LastLine]) end. log_lines_not_last(String) -> @@ -1094,7 +1094,7 @@ log_lines_not_last(String) -> {[],LastLine} -> LastLine; {String1,LastLine} -> - try_cont_log("~s",[String1]), + try_cont_log("~ts",[String1]), LastLine end. diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl index d703b39ac5..7329498ed6 100644 --- a/lib/common_test/src/ct_telnet_client.erl +++ b/lib/common_test/src/ct_telnet_client.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -308,7 +308,7 @@ cmd_dbg(_Cmd) -> [Opt] -> Opt; _ -> Opts end, - io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]); + io:format("~ts(~w): ~w\n", [CtrlStr,Ctrl,Opts1]); Any -> io:format("Unexpected in cmd_dbg:~n~w~n",[Any]) end. diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index 4f3597dffb..e341391a91 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -736,7 +736,7 @@ add_option({Key,Value},Node,List,WarnIfExists) when is_list(Value) -> NewOption = case lists:keyfind(Key,1,OldOptions) of {Key,OldOption} when WarnIfExists,OldOption/=[]-> io:format("There is an option ~w=~w already " - "defined for node ~p, skipping new ~w~n", + "defined for node ~w, skipping new ~w~n", [Key,OldOption,Node,Value]), OldOption; {Key,OldOption}-> diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index cf891ed043..0f2b2081d9 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2013. 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 @@ -396,14 +396,14 @@ loop(Mode,TestData,StartDir) -> %% A connection crashed - remove the connection but don't die ct_logs:tc_log_async(ct_error_notify, "Connection process died: " - "Pid: ~p, Address: ~p, Callback: ~p\n" + "Pid: ~w, Address: ~p, Callback: ~w\n" "Reason: ~p\n\n", [Pid,A,CB,Reason]), catch CB:close(Pid), loop(Mode,TestData,StartDir); _ -> %% Let process crash in case of error, this shouldn't happen! - io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n", + io:format("\n\nct_util_server got EXIT from ~w: ~p\n\n", [Pid,Reason]), file:set_cwd(StartDir), exit(Reason) @@ -956,7 +956,7 @@ open_url(iexplore, Args, URL) -> Path = proplists:get_value(default, Paths), [Cmd | _] = string:tokens(Path, "%"), Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL, - io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd1]), + io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd1]), open_port({spawn,Cmd1}, []); _ -> io:format("~nNo path to iexplore.exe~n",[]) @@ -969,6 +969,6 @@ open_url(Prog, Args, URL) -> is_list(Prog) -> Prog end, Cmd = ProgStr ++ " " ++ Args ++ " " ++ URL, - io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd]), + io:format(user, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd]), open_port({spawn,Cmd},[]), ok. diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl index 255f3ec78a..644594e34d 100644 --- a/lib/common_test/src/cth_conn_log.erl +++ b/lib/common_test/src/cth_conn_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -105,8 +105,9 @@ pre_init_per_testcase(TestCase,Config,CthState) -> "<table borders=1>" "<b>" ++ ConnModStr ++ " logs:</b>\n" ++ [io_lib:format( - "<tr><td>~p</td><td><a href=~p>~s</a></td></tr>", - [S,L,filename:basename(L)]) + "<tr><td>~p</td><td><a href=\"~ts\">~ts</a>" + "</td></tr>", + [S,ct_logs:uri(L),filename:basename(L)]) || {S,L} <- Ls] ++ "</table>", io:format(Str,[]), diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl index e6eaad8d48..1a38b6584b 100644 --- a/lib/common_test/src/cth_surefire.erl +++ b/lib/common_test/src/cth_surefire.erl @@ -1,7 +1,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -297,7 +297,7 @@ 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]). + io_lib:format("~w-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]). make_url(undefined,_) -> undefined; diff --git a/lib/common_test/src/unix_telnet.erl b/lib/common_test/src/unix_telnet.erl index 25b9d4d5d2..99ce92e9f1 100644 --- a/lib/common_test/src/unix_telnet.erl +++ b/lib/common_test/src/unix_telnet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -110,7 +110,7 @@ connect1(Ip,Port,Timeout,KeepAlive,Username,Password) -> case ct_telnet:silent_teln_expect(Pid,[],[prompt],?prx,[]) of {ok,{prompt,?username},_} -> ok = ct_telnet_client:send_data(Pid,Username), - cont_log("Username: ~s",[Username]), + cont_log("Username: ~ts",[Username]), case ct_telnet:silent_teln_expect(Pid,[],prompt,?prx,[]) of {ok,{prompt,?password},_} -> ok = ct_telnet_client:send_data(Pid,Password), diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index 6d90b29f41..86ec71c98e 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -44,8 +44,12 @@ %% there will be clashes with logging processes etc). %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config1 = ct_test_support:init_per_suite(Config), - Config1. + DataDir = ?config(data_dir, Config), + TestDir = filename:join(DataDir, "error/test/"), + CTH = filename:join(TestDir, "verify_config.erl"), + ct:pal("Compiling ~p: ~p", + [CTH,compile:file(CTH,[{outdir,TestDir},debug_info])]), + ct_test_support:init_per_suite([{path_dirs,[TestDir]} | Config]). end_per_suite(Config) -> ct_test_support:end_per_suite(Config). @@ -61,7 +65,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [cfg_error, lib_error, no_compile, timetrap_end_conf, timetrap_normal, timetrap_extended, timetrap_parallel, - timetrap_fun, timetrap_fun_group, misc_errors]. + timetrap_fun, timetrap_fun_group, misc_errors, + config_restored]. groups() -> []. @@ -285,6 +290,24 @@ misc_errors(Config) when is_list(Config) -> TestEvents = events_to_check(misc_errors), ok = ct_test_support:verify_events(TestEvents, Events, Config). +%%%----------------------------------------------------------------- +%%% +config_restored(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "error/test/config_restored_SUITE"), + {Opts,ERPid} = setup([{suite,Suite}, + {ct_hooks,[verify_config]}], + Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(config_restored, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(config_restored), + ok = ct_test_support:verify_events(TestEvents, Events, Config). %%%----------------------------------------------------------------- %%% HELP FUNCTIONS @@ -1439,4 +1462,40 @@ test_events(misc_errors) -> {?eh,test_stats,{2,7,{0,0}}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} + ]; + +test_events(config_restored) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{1,1,4}}, + {?eh,tc_start,{config_restored_SUITE,init_per_suite}}, + {?eh,tc_done,{config_restored_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{config_restored_SUITE,to_tc}}, + {?eh,cth,{verify_config,post_end_per_testcase,{to_tc,diff_ok}}}, + {?eh,tc_done, + {config_restored_SUITE,to_tc,{failed,{timetrap_timeout,1000}}}}, + {?eh,test_stats,{0,1,{0,0}}}, + {?eh,tc_start,{config_restored_SUITE,exit_tc}}, + {?eh,cth,{verify_config,post_end_per_testcase,{exit_tc,diff_ok}}}, + {?eh,tc_done,{config_restored_SUITE,exit_tc, + {failed,{error,{test_case_failed,"Goodbye!"}}}}}, + {?eh,test_stats,{0,2,{0,0}}}, + [{?eh,tc_start,{config_restored_SUITE,{init_per_group,g1,[]}}}, + {?eh,tc_start,{config_restored_SUITE,to_tc}}, + {?eh,cth,{verify_config,post_end_per_testcase,{to_tc,diff_ok}}}, + {?eh,tc_done, + {config_restored_SUITE,to_tc,{failed,{timetrap_timeout,1000}}}}, + {?eh,test_stats,{0,3,{0,0}}}, + {?eh,tc_start,{config_restored_SUITE,exit_tc}}, + {?eh,cth,{verify_config,post_end_per_testcase,{exit_tc,diff_ok}}}, + {?eh,tc_done,{config_restored_SUITE,exit_tc, + {failed,{error,{test_case_failed,"Goodbye!"}}}}}, + {?eh,test_stats,{0,4,{0,0}}}, + {?eh,tc_start,{config_restored_SUITE,{end_per_group,g1,[]}}}, + {?eh,tc_done,{config_restored_SUITE,{end_per_group,g1,[]},ok}}], + {?eh,tc_start,{config_restored_SUITE,end_per_suite}}, + {?eh,tc_done,{config_restored_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} ]. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl new file mode 100644 index 0000000000..fb47f2bcb9 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/config_restored_SUITE.erl @@ -0,0 +1,175 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% 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(config_restored_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% Function: suite() -> Info +%% Info = [tuple()] +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,1000}]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = [{init_per_suite,?MODULE} | Config], + TabPid = spawn(fun() -> + ets:new(?MODULE, [named_table, set, public]), + receive _ -> ok end + end), + [{tab,TabPid} | Config1]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + ets:delete(?MODULE), + exit(?config(tab, Config), kill), + 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(GroupName, Config) -> + [{init_per_group,GroupName} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +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(TC, Config) -> + Config1 = [{init_per_testcase,TC} | Config], + ets:insert(?MODULE, {config,Config}), + %% ct:pal("Config after init_per_testcase(~w) = ~p", [TC,Config1]), + Config1. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%%-------------------------------------------------------------------- +end_per_testcase(TC, Config) -> + ct:pal("Config in end_per_testcase(~w) = ~p", [TC,Config]), + [{_,MemConfig}] = ets:lookup(?MODULE, config), + diff_config(Config, MemConfig, [tc_status]), + ?MODULE = proplists:get_value(init_per_suite, Config), + TC = proplists:get_value(init_per_testcase, Config), + case ?config(tc_group_properties, Config) of + undefined -> + ok; + Props -> + GName = proplists:get_value(name, Props), + GName = proplists:get_value(init_per_group, Config) + end, + 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() -> + [{g1,[],[to_tc, exit_tc]}]. + +%%-------------------------------------------------------------------- +%% Function: all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%%-------------------------------------------------------------------- +all() -> + [to_tc, exit_tc, + {group,g1}]. + + +to_tc(Config) -> + %% ct:pal("Config for to_tc = ~p", [Config]), + [{_,MemConfig}] = ets:lookup(?MODULE, config), + diff_config(Config, MemConfig, []), + ct:sleep(2000). + +exit_tc(Config) -> + %% ct:pal("Config for exit_tc = ~p", [Config]), + [{_,MemConfig}] = ets:lookup(?MODULE, config), + diff_config(Config, MemConfig, []), + ct:fail("Goodbye!"). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS + +diff_config(Cfg1, Cfg2, DiffKeys) -> + diff_config(Cfg1, Cfg2, DiffKeys, []). + +diff_config([{K,V} | Cfg1], Cfg2, DiffKeys, RemKeys) -> + case proplists:get_value(K, Cfg2) of + undefined -> + diff_config(Cfg1, Cfg2, proplists:delete(K, DiffKeys), RemKeys); + V -> + diff_config(Cfg1, proplists:delete(K, Cfg2), DiffKeys, RemKeys); + _Other -> + case proplists:is_defined(K, DiffKeys) of + true -> + diff_config(Cfg1, Cfg2, proplists:delete(K, DiffKeys), RemKeys); + false -> + diff_config(Cfg1, Cfg2, DiffKeys, [K | RemKeys]) + end + end; +diff_config([], [], [], []) -> + ct:pal("Diff ok!", []), + ok; +diff_config([], Cfg2, DiffKeys, RemKeys) -> + Result = {diff_failed, {cfg2,Cfg2}, {diffkeys,DiffKeys}, {remkeys,RemKeys}}, + ct:pal("Diff failed! Result = ~p", [Result]), + exit(Result). + diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/verify_config.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/verify_config.erl new file mode 100644 index 0000000000..40a54b9f99 --- /dev/null +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/verify_config.erl @@ -0,0 +1,239 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%% @doc Common Test Example Suite Callback module. +%%% +%%% <p>This module gives an example of a common test CTH (Common Test Hook). +%%% There are many ways to add a CTH to a test run, you can do it either in +%%% the command line using -ct_hook, in a test spec using +%%% {ct_hook,M} or in the suite it self by returning ct_hook +%%% from either suite/0, init_per_suite/1, init_per_group/2 and +%%% init_per_testcase/2. The scope of the CTH is determined by where is it +%%% started. If it is started in the command line or test spec then it will +%%% be stopped at the end of all tests. If it is started in init_per_suite, +%%% it will be stopped after end_per_suite and so on. See terminate +%%% documentation for a table describing the scoping machanics. +%%% +%%% All of callbacks except init/1 in a CTH are optional.</p> + +-module(verify_config). + +%% CT Hooks +-export([id/1]). +-export([init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-type config() :: proplists:proplist(). +-type reason() :: term(). +-type skip_or_fail() :: {skip, reason()} | + {auto_skip, reason()} | + {fail, reason()} | + {'EXIT',reason()}. + +-record(state, { id = ?MODULE :: term()}). + +%% @doc Always called before any other callback function. Use this to initiate +%% any common state. It should return an state for this CTH. +-spec init(Id :: term(), Opts :: proplists:proplist()) -> + {ok, State :: #state{}}. +init(Id, Opts) -> + {ok,Opts}. + +%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's +%% return the same ID the seconds CTH is ignored. This function should NOT +%% have any side effects as it might be called multiple times by common test. +-spec id(Opts :: proplists:proplist()) -> + Id :: term(). +id(Opts) -> + now(). + +%% @doc Called before init_per_suite is called. Note that this callback is +%% only called if the CTH is added before init_per_suite is run (eg. in a test +%% specification, suite/0 function etc). +%% You can change the config in the this function. +-spec pre_init_per_suite(Suite :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_suite(Suite,Config,State) -> + {Config, State}. + +%% @doc Called after init_per_suite. +%% you can change the return value in this function. +-spec post_init_per_suite(Suite :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_suite(Suite,Config,Return,State) -> + {Return, State}. + +%% @doc Called before end_per_suite. The config/state can be changed here, +%% though it will only affect the *end_per_suite function. +-spec pre_end_per_suite(Suite :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_suite(Suite,Config,State) -> + {Config, State}. + +%% @doc Called after end_per_suite. Note that the config cannot be +%% changed here, only the status of the suite. +-spec post_end_per_suite(Suite :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_suite(Suite,Config,Return,State) -> + {Return, State}. + +%% @doc Called before each init_per_group. +%% You can change the config in this function. +-spec pre_init_per_group(Group :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_group(Group,Config,State) -> + {Config, State}. + +%% @doc Called after each init_per_group. +%% You can change the return value in this function. +-spec post_init_per_group(Group :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_group(Group,Config,Return,State) -> + {Return, State}. + +%% @doc Called after each end_per_group. The config/state can be changed here, +%% though it will only affect the *end_per_group functions. +-spec pre_end_per_group(Group :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_group(Group,Config,State) -> + {Config, State}. + +%% @doc Called after each end_per_group. Note that the config cannot be +%% changed here, only the status of the group. +-spec post_end_per_group(Group :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_group(Group,Config,Return,State) -> + {Return, State}. + +%% @doc Called before each test case. +%% You can change the config in this function. +-spec pre_init_per_testcase(TC :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_testcase(TC,Config,State) -> + {Config, State}. + +%% @doc Called after each test case. Note that the config cannot be +%% changed here, only the status of the test case. +-spec post_end_per_testcase(TC :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_testcase(TC,Config,Return,State) -> + %% check that config has been restored + ct:pal("Config in verify_config:post_end_per_testcase(~w) = ~p", + [TC,Config]), + [{_,MemConfig}] = ets:lookup(config_restored_SUITE, config), + try config_restored_SUITE:diff_config(Config, MemConfig, [tc_status]) of + ok -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_testcase, + {TC,diff_ok}}}) + catch + _:_ -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_testcase, + {TC,diff_failed}}}) + end, + {Return, State}. + +%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group, +%% post_end_per_group and post_end_per_tc if the suite, group or test case failed. +%% This function should be used for extra cleanup which might be needed. +%% It is not possible to modify the config or the status of the test run. +-spec on_tc_fail(TC :: init_per_suite | end_per_suite | + init_per_group | end_per_group | atom(), + Reason :: term(), State :: #state{}) -> + NewState :: #state{}. +on_tc_fail(TC, Reason, State) -> + State. + +%% @doc Called when a test case is skipped by either user action +%% or due to an init function failing. Test case can be +%% end_per_suite, init_per_group, end_per_group and the actual test cases. +-spec on_tc_skip(TC :: end_per_suite | + init_per_group | end_per_group | atom(), + {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} | + {tc_user_skip, {skipped, Reason :: term()}}, + State :: #state{}) -> + NewState :: #state{}. +on_tc_skip(TC, Reason, State) -> + State. + +%% @doc Called when the scope of the CTH is done, this depends on +%% when the CTH was specified. This translation table describes when this +%% function is called. +%% +%% | Started in | terminate called | +%% |---------------------|-------------------------| +%% | command_line | after all tests are run | +%% | test spec | after all tests are run | +%% | suite/0 | after SUITE is done | +%% | init_per_suite/1 | after SUITE is done | +%% | init_per_group/2 | after group is done | +%% |-----------------------------------------------| +%% +-spec terminate(State :: #state{}) -> + term(). +terminate(State) -> + ok. diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 405df1e978..796a0832d7 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -64,7 +64,7 @@ end_per_testcase(TestCase, Config) -> suite() -> - [{timetrap,{seconds,20}}]. + [{timetrap,{minutes,1}}]. all() -> all(suite). diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl index 64d34a0c9b..0f336d2d79 100644 --- a/lib/common_test/test/ct_master_SUITE.erl +++ b/lib/common_test/test/ct_master_SUITE.erl @@ -154,6 +154,9 @@ make_spec(DataDir, FileName, NodeNames, Suites, Config) -> {init,NodeName,[ {node_start,[{startup_functions,[]}, {monitor_master,true}, + {boot_timeout,10}, + {init_timeout,10}, + {startup_timeout,10}, {env,Env}]}, {eval,{erlang,nodes,[]}}] } 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 8a5009ad62..df54c4419c 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 @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl index 3042a924fe..c89a4cdabe 100644 --- a/lib/common_test/test/ct_netconfc_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE.erl @@ -43,12 +43,11 @@ %% there will be clashes with logging processes etc). %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config1 = ct_test_support:init_per_suite(Config), case application:load(crypto) of - {error,Reason} -> + {error,Reason} when Reason=/={already_loaded,crypto} -> {skip, Reason}; _ -> - Config1 + ct_test_support:init_per_suite(Config) end. end_per_suite(Config) -> diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index d337158bce..54526e8e83 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -1044,12 +1044,9 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> Key = gen_dsa2(LSize, NSize), {Key, encode_key(Key)}. -encode_key(Key = #'RSAPrivateKey'{}) -> - {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), - {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> - {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + Der = public_key:der_encode('DSAPrivateKey', Key), + {'DSAPrivateKey', Der, not_encrypted}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl index 2427f37f52..09217f60a3 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -1,7 +1,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -426,7 +426,7 @@ reply(ConnRef,Reply) -> send(ConnRef, make_msg(Reply)). from_simple(Simple) -> - list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)). + unicode_c2b(xmerl:export_simple_element(Simple,xmerl_xml)). xml(Content) -> <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", @@ -435,30 +435,30 @@ xml(Content) -> rpc_reply(Content) when is_binary(Content) -> MsgId = case erase(msg_id) of undefined -> <<>>; - Id -> list_to_binary([" message-id=\"",Id,"\""]) + Id -> unicode_c2b([" message-id=\"",Id,"\""]) end, <<"<rpc-reply xmlns=\"",?NETCONF_NAMESPACE,"\"",MsgId/binary,">\n", Content/binary,"\n</rpc-reply>">>; rpc_reply(Content) -> - rpc_reply(list_to_binary(Content)). + rpc_reply(unicode_c2b(Content)). session_id(no_session_id) -> <<>>; session_id(SessionId0) -> - SessionId = list_to_binary(integer_to_list(SessionId0)), + SessionId = unicode_c2b(integer_to_list(SessionId0)), <<"<session-id>",SessionId/binary,"</session-id>\n">>. capabilities(undefined) -> - CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + CapsXml = unicode_c2b([["<capability>",C,"</capability>\n"] || C <- ?CAPABILITIES]), <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; capabilities({base,Vsn}) -> - CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + CapsXml = unicode_c2b([["<capability>",C,"</capability>\n"] || C <- ?CAPABILITIES_VSN(Vsn)]), <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; capabilities(no_base) -> [_|Caps] = ?CAPABILITIES, - CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] || C <- Caps]), + CapsXml = unicode_c2b([["<capability>",C,"</capability>\n"] || C <- Caps]), <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; capabilities(no_caps) -> <<>>. @@ -553,3 +553,8 @@ make_msg(Xml) when is_binary(Xml) -> xml(Xml); make_msg(Simple) when is_tuple(Simple) -> xml(from_simple(Simple)). + +%%%----------------------------------------------------------------- +%%% Convert to unicode binary, since we use UTF-8 encoding in XML +unicode_c2b(Characters) -> + unicode:characters_to_binary(Characters). diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl index 69e98cef48..b86b47f0a2 100644 --- a/lib/common_test/test/ct_surefire_SUITE.erl +++ b/lib/common_test/test/ct_surefire_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2013. 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 @@ -32,6 +32,7 @@ -include_lib("common_test/include/ct_event.hrl"). -include_lib("xmerl/include/xmerl.hrl"). +-include_lib("kernel/include/file.hrl"). -define(eh, ct_test_support_eh). @@ -77,53 +78,52 @@ all() -> %%%----------------------------------------------------------------- %%% 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). + run(default,[cth_surefire],"junit_report.xml",Config). 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). + run(absolute_path,[{cth_surefire,[{path,Path}]}],Path,Config). 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). + run(relative_path,[{cth_surefire,[{path,Path}]}],Path,Config). 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). + run(url,[{cth_surefire,[{url_base,?url_base},{path,Path}]}], + Path,Config). logdir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir,Config), - LogDir = filename:join(PrivDir,"specific_logdir"), - file:make_dir(LogDir), + Opts = ct_test_support:get_opts(Config), + LogDir = + case lists:keyfind(logdir,1,Opts) of + {logdir,LD} -> LD; + false -> ?config(priv_dir,Config) + end, + MyLogDir = filename:join(LogDir,"specific_logdir"), + ensure_exists_empty(MyLogDir), 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). + run(logdir,[{cth_surefire,[{path,Path}]}],Path,Config,[{logdir,MyLogDir}]). %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- -run(Case,CTHs,Config) -> - run(Case,CTHs,Config,[]). -run(Case,CTHs,Config,ExtraOpts) -> +run(Case,CTHs,Report,Config) -> + run(Case,CTHs,Report,Config,[]). +run(Case,CTHs,Report,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). + ok = execute(Case, Opts, ERPid, Config), + LogDir = + case lists:keyfind(logdir,1,Opts) of + {logdir,LD} -> LD; + false -> ?config(priv_dir,Config) + end, + Re = filename:join([LogDir,"*",Report]), + check_xml(Case,Re). setup(Test, Config) -> Opts0 = ct_test_support:get_opts(Config), @@ -349,3 +349,26 @@ 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}. + +ensure_exists_empty(Dir) -> + case file:list_dir(Dir) of + {error,enoent} -> + file:make_dir(Dir); + {ok,Files} -> + del_files(Dir,Files) + end. + +del_files(Dir,[F0|Fs] ) -> + F = filename:join(Dir,F0), + case file:read_file_info(F) of + {ok,#file_info{type=directory}} -> + {ok,Files} = file:list_dir(F), + del_files(F,Files), + file:del_dir(F), + del_files(Dir,Fs); + _ -> + file:delete(F), + del_files(Dir,Fs) + end; +del_files(_,[]) -> + ok. diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 1319fa4154..7c33fd404d 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. 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 @@ -1043,8 +1043,8 @@ result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}}, true; result_match({failed,{timetrap_timeout,{'$approx',Num}}}, {failed,{timetrap_timeout,Value}}) -> - if Value >= trunc(Num-0.02*Num), - Value =< trunc(Num+0.02*Num) -> true; + if Value >= trunc(Num-0.05*Num), + Value =< trunc(Num+0.05*Num) -> true; true -> false end; result_match({user_timetrap_error,{Why,'_'}}, diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index f9bb22867e..c92fb2ca37 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1 +1 @@ -COMMON_TEST_VSN = 1.6.3 +COMMON_TEST_VSN = 1.7 |