def test_sum(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) n2 = Numbers(n_files=1, n_statements=10, n_missing=8) n3 = sum([n1, n2]) assert n3.n_files == 2 assert n3.n_statements == 210 assert n3.n_executed == 182 assert n3.n_missing == 28 assert round(abs(n3.pc_covered - 86.666666666), 7) == 0
def test_pc_covered_str(self): n0 = Numbers(n_files=1, n_statements=1000, n_missing=0) n1 = Numbers(n_files=1, n_statements=1000, n_missing=1) n999 = Numbers(n_files=1, n_statements=1000, n_missing=999) n1000 = Numbers(n_files=1, n_statements=1000, n_missing=1000) self.assertEqual(n0.pc_covered_str, "100") self.assertEqual(n1.pc_covered_str, "99") self.assertEqual(n999.pc_covered_str, "1") self.assertEqual(n1000.pc_covered_str, "0")
def test_sum(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) n2 = Numbers(n_files=1, n_statements=10, n_missing=8) n3 = sum([n1, n2]) assert n3.n_files == 2 assert n3.n_statements == 210 assert n3.n_executed == 182 assert n3.n_missing == 28 assert math.isclose(n3.pc_covered, 86.666666666)
def test_covered_ratio(self): n = Numbers(n_files=1, n_statements=200, n_missing=47) assert n.ratio_covered == (153, 200) n = Numbers( n_files=1, n_statements=200, n_missing=47, n_branches=10, n_missing_branches=3, n_partial_branches=1000, ) assert n.ratio_covered == (160, 210)
def test_sum(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) n2 = Numbers(n_files=1, n_statements=10, n_missing=8) n3 = sum([n1, n2]) self.assertEqual(n3.n_files, 2) self.assertEqual(n3.n_statements, 210) self.assertEqual(n3.n_executed, 182) self.assertEqual(n3.n_missing, 28) self.assertAlmostEqual(n3.pc_covered, 86.666666666)
def report(self, morfs, outfile=None): self.find_code_units(morfs) max_name = max([len(cu.name) for cu in self.code_units] + [5]) fmt_name = '%%- %ds ' % max_name fmt_err = '%s %s: %s\n' header = fmt_name % 'Name' + ' Stmts Miss' fmt_coverage = fmt_name + '%6d %6d' if self.branches: header += ' Branch BrMiss' fmt_coverage += ' %6d %6d' width100 = Numbers.pc_str_width() header += '%*s' % (width100 + 4, 'Cover') fmt_coverage += '%%%ds%%%%' % (width100 + 3, ) if self.config.show_missing: header += ' Missing' fmt_coverage += ' %s' rule = '-' * len(header) + '\n' header += '\n' fmt_coverage += '\n' if not outfile: outfile = sys.stdout outfile.write(header) outfile.write(rule) total = Numbers() for cu in self.code_units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers args = (cu.name, nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_missing_branches) args += (nums.pc_covered_str, ) if self.config.show_missing: args += (analysis.missing_formatted(), ) outfile.write(fmt_coverage % args) total += nums except KeyboardInterrupt: raise except: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] if typ is NotPython and not cu.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ('TOTAL', total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_missing_branches) args += (total.pc_covered_str, ) if self.config.show_missing: args += ('', ) outfile.write(fmt_coverage % args) return total.pc_covered
def test_pc_covered_str(self): # Numbers._precision is a global, which is bad. Numbers.set_precision(0) n0 = Numbers(n_files=1, n_statements=1000, n_missing=0) n1 = Numbers(n_files=1, n_statements=1000, n_missing=1) n999 = Numbers(n_files=1, n_statements=1000, n_missing=999) n1000 = Numbers(n_files=1, n_statements=1000, n_missing=1000) self.assertEqual(n0.pc_covered_str, "100") self.assertEqual(n1.pc_covered_str, "99") self.assertEqual(n999.pc_covered_str, "1") self.assertEqual(n1000.pc_covered_str, "0")
def test_pc_covered_str_precision(self): assert Numbers._precision == 0 Numbers.set_precision(1) n0 = Numbers(n_files=1, n_statements=10000, n_missing=0) n1 = Numbers(n_files=1, n_statements=10000, n_missing=1) n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999) n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000) self.assertEqual(n0.pc_covered_str, "100.0") self.assertEqual(n1.pc_covered_str, "99.9") self.assertEqual(n9999.pc_covered_str, "0.1") self.assertEqual(n10000.pc_covered_str, "0.0") Numbers.set_precision(0)
def total_for_files(data, files): total = Numbers(precision=3) for f in files: sel_summ = data["files"][f]["summary"] total += Numbers( n_statements=sel_summ["num_statements"], n_excluded=sel_summ["excluded_lines"], n_missing=sel_summ["missing_lines"], n_branches=sel_summ.get("num_branches", 0), n_partial_branches=sel_summ.get("num_partial_branches", 0), n_missing_branches=sel_summ.get("missing_branches", 0), ) return total
def report(self, morfs, omit_prefixes=None, outfile=None): """Writes a report summarizing coverage statistics per module.""" self.find_code_units(morfs, omit_prefixes) # Prepare the formatting strings max_name = max([len(cu.name) for cu in self.code_units] + [5]) fmt_name = "%%- %ds " % max_name fmt_err = "%s %s: %s\n" header = (fmt_name % "Name") + " Stmts Exec" fmt_coverage = fmt_name + "%6d %6d" if self.branches: header += " Branch BrExec" fmt_coverage += " %6d %6d" header += " Cover" fmt_coverage += " %5d%%" if self.show_missing: header += " Missing" fmt_coverage += " %s" rule = "-" * len(header) + "\n" header += "\n" fmt_coverage += "\n" if not outfile: outfile = sys.stdout # Write the header outfile.write(header) outfile.write(rule) total = Numbers() for cu in self.code_units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers args = (cu.name, nums.n_statements, nums.n_executed) if self.branches: args += (nums.n_branches, nums.n_executed_branches) args += (nums.pc_covered,) if self.show_missing: args += (analysis.missing_formatted(),) outfile.write(fmt_coverage % args) total += nums except KeyboardInterrupt: #pragma: no cover raise except: if not self.ignore_errors: typ, msg = sys.exc_info()[:2] outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ("TOTAL", total.n_statements, total.n_executed) if self.branches: args += (total.n_branches, total.n_executed_branches) args += (total.pc_covered,) if self.show_missing: args += ("",) outfile.write(fmt_coverage % args)
def _analyze(self, it): """Analyze a single morf or code unit. Returns an `Analysis` object. """ # All reporting comes through here, so do reporting initialization. self._init() Numbers.set_precision(self.config.precision) self._post_init() data = self.get_data() if not isinstance(it, FileReporter): it = self._get_file_reporter(it) return Analysis(data, it)
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. """ self.find_code_units(morfs) # Prepare the formatting strings fmt_err = "%s %s: %s\n" header = self.header() # fmt_coverage = self.fmt_coverage() rule = "-" * len(header) + "\n" if not outfile: outfile = sys.stdout # Write the header outfile.write(header) outfile.write(rule) total = Numbers() for cu in self.code_units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers args = (cu.name, nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_missing_branches) args += (nums.pc_covered_str, ) if self.config.show_missing: args += (analysis.missing_formatted(), ) outfile.write(self.fmt_coverage(nums.pc_covered) % args) total += nums except KeyboardInterrupt: # pragma: not covered raise except: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] if typ is NotPython and not cu.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_missing_branches) args += (total.pc_covered_str, ) if self.config.show_missing: args += ("", ) # outfile.write(self.fmt_coverage(total.pc_covered) % args) return total
def __init__(self, cov, config): super(HtmlReporter, self).__init__(cov, config) self.directory = None title = self.config.html_title if env.PY2: title = title.decode("utf8") self.template_globals = { 'escape': escape, 'pair': pair, 'title': title, '__url__': coverage.__url__, '__version__': coverage.__version__, } self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals) self.coverage = cov self.files = [] self.all_files_nums = [] self.has_arcs = self.coverage.data.has_arcs() self.status = HtmlStatus() self.extra_css = None self.totals = Numbers() self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
def _analyze(self, it): """Analyze a single morf or code unit. Returns an `Analysis` object. """ # All reporting comes through here, so do reporting initialization. self._init() Numbers.set_precision(self.config.precision) self._post_init() data = self.get_data() if not isinstance(it, FileReporter): it = self._get_file_reporter(it) return Analysis(data, it, self._file_mapper)
def report(self): self.find_file_reporters(None) total = Numbers() result = {"coverage": 0.0, "covered": {}, "format": 5, } for fr in self.file_reporters: try: analysis = self.coverage._analyze(fr) nums = analysis.numbers missing_nums = sorted(analysis.missing) with open(analysis.filename) as file: lines = file.read().splitlines() missing_lines = [ lines[l-1] for l in missing_nums ] result["covered"][fr.relative_filename()] = (nums.n_statements, nums.pc_covered/100.0, missing_nums, missing_lines) total += nums except KeyboardInterrupt: # pragma: not covered raise except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: raise result["coverage"] = total.pc_covered/100.0 return result
def dump_statistics(self, cov): """ Dump test run statistics :param cov: :return: """ from coverage.results import Numbers from coverage.report import Reporter from noc.tests.conftest import _stats as stats self.print("---[ Test session statistics ]------") cov.get_data() reporter = Reporter(cov, cov.config) totals = Numbers() for fr in reporter.find_file_reporters(None): analysis = cov._analyze(fr) totals += analysis.numbers n_passed = len(stats.get("passed", [])) n_skipped = len(stats.get("skipped", [])) n_error = len(stats.get("error", [])) n_failed = len(stats.get("failed", [])) if n_error or n_failed: status = "Failed" else: status = "Passed" self.print("Status : %s" % status) self.print("Tests Passed: : %s" % n_passed) self.print("Tests Skipped: : %s" % n_skipped) self.print("Tests Failed: : %s" % n_failed) self.print("Tests Error: : %s" % n_error) self.print("Coverage : %d%%" % totals.pc_covered) self.print("Coverage Statements : %s" % totals.n_statements) self.print("Coverage Missing : %s" % totals.n_missing) self.print("Coverage Excluded : %s" % totals.n_excluded)
def header(self): header = (self.fmt_name % "Name") + " Stmts Miss" if self.branches: header += " Branch BrMiss" width100 = Numbers.pc_str_width() header += "%*s" % (width100 + 4, "Cover") header += "\n" return header
def __init__(self, cov): self.coverage = cov self.config = self.coverage.config self.directory = self.config.html_dir self.skip_covered = self.config.html_skip_covered if self.skip_covered is None: self.skip_covered = self.config.skip_covered self.skip_empty = self.config.html_skip_empty if self.skip_empty is None: self.skip_empty = self.config.skip_empty self.skipped_covered_count = 0 self.skipped_empty_count = 0 title = self.config.html_title if self.config.extra_css: self.extra_css = os.path.basename(self.config.extra_css) else: self.extra_css = None self.data = self.coverage.get_data() self.has_arcs = self.data.has_arcs() self.file_summaries = [] self.all_files_nums = [] self.incr = IncrementalChecker(self.directory) self.datagen = HtmlDataGeneration(self.coverage) self.totals = Numbers(precision=self.config.precision) self.directory_was_empty = False self.template_globals = { # Functions available in the templates. 'escape': escape, 'pair': pair, 'len': len, # Constants for this report. '__url__': coverage.__url__, '__version__': coverage.__version__, 'title': title, 'time_stamp': format_local_datetime(datetime.datetime.now()), 'extra_css': self.extra_css, 'has_arcs': self.has_arcs, 'show_contexts': self.config.show_contexts, # Constants for all reports. # These css classes determine which lines are highlighted by default. 'category': { 'exc': 'exc show_exc', 'mis': 'mis show_mis', 'par': 'par run show_par', 'run': 'run', } } self.pyfile_html_source = read_data("pyfile.html") self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
def __init__(self, coverage): self.coverage = coverage self.config = self.coverage.config self.branches = coverage.get_data().has_arcs() self.outfile = None self.fr_analysis = [] self.skipped_count = 0 self.total = Numbers() self.fmt_err = u"%s %s: %s"
def report(self, morfs, outfile = None): self.find_code_units(morfs) max_name = max([ len(cu.name) for cu in self.code_units ] + [5]) fmt_name = '%%- %ds ' % max_name fmt_err = '%s %s: %s\n' header = fmt_name % 'Name' + ' Stmts Miss' fmt_coverage = fmt_name + '%6d %6d' if self.branches: header += ' Branch BrMiss' fmt_coverage += ' %6d %6d' width100 = Numbers.pc_str_width() header += '%*s' % (width100 + 4, 'Cover') fmt_coverage += '%%%ds%%%%' % (width100 + 3,) if self.config.show_missing: header += ' Missing' fmt_coverage += ' %s' rule = '-' * len(header) + '\n' header += '\n' fmt_coverage += '\n' if not outfile: outfile = sys.stdout outfile.write(header) outfile.write(rule) total = Numbers() for cu in self.code_units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers args = (cu.name, nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_missing_branches) args += (nums.pc_covered_str,) if self.config.show_missing: args += (analysis.missing_formatted(),) outfile.write(fmt_coverage % args) total += nums except KeyboardInterrupt: raise except: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] if typ is NotPython and not cu.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ('TOTAL', total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_missing_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ('',) outfile.write(fmt_coverage % args) return total.pc_covered
def test_pc_covered_str_precision(self): # Numbers._precision is a global, which is bad. Numbers.set_precision(1) n0 = Numbers(n_files=1, n_statements=10000, n_missing=0) n1 = Numbers(n_files=1, n_statements=10000, n_missing=1) n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999) n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000) self.assertEqual(n0.pc_covered_str, "100.0") self.assertEqual(n1.pc_covered_str, "99.9") self.assertEqual(n9999.pc_covered_str, "0.1") self.assertEqual(n10000.pc_covered_str, "0.0") Numbers.set_precision(0)
def test_pc_covered_str_precision(self): # Numbers._precision is a global, which is bad. Numbers.set_precision(1) n0 = Numbers(n_files=1, n_statements=10000, n_missing=0) n1 = Numbers(n_files=1, n_statements=10000, n_missing=1) n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999) n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000) assert n0.pc_covered_str == "100.0" assert n1.pc_covered_str == "99.9" assert n9999.pc_covered_str == "0.1" assert n10000.pc_covered_str == "0.0" Numbers.set_precision(0)
def report(self, morfs, outfile=None): if hasattr(self, 'find_code_units'): self.find_code_units(morfs) else: self.find_file_reporters(morfs) total = Numbers() if hasattr(self, 'code_units'): units = self.code_units else: units = self.file_reporters for cu in units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers total += nums except KeyboardInterrupt: raise except Exception: if self.config.ignore_errors: continue err = sys.exc_info() typ, msg = err[:2] if typ is NotPython and not cu.should_be_python(): continue test_id = cu.name details = convert_error_to_string(err) self.messages.testStarted(test_id, flowId=test_id) self.messages.testFailed( test_id, message="Coverage analysis failed", details=details, flowId=test_id) self.messages.testFinished(test_id, flowId=test_id) if total.n_files > 0: covered = total.n_executed total_statements = total.n_statements if self.branches: covered += total.n_executed_branches total_statements += total.n_branches self.messages.buildStatisticLinesCovered(covered) self.messages.buildStatisticTotalLines(total_statements) self.messages.buildStatisticLinesUncovered( total_statements - covered)
def __init__(self, cov): self.coverage = cov self.config = self.coverage.config self.directory = self.config.html_dir title = self.config.html_title if env.PY2: title = title.decode("utf8") if self.config.extra_css: self.extra_css = os.path.basename(self.config.extra_css) else: self.extra_css = None self.data = self.coverage.get_data() self.has_arcs = self.data.has_arcs() self.file_summaries = [] self.all_files_nums = [] self.incr = IncrementalChecker(self.directory) self.datagen = HtmlDataGeneration(self.coverage) self.totals = Numbers() self.template_globals = { # Functions available in the templates. 'escape': escape, 'pair': pair, 'len': len, # Constants for this report. '__url__': coverage.__url__, '__version__': coverage.__version__, 'title': title, 'time_stamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M'), 'extra_css': self.extra_css, 'has_arcs': self.has_arcs, 'show_contexts': self.config.show_contexts, # Constants for all reports. # These css classes determine which lines are highlighted by default. 'category': { 'exc': 'exc', 'mis': 'mis', 'par': 'par run hide_run', 'run': 'run hide_run', } } self.pyfile_html_source = read_data("pyfile.html") self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
def __init__(self, cov, code_unit): self.coverage = cov self.code_unit = code_unit self.filename = self.code_unit.filename ext = os.path.splitext(self.filename)[1] source = None if os.path.exists(self.filename): try: self.source = read_file(self.filename) except: _, err, _ = sys.exc_info() raise NoSource("No source for code: %r: %s" % (self.filename, err)) if self.source is None: raise NoSource("No source for code: %r" % self.filename) self.parser = DjangoTemplateCodeParser( text=source, filename=self.filename, exclude=self.coverage._exclude_regex('exclude')) self.statements, self.excluded = self.parser.parse_source() # Identify missing statements. executed = self.coverage.data.executed_lines(self.filename) self.missing = sorted(set(self.statements) - set(executed)) if self.coverage.data.has_arcs(): self.no_branch = self.parser.lines_matching( join_regex(self.coverage.config.partial_list), join_regex(self.coverage.config.partial_always_list)) n_branches = self.total_branches() mba = self.missing_branch_arcs() n_missing_branches = sum( [len(v) for k, v in mba.items() if k not in self.missing]) else: n_branches = n_missing_branches = 0 self.no_branch = set() self.numbers = Numbers( n_files=1, n_statements=len(self.statements), n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, n_missing_branches=n_missing_branches, )
def __init__(self, cov, config): super(HtmlReporter, self).__init__(cov, config) self.directory = None self.template_globals = { 'escape': escape, 'title': self.config.html_title, '__url__': coverage.__url__, '__version__': coverage.__version__ } self.source_tmpl = Templite(data('pyfile.html'), self.template_globals) self.coverage = cov self.files = [] self.arcs = self.coverage.data.has_arcs() self.status = HtmlStatus() self.extra_css = None self.totals = Numbers()
def fmt_coverage(self, perc): fmt_coverage = self.fmt_name + "%6d %6d" if self.branches: fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() color = green if perc < 80: color = blue if perc < 50: color = red fmt_coverage += color("%%%ds%%%%" % (width100 + 3, )) if self.config.show_missing: fmt_coverage += " %s" fmt_coverage += "\n" return fmt_coverage
def fmt_coverage(self, perc): fmt_coverage = self.fmt_name + "%6d %6d" if self.branches: fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() color = green if perc < 80: color = blue if perc < 50: color = red fmt_coverage += color("%%%ds%%%%" % (width100 + 3,)) if self.config.show_missing: fmt_coverage += " %s" fmt_coverage += "\n" return fmt_coverage
def __init__(self, cov, config): super(HtmlReporter, self).__init__(cov, config) self.directory = None title = self.config.html_title try: self.inline_styles = self.config.inline_styles self.not_inline_styles = False # reading the css stylesheet f = open( os.path.join(os.path.dirname(__file__), *["htmlfiles", "style.css"]), "rb") self.css_styles = f.read().decode('utf-8').strip() f.flush() f.close() except Exception as e: print(e) self.inline_styles = False self.not_inline_styles = True self.css_styles = None if env.PY2: title = title.decode("utf8") self.template_globals = { 'escape': escape, 'pair': pair, 'title': title, '__url__': coverage.__url__, '__version__': coverage.__version__, } self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals) self.data = cov.get_data() self.files = [] self.all_files_nums = [] self.has_arcs = self.data.has_arcs() self.status = HtmlStatus() self.extra_css = None self.totals = Numbers() self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
def teamcity_report(data_file, config_file=None): """ Generate teamcity coverage report. :param data_file: Coverage data file :param config_file: Config file for coverage """ import coverage from teamcity import messages service_messages = messages.TeamcityServiceMessages() cov = coverage.Coverage(data_file, config_file=config_file) cov.load() total_nums = Numbers() for path in cov.data.measured_files(): if path.endswith("py"): analysis = cov._analyze(path) total_nums += analysis.numbers service_messages.buildStatisticLinesCovered(total_nums.n_executed) service_messages.buildStatisticTotalLines(total_nums.n_statements)
def read(self, directory): """Read the last status in `directory`.""" usable = False try: status_file = os.path.join(directory, self.STATUS_FILE) with open(status_file, "r") as fstatus: status = json.load(fstatus) except (IOError, ValueError): usable = False else: usable = True if status['format'] != self.STATUS_FORMAT: usable = False elif status['version'] != coverage.__version__: usable = False if usable: self.files = {} for filename, fileinfo in iitems(status['files']): fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums']) self.files[filename] = fileinfo self.settings = status['settings'] else: self.reset()
def read(self): """Read the information we stored last time.""" usable = False try: status_file = os.path.join(self.directory, self.STATUS_FILE) with open(status_file) as fstatus: status = json.load(fstatus) except (OSError, ValueError): usable = False else: usable = True if status['format'] != self.STATUS_FORMAT: usable = False elif status['version'] != coverage.__version__: usable = False if usable: self.files = {} for filename, fileinfo in status['files'].items(): fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums']) self.files[filename] = fileinfo self.globals = status['globals'] else: self.reset()
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. """ self.find_file_reporters(morfs) # Prepare the formatting strings max_name = max([len(fr.relative_filename()) for fr in self.file_reporters] + [5]) fmt_name = "%%- %ds " % max_name fmt_err = "%s %s: %s\n" header = (fmt_name % "Name") + " Stmts Miss" fmt_coverage = fmt_name + "%6d %6d" if self.branches: header += " Branch BrPart" fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() header += "%*s" % (width100+4, "Cover") fmt_coverage += "%%%ds%%%%" % (width100+3,) if self.config.show_missing: header += " Missing" fmt_coverage += " %s" rule = "-" * len(header) + "\n" header += "\n" fmt_coverage += "\n" if not outfile: outfile = sys.stdout # Write the header outfile.write(header) outfile.write(rule) total = Numbers() for fr in self.file_reporters: try: analysis = self.coverage._analyze(fr) nums = analysis.numbers if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: continue args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str,) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd,) outfile.write(fmt_coverage % args) total += nums except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (fr.relative_filename(), typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ("",) outfile.write(fmt_coverage % args) if not total.n_files: raise CoverageException("No data to report.") return total.n_statements and total.pc_covered
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. It must be opened for native strings (bytes on Python 2, Unicode on Python 3). """ file_reporters = self.find_file_reporters(morfs) # Prepare the formatting strings, header, and column sorting. max_name = max([len(fr.relative_filename()) for fr in file_reporters] + [5]) fmt_name = u"%%- %ds " % max_name fmt_err = u"%s %s: %s" fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." header = (fmt_name % "Name") + u" Stmts Miss" fmt_coverage = fmt_name + u"%6d %6d" if self.branches: header += u" Branch BrPart" fmt_coverage += u" %6d %6d" width100 = Numbers.pc_str_width() header += u"%*s" % (width100+4, "Cover") fmt_coverage += u"%%%ds%%%%" % (width100+3,) if self.config.show_missing: header += u" Missing" fmt_coverage += u" %s" rule = u"-" * len(header) column_order = dict(name=0, stmts=1, miss=2, cover=-1) if self.branches: column_order.update(dict(branch=3, brpart=4)) if outfile is None: outfile = sys.stdout def writeout(line): """Write a line to the output, adding a newline.""" if env.PY2: line = line.encode(output_encoding()) outfile.write(line.rstrip()) outfile.write("\n") # Write the header writeout(header) writeout(rule) # `lines` is a list of pairs, (line text, line values). The line text # is a string that will be printed, and line values is a tuple of # sortable values. lines = [] total = Numbers() skipped_count = 0 for fr in file_reporters: try: analysis = self.coverage._analyze(fr) nums = analysis.numbers total += nums if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: skipped_count += 1 continue args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str,) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd,) text = fmt_coverage % args # Add numeric percent coverage so that sorting makes sense. args += (nums.pc_covered,) lines.append((text, args)) except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) # Sort the lines and write them out. if getattr(self.config, 'sort', None): position = column_order.get(self.config.sort.lower()) if position is None: raise CoverageException("Invalid sorting option: {0!r}".format(self.config.sort)) lines.sort(key=lambda l: (l[1][position], l[0])) for line in lines: writeout(line[0]) # Write a TOTAl line if we had more than one file. if total.n_files > 1: writeout(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ("",) writeout(fmt_coverage % args) # Write other final lines. if not total.n_files and not skipped_count: raise CoverageException("No data to report.") if self.config.skip_covered and skipped_count: writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) return total.n_statements and total.pc_covered
def test_basic(self): n1 = Numbers(n_files=1, n_statements=200, n_missing=20) self.assertEqual(n1.n_statements, 200) self.assertEqual(n1.n_executed, 180) self.assertEqual(n1.n_missing, 20) self.assertEqual(n1.pc_covered, 90)
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. It must be opened for native strings (bytes on Python 2, Unicode on Python 3). """ if outfile is None: outfile = sys.stdout def writeout(line): """Write a line to the output, adding a newline.""" if env.PY2: line = line.encode(output_encoding()) outfile.write(line.rstrip()) outfile.write("\n") fr_analysis = [] skipped_count = 0 total = Numbers() fmt_err = u"%s %s: %s" for fr in self.find_file_reporters(morfs): try: analysis = self.coverage._analyze(fr) nums = analysis.numbers total += nums if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: skipped_count += 1 continue fr_analysis.append((fr, analysis)) except StopEverything: # Don't report this on single files, it's a systemic problem. raise except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if issubclass(typ, NotPython) and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) # Prepare the formatting strings, header, and column sorting. max_name = max( [len(fr.relative_filename()) for (fr, analysis) in fr_analysis] + [5]) fmt_name = u"%%- %ds " % max_name fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." header = (fmt_name % "Name") + u" Stmts Miss" fmt_coverage = fmt_name + u"%6d %6d" if self.branches: header += u" Branch BrPart" fmt_coverage += u" %6d %6d" width100 = Numbers.pc_str_width() header += u"%*s" % (width100 + 4, "Cover") fmt_coverage += u"%%%ds%%%%" % (width100 + 3, ) if self.config.show_missing: header += u" Missing" fmt_coverage += u" %s" rule = u"-" * len(header) column_order = dict(name=0, stmts=1, miss=2, cover=-1) if self.branches: column_order.update(dict(branch=3, brpart=4)) # Write the header writeout(header) writeout(rule) # `lines` is a list of pairs, (line text, line values). The line text # is a string that will be printed, and line values is a tuple of # sortable values. lines = [] for (fr, analysis) in fr_analysis: try: nums = analysis.numbers args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str, ) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd, ) text = fmt_coverage % args # Add numeric percent coverage so that sorting makes sense. args += (nums.pc_covered, ) lines.append((text, args)) except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) # Sort the lines and write them out. if getattr(self.config, 'sort', None): position = column_order.get(self.config.sort.lower()) if position is None: raise CoverageException("Invalid sorting option: {0!r}".format( self.config.sort)) lines.sort(key=lambda l: (l[1][position], l[0])) for line in lines: writeout(line[0]) # Write a TOTAl line if we had more than one file. if total.n_files > 1: writeout(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str, ) if self.config.show_missing: args += ("", ) writeout(fmt_coverage % args) # Write other final lines. if not total.n_files and not skipped_count: raise CoverageException("No data to report.") if self.config.skip_covered and skipped_count: writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) return total.n_statements and total.pc_covered
def _init(self): """Set all the initial state. This is called by the public methods to initialize state. This lets us construct a :class:`Coverage` object, then tweak its state before this function is called. """ if self._inited: return # Create and configure the debugging controller. COVERAGE_DEBUG_FILE # is an environment variable, the name of a file to append debug logs # to. if self._debug_file is None: debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE") if debug_file_name: self._debug_file = open(debug_file_name, "a") else: self._debug_file = sys.stderr self.debug = DebugControl(self.config.debug, self._debug_file) # Load plugins self.plugins = Plugins.load_plugins(self.config.plugins, self.config, self.debug) # _exclude_re is a dict that maps exclusion list names to compiled # regexes. self._exclude_re = {} self._exclude_regex_stale() files.set_relative_directory() # The source argument can be directories or package names. self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.exists(src): self.source.append(files.canonical_filename(src)) else: self.source_pkgs.append(src) self.omit = prep_patterns(self.config.omit) self.include = prep_patterns(self.config.include) concurrency = self.config.concurrency if concurrency == "multiprocessing": patch_multiprocessing() concurrency = None self.collector = Collector( should_trace=self._should_trace, check_include=self._check_include_omit_etc, timid=self.config.timid, branch=self.config.branch, warn=self._warn, concurrency=concurrency, ) # Early warning if we aren't going to be able to support plugins. if self.plugins.file_tracers and not self.collector.supports_plugins: self._warn( "Plugin file tracers (%s) aren't supported with %s" % ( ", ".join( plugin._coverage_plugin_name for plugin in self.plugins.file_tracers ), self.collector.tracer_name(), ) ) for plugin in self.plugins.file_tracers: plugin._coverage_enabled = False # Suffixes are a bit tricky. We want to use the data suffix only when # collecting data, not when combining data. So we save it as # `self.run_suffix` now, and promote it to `self.data_suffix` if we # find that we are collecting data later. if self._data_suffix or self.config.parallel: if not isinstance(self._data_suffix, string_class): # if data_suffix=True, use .machinename.pid.random self._data_suffix = True else: self._data_suffix = None self.data_suffix = None self.run_suffix = self._data_suffix # Create the data file. We do this at construction time so that the # data file will be written into the directory where the process # started rather than wherever the process eventually chdir'd to. self.data = CoverageData(debug=self.debug) self.data_files = CoverageDataFiles(basename=self.config.data_file, warn=self._warn) # The directories for files considered "installed with the interpreter". self.pylib_dirs = set() if not self.config.cover_pylib: # Look at where some standard modules are located. That's the # indication for "installed with the interpreter". In some # environments (virtualenv, for example), these modules may be # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. for m in (atexit, inspect, os, platform, re, _structseq, traceback): if m is not None and hasattr(m, "__file__"): self.pylib_dirs.add(self._canonical_dir(m)) if _structseq and not hasattr(_structseq, '__file__'): # PyPy 2.4 has no __file__ in the builtin modules, but the code # objects still have the file names. So dig into one to find # the path to exclude. structseq_new = _structseq.structseq_new try: structseq_file = structseq_new.func_code.co_filename except AttributeError: structseq_file = structseq_new.__code__.co_filename self.pylib_dirs.add(self._canonical_dir(structseq_file)) # To avoid tracing the coverage.py code itself, we skip anything # located where we are. self.cover_dirs = [self._canonical_dir(__file__)] if env.TESTING: # When testing, we use PyContracts, which should be considered # part of coverage.py, and it uses six. Exclude those directories # just as we exclude ourselves. import contracts, six for mod in [contracts, six]: self.cover_dirs.append(self._canonical_dir(mod)) # Set the reporting precision. Numbers.set_precision(self.config.precision) atexit.register(self._atexit) self._inited = True # Create the matchers we need for _should_trace if self.source or self.source_pkgs: self.source_match = TreeMatcher(self.source) self.source_pkgs_match = ModuleMatcher(self.source_pkgs) else: if self.cover_dirs: self.cover_match = TreeMatcher(self.cover_dirs) if self.pylib_dirs: self.pylib_match = TreeMatcher(self.pylib_dirs) if self.include: self.include_match = FnmatchMatcher(self.include) if self.omit: self.omit_match = FnmatchMatcher(self.omit) # The user may want to debug things, show info if desired. wrote_any = False if self.debug.should('config'): config_info = sorted(self.config.__dict__.items()) self.debug.write_formatted_info("config", config_info) wrote_any = True if self.debug.should('sys'): self.debug.write_formatted_info("sys", self.sys_info()) for plugin in self.plugins: header = "sys: " + plugin._coverage_plugin_name info = plugin.sys_info() self.debug.write_formatted_info(header, info) wrote_any = True if wrote_any: self.debug.write_formatted_info("end", ())
def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to create the final file name. If `data_suffix` is simply True, then a suffix is created with the machine and process identity included. `cover_pylib` is a boolean determining whether Python code installed with the Python interpreter is measured. This includes the Python standard library and any packages installed with the interpreter. If `auto_data` is true, then any existing data file will be read when coverage measurement starts, and data will be saved automatically when measurement stops. If `timid` is true, then a slower and simpler trace function will be used. This is important for some environments where manipulation of tracing functions breaks the faster trace function. If `branch` is true, then branch coverage will be measured in addition to the usual statement coverage. `config_file` determines what config file to read. If it is a string, it is the name of the config file to read. If it is True, then a standard file is read (".coveragerc"). If it is False, then no file is read. `source` is a list of file paths or package names. Only code located in the trees indicated by the file paths or package names will be measured. `include` and `omit` are lists of filename patterns. Files that match `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. """ from coverage import __version__ # A record of all the warnings that have been issued. self._warnings = [] # Build our configuration from a number of sources: # 1: defaults: self.config = CoverageConfig() # 2: from the coveragerc file: if config_file: if config_file is True: config_file = ".coveragerc" try: self.config.from_file(config_file) except ValueError: _, err, _ = sys.exc_info() raise CoverageException( "Couldn't read config file %s: %s" % (config_file, err) ) # 3: from environment variables: self.config.from_environment('COVERAGE_OPTIONS') env_data_file = os.environ.get('COVERAGE_FILE') if env_data_file: self.config.data_file = env_data_file # 4: from constructor arguments: if isinstance(omit, string_class): omit = [omit] if isinstance(include, string_class): include = [include] self.config.from_args( data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, omit=omit, include=include ) self.auto_data = auto_data self.atexit_registered = False # _exclude_re is a dict mapping exclusion list names to compiled # regexes. self._exclude_re = {} self._exclude_regex_stale() self.file_locator = FileLocator() # The source argument can be directories or package names. self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.exists(src): self.source.append(self.file_locator.canonical_filename(src)) else: self.source_pkgs.append(src) self.omit = self._prep_patterns(self.config.omit) self.include = self._prep_patterns(self.config.include) self.collector = Collector( self._should_trace, timid=self.config.timid, branch=self.config.branch, warn=self._warn ) # Suffixes are a bit tricky. We want to use the data suffix only when # collecting data, not when combining data. So we save it as # `self.run_suffix` now, and promote it to `self.data_suffix` if we # find that we are collecting data later. if data_suffix or self.config.parallel: if not isinstance(data_suffix, string_class): # if data_suffix=True, use .machinename.pid.random data_suffix = True else: data_suffix = None self.data_suffix = None self.run_suffix = data_suffix # Create the data file. We do this at construction time so that the # data file will be written into the directory where the process # started rather than wherever the process eventually chdir'd to. self.data = CoverageData( basename=self.config.data_file, collector="coverage v%s" % __version__ ) # The dirs for files considered "installed with the interpreter". self.pylib_dirs = [] if not self.config.cover_pylib: # Look at where some standard modules are located. That's the # indication for "installed with the interpreter". In some # environments (virtualenv, for centralfitestoque), these modules may be # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. for m in (atexit, os, random, socket): if hasattr(m, "__file__"): m_dir = self._canonical_dir(m.__file__) if m_dir not in self.pylib_dirs: self.pylib_dirs.append(m_dir) # To avoid tracing the coverage code itself, we skip anything located # where we are. self.cover_dir = self._canonical_dir(__file__) # The matchers for _should_trace, created when tracing starts. self.source_match = None self.pylib_match = self.cover_match = None self.include_match = self.omit_match = None # Only _harvest_data once per measurement cycle. self._harvested = False # Set the reporting precision. Numbers.set_precision(self.config.precision) # When tearing down the coverage object, modules can become None. # Saving the modules as object attributes avoids problems, but it is # quite ad-hoc which modules need to be saved and which references # need to use the object attributes. self.socket = socket self.os = os self.random = random
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. """ self.find_code_units(morfs) # Prepare the formatting strings max_name = max([len(cu.name) for cu in self.code_units] + [5]) fmt_name = "%%- %ds " % max_name fmt_err = "%s %s: %s\n" header = (fmt_name % "Name") + " Stmts Miss" fmt_coverage = fmt_name + "%6d %6d" if self.branches: header += " Branch BrMiss" fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() header += "%*s" % (width100+4, "Cover") fmt_coverage += "%%%ds%%%%" % (width100+3,) if self.config.show_missing: header += " Missing" fmt_coverage += " %s" rule = "-" * len(header) + "\n" header += "\n" fmt_coverage += "\n" if not outfile: outfile = sys.stdout # Write the header outfile.write(header) outfile.write(rule) total = Numbers() for cu in self.code_units: try: analysis = self.coverage._analyze(cu) nums = analysis.numbers args = (cu.name, nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_missing_branches) args += (nums.pc_covered_str,) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd,) outfile.write(fmt_coverage % args) total += nums except KeyboardInterrupt: # pragma: not covered raise except: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] if typ is NotPython and not cu.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (cu.name, typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_missing_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ("",) outfile.write(fmt_coverage % args) return total.pc_covered
def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, auto_data=False, timid=None, branch=None, config_file=True, source=None, omit=None, include=None, debug=None, debug_file=None, concurrency=None, plugins=None): """ `data_file` is the base name of the data file to use, defaulting to ".coverage". `data_suffix` is appended (with a dot) to `data_file` to create the final file name. If `data_suffix` is simply True, then a suffix is created with the machine and process identity included. `cover_pylib` is a boolean determining whether Python code installed with the Python interpreter is measured. This includes the Python standard library and any packages installed with the interpreter. If `auto_data` is true, then any existing data file will be read when coverage measurement starts, and data will be saved automatically when measurement stops. If `timid` is true, then a slower and simpler trace function will be used. This is important for some environments where manipulation of tracing functions breaks the faster trace function. If `branch` is true, then branch coverage will be measured in addition to the usual statement coverage. `config_file` determines what config file to read. If it is a string, it is the name of the config file to read. If it is True, then a standard file is read (".coveragerc"). If it is False, then no file is read. `source` is a list of file paths or package names. Only code located in the trees indicated by the file paths or package names will be measured. `include` and `omit` are lists of filename patterns. Files that match `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. `debug` is a list of strings indicating what debugging information is desired. `debug_file` is the file to write debug messages to, defaulting to stderr. `concurrency` is a string indicating the concurrency library being used in the measured code. Without this, coverage.py will get incorrect results. Valid strings are "greenlet", "eventlet", "gevent", or "thread" (the default). `plugins` TODO. """ from coverage import __version__ # A record of all the warnings that have been issued. self._warnings = [] # Build our configuration from a number of sources: # 1: defaults: self.config = CoverageConfig() # 2: from the .coveragerc or setup.cfg file: if config_file: did_read_rc = should_read_setupcfg = False if config_file is True: config_file = ".coveragerc" should_read_setupcfg = True try: did_read_rc = self.config.from_file(config_file) except ValueError as err: raise CoverageException( "Couldn't read config file %s: %s" % (config_file, err) ) if not did_read_rc and should_read_setupcfg: self.config.from_file("setup.cfg", section_prefix="coverage:") # 3: from environment variables: self.config.from_environment('COVERAGE_OPTIONS') env_data_file = os.environ.get('COVERAGE_FILE') if env_data_file: self.config.data_file = env_data_file # 4: from constructor arguments: self.config.from_args( data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, omit=omit, include=include, debug=debug, concurrency=concurrency, plugins=plugins, ) # Create and configure the debugging controller. self.debug = DebugControl(self.config.debug, debug_file or sys.stderr) # Load plugins self.plugins = Plugins.load_plugins(self.config.plugins, self.config) self.trace_judges = [] for plugin in self.plugins: if plugin_implements(plugin, "trace_judge"): self.trace_judges.append(plugin) self.trace_judges.append(None) # The Python case. self.auto_data = auto_data # _exclude_re is a dict mapping exclusion list names to compiled # regexes. self._exclude_re = {} self._exclude_regex_stale() self.file_locator = FileLocator() # The source argument can be directories or package names. self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.exists(src): self.source.append(self.file_locator.canonical_filename(src)) else: self.source_pkgs.append(src) self.omit = prep_patterns(self.config.omit) self.include = prep_patterns(self.config.include) self.collector = Collector( should_trace=self._should_trace, check_include=self._tracing_check_include_omit_etc, timid=self.config.timid, branch=self.config.branch, warn=self._warn, concurrency=self.config.concurrency, ) # Suffixes are a bit tricky. We want to use the data suffix only when # collecting data, not when combining data. So we save it as # `self.run_suffix` now, and promote it to `self.data_suffix` if we # find that we are collecting data later. if data_suffix or self.config.parallel: if not isinstance(data_suffix, string_class): # if data_suffix=True, use .machinename.pid.random data_suffix = True else: data_suffix = None self.data_suffix = None self.run_suffix = data_suffix # Create the data file. We do this at construction time so that the # data file will be written into the directory where the process # started rather than wherever the process eventually chdir'd to. self.data = CoverageData( basename=self.config.data_file, collector="coverage v%s" % __version__, debug=self.debug, ) # The dirs for files considered "installed with the interpreter". self.pylib_dirs = set() if not self.config.cover_pylib: # Look at where some standard modules are located. That's the # indication for "installed with the interpreter". In some # environments (virtualenv, for example), these modules may be # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. for m in (atexit, os, platform, random, socket, _structseq): if m is not None and hasattr(m, "__file__"): self.pylib_dirs.add(self._canonical_dir(m)) # To avoid tracing the coverage code itself, we skip anything located # where we are. self.cover_dir = self._canonical_dir(__file__) # The matchers for _should_trace. self.source_match = None self.pylib_match = self.cover_match = None self.include_match = self.omit_match = None # Set the reporting precision. Numbers.set_precision(self.config.precision) # Is it ok for no data to be collected? self._warn_no_data = True self._warn_unimported_source = True # State machine variables: # Have we started collecting and not stopped it? self._started = False # Have we measured some data and not harvested it? self._measured = False atexit.register(self._atexit)
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. It must be opened for native strings (bytes on Python 2, Unicode on Python 3). """ self.find_file_reporters(morfs) # Prepare the formatting strings max_name = max([len(fr.relative_filename()) for fr in self.file_reporters] + [5]) fmt_name = u"%%- %ds " % max_name fmt_err = u"%s %s: %s" fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." header = (fmt_name % "Name") + u" Stmts Miss" fmt_coverage = fmt_name + u"%6d %6d" if self.branches: header += u" Branch BrPart" fmt_coverage += u" %6d %6d" width100 = Numbers.pc_str_width() header += u"%*s" % (width100+4, "Cover") fmt_coverage += u"%%%ds%%%%" % (width100+3,) if self.config.show_missing: header += u" Missing" fmt_coverage += u" %s" rule = u"-" * len(header) if outfile is None: outfile = sys.stdout def writeout(line): """Write a line to the output, adding a newline.""" if env.PY2: line = line.encode(output_encoding()) outfile.write(line.rstrip()) outfile.write("\n") # Write the header writeout(header) writeout(rule) total = Numbers() skipped_count = 0 for fr in self.file_reporters: try: analysis = self.coverage._analyze(fr) nums = analysis.numbers total += nums if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: skipped_count += 1 continue args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str,) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd,) writeout(fmt_coverage % args) except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) if total.n_files > 1: writeout(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str,) if self.config.show_missing: args += ("",) writeout(fmt_coverage % args) if not total.n_files and not skipped_count: raise CoverageException("No data to report.") if self.config.skip_covered and skipped_count: writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) return total.n_statements and total.pc_covered
def report(self, morfs, outfile=None): """Writes a report summarizing coverage statistics per module. `outfile` is a file object to write the summary to. """ self.find_file_reporters(morfs) # Prepare the formatting strings max_name = max( [len(fr.relative_filename()) for fr in self.file_reporters] + [5]) fmt_name = "%%- %ds " % max_name fmt_err = "%s %s: %s\n" fmt_skip_covered = "\n%s file%s skipped due to complete coverage.\n" header = (fmt_name % "Name") + " Stmts Miss" fmt_coverage = fmt_name + "%6d %6d" if self.branches: header += " Branch BrPart" fmt_coverage += " %6d %6d" width100 = Numbers.pc_str_width() header += "%*s" % (width100 + 4, "Cover") fmt_coverage += "%%%ds%%%%" % (width100 + 3, ) if self.config.show_missing: header += " Missing" fmt_coverage += " %s" rule = "-" * len(header) + "\n" header += "\n" fmt_coverage += "\n" if not outfile: outfile = sys.stdout # Write the header outfile.write(header) outfile.write(rule) total = Numbers() skipped_count = 0 for fr in self.file_reporters: try: analysis = self.coverage._analyze(fr) nums = analysis.numbers if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: skipped_count += 1 continue args = (fr.relative_filename(), nums.n_statements, nums.n_missing) if self.branches: args += (nums.n_branches, nums.n_partial_branches) args += (nums.pc_covered_str, ) if self.config.show_missing: missing_fmtd = analysis.missing_formatted() if self.branches: branches_fmtd = analysis.arcs_missing_formatted() if branches_fmtd: if missing_fmtd: missing_fmtd += ", " missing_fmtd += branches_fmtd args += (missing_fmtd, ) outfile.write(fmt_coverage % args) total += nums except Exception: report_it = not self.config.ignore_errors if report_it: typ, msg = sys.exc_info()[:2] # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if typ is NotPython and not fr.should_be_python(): report_it = False if report_it: outfile.write(fmt_err % (fr.relative_filename(), typ.__name__, msg)) if total.n_files > 1: outfile.write(rule) args = ("TOTAL", total.n_statements, total.n_missing) if self.branches: args += (total.n_branches, total.n_partial_branches) args += (total.pc_covered_str, ) if self.config.show_missing: args += ("", ) outfile.write(fmt_coverage % args) if not total.n_files and not skipped_count: raise CoverageException("No data to report.") if self.config.skip_covered and skipped_count: outfile.write(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) return total.n_statements and total.pc_covered
def __init__(self, data_file = None, data_suffix = None, cover_pylib = None, auto_data = False, timid = None, branch = None, config_file = True, source = None, omit = None, include = None, debug = None, debug_file = None): from coverage import __version__ self._warnings = [] self.config = CoverageConfig() if config_file: if config_file is True: config_file = '.coveragerc' try: self.config.from_file(config_file) except ValueError: _, err, _ = sys.exc_info() raise CoverageException("Couldn't read config file %s: %s" % (config_file, err)) self.config.from_environment('COVERAGE_OPTIONS') env_data_file = os.environ.get('COVERAGE_FILE') if env_data_file: self.config.data_file = env_data_file self.config.from_args(data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), source=source, omit=omit, include=include, debug=debug) self.debug = DebugControl(self.config.debug, debug_file or sys.stderr) self.auto_data = auto_data self._exclude_re = {} self._exclude_regex_stale() self.file_locator = FileLocator() self.source = [] self.source_pkgs = [] for src in self.config.source or []: if os.path.exists(src): self.source.append(self.file_locator.canonical_filename(src)) else: self.source_pkgs.append(src) self.omit = prep_patterns(self.config.omit) self.include = prep_patterns(self.config.include) self.collector = Collector(self._should_trace, timid=self.config.timid, branch=self.config.branch, warn=self._warn) if data_suffix or self.config.parallel: if not isinstance(data_suffix, string_class): data_suffix = True else: data_suffix = None self.data_suffix = None self.run_suffix = data_suffix self.data = CoverageData(basename=self.config.data_file, collector='coverage v%s' % __version__, debug=self.debug) self.pylib_dirs = [] if not self.config.cover_pylib: for m in (atexit, os, random, socket, _structseq): if m is not None and hasattr(m, '__file__'): m_dir = self._canonical_dir(m) if m_dir not in self.pylib_dirs: self.pylib_dirs.append(m_dir) self.cover_dir = self._canonical_dir(__file__) self.source_match = None self.pylib_match = self.cover_match = None self.include_match = self.omit_match = None Numbers.set_precision(self.config.precision) self._warn_no_data = True self._warn_unimported_source = True self._started = False self._measured = False atexit.register(self._atexit)