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