diff options
Diffstat (limited to 'lib/common_test')
19 files changed, 708 insertions, 154 deletions
diff --git a/lib/common_test/configure.in b/lib/common_test/configure.in index b2e6ad997a..b2e6ad997a 100755..100644 --- a/lib/common_test/configure.in +++ b/lib/common_test/configure.in diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml index a215c8c2f3..accb94e1a9 100644 --- a/lib/common_test/doc/src/cover_chapter.xml +++ b/lib/common_test/doc/src/cover_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2013</year> + <year>2006</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -81,10 +81,7 @@ specify that previously exported data should be imported and included in the analysis for a test (you can specify multiple import files). This way it is possible to analyse total code coverage - without necessarily running all tests at once. Note that even if - you run separate tests in one test run, code coverage data will - not be passed on from one test to another unless you specify an - export file for Common Test to use for this purpose.</p> + without necessarily running all tests at once.</p> <p>To activate the code coverage support, you simply specify the name of the cover specification file as you start Common Test. @@ -266,10 +263,20 @@ ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).</code> <section> <title>Logging</title> - <p>To view the result of a code coverage test, follow the - "Coverage log" link on the test suite results page. This - takes you to the code coverage overview page. If you have - successfully performed a detailed coverage analysis, you + <p>To view the result of a code coverage test, click the button + labled "COVER LOG" in the top level index page for the test run.</p> + + <p>Prior to Erlang/OTP 17.1, if your test run consisted of + multiple tests, cover would be started and stopped for each test + within the test run. Separate logs would be available via the + "Coverage log" link on the test suite result pages. These links + are still available, but now they all point to the same page as + the button on the top level index page. The log contains the + accumulated results for the complete test run. See the release + notes for more information about this change.</p> + + <p>The buttonc takes you to the code coverage overview page. If you + have successfully performed a detailed coverage analysis, you find links to each individual module coverage page here.</p> <p>If cross cover analysis has been performed, and there are diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index ddfeb0964b..b53ba32e6c 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -32,6 +32,71 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.8.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Substrings in long telnet messages would sometimes get + wrongly reversed. This error has been corrected.</p> + <p> + Own Id: OTP-11871 Aux Id: seq12581 </p> + </item> + <item> + <p> + The basic_html logging mode in Common Test (for + compatibility with old browsers) generated HTML code with + unbalanced tags. This has been fixed.</p> + <p> + Own Id: OTP-11917 Aux Id: seq12598 </p> + </item> + <item> + <p> + The mechanism for running code cover analysis with + common_test has been improved. Earlier, if a test run + consisted of multiple tests, cover would be started and + stopped for each test. This would give "intermediate" + cover logs available from the "Coverage log" link on the + test suite result pages. To accumulate cover data over + all tests, the 'export' option had to be used in the + cover spec file. This was not well documented, and the + functionality was quite confusing.</p> + <p> + Using the 'nodes' option in the cover spec file would + fail when the test run consisted of multiple tests, since + the specified nodes would only be included in the cover + analysis of the first test.</p> + <p> + The repeated compilation and analysis of the same modules + was also very time consuming.</p> + <p> + To overcome these problems, ct will now only cover + compile and analyze modules once per test run, i.e. once + for each cover spec file. The log file is available via a + new button on the top level index page. The old "Coverage + log" links on the test suite result pages still exist, + but they all point to the same log containing the + accumulated result.</p> + <p> + Own Id: OTP-11971</p> + </item> + <item> + <p> + If multiple tests would run simultaneously on different + Erlang nodes, writing their logs to the same directory, + then there would often be entries in the all_runs.html + log file showing incomplete results (all zeroes) upon + completion. This problem was caused by a bug in the + Common Test log cache mechanism, which has been fixed.</p> + <p> + Own Id: OTP-11988 Aux Id: seq12611 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.8</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/priv/run_test.in b/lib/common_test/priv/run_test.in index 1508751e4f..1508751e4f 100755..100644 --- a/lib/common_test/priv/run_test.in +++ b/lib/common_test/priv/run_test.in diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl index ae671c750a..cf2860ae25 100644 --- a/lib/common_test/src/ct_cover.erl +++ b/lib/common_test/src/ct_cover.erl @@ -47,18 +47,21 @@ add_nodes(Nodes) -> undefined -> {error,cover_not_running}; _ -> - {File,Nodes0,Import,Export,AppInfo} = ct_util:get_testdata(cover), + Nodes0 = cover:which_nodes(), Nodes1 = [Node || Node <- Nodes, lists:member(Node,Nodes0) == false], ct_logs:log("COVER INFO", "Adding nodes to cover test: ~w", [Nodes1]), case cover:start(Nodes1) of - Result = {ok,_} -> - ct_util:set_testdata({cover,{File,Nodes1++Nodes0, - Import,Export,AppInfo}}), - + Result = {ok,StartedNodes} -> + ct_logs:log("COVER INFO", + "Successfully added nodes to cover test: ~w", + [StartedNodes]), Result; Error -> + ct_logs:log("COVER INFO", + "Failed to add nodes to cover test: ~tp", + [Error]), Error end end. @@ -81,19 +84,20 @@ remove_nodes(Nodes) -> undefined -> {error,cover_not_running}; _ -> - {File,Nodes0,Import,Export,AppInfo} = ct_util:get_testdata(cover), + Nodes0 = cover:which_nodes(), ToRemove = [Node || Node <- Nodes, lists:member(Node,Nodes0)], ct_logs:log("COVER INFO", - "Removing nodes from cover test: ~w", [ToRemove]), + "Removing nodes from cover test: ~w", [ToRemove]), case cover:stop(ToRemove) of ok -> - Nodes1 = lists:foldl(fun(N,Deleted) -> - lists:delete(N,Deleted) - end, Nodes0, ToRemove), - ct_util:set_testdata({cover,{File,Nodes1, - Import,Export,AppInfo}}), + ct_logs:log("COVER INFO", + "Successfully removed nodes from cover test.", + []), ok; Error -> + ct_logs:log("COVER INFO", + "Failed to remove nodes from cover test: ~tp", + [Error]), Error end end. @@ -149,7 +153,7 @@ get_spec_test(File) -> {value,{_,[Exp]}} -> filename:absname(Exp); _ -> - [] + undefined end, Nodes = case lists:keysearch(nodes, 1, Terms) of diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 20903607dc..e8ea7992b4 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -1244,38 +1244,7 @@ report(What,Data) -> ct_logs:make_all_suites_index({TestName,RunDir}), ok; tests_start -> - case ct_util:get_testdata(cover) of - undefined -> - ok; - {_CovFile,_CovNodes,CovImport,CovExport,_CovAppData} -> - %% Always import cover data from files specified by CovImport - %% if no CovExport defined. If CovExport is defined, only - %% import from CovImport files initially, then use CovExport - %% to pass coverdata between proceeding tests (in the same run). - Imps = - case CovExport of - [] -> % don't export data between tests - CovImport; - _ -> - case filelib:is_file(CovExport) of - true -> - [CovExport]; - false -> - CovImport - end - end, - lists:foreach( - fun(Imp) -> - case cover:import(Imp) of - ok -> - ok; - {error,Reason} -> - ct_logs:log("COVER INFO", - "Importing cover data from: ~ts fails! " - "Reason: ~p", [Imp,Reason]) - end - end, Imps) - end; + ok; tests_done -> ok; severe_error -> diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index a4ad65c0a4..43eabb18d5 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-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2014. 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 @@ -62,6 +62,7 @@ -define(totals_name, "totals.info"). -define(log_cache_name, "ct_log_cache"). -define(misc_io_log, "misc_io.log.html"). +-define(coverlog_name, "cover.html"). % must be same as in test_server_ctrl -define(table_color1,"#ADD8E6"). -define(table_color2,"#E4F0FE"). @@ -1304,7 +1305,8 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> "<td align=right>",integer_to_list(AllSkip), " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", "<td align=right><b>",integer_to_list(NotBuilt),"<b></td>\n", - AllInfo, "</tr>\n</tfoot>\n"]. + AllInfo, "</tr>\n", + xhtml("","</tfoot>\n")]. not_built(_BaseName,_LogDir,_All,[]) -> 0; @@ -1368,6 +1370,19 @@ index_header(Label, StartTime) -> format_time(StartTime), {[],[1],[2,3,4,5]}) end, + Cover = + case filelib:is_regular(?abs(?coverlog_name)) of + true -> + xhtml(["<p><a href=\"",?coverlog_name, + "\">Cover Log</a></p><br>\n"], + ["<br />" + "<div id=\"button_holder\" class=\"btn\">\n" + "<a href=\"",?coverlog_name, + "\">COVER LOG</a>\n</div><br /><br />"]); + false -> + xhtml("<br>\n", "<br /><br /><br />\n") + end, + [Head | ["<center>\n", xhtml(["<p><a href=\"",?ct_log_name, @@ -1375,8 +1390,8 @@ index_header(Label, StartTime) -> ["<br />" "<div id=\"button_holder\" class=\"btn\">\n" "<a href=\"",?ct_log_name, - "\">COMMON TEST FRAMEWORK LOG</a>\n</div>"]), - xhtml("<br>\n", "<br /><br /><br />\n"), + "\">COMMON TEST FRAMEWORK LOG</a>\n</div><br>\n"]), + Cover, xhtml(["<table border=\"3\" cellpadding=\"5\" " "bgcolor=\"",?table_color3,"\">\n"], ["<table id=\"",?sortable_table_name,"\">\n", @@ -1519,7 +1534,8 @@ all_suites_index_footer() -> xhtml("<br><br>\n", "<br /><br />\n") | footer()]. all_runs_index_footer() -> - ["</tbody>\n</table>\n", + [xhtml("", "</tbody>\n"), + "</table>\n", "</center>\n", xhtml("<br><br>\n", "<br /><br />\n") | footer()]. @@ -1676,7 +1692,7 @@ config_table(Vars) -> config_table_header() -> [ xhtml(["<h2>Configuration</h2>\n" - "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\"\n"], + "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\">\n"], ["<h4>CONFIGURATION</h4>\n", "<table id=\"",?sortable_table_name,"\">\n", "<thead>\n"]), @@ -1692,7 +1708,7 @@ config_table1([{Key,Value}|Vars]) -> "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) | config_table1(Vars)]; config_table1([]) -> - ["</tbody>\n</table>\n"]. + [xhtml("","</tbody>\n"),"</table>\n"]. make_all_runs_index(When) -> @@ -1842,14 +1858,27 @@ dir_diff_all_runs(LogDirs=[Dir|Dirs], Cached=[CElem|CElems], LatestInCache, AllRunsDirs) -> DirDate = datestr_from_dirname(Dir), if DirDate > LatestInCache -> - %% Dir is a new run entry + %% Dir is a new run entry (not cached) dir_diff_all_runs(Dirs, Cached, LatestInCache, [Dir|AllRunsDirs]); DirDate == LatestInCache, CElems /= [] -> - %% Dir is an existing run entry + %% Dir is an existing (cached) run entry + + %% Only add the cached element instead of Dir if the totals + %% are "non-empty" (a test might be executing on a different + %% node and results haven't been saved yet) + ElemToAdd = + case CElem of + {_CDir,{_NodeStr,_Label,_Logs,{0,0,0,0,0}},_IxLink} -> + %% "empty" element in cache - this could be an + %% incomplete test and should be checked again + Dir; + _ -> + CElem + end, dir_diff_all_runs(Dirs, CElems, datestr_from_dirname(element(1,hd(CElems))), - [CElem|AllRunsDirs]); + [ElemToAdd|AllRunsDirs]); DirDate == LatestInCache, CElems == [] -> %% we're done, Dirs must all be new lists:reverse(Dirs)++[CElem|AllRunsDirs]; diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 03cf06abed..00d0aab507 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-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -1646,7 +1646,7 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc), do_run(Tests, [], Opts#opts{logdir = LogDir}, []); do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> - #opts{label = Label, profile = Profile, cover = Cover, + #opts{label = Label, profile = Profile, verbosity = VLvls} = Opts, %% label - used by ct_logs TestLabel = @@ -1670,22 +1670,6 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> non_existing -> {error,no_path_to_test_server}; _ -> - Opts1 = if Cover == undefined -> - Opts; - true -> - case ct_cover:get_spec(Cover) of - {error,Reason} -> - exit({error,Reason}); - CoverSpec -> - CoverStop = - case Opts#opts.cover_stop of - undefined -> true; - Stop -> Stop - end, - Opts#opts{coverspec = CoverSpec, - cover_stop = CoverStop} - end - end, %% This env variable is used by test_server to determine %% which framework it runs under. case os:getenv("TEST_SERVER_FRAMEWORK") of @@ -1711,7 +1695,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> _Pid -> ct_util:set_testdata({starter,Opts#opts.starter}), compile_and_run(Tests, Skip, - Opts1#opts{verbosity=Verbosity}, Args) + Opts#opts{verbosity=Verbosity}, Args) end end. @@ -2146,67 +2130,11 @@ check_and_add([{TestDir0,M,_} | Tests], Added, PA) -> check_and_add([], _, PA) -> {ok,PA}. -do_run_test(Tests, Skip, Opts) -> +do_run_test(Tests, Skip, Opts0) -> case check_and_add(Tests, [], []) of {ok,AddedToPath} -> ct_util:set_testdata({stats,{0,0,{0,0}}}), - ct_util:set_testdata({cover,undefined}), test_server_ctrl:start_link(local), - case Opts#opts.coverspec of - CovData={CovFile, - CovNodes, - _CovImport, - CovExport, - #cover{app = CovApp, - level = CovLevel, - excl_mods = CovExcl, - incl_mods = CovIncl, - cross = CovCross, - src = _CovSrc}} -> - ct_logs:log("COVER INFO", - "Using cover specification file: ~ts~n" - "App: ~w~n" - "Cross cover: ~w~n" - "Including ~w modules~n" - "Excluding ~w modules", - [CovFile,CovApp,CovCross, - length(CovIncl),length(CovExcl)]), - - %% cover export file will be used for export and import - %% between tests so make sure it doesn't exist initially - case filelib:is_file(CovExport) of - true -> - DelResult = file:delete(CovExport), - ct_logs:log("COVER INFO", - "Warning! " - "Export file ~ts already exists. " - "Deleting with result: ~p", - [CovExport,DelResult]); - false -> - ok - end, - - %% tell test_server which modules should be cover compiled - %% note that actual compilation is done when tests start - test_server_ctrl:cover(CovApp, CovFile, CovExcl, CovIncl, - CovCross, CovExport, CovLevel, - Opts#opts.cover_stop), - %% save cover data (used e.g. to add nodes dynamically) - ct_util:set_testdata({cover,CovData}), - %% start cover on specified nodes - if (CovNodes /= []) and (CovNodes /= undefined) -> - ct_logs:log("COVER INFO", - "Nodes included in cover " - "session: ~w", - [CovNodes]), - cover:start(CovNodes); - true -> - ok - end, - true; - _ -> - false - end, %% let test_server expand the test tuples and count no of cases {Suites,NoOfCases} = count_test_cases(Tests, Skip), @@ -2231,24 +2159,31 @@ do_run_test(Tests, Skip, Opts) -> end, %% if the verbosity level is set lower than ?STD_IMPORTANCE, tell %% test_server to ignore stdout printouts to the test case log file - case proplists:get_value(default, Opts#opts.verbosity) of + case proplists:get_value(default, Opts0#opts.verbosity) of VLvl when is_integer(VLvl), (?STD_IMPORTANCE < (100-VLvl)) -> test_server_ctrl:reject_io_reqs(true); _Lower -> ok end, - test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps), - test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps), + test_server_ctrl:multiply_timetraps(Opts0#opts.multiply_timetraps), + test_server_ctrl:scale_timetraps(Opts0#opts.scale_timetraps), test_server_ctrl:create_priv_dir(choose_val( - Opts#opts.create_priv_dir, + Opts0#opts.create_priv_dir, auto_per_run)), + + {ok,LogDir} = ct_logs:get_log_dir(true), + {TsCoverInfo,Opts} = maybe_start_cover(Opts0, LogDir), + ct_event:notify(#event{name=start_info, node=node(), data={NoOfTests,NoOfSuites,NoOfCases}}), CleanUp = add_jobs(Tests, Skip, Opts, []), unlink(whereis(test_server_ctrl)), catch test_server_ctrl:wait_finish(), + + maybe_stop_cover(Opts, TsCoverInfo, LogDir), + %% check if last testcase has left a "dead" trace window %% behind, and if so, kill it case ct_util:get_testdata(interpret) of @@ -2281,6 +2216,102 @@ do_run_test(Tests, Skip, Opts) -> exit(Error) end. +maybe_start_cover(Opts=#opts{cover=Cover,cover_stop=CoverStop0},LogDir) -> + if Cover == undefined -> + {undefined,Opts}; + true -> + case ct_cover:get_spec(Cover) of + {error,Reason} -> + exit({error,Reason}); + CoverSpec -> + CoverStop = + case CoverStop0 of + undefined -> true; + Stop -> Stop + end, + start_cover(Opts#opts{coverspec=CoverSpec, + cover_stop=CoverStop}, + LogDir) + end + end. + +start_cover(Opts=#opts{coverspec=CovData,cover_stop=CovStop},LogDir) -> + {CovFile, + CovNodes, + CovImport, + _CovExport, + #cover{app = CovApp, + level = CovLevel, + excl_mods = CovExcl, + incl_mods = CovIncl, + cross = CovCross, + src = _CovSrc}} = CovData, + ct_logs:log("COVER INFO", + "Using cover specification file: ~ts~n" + "App: ~w~n" + "Cross cover: ~w~n" + "Including ~w modules~n" + "Excluding ~w modules", + [CovFile,CovApp,CovCross, + length(CovIncl),length(CovExcl)]), + + %% Tell test_server to print a link in its coverlog + %% pointing to the real coverlog which will be written in + %% maybe_stop_cover/2 + test_server_ctrl:cover({log,LogDir}), + + %% Cover compile all modules + {ok,TsCoverInfo} = test_server_ctrl:cover_compile(CovApp,CovFile, + CovExcl,CovIncl, + CovCross,CovLevel, + CovStop), + ct_logs:log("COVER INFO", + "Compilation completed - test_server cover info: ~tp", + [TsCoverInfo]), + + %% start cover on specified nodes + if (CovNodes /= []) and (CovNodes /= undefined) -> + ct_logs:log("COVER INFO", + "Nodes included in cover " + "session: ~w", + [CovNodes]), + cover:start(CovNodes); + true -> + ok + end, + lists:foreach( + fun(Imp) -> + case cover:import(Imp) of + ok -> + ok; + {error,Reason} -> + ct_logs:log("COVER INFO", + "Importing cover data from: ~ts fails! " + "Reason: ~p", [Imp,Reason]) + end + end, CovImport), + {TsCoverInfo,Opts}. + +maybe_stop_cover(_,undefined,_) -> + ok; +maybe_stop_cover(#opts{coverspec=CovData},TsCoverInfo,LogDir) -> + {_CovFile, + _CovNodes, + _CovImport, + CovExport, + _AppData} = CovData, + case CovExport of + undefined -> ok; + _ -> + ct_logs:log("COVER INFO","Exporting cover data to ~tp",[CovExport]), + cover:export(CovExport) + end, + ct_logs:log("COVER INFO","Analysing cover data to ~tp",[LogDir]), + test_server_ctrl:cover_analyse(TsCoverInfo,LogDir), + ct_logs:log("COVER INFO","Analysis completed.",[]), + ok. + + delete_dups([S | Suites]) -> Suites1 = lists:delete(S, Suites), [S | delete_dups(Suites1)]; diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 085f19d023..a0ac47f12a 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2013. All Rights Reserved. +# Copyright Ericsson AB 2008-2014. 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 @@ -61,6 +61,7 @@ MODULES= \ ct_snmp_SUITE \ ct_group_leader_SUITE \ ct_cover_SUITE \ + ct_cover_nomerge_SUITE \ ct_groups_search_SUITE \ ct_surefire_SUITE \ ct_telnet_SUITE diff --git a/lib/common_test/test/ct_cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE.erl index ec2680f664..47080b5577 100644 --- a/lib/common_test/test/ct_cover_SUITE.erl +++ b/lib/common_test/test/ct_cover_SUITE.erl @@ -172,8 +172,8 @@ cross(Config) -> check_calls(Events2,1), %% Get the log dirs for each test and run cross cover analyse - [D11,D12] = lists:sort(get_run_dirs(Events1)), - [D21,D22] = lists:sort(get_run_dirs(Events2)), + [D11,D12] = lists:sort(get_log_dirs(Events1)), + [D21,D22] = lists:sort(get_log_dirs(Events2)), ct_cover:cross_cover_analyse(details,[{cross1,D11},{cross2,D21}]), ct_cover:cross_cover_analyse(details,[{cross1,D12},{cross2,D22}]), @@ -267,18 +267,17 @@ check_cover(Node) when is_atom(Node) -> false end. -%% Get the log dir "run.<timestamp>" for all (both!) tests -get_run_dirs(Events) -> - [filename:dirname(TCLog) || +%% Get the log dir "ct_run.<timestamp>" for all (both!) tests +get_log_dirs(Events) -> + [LogDir || {ct_test_support_eh, - {event,tc_logfile,_Node, - {{?suite,init_per_suite},TCLog}}} <- Events]. + {event,start_logging,_Node,LogDir}} <- Events]. %% Check that each coverlog includes N calls to ?mod:foo/0 check_calls(Events,N) -> check_calls(Events,{?mod,foo,0},N). check_calls(Events,MFA,N) -> - CoverLogs = [filename:join(D,"all.coverdata") || D <- get_run_dirs(Events)], + CoverLogs = [filename:join(D,"all.coverdata") || D <- get_log_dirs(Events)], do_check_logs(CoverLogs,MFA,N). do_check_logs([CoverLog|CoverLogs],{Mod,_,_} = MFA,N) -> diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE.erl b/lib/common_test/test/ct_cover_nomerge_SUITE.erl new file mode 100644 index 0000000000..8e2ee1b500 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE.erl @@ -0,0 +1,221 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_cover_nomerge_SUITE +%%% +%%% Description: +%%% Test code cover analysis support when merge_tests=false +%%% +%%%------------------------------------------------------------------- +-module(ct_cover_nomerge_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). +-define(mod, cover_test_mod). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case test_server:is_cover() of + true -> + {skip,"Test server is running cover already - skipping"}; + false -> + ct_test_support:init_per_suite(Config) + end. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + try apply(?MODULE,TestCase,[cleanup,Config]) + catch error:undef -> ok + end, + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + local, + remote, + remote_nostop + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +local(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "local.spec"), + CoverSpec = [{incl_mods,[?mod]}], + CoverFile = create_cover_file(local,CoverSpec,Config), + {Opts,ERPid} = setup([{spec,Spec},{label,local},{cover,CoverFile}], Config), + {ok,Events} = execute(local, local, Opts, ERPid, Config), + false = check_cover(Config), + check_calls(Events,2), + ok. + +remote(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "remote.spec"), + %% extending some timers for slow test hosts + {ok,Node} = ct_slave:start(ct_nomerge,[{boot_timeout,15}, + {init_timeout,15}, + {startup_timeout,15}]), + + CoverSpec = [{nodes,[Node]}, + {incl_mods,[?mod]}], + CoverFile = create_cover_file(remote,CoverSpec,Config), + {Opts,ERPid} = setup([{spec,Spec},{label,remote},{cover,CoverFile}], Config), + {ok,Events} = execute(remote, remote, Opts, ERPid, Config), + false = check_cover(Config), + check_calls(Events,2), + ok. +remote(cleanup,_Config) -> + {ok,_} = ct_slave:stop(ct_nomerge), + ok. + +remote_nostop(Config) -> + DataDir = ?config(data_dir, Config), + Spec = filename:join(DataDir, "remote_nostop.spec"), + %% extending some timers for slow test hosts + {ok,Node} = ct_slave:start(ct_nomerge,[{boot_timeout,15}, + {init_timeout,15}, + {startup_timeout,15}]), + + CoverSpec = [{nodes,[Node]}, + {incl_mods,[?mod]}], + CoverFile = create_cover_file(remote_nostop,CoverSpec,Config), + {Opts,ERPid} = setup([{spec,Spec},{label,remote_nostop}, + {cover,CoverFile},{cover_stop,false}], + Config), + {ok,Events} = execute(remote_nostop, remote_nostop, Opts, ERPid, Config), + {true,[Node],[cover_test_mod]} = check_cover(Config), + check_calls(Events,2), + ok. +remote_nostop(cleanup,Config) -> + CtNode = ?config(ct_node,Config), + ok = rpc:call(CtNode,cover,stop,[]), + {ok,_} = ct_slave:stop(ct_nomerge), + ok. + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Testcase, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + TestEvents = events_to_check(Testcase), + R = ct_test_support:verify_events(TestEvents, Events, Config), + {R,Events}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +events_to_check(local) -> + events_to_check1(cover_nomerge_local_SUITE); +events_to_check(remote) -> + events_to_check1(cover_nomerge_remote_SUITE); +events_to_check(remote_nostop) -> + events_to_check1(cover_nomerge_remote_nostop_SUITE). +events_to_check1(Suite) -> + OneTest = + [{?eh,start_logging,{'DEF','RUNDIR'}}] ++ + [{?eh,tc_done,{Suite,t1,ok}}] ++ + [{?eh,tc_done,{Suite,t2,ok}}] ++ + [{?eh,stop_logging,[]}], + + %% 2 tests (ct:run_test + script_start) is default + OneTest ++ OneTest. + +check_cover(Config) when is_list(Config) -> + CTNode = proplists:get_value(ct_node, Config), + check_cover(CTNode); +check_cover(Node) when is_atom(Node) -> + case rpc:call(Node,test_server,is_cover,[]) of + true -> + {true, + rpc:call(Node,cover,which_nodes,[]), + rpc:call(Node,cover,modules,[])}; + false -> + false + end. + +%% Get the log dir "ct_run.<timestamp>" for all (both!) tests +get_log_dirs(Events) -> + [LogDir || + {ct_test_support_eh, + {event,start_logging,_Node,LogDir}} <- Events]. + +%% Check that each coverlog includes N calls to ?mod:foo/0 +check_calls(Events,N) -> + check_calls(Events,{?mod,foo,0},N). +check_calls(Events,MFA,N) -> + CoverLogs = [filename:join(D,"all.coverdata") || D <- get_log_dirs(Events)], + do_check_logs(CoverLogs,MFA,N). + +do_check_logs([CoverLog|CoverLogs],{Mod,_,_} = MFA,N) -> + {ok,_} = cover:start(), + ok = cover:import(CoverLog), + {ok,Calls} = cover:analyse(Mod,calls,function), + ok = cover:stop(), + {MFA,N} = lists:keyfind(MFA,1,Calls), + do_check_logs(CoverLogs,MFA,N); +do_check_logs([],_,_) -> + ok. + +create_cover_file(Filename,Terms,Config) -> + PrivDir = ?config(priv_dir,Config), + File = filename:join(PrivDir,Filename) ++ ".cover", + {ok,Fd} = file:open(File,[write]), + lists:foreach(fun(Term) -> + file:write(Fd,io_lib:format("~p.~n",[Term])) + end,Terms), + ok = file:close(Fd), + File. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_local_SUITE.erl b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_local_SUITE.erl new file mode 100644 index 0000000000..e1fe3b5fc9 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_local_SUITE.erl @@ -0,0 +1,63 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. 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(cover_nomerge_local_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +suite() -> + []. + +all() -> + [t1,t2]. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +break(_Config) -> + test_server:break(""), + ok. + +t1(_Config) -> + cover_compiled = code:which(cover_test_mod), + ok = cover_test_mod:foo(), + ok. + +t2(_Config) -> + cover_compiled = code:which(cover_test_mod), + ok = cover_test_mod:foo(), + ok. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_SUITE.erl b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_SUITE.erl new file mode 100644 index 0000000000..a77ae0c2db --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_SUITE.erl @@ -0,0 +1,75 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. 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(cover_nomerge_remote_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +suite() -> + []. + +all() -> + [t1,t2]. + +init_per_suite(Config) -> + {ok,Host} = inet:gethostname(), + Node = list_to_atom("ct_nomerge@"++Host), + pong = net_adm:ping(Node), + +%% Include this row, and exclude the equivalent row in end_per_suite => +%% fails every now and then with missing data. Why? +%% ct_cover:remove_nodes([Node]), + ct_cover:add_nodes([Node]), + [{node,Node}|Config]. + +end_per_suite(Config) -> + Node = ?config(node,Config), + ct_cover:remove_nodes([Node]), + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +break(_Config) -> + test_server:break(""), + ok. + +t1(Config) -> + Node = ?config(node,Config), + cover_compiled = rpc:call(Node, code, which, [cover_test_mod]), + ok = rpc:call(Node, cover_test_mod, foo, []), + ok. + +t2(Config) -> + Node = ?config(node,Config), + cover_compiled = rpc:call(Node, code, which, [cover_test_mod]), + ok = rpc:call(Node, cover_test_mod, foo, []), + ok. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_nostop_SUITE.erl b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_nostop_SUITE.erl new file mode 100644 index 0000000000..0b3159f2c3 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_nomerge_remote_nostop_SUITE.erl @@ -0,0 +1,68 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. 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(cover_nomerge_remote_nostop_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +suite() -> + []. + +all() -> + [t1,t2]. + +init_per_suite(Config) -> + {ok,Host} = inet:gethostname(), + Node = list_to_atom("ct_nomerge@"++Host), + pong = net_adm:ping(Node), + [{node,Node}|Config]. + +end_per_suite(Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +break(_Config) -> + test_server:break(""), + ok. + +t1(Config) -> + Node = ?config(node,Config), + cover_compiled = rpc:call(Node, code, which, [cover_test_mod]), + ok = rpc:call(Node, cover_test_mod, foo, []), + ok. + +t2(Config) -> + Node = ?config(node,Config), + cover_compiled = rpc:call(Node, code, which, [cover_test_mod]), + ok = rpc:call(Node, cover_test_mod, foo, []), + ok. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_test_mod.erl b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_test_mod.erl new file mode 100644 index 0000000000..d4f69452c3 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/cover_test_mod.erl @@ -0,0 +1,4 @@ +-module(cover_test_mod). +-compile(export_all). +foo() -> + ok. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/local.spec b/lib/common_test/test/ct_cover_nomerge_SUITE_data/local.spec new file mode 100644 index 0000000000..893c48b010 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/local.spec @@ -0,0 +1,6 @@ +{merge_tests,false}. + +{alias,dir,"."}. + +{cases, dir, cover_nomerge_local_SUITE, [t1]}. +{cases, dir, cover_nomerge_local_SUITE, [t2]}. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote.spec b/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote.spec new file mode 100644 index 0000000000..78c4332270 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote.spec @@ -0,0 +1,6 @@ +{merge_tests,false}. + +{alias,dir,"."}. + +{cases, dir, cover_nomerge_remote_SUITE, [t1]}. +{cases, dir, cover_nomerge_remote_SUITE, [t2]}. diff --git a/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote_nostop.spec b/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote_nostop.spec new file mode 100644 index 0000000000..049f586c72 --- /dev/null +++ b/lib/common_test/test/ct_cover_nomerge_SUITE_data/remote_nostop.spec @@ -0,0 +1,6 @@ +{merge_tests,false}. + +{alias,dir,"."}. + +{cases, dir, cover_nomerge_remote_nostop_SUITE, [t1]}. +{cases, dir, cover_nomerge_remote_nostop_SUITE, [t2]}. diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk index f8a5aab686..def8a6a6f4 100644 --- a/lib/common_test/vsn.mk +++ b/lib/common_test/vsn.mk @@ -1 +1 @@ -COMMON_TEST_VSN = 1.8 +COMMON_TEST_VSN = 1.8.1 |