]> git.donarmstrong.com Git - lilypond.git/blob - buildscripts/coverage.py
Merge with master
[lilypond.git] / buildscripts / coverage.py
1 #!/bin/sh
2 import os
3 import glob
4 import re
5 import sys
6 import optparse
7
8 #File 'accidental-engraver.cc'
9 #Lines executed:87.70% of 252
10
11 def summary (args):
12     results = []
13     for f in args:
14         str = open (f).read ()
15         m = re.search ("File '([^']+.cc)'\s*Lines executed:([0-9.]+)% of ([0-9]+)", str)
16
17         if m and '/usr/lib' in m.group (1):
18             continue
19
20         if m:
21             cov = float (m.group (2))
22             lines = int (m.group (3))
23             pain = lines * (100.0 - cov)
24             file = m.group (1)
25             tup = (pain, locals ().copy())
26
27             results.append(tup)
28
29     results.sort ()
30     results.reverse()
31
32     print 'files sorted by number of untested lines (decreasing)'
33     print
34     print '%5s (%6s): %s' % ('cov %', 'lines', 'file')
35     print '----------------------------------------------'
36
37     for (pain, d) in results:
38         print '%(cov)5.2f (%(lines)6d): %(file)s' % d
39
40 class Chunk:
41     def __init__ (self, range, coverage_count, all_lines, file):
42         assert coverage_count >= 0
43         assert type (range) == type (())
44         
45         self.coverage_count = coverage_count
46         self.range = range
47         self.all_lines = all_lines
48         self.file = file
49
50     def length (self):
51         return self.range[1] - self.range[0]
52
53     def text (self):
54         return ''.join ([l[2] for l in self.lines()])
55         
56     def lines (self):
57         return self.all_lines[self.range[0]:
58                               self.range[1]]
59     def widen (self):
60         self.range = (min (self.range[0] -1, 0),
61                       self.range[0] +1)
62     def write (self):
63         print 'chunk in', self.file
64         for (c, n, l) in self.lines ():
65             cov = '%d' % c
66             if c == 0:
67                 cov = '#######'
68             elif c < 0:
69                 cov = ''
70             sys.stdout.write ('%8s:%8d:%s' % (cov, n, l))
71             
72     def uncovered_score (self):
73         return self.length ()
74     
75 class SchemeChunk (Chunk):
76     def uncovered_score (self):
77         text = self.text ()
78         if (text.startswith  ('(define ')
79             and not text.startswith ('(define (')):
80             return 0
81
82         if text.startswith  ('(use-modules '):
83             return 0
84
85         if (text.startswith  ('(define-public ')
86             and not text.startswith ('(define-public (')):
87             return 0
88
89         return len ([l for (c,n,l) in self.lines() if (c == 0)]) 
90
91 def read_gcov (f):
92     ls = []
93
94     in_lines = [l for l in open (f).readlines ()]
95     (count_len, line_num_len) = tuple (map (len, in_lines[0].split (':')[:2]))
96     
97     for l in in_lines:
98         c = l[:count_len].strip ()
99         l = l[count_len+1:]
100         n = int (l[:line_num_len].strip ())
101
102         if n == 0:
103             continue
104
105         if '#' in c:
106             c = 0
107         elif c == '-':
108             c = -1
109         else:
110             c = int (c)
111         
112         l = l[line_num_len+1:]
113
114         ls.append ((c,n,l))
115         
116     return ls
117
118 def get_c_chunks (ls, file):
119     chunks = []
120     chunk = []
121
122     last_c = -1
123     for (c, n, l) in ls:
124         if not (c == last_c or c < 0 and l != '}\n'):
125             if chunk and last_c >= 0:
126                 nums = [n-1 for (n, l) in chunk]
127                 chunks.append (Chunk ((min (nums), max (nums)+1),
128                                       last_c, ls, file))
129                 chunk = []
130
131         chunk.append ((n,l))
132         if c >= 0:
133             last_c = c
134             
135     return chunks
136
137 def get_scm_chunks (ls, file):
138     chunks = []
139     chunk = []
140
141     def new_chunk ():
142         if chunk:
143             nums = [n-1 for (n, l) in chunk]
144             chunks.append (SchemeChunk ((min (nums), max (nums)+1),
145                                         max (last_c, 0), ls, file))
146             chunk[:] = []
147         
148     last_c = -1
149     for (cov_count, line_number, line) in ls:
150         if line.startswith ('('):
151             new_chunk ()
152             last_c = -1
153         
154         chunk.append ((line_number, line))
155         if cov_count >= 0:
156             last_c = cov_count
157
158     return chunks
159
160 def widen_chunk (ch, ls):
161     a -= 1
162     b += 1
163
164     return [(n, l)  for (c, n, l) in ls[a:b]]
165     
166
167 def extract_chunks (file):
168     try:
169         ls = read_gcov (file)
170     except IOError, s :
171         print s
172         return []
173         
174     cs = []
175     if 'scm' in file:
176         cs = get_scm_chunks (ls, file)
177     else:
178         cs = get_c_chunks (ls, file)
179     return cs
180
181
182 def filter_uncovered (chunks):
183     def interesting (c):
184         if c.coverage_count > 0:
185             return False
186         
187         t = c.text()
188         for stat in  ('warning', 'error', 'print', 'scm_gc_mark'):
189             if stat in t:
190                 return False
191         return True
192    
193     return [c for c in chunks if interesting (c)]
194     
195
196 def main ():
197     p = optparse.OptionParser (usage="usage coverage.py [options] files",
198                                description="")
199     p.add_option ("--summary",
200                   action='store_true',
201                   default=False,
202                   dest="summary")
203     
204     p.add_option ("--hotspots",
205                   default=False,
206                   action='store_true',
207                   dest="hotspots")
208     
209     p.add_option ("--uncovered",
210                   default=False,
211                   action='store_true',
212                   dest="uncovered")
213
214     
215     (options, args) = p.parse_args ()
216     
217
218     if options.summary:
219         summary (['%s.gcov-summary' % s for s in args])
220
221     if options.uncovered or options.hotspots:
222         chunks = []
223         for a in args:
224             name = a
225             if name.endswith ('scm'):
226                 name += '.cov'
227             else:
228                 name += '.gcov'
229             
230             chunks += extract_chunks  (name)
231
232         if options.uncovered:
233             chunks = filter_uncovered (chunks)
234             chunks = [(c.uncovered_score (), c) for c in chunks if c.uncovered_score() > 0]
235         elif options.hotspots:
236             chunks = [((c.coverage_count, -c.length()), c) for c in chunks]
237             
238             
239         chunks.sort ()
240         chunks.reverse ()
241         for (score, c) in chunks:
242             c.write ()
243
244             
245         
246 if __name__ == '__main__':
247     main ()