aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools/doc/src/cover_chapter.xml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools/doc/src/cover_chapter.xml')
-rw-r--r--lib/tools/doc/src/cover_chapter.xml490
1 files changed, 490 insertions, 0 deletions
diff --git a/lib/tools/doc/src/cover_chapter.xml b/lib/tools/doc/src/cover_chapter.xml
new file mode 100644
index 0000000000..b4f7919183
--- /dev/null
+++ b/lib/tools/doc/src/cover_chapter.xml
@@ -0,0 +1,490 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2001</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>cover</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>cover_chapter.xml</file>
+ </header>
+
+ <section>
+ <title>Introduction</title>
+ <p>The module <c>cover</c> provides a set of functions for coverage
+ analysis of Erlang programs, counting how many times each
+ <seealso marker="#lines">executable line</seealso> is executed.</p>
+ <p>Coverage analysis can be used to verify test cases, making sure all
+ relevant code is covered, and may be helpful when looking for
+ bottlenecks in the code.</p>
+ </section>
+
+ <section>
+ <title>Getting Started With Cover</title>
+
+ <section>
+ <title>Example</title>
+ <p>Assume that a test case for the following program should be
+ verified:</p>
+ <code type="none">
+-module(channel).
+-behaviour(gen_server).
+
+-export([start_link/0,stop/0]).
+-export([alloc/0,free/1]). % client interface
+-export([init/1,handle_call/3,terminate/2]). % callback functions
+
+start_link() ->
+ gen_server:start_link({local,channel},channel,[],[]).
+
+stop() ->
+ gen_server:call(channel,stop).
+
+%%%-Client interface functions-------------------------------------------
+
+alloc() ->
+ gen_server:call(channel,alloc).
+
+free(Channel) ->
+ gen_server:call(channel,{free,Channel}).
+
+%%%-gen_server callback functions----------------------------------------
+
+init(_Arg) ->
+ {ok,channels()}.
+
+handle_call(stop,Client,Channels) ->
+ {stop,normal,ok,Channels};
+
+handle_call(alloc,Client,Channels) ->
+ {Ch,Channels2} = alloc(Channels),
+ {reply,{ok,Ch},Channels2};
+
+handle_call({free,Channel},Client,Channels) ->
+ Channels2 = free(Channel,Channels),
+ {reply,ok,Channels2}.
+
+terminate(_Reason,Channels) ->
+ ok.
+
+%%%-Internal functions---------------------------------------------------
+
+channels() ->
+ [ch1,ch2,ch3].
+
+alloc([Channel|Channels]) ->
+ {Channel,Channels};
+alloc([]) ->
+ false.
+
+free(Channel,Channels) ->
+ [Channel|Channels].</code>
+ <p>The test case is implemented as follows:</p>
+ <code type="none">
+-module(test).
+-export([s/0]).
+
+s() ->
+ {ok,Pid} = channel:start_link(),
+ {ok,Ch1} = channel:alloc(),
+ ok = channel:free(Ch1),
+ ok = channel:stop().</code>
+ </section>
+
+ <section>
+ <title>Preparation</title>
+ <p>First of all, Cover must be started. This spawns a process which
+ owns the Cover database where all coverage data will be stored.</p>
+ <pre>
+1> <input>cover:start().</input>
+{ok,&lt;0.30.0>}</pre>
+ <p>To include other nodes in the coverage analysis, use
+ <c>start/1</c>. All cover compiled modules will then be loaded
+ on all nodes, and data from all nodes will be summed up when
+ analysing. For simplicity this example only involves the
+ current node.</p>
+ <p>Before any analysis can take place, the involved modules must be
+ <em>Cover compiled</em>. This means that some extra information is
+ added to the module before it is compiled into a binary which then
+ is <seealso marker="#loading">loaded</seealso>. The source file of
+ the module is not affected and no <c>.beam</c> file is created.</p>
+ <pre>
+2> <input>cover:compile_module(channel).</input>
+{ok,channel}</pre>
+ <p>Each time a function in the Cover compiled module <c>channel</c>
+ is called, information about the call will be added to the Cover
+ database. Run the test case:</p>
+ <pre>
+3> <input>test:s().</input>
+ok</pre>
+ <p>Cover analysis is performed by examining the contents of the Cover
+ database. The output is determined by two parameters, <c>Level</c>
+ and <c>Analysis</c>. <c>Analysis</c> is either <c>coverage</c> or
+ <c>calls</c> and determines the type of the analysis. <c>Level</c>
+ is either <c>module</c>, <c>function</c>, <c>clause</c>, or
+ <c>line</c> and determines the level of the analysis.</p>
+ </section>
+
+ <section>
+ <title>Coverage Analysis</title>
+ <p>Analysis of type <c>coverage</c> is used to find out how much of
+ the code has been executed and how much has not been executed.
+ Coverage is represented by a tuple <c>{Cov,NotCov}</c>, where
+ <c>Cov</c> is the number of executable lines that have been executed
+ at least once and <c>NotCov</c> is the number of executable lines
+ that have not been executed.</p>
+ <p>If the analysis is made on module level, the result is given for
+ the entire module as a tuple <c>{Module,{Cov,NotCov}}</c>:</p>
+ <pre>
+4> <input>cover:analyse(channel,coverage,module).</input>
+{ok,{channel,{14,1}}}</pre>
+ <p>For <c>channel</c>, the result shows that 14 lines in the module
+ are covered but one line is not covered.</p>
+ <p>If the analysis is made on function level, the result is given as
+ a list of tuples <c>{Function,{Cov,NotCov}}</c>, one for each
+ function in the module. A function is specified by its module name,
+ function name and arity:</p>
+ <pre>
+5> <input>cover:analyse(channel,coverage,function).</input>
+{ok,[{{channel,start_link,0},{1,0}},
+ {{channel,stop,0},{1,0}},
+ {{channel,alloc,0},{1,0}},
+ {{channel,free,1},{1,0}},
+ {{channel,init,1},{1,0}},
+ {{channel,handle_call,3},{5,0}},
+ {{channel,terminate,2},{1,0}},
+ {{channel,channels,0},{1,0}},
+ {{channel,alloc,1},{1,1}},
+ {{channel,free,2},{1,0}}]}</pre>
+ <p>For <c>channel</c>, the result shows that the uncovered line is in
+ the function <c>channel:alloc/1</c>.</p>
+ <p>If the analysis is made on clause level, the result is given as
+ a list of tuples <c>{Clause,{Cov,NotCov}}</c>, one for each
+ function clause in the module. A clause is specified by its module
+ name, function name, arity and position within the function
+ definition:</p>
+ <pre>
+6> <input>cover:analyse(channel,coverage,clause).</input>
+{ok,[{{channel,start_link,0,1},{1,0}},
+ {{channel,stop,0,1},{1,0}},
+ {{channel,alloc,0,1},{1,0}},
+ {{channel,free,1,1},{1,0}},
+ {{channel,init,1,1},{1,0}},
+ {{channel,handle_call,3,1},{1,0}},
+ {{channel,handle_call,3,2},{2,0}},
+ {{channel,handle_call,3,3},{2,0}},
+ {{channel,terminate,2,1},{1,0}},
+ {{channel,channels,0,1},{1,0}},
+ {{channel,alloc,1,1},{1,0}},
+ {{channel,alloc,1,2},{0,1}},
+ {{channel,free,2,1},{1,0}}]}</pre>
+ <p>For <c>channel</c>, the result shows that the uncovered line is in
+ the second clause of <c>channel:alloc/1</c>.</p>
+ <p>Finally, if the analysis is made on line level, the result is given
+ as a list of tuples <c>{Line,{Cov,NotCov}}</c>, one for each
+ executable line in the source code. A line is specified by its
+ module name and line number.</p>
+ <pre>
+7> <input>cover:analyse(channel,coverage,line).</input>
+{ok,[{{channel,9},{1,0}},
+ {{channel,12},{1,0}},
+ {{channel,17},{1,0}},
+ {{channel,20},{1,0}},
+ {{channel,25},{1,0}},
+ {{channel,28},{1,0}},
+ {{channel,31},{1,0}},
+ {{channel,32},{1,0}},
+ {{channel,35},{1,0}},
+ {{channel,36},{1,0}},
+ {{channel,39},{1,0}},
+ {{channel,44},{1,0}},
+ {{channel,47},{1,0}},
+ {{channel,49},{0,1}},
+ {{channel,52},{1,0}}]}</pre>
+ <p>For <c>channel</c>, the result shows that the uncovered line is
+ line number 49.</p>
+ </section>
+
+ <section>
+ <title>Call Statistics</title>
+ <p>Analysis of type <c>calls</c> is used to find out how many times
+ something has been called and is represented by an integer
+ <c>Calls</c>.</p>
+ <p>If the analysis is made on module level, the result is given as a
+ tuple <c>{Module,Calls}</c>. Here <c>Calls</c> is the total number
+ of calls to functions in the module:</p>
+ <pre>
+8> <input>cover:analyse(channel,calls,module).</input>
+{ok,{channel,12}}</pre>
+ <p>For <c>channel</c>, the result shows that a total of twelve calls
+ have been made to functions in the module.</p>
+ <p>If the analysis is made on function level, the result is given as
+ a list of tuples <c>{Function,Calls}</c>. Here <c>Calls</c> is
+ the number of calls to each function:</p>
+ <pre>
+9> <input>cover:analyse(channel,calls,function).</input>
+{ok,[{{channel,start_link,0},1},
+ {{channel,stop,0},1},
+ {{channel,alloc,0},1},
+ {{channel,free,1},1},
+ {{channel,init,1},1},
+ {{channel,handle_call,3},3},
+ {{channel,terminate,2},1},
+ {{channel,channels,0},1},
+ {{channel,alloc,1},1},
+ {{channel,free,2},1}]}</pre>
+ <p>For <c>channel</c>, the result shows that <c>handle_call/3</c> is
+ the most called function in the module (three calls). All other
+ functions have been called once.</p>
+ <p>If the analysis is made on clause level, the result is given as
+ a list of tuples <c>{Clause,Calls}</c>. Here <c>Calls</c> is
+ the number of calls to each function clause:</p>
+ <pre>
+10> <input>cover:analyse(channel,calls,clause).</input>
+{ok,[{{channel,start_link,0,1},1},
+ {{channel,stop,0,1},1},
+ {{channel,alloc,0,1},1},
+ {{channel,free,1,1},1},
+ {{channel,init,1,1},1},
+ {{channel,handle_call,3,1},1},
+ {{channel,handle_call,3,2},1},
+ {{channel,handle_call,3,3},1},
+ {{channel,terminate,2,1},1},
+ {{channel,channels,0,1},1},
+ {{channel,alloc,1,1},1},
+ {{channel,alloc,1,2},0},
+ {{channel,free,2,1},1}]}</pre>
+ <p>For <c>channel</c>, the result shows that all clauses have been
+ called once, except the second clause of <c>channel:alloc/1</c>
+ which has not been called at all.</p>
+ <p>Finally, if the analysis is made on line level, the result is given
+ as a list of tuples <c>{Line,Calls}</c>. Here <c>Calls</c> is
+ the number of times each line has been executed:</p>
+ <pre>
+11> <input>cover:analyse(channel,calls,line).</input>
+{ok,[{{channel,9},1},
+ {{channel,12},1},
+ {{channel,17},1},
+ {{channel,20},1},
+ {{channel,25},1},
+ {{channel,28},1},
+ {{channel,31},1},
+ {{channel,32},1},
+ {{channel,35},1},
+ {{channel,36},1},
+ {{channel,39},1},
+ {{channel,44},1},
+ {{channel,47},1},
+ {{channel,49},0},
+ {{channel,52},1}]}</pre>
+ <p>For <c>channel</c>, the result shows that all lines have been
+ executed once, except line number 49 which has not been executed at
+ all.</p>
+ </section>
+
+ <section>
+ <title>Analysis to File</title>
+ <p>A line level calls analysis of <c>channel</c> can be written to
+ a file using <c>cover:analysis_to_file/1</c>:</p>
+ <pre>
+12> <input>cover:analyse_to_file(channel).</input>
+{ok,"channel.COVER.out"}</pre>
+ <p>The function creates a copy of <c>channel.erl</c> where it for
+ each executable line is specified how many times that line has been
+ executed. The output file is called <c>channel.COVER.out</c>.</p>
+ <pre>
+File generated from channel.erl by COVER 2001-05-21 at 11:16:38
+
+****************************************************************************
+
+ | -module(channel).
+ | -behaviour(gen_server).
+ |
+ | -export([start_link/0,stop/0]).
+ | -export([alloc/0,free/1]). % client interface
+ | -export([init/1,handle_call/3,terminate/2]). % callback functions
+ |
+ | start_link() ->
+ 1..| gen_server:start_link({local,channel},channel,[],[]).
+ |
+ | stop() ->
+ 1..| gen_server:call(channel,stop).
+ |
+ | %%%-Client interface functions------------------------------------
+ |
+ | alloc() ->
+ 1..| gen_server:call(channel,alloc).
+ |
+ | free(Channel) ->
+ 1..| gen_server:call(channel,{free,Channel}).
+ |
+ | %%%-gen_server callback functions---------------------------------
+ |
+ | init(_Arg) ->
+ 1..| {ok,channels()}.
+ |
+ | handle_call(stop,Client,Channels) ->
+ 1..| {stop,normal,ok,Channels};
+ |
+ | handle_call(alloc,Client,Channels) ->
+ 1..| {Ch,Channels2} = alloc(Channels),
+ 1..| {reply,{ok,Ch},Channels2};
+ |
+ | handle_call({free,Channel},Client,Channels) ->
+ 1..| Channels2 = free(Channel,Channels),
+ 1..| {reply,ok,Channels2}.
+ |
+ | terminate(_Reason,Channels) ->
+ 1..| ok.
+ |
+ | %%%-Internal functions--------------------------------------------
+ |
+ | channels() ->
+ 1..| [ch1,ch2,ch3].
+ |
+ | alloc([Channel|Channels]) ->
+ 1..| {Channel,Channels};
+ | alloc([]) ->
+ 0..| false.
+ |
+ | free(Channel,Channels) ->
+ 1..| [Channel|Channels].</pre>
+ </section>
+
+ <section>
+ <title>Conclusion</title>
+ <p>By looking at the results from the analyses, it can be deducted
+ that the test case does not cover the case when all channels are
+ allocated and <c>test.erl</c> should be extended accordingly. <br></br>
+
+ Incidentally, when the test case is corrected a bug in <c>channel</c>
+ should indeed be discovered.</p>
+ <p>When the Cover analysis is ready, Cover is stopped and all Cover
+ compiled modules are <seealso marker="#loading">unloaded</seealso>.
+ The code for <c>channel</c> is now loaded as usual from a
+ <c>.beam</c> file in the current path.</p>
+ <pre>
+13> <input>code:which(channel).</input>
+cover_compiled
+14> <input>cover:stop().</input>
+ok
+15> <input>code:which(channel).</input>
+"./channel.beam"</pre>
+ </section>
+ </section>
+
+ <section>
+ <title>Miscellaneous</title>
+
+ <section>
+ <title>Performance</title>
+ <p>Execution of code in Cover compiled modules is slower and more
+ memory consuming than for regularly compiled modules. As the Cover
+ database contains information about each executable line in each
+ Cover compiled module, performance decreases proportionally to
+ the size and number of the Cover compiled modules.</p>
+ </section>
+
+ <section>
+ <marker id="lines"></marker>
+ <title>Executable Lines</title>
+ <p>Cover uses the concept of <em>executable lines</em>, which is lines
+ of code containing an executable expression such as a matching or
+ a function call. A blank line or a line containing a comment,
+ function head or pattern in a <c>case</c>- or <c>receive</c>
+ statement is not executable.</p>
+ <p>In the example below, lines number 2,4,6,8 and 11 are executable
+ lines:</p>
+ <p></p>
+ <pre>
+1: is_loaded(Module,Compiled) ->
+2: case get_file(Module,Compiled) of
+3: {ok,File} ->
+4: case code:which(Module) of
+5: ?TAG ->
+6: {loaded,File};
+7: _ ->
+8: unloaded
+9: end;
+10: false ->
+11: false
+12: end.</pre>
+ </section>
+
+ <section>
+ <marker id="loading"></marker>
+ <title>Code Loading Mechanism</title>
+ <p>When a module is Cover compiled, it is also loaded using the normal
+ code loading mechanism of Erlang. This means that if a Cover
+ compiled module is re-loaded during a Cover session, for example
+ using <c>c(Module)</c>, it will no longer be Cover compiled.</p>
+ <p>Use <c>cover:is_compiled/1</c> or <c>code:which/1</c> to see if
+ a module is Cover compiled (and still loaded) or not.</p>
+ <p>When Cover is stopped, all Cover compiled modules are unloaded.</p>
+ </section>
+ </section>
+
+ <section>
+ <title>Using the Web Based User Interface to Cover</title>
+
+ <section>
+ <title>Introduction</title>
+ <p>To ease the use of Cover there is a web based user interface
+ to Cover called WebCover. WebCover is designed to be started
+ and used via WebTool. It is possible to Cover compile Erlang
+ modules and to generate printable Cover and Call analyses via
+ the web based user interface.</p>
+ </section>
+
+ <section>
+ <title>Start the Web Based User Interface to Cover</title>
+ <p>To start WebCover you can either start WebTool, point a
+ browser to the start page of WebTool and start WebCover from
+ there, or you can use the <c>start_webtool</c> script to start
+ Webtool, WebCover and a browser. See WebTool documentation for
+ further information.</p>
+ <p>Currently WebCover is only compatible
+ with Internet Explorer and Netscape Navigator 4.0 and higher.</p>
+ </section>
+
+ <section>
+ <title>Navigating WebCover</title>
+ <p>From the menu in the lefthand frame you can select the
+ <c>Nodes</c>, <c>Compile</c>, <c>Import</c> or <c>Result</c>
+ page.</p>
+ <p>From the <c>Nodes</c> page you can add remote nodes to
+ participate in the coverage analysis. Coverage data from all
+ involved nodes will then be merged during analysis.</p>
+ <p>From the <c>Compile</c> page you can Cover compile <c>.erl</c>
+ or <c>.beam</c> files.</p>
+ <p>From the <c>Import</c> page you can import coverage data from
+ a previous analysis. Imported data will then be merged with
+ the current coverage data. <em>Note</em> that it is only possible to
+ import files with the extension <c>.coverdata</c>.</p>
+ <p>From the <c>Result</c> page you can analyse, reset or export
+ coverage data.</p>
+ <p>Please follow the instructions on each page.</p>
+ </section>
+ </section>
+</chapter>
+