root/trunk/docsrc/code_coverage.dd

Revision 1396, 6.4 kB (checked in by walter, 2 years ago)

update docs

  • Property svn:eol-style set to native
Line 
1 Ddoc
2
3 $(D_S Code Coverage Analysis,
4
5 $(P A major part of the engineering of a professional software project
6 is creating a test suite for it. Without some sort of test suite,
7 it is impossible to know if the software works at all.
8 The D language has
9 many features to aid in the creation of test suites, such as
10 $(LINK2 unittest.html#unittest, unit tests) and
11 $(LINK2 dbc.html, contract programming).
12 But there's the issue of how thoroughly the test suite tests
13 the code.
14 The $(LINK2 http://www.digitalmars.com/ctg/trace.html, profiler)
15 can give valuable information on which functions were called, and
16 by whom. But to look inside a function, and determine which statements
17 were executed and which were not, requires a code coverage analyzer.
18 )
19
20 $(P A code coverage analyzer will help in these ways:)
21
22 $(OL
23 $(LI Expose code that is not exercised by the test suite.
24      Add test cases that will exercise it.)
25
26 $(LI Identify code that is unreachable. Unreachable code is often
27      the leftover result of program design changes.
28      Unreachable code should be removed,
29      as it can be very confusing to the maintenance programmer.)
30
31 $(LI It can be used to track down why a particular section of code
32      exists, as the test case that causes it to execute will
33      illuminate why.)
34
35 $(LI Since execution counts are given for each line, it is possible
36      to use the coverage analysis to reorder the basic blocks in
37      a function to minimize jmps in the most used path, thus
38      optimizing it.)
39 )
40
41 $(P Experience with code coverage analyzers show that they dramatically
42 reduce the number of bugs in shipping code.
43 But it isn't a panacea, a code coverage analyzer won't help with:)
44
45 $(OL
46 $(LI Identifying race conditions.)
47 $(LI Memory consumption problems.)
48 $(LI Pointer bugs.)
49 $(LI Verifying that the program got the correct result.)
50 )
51
52 $(P Code coverage analysers are available for many popular languages
53 such as C++, but they are often third party products that integrate
54 poorly with the compiler, and are often very expensive.
55 A big problem with third party products is, in order to instrument
56 the source code, they must include what is essentially a full blown
57 compiler front end for the same language. Not only is this an expensive
58 proposition, it often winds up out of step with the various compiler
59 vendors as their implementations change and as they evolve various extensions.
60 ($(LINK2 http://gcc.gnu.org/onlinedocs/gcc-3.0/gcc_8.html, gcov),
61 the Gnu coverage analyzer, is an exception as it is both free
62 and is integrated into gcc.)
63 )
64
65 $(P The D code coverage analyser is built in as part of the D compiler.
66 Therefore, it is always in perfect synchronization with the language
67 implementation. It's implemented by establishing a counter for each
68 line in each module compiled with the $(B -cov) switch. Code is inserted
69 at the beginning of each statement to increment the corresponding counter.
70 When the program finishes, a static destructor for std.cover collects all
71 the counters, merges it with the source files, and writes the reports out
72 to listing (.lst) files.)
73
74 $(P For example, consider the Sieve program:)
75 ----------------------
76 /* Eratosthenes Sieve prime number calculation. */
77
78 import std.stdio;
79
80 bit flags[8191];
81  
82 int main()
83 {   int     i, prime, k, count, iter;
84
85     writefln("10 iterations");
86     for (iter = 1; iter <= 10; iter++)
87     {   count = 0;
88     flags[] = true;
89     for (i = 0; i < flags.length; i++)
90     {   if (flags[i])
91         {   prime = i + i + 3;
92         k = i + prime;
93         while (k < flags.length)
94         {
95             flags[k] = false;
96             k += prime;
97         }
98         count += 1;
99         }
100     }
101     }
102     writefln("%d primes", count);
103     return 0;
104 }
105 ----------------------
106
107 $(P Compile and run it with:)
108
109 $(CONSOLE
110 dmd sieve -cov
111 sieve
112 )
113
114 $(P The output file will be created called $(TT sieve.lst), the contents of
115 which are:)
116
117 $(CONSOLE
118        |/* Eratosthenes Sieve prime number calculation. */
119        |
120        |import std.stdio;
121        |
122        |bit flags[8191];
123        |
124        |int main()
125       5|{   int     i, prime, k, count, iter;
126        |
127       1|    writefln("10 iterations");
128      22|    for (iter = 1; iter <= 10; iter++)
129      10|    {   count = 0;
130      10|        flags[] = true;
131  163840|        for (i = 0; i < flags.length; i++)
132   81910|        {   if (flags[i])
133   18990|            {   prime = i + i + 3;
134   18990|                k = i + prime;
135  168980|                while (k < flags.length)
136        |                {
137  149990|                    flags[k] = false;
138  149990|                    k += prime;
139        |                }
140   18990|                count += 1;
141        |            }
142        |        }
143        |    }
144       1|    writefln("%d primes", count);
145       1|    return 0;
146        |}
147 sieve.d is 100% covered
148 )
149
150 $(P The numbers to the left of the $(B |) are the execution counts for that
151 line. Lines that have no executable code are left blank.
152 Lines that have executable code, but were not executed, have a "0000000"
153 as the execution count.
154 At the end of the .lst file, the percent coverage is given.
155 )
156
157 $(P There are 3 lines with an exection count
158 of 1, these were each executed once. The declaration line for $(TT i, prime),
159 etc., has 5 because there are 5 declarations, and the initialization of
160 each declaration counts as one statement.)
161
162 $(P The first $(TT for) loop shows 22. This is the sum of the 3 parts
163 of the for header. If the for header is broken up into 3 lines, the
164 data is similarly divided:)
165
166 $(CONSOLE
167       1|    for (iter = 1;
168      11|         iter <= 10;
169      10|         iter++)
170 )
171
172 $(P which adds up to 22.)
173
174 $(P $(TT e1&amp;&amp;e2) and $(TT e1||e2) expressions conditionally
175 execute the rvalue $(TT e2).
176 Therefore, the rvalue is treated as a separate statement with its own
177 counter:)
178
179 $(CONSOLE
180         |void foo(int a, int b)
181         |{
182        5|   bar(a);
183        8|   if (a && b)
184        1|   bar(b);
185         |}
186 )
187
188 $(P By putting the rvalue on a separate line, this illuminates things:)
189
190 $(CONSOLE
191         |void foo(int a, int b)
192         |{
193        5|   bar(a);
194        5|   if (a &&
195        3|   b)
196        1|   bar(b);
197         |}
198 )
199
200 $(P Similarly, for the $(TT e?e1:e2) expressions, $(TT e1) and
201 $(TT e2) are treated as separate statements.)
202
203 <h3>Controlling the Coverage Analyser</h3>
204
205 $(P The behavior of the coverage analyser can be controlled through
206 the $(LINK2 phobos/std_cover.html, std.cover) module.)
207
208 $(P When the $(B -cov) switch is thrown, the version identifier
209 $(B D_Coverage) is defined.)
210
211 <h3>References</h3>
212
213 $(LINK2 http://en.wikipedia.org/wiki/Code_coverage, Wikipedia)
214
215 )
216
217 Macros:
218     TITLE=Code Coverage Analysis
219     WIKI=Dcover
220     RPAREN=)
Note: See TracBrowser for help on using the browser.