#!/usr/bin/env python import os import glob import re import sys import optparse #File 'accidental-engraver.cc' #Lines executed:87.70% of 252 def summary (args): results = [] for f in args: str = open (f).read () m = re.search ("File '([^']+.cc)'\s*Lines executed:([0-9.]+)% of ([0-9]+)", str) if m and '/usr/lib' in m.group (1): continue if m: cov = float (m.group (2)) lines = int (m.group (3)) pain = lines * (100.0 - cov) file = m.group (1) tup = (pain, locals ().copy()) results.append(tup) results.sort () results.reverse() print 'files sorted by number of untested lines (decreasing)' print print '%5s (%6s): %s' % ('cov %', 'lines', 'file') print '----------------------------------------------' for (pain, d) in results: print '%(cov)5.2f (%(lines)6d): %(file)s' % d class Chunk: def __init__ (self, range, coverage_count, all_lines, file): assert coverage_count >= 0 assert type (range) == type (()) self.coverage_count = coverage_count self.range = range self.all_lines = all_lines self.file = file def length (self): return self.range[1] - self.range[0] def text (self): return ''.join ([l[2] for l in self.lines()]) def lines (self): return self.all_lines[self.range[0]: self.range[1]] def widen (self): self.range = (min (self.range[0] -1, 0), self.range[0] +1) def write (self): print 'chunk in', self.file for (c, n, l) in self.lines (): cov = '%d' % c if c == 0: cov = '#######' elif c < 0: cov = '' sys.stdout.write ('%8s:%8d:%s' % (cov, n, l)) def uncovered_score (self): return self.length () class SchemeChunk (Chunk): def uncovered_score (self): text = self.text () if (text.startswith ('(define ') and not text.startswith ('(define (')): return 0 if text.startswith ('(use-modules '): return 0 if (text.startswith ('(define-public ') and not text.startswith ('(define-public (')): return 0 return len ([l for (c,n,l) in self.lines() if (c == 0)]) def read_gcov (f): ls = [] in_lines = [l for l in open (f).readlines ()] (count_len, line_num_len) = tuple (map (len, in_lines[0].split (':')[:2])) for l in in_lines: c = l[:count_len].strip () l = l[count_len+1:] n = int (l[:line_num_len].strip ()) if n == 0: continue if '#' in c: c = 0 elif c == '-': c = -1 else: c = int (c) l = l[line_num_len+1:] ls.append ((c,n,l)) return ls def get_c_chunks (ls, file): chunks = [] chunk = [] last_c = -1 for (c, n, l) in ls: if not (c == last_c or c < 0 and l != '}\n'): if chunk and last_c >= 0: nums = [n-1 for (n, l) in chunk] chunks.append (Chunk ((min (nums), max (nums)+1), last_c, ls, file)) chunk = [] chunk.append ((n,l)) if c >= 0: last_c = c return chunks def get_scm_chunks (ls, file): chunks = [] chunk = [] def new_chunk (): if chunk: nums = [n-1 for (n, l) in chunk] chunks.append (SchemeChunk ((min (nums), max (nums)+1), max (last_c, 0), ls, file)) chunk[:] = [] last_c = -1 for (cov_count, line_number, line) in ls: if line.startswith ('('): new_chunk () last_c = -1 chunk.append ((line_number, line)) if cov_count >= 0: last_c = cov_count return chunks def widen_chunk (ch, ls): a -= 1 b += 1 return [(n, l) for (c, n, l) in ls[a:b]] def extract_chunks (file): try: ls = read_gcov (file) except IOError, s : print s return [] cs = [] if 'scm' in file: cs = get_scm_chunks (ls, file) else: cs = get_c_chunks (ls, file) return cs def filter_uncovered (chunks): def interesting (c): if c.coverage_count > 0: return False t = c.text() for stat in ('warning', 'error', 'print', 'scm_gc_mark'): if stat in t: return False return True return [c for c in chunks if interesting (c)] def main (): p = optparse.OptionParser (usage="usage coverage.py [options] files", description="") p.add_option ("--summary", action='store_true', default=False, dest="summary") p.add_option ("--hotspots", default=False, action='store_true', dest="hotspots") p.add_option ("--uncovered", default=False, action='store_true', dest="uncovered") (options, args) = p.parse_args () if options.summary: summary (['%s.gcov-summary' % s for s in args]) if options.uncovered or options.hotspots: chunks = [] for a in args: name = a if name.endswith ('scm'): name += '.cov' else: name += '.gcov' chunks += extract_chunks (name) if options.uncovered: chunks = filter_uncovered (chunks) chunks = [(c.uncovered_score (), c) for c in chunks if c.uncovered_score() > 0] elif options.hotspots: chunks = [((c.coverage_count, -c.length()), c) for c in chunks] chunks.sort () chunks.reverse () for (score, c) in chunks: c.write () if __name__ == '__main__': main ()