def record_coverage_data(self, cov_data): cov = self.coverage # initialize reporter try: rep = Reporter(cov, cov.config) except TypeError: rep = Reporter(cov) # process all files rep.find_code_units(None, cov.config) for cu in rep.code_units: filename = cu.name + '.py' if filename not in self.diff_data: continue linenos = cov.data.executed_lines(cu.filename).keys() diff = self.diff_data[filename] cov_linenos = [l for l in linenos if l in diff] if cov_linenos: cov_data[filename].update(cov_linenos)
def report(self, cov, conf, morfs=None): """ Generate a part of json report for coveralls. `morfs` is a list of modules or filenames. `outfile` is a file object to write the json to. """ # pylint: disable=too-many-branches try: from coverage.report import Reporter # pylint: disable=import-outside-toplevel self.reporter = Reporter(cov, conf) except ImportError: # coverage >= 5.0 return self.report5(cov) for cu in self.reporter.find_file_reporters(morfs): try: _fn = self.reporter.coverage._analyze # pylint: disable=W0212 analyzed = _fn(cu) self.parse_file(cu, analyzed) except NoSource: if not self.reporter.config.ignore_errors: log.warning('No source for %s', cu.filename) except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. if (cu.should_be_python() and not self.reporter.config.ignore_errors): log.warning('Source file is not python %s', cu.filename) return self.coverage
def results(self): cov = coverage( data_file=self._data_file, config_file=self._config_file) cov.load() reporter = Reporter(cov, cov.config) reporter.find_code_units(None) return self.results_for_reporter(reporter)
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 report(self, cov, conf, morfs=None): """ Generate a part of json report for coveralls `morfs` is a list of modules or filenames. `outfile` is a file object to write the json to. """ # pylint: disable=too-many-branches try: from coverage.report import Reporter self.reporter = Reporter(cov, conf) except ImportError: # coverage >= 5.0 return self.report5(cov) units = None if hasattr(self.reporter, 'find_code_units'): self.reporter.find_code_units(morfs) else: units = self.reporter.find_file_reporters(morfs) if units is None: if hasattr(self.reporter, 'code_units'): units = self.reporter.code_units else: units = self.reporter.file_reporters for cu in units: try: _fn = self.reporter.coverage._analyze # pylint: disable=W0212 analyzed = _fn(cu) self.parse_file(cu, analyzed) except NoSource: if not self.reporter.config.ignore_errors: log.warning('No source for %s', cu.filename) except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. if (cu.should_be_python() and not self.reporter.config.ignore_errors): log.warning('Source file is not python %s', cu.filename) except KeyError: version = [int(x) for x in __version__.split('.')] cov3x = version[0] < 4 cov40 = version[0] == 4 and version[1] < 1 if cov3x or cov40: raise CoverallsException( 'Old (<4.1) versions of coverage.py do not work ' 'consistently on new versions of Python. Please ' 'upgrade your coverage.py.' ) raise return self.coverage
class _Reporter(object): def __init__(self, coverage, config): try: from coverage.report import Reporter except ImportError: # Support for coverage >= 5.0.1. from coverage.report import get_analysis_to_report class Reporter(object): def __init__(self, coverage, config): self.coverage = coverage self.config = config self._file_reporters = [] def find_file_reporters(self, morfs): return [ fr for fr, _ in get_analysis_to_report( self.coverage, morfs) ] self._reporter = Reporter(coverage, config) def find_file_reporters(self, morfs): self.file_reporters = self._reporter.find_file_reporters(morfs) def __getattr__(self, name): return getattr(self._reporter, name)
def stopTest(self, test): if not (self.config.record or self.config.report): return cov = self.coverage cov.stop() # this must have been imported under a different name # if self.discover and test_name not in self.pending_funcs: # self.logger.warning("Unable to determine origin for test: %s", test_name) # return # initialize reporter rep = Reporter(cov) # process all files rep.find_code_units(None, cov.config) # Compute the standard deviation for all code executed from this test linenos = [] for filename in cov.data.measured_files(): linenos.extend(cov.data.executed_lines(filename).values()) # We're recording so fetch the test data if self.config.record: test_ = test.test test_name = self._get_name_from_test(test_) test_data = self.test_data[test_name] for cu in rep.code_units: # if sys.modules[test_.__module__].__file__ == cu.filename: # continue filename = cu.name + '.py' linenos = cov.data.executed_lines(cu.filename) if self.config.record: linenos_in_prox = dict((k, v) for k, v in linenos.iteritems() if v < self.config.max_distance) if linenos_in_prox: test_data[filename] = linenos_in_prox if self.config.report: diff = self.diff_data[filename] cov_linenos = [l for l in linenos if l in diff] if cov_linenos: self.cov_data[filename].update(cov_linenos) cov.erase()
def test_reporter_file_reporters(self): rep = Reporter(None, None) with warnings.catch_warnings(record=True) as warns: warnings.simplefilter("always") # Accessing this attribute will raise a DeprecationWarning. rep.file_reporters # pylint: disable=pointless-statement self.assertEqual(len(warns), 1) self.assertTrue(issubclass(warns[0].category, DeprecationWarning))
class CoverallReporter: """Custom coverage.py reporter for coveralls.io.""" def __init__(self, cov, conf): self.coverage = [] self.report(cov, conf) def report5(self, cov): # N.B. this method is 99% copied from the coverage source code; # unfortunately, the coverage v5 style of `get_analysis_to_report` # errors out entirely if any source file has issues -- which would be a # breaking change for us. In the interest of backwards compatibility, # I've copied their code here so we can maintain the same `coveralls` # API regardless of which `coverage` version is being used. # # TODO: deprecate the relevant APIs so we can just use the coverage # public API directly. # # from coverage.report import get_analysis_to_report # try: # for cu, analyzed in get_analysis_to_report(cov, None): # self.parse_file(cu, analyzed) # except NoSource: # # Note that this behavior must necessarily change between # # coverage<5 and coverage>=5, as we are no longer interweaving # # with get_analysis_to_report (a single exception breaks the # # whole loop) # log.warning('No source for at least one file') # except NotPython: # # Note that this behavior must necessarily change between # # coverage<5 and coverage>=5, as we are no longer interweaving # # with get_analysis_to_report (a single exception breaks the # # whole loop) # log.warning('A source file is not python') # except CoverageException as e: # if str(e) != 'No data to report.': # raise from coverage.files import FnmatchMatcher, prep_patterns # pylint: disable=import-outside-toplevel # get_analysis_to_report starts here; changes marked with TODOs file_reporters = cov._get_file_reporters(None) # pylint: disable=W0212 config = cov.config if config.report_include: matcher = FnmatchMatcher(prep_patterns(config.report_include)) file_reporters = [ fr for fr in file_reporters if matcher.match(fr.filename) ] if config.report_omit: matcher = FnmatchMatcher(prep_patterns(config.report_omit)) file_reporters = [ fr for fr in file_reporters if not matcher.match(fr.filename) ] # TODO: deprecate changes # if not file_reporters: # raise CoverageException("No data to report.") for fr in sorted(file_reporters): try: analysis = cov._analyze(fr) # pylint: disable=W0212 except NoSource: if not config.ignore_errors: # TODO: deprecate changes # raise log.warning('No source for %s', fr.filename) except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if fr.should_be_python(): if config.ignore_errors: msg = "Couldn't parse Python file '{}'".format( fr.filename) cov._warn( msg, # pylint: disable=W0212 slug='couldnt-parse') else: # TODO: deprecate changes # raise log.warning('Source file is not python %s', fr.filename) else: # TODO: deprecate changes (well, this one is fine /shrug) # yield (fr, analysis) self.parse_file(fr, analysis) def report(self, cov, conf, morfs=None): """ Generate a part of json report for coveralls. `morfs` is a list of modules or filenames. `outfile` is a file object to write the json to. """ # pylint: disable=too-many-branches try: from coverage.report import Reporter # pylint: disable=import-outside-toplevel self.reporter = Reporter(cov, conf) except ImportError: # coverage >= 5.0 return self.report5(cov) for cu in self.reporter.find_file_reporters(morfs): try: _fn = self.reporter.coverage._analyze # pylint: disable=W0212 analyzed = _fn(cu) self.parse_file(cu, analyzed) except NoSource: if not self.reporter.config.ignore_errors: log.warning('No source for %s', cu.filename) except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. if (cu.should_be_python() and not self.reporter.config.ignore_errors): log.warning('Source file is not python %s', cu.filename) return self.coverage @staticmethod def get_hits(line_num, analysis): """ Source file stats for each line. * A positive integer if the line is covered, representing the number of times the line is hit during the test suite. * 0 if the line is not covered by the test suite. * null to indicate the line is not relevant to code coverage (it may be whitespace or a comment). """ if line_num in analysis.missing: return 0 if line_num not in analysis.statements: return None return 1 @staticmethod def get_arcs(analysis): """ Hit stats for each branch. Returns a flat list where every four values represent a branch: 1. line-number 2. block-number (not used) 3. branch-number 4. hits (we only get 1/0 from coverage.py) """ if not analysis.has_arcs(): return None if not hasattr(analysis, 'branch_lines'): # N.B. switching to the public method analysis.missing_branch_arcs # would work for half of what we need, but there doesn't seem to be # an equivalent analysis.executed_branch_arcs branch_lines = analysis._branch_lines() # pylint: disable=W0212 else: branch_lines = analysis.branch_lines() branches = [] for l1, l2 in analysis.arcs_executed(): if l1 in branch_lines: branches.extend((l1, 0, abs(l2), 1)) for l1, l2 in analysis.arcs_missing(): if l1 in branch_lines: branches.extend((l1, 0, abs(l2), 0)) return branches def parse_file(self, cu, analysis): """Generate data for single file.""" filename = cu.relative_filename() # ensure results are properly merged between platforms posix_filename = filename.replace(os.path.sep, '/') source = analysis.file_reporter.source() token_lines = analysis.file_reporter.source_token_lines() coverage_lines = [ self.get_hits(i, analysis) for i, _ in enumerate(token_lines, 1) ] results = { 'name': posix_filename, 'source': source, 'coverage': coverage_lines, } branches = self.get_arcs(analysis) if branches: results['branches'] = branches self.coverage.append(results)