From e8ee3365eff0dbb1daa517447b46b6ea148ab949 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Wed, 3 Jan 2007 22:06:51 -0700 Subject: [PATCH] move figleaf2html and figleaf_htmlizer.py into our tree, for easier customization --- Makefile | 3 +- misc/figleaf2html | 3 + src/allmydata/util/figleaf_htmlizer.py | 211 +++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 misc/figleaf2html create mode 100644 src/allmydata/util/figleaf_htmlizer.py diff --git a/Makefile b/Makefile index 2c32b3b9..5f0d4927 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,8 @@ test-figleaf: $(PP) trial --reporter=bwverbose-figleaf $(TEST) figleaf-output: - figleaf2html -d coverage-html -x src/allmydata/test/figleaf.excludes + $(PP) python misc/figleaf2html -d coverage-html -x src/allmydata/test/figleaf.excludes + @echo "now point your browser at coverage-html/index.html" # after doing test-figleaf and figleaf-output, point your browser at # coverage-html/index.html diff --git a/misc/figleaf2html b/misc/figleaf2html new file mode 100644 index 00000000..9157f813 --- /dev/null +++ b/misc/figleaf2html @@ -0,0 +1,3 @@ +#! /usr/bin/env python +from allmydata.util import figleaf_htmlizer +figleaf_htmlizer.main() diff --git a/src/allmydata/util/figleaf_htmlizer.py b/src/allmydata/util/figleaf_htmlizer.py new file mode 100644 index 00000000..63a7960d --- /dev/null +++ b/src/allmydata/util/figleaf_htmlizer.py @@ -0,0 +1,211 @@ +#! /usr/bin/env python +import sys +import figleaf +from cPickle import load +import os +import re + +from optparse import OptionParser + +import logging +logging.basicConfig(level=logging.DEBUG) + +logger = logging.getLogger('figleaf.htmlizer') + +def read_exclude_patterns(f): + if not f: + return [] + exclude_patterns = [] + + fp = open(f) + for line in fp: + line = line.rstrip() + if line and not line.startswith('#'): + pattern = re.compile(line) + exclude_patterns.append(pattern) + + return exclude_patterns + +def report_as_html(coverage, directory, exclude_patterns=[], ): + ### now, output. + + keys = coverage.keys() + info_dict = {} + for k in keys: + skip = False + for pattern in exclude_patterns: + if pattern.search(k): + logger.debug('SKIPPING %s -- matches exclusion pattern' % k) + skip = True + break + + if skip: + continue + + if k.endswith('figleaf.py'): + continue + + if not k.startswith("/"): + continue + + try: + pyfile = open(k) +# print 'opened', k + except IOError: + logger.warning('CANNOT OPEN: %s' % k) + continue + + try: + lines = figleaf.get_lines(pyfile) + except KeyboardInterrupt: + raise + except Exception, e: + pyfile.close() + logger.warning('ERROR: %s %s' % (k, str(e))) + continue + + # ok, got all the info. now annotate file ==> html. + + covered = coverage[k] + n_covered = n_lines = 0 + + pyfile = open(k) + output = [] + for i, line in enumerate(pyfile): + is_covered = False + is_line = False + + i += 1 + + if i in covered: + is_covered = True + + n_covered += 1 + n_lines += 1 + elif i in lines: + is_line = True + + n_lines += 1 + + color = 'black' + if is_covered: + color = 'green' + elif is_line: + color = 'red' + + line = escape_html(line.rstrip()) + output.append('%4d. %s' % (color, i, line.rstrip())) + + try: + pcnt = n_covered * 100. / n_lines + except ZeroDivisionError: + pcnt = 100 + info_dict[k] = (n_lines, n_covered, pcnt) + + html_outfile = make_html_filename(os.path.basename(k)) + html_outfp = open(os.path.join(directory, html_outfile), 'w') + html_outfp.write('source file: %s
\n' % (k,)) + html_outfp.write('file stats: %d lines, %d executed: %.1f%% covered\n' % (n_lines, n_covered, pcnt)) + + html_outfp.write('
\n')
+		html_outfp.write("\n".join(output))
+		html_outfp.close()
+
+	### print a summary, too.
+
+	info_dict_items = info_dict.items()
+
+	def sort_by_pcnt(a, b):
+		a = a[1][2]
+		b = b[1][2]
+
+		return -cmp(a,b)
+	info_dict_items.sort(sort_by_pcnt)
+
+	summary_lines = sum([ v[0] for (k, v) in info_dict_items])
+	summary_cover = sum([ v[1] for (k, v) in info_dict_items])
+
+	summary_pcnt = 100
+	if summary_lines:
+		summary_pcnt = float(summary_cover) * 100. / float(summary_lines)
+
+
+	pcnts = [ float(v[1]) * 100. / float(v[0]) for (k, v) in info_dict_items if v[0] ]
+	pcnt_90 = [ x for x in pcnts if x >= 90 ]
+	pcnt_75 = [ x for x in pcnts if x >= 75 ]
+	pcnt_50 = [ x for x in pcnts if x >= 50 ]
+
+	index_fp = open('%s/index.html' % (directory,), 'w')
+	index_fp.write('figleaf code coverage report\n')
+	index_fp.write('

Summary

%d files total: %d files > 90%%, %d files > 75%%, %d files > 50%%

' % (len(pcnts), len(pcnt_90), len(pcnt_75), len(pcnt_50))) + index_fp.write('\n') + index_fp.write('\n' % (summary_lines, summary_cover, summary_pcnt,)) + + for filename, (n_lines, n_covered, percent_covered,) in info_dict_items: + html_outfile = make_html_filename(os.path.basename(filename)) + + index_fp.write('\n' % (html_outfile, filename, n_lines, n_covered, percent_covered,)) + + index_fp.write('
Filename# lines# covered% covered
totals:%d%d%.1f%%
%s%d%d%.1f
\n') + index_fp.close() + + logger.info('reported on %d file(s) total\n' % len(info_dict)) + return len(info_dict) + +def prepare_reportdir(dirname='html'): + try: + os.mkdir(dirname) + except OSError: # already exists + pass + +def make_html_filename(orig): + orig = os.path.splitdrive(orig)[1].replace('_', '__') + return orig.replace(os.path.sep, '_') + '.html' + +def escape_html(s): + s = s.replace("&", "&") + s = s.replace("<", "<") + s = s.replace(">", ">") + s = s.replace('"', """) + return s + +def main(): + ### + + option_parser = OptionParser() + + option_parser.add_option('-x', '--exclude-patterns', action="store", + dest="exclude_patterns_file", + help="file containing regexp patterns to exclude") + + option_parser.add_option('-d', '--output-directory', action='store', + dest="output_dir", + default = "html", + help="directory for HTML output") + + option_parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='Suppress all but error messages') + + (options, args) = option_parser.parse_args() + + if options.quiet: + logging.disable(logging.DEBUG) + + ### load + + if not args: + args = ['.figleaf'] + + coverage = {} + for filename in args: + logger.debug("loading coverage info from '%s'\n" % (filename,)) + d = figleaf.read_coverage(filename) + coverage = figleaf.combine_coverage(coverage, d) + + if not coverage: + logger.warning('EXITING -- no coverage info!\n') + sys.exit(-1) + + ### make directory + prepare_reportdir(options.output_dir) + report_as_html(coverage, options.output_dir, read_exclude_patterns(options.exclude_patterns_file)) + -- 2.45.2