Source code for bugzoo.core.spectra

from typing import List, Dict, Iterator
import logging

from .coverage import TestSuiteCoverage
from .fileline import FileLine

logger = logging.getLogger(__name__)  # type: logging.Logger
logger.setLevel(logging.DEBUG)


[docs]class LineSpectra(object): """ Summarises the coverage information for a single line in the program in terms of the number of passing and failing tests that do and do not cover it, respectively. """ def __init__(self, ep: int, ef: int, np: int, nf: int) -> None: assert ep >= 0 assert ef >= 0 assert np >= 0 assert nf >= 0 self.__ep = ep self.__ef = ef self.__np = np self.__nf = nf @property def ep(self) -> int: """ The number of passing tests that cover this line. """ return self.__ep @property def ef(self) -> int: """ The number of failing tests that cover this line. """ return self.__ef @property def np(self) -> int: """ The number of passing tests that do not cover this line. """ return self.__np @property def nf(self) -> int: """ The number of failing tests that do not cover this line. """ return self.__nf def __repr__(self) -> str: return "({},{}; {},{})".format(self.ep, self.np, self.ef, self.nf)
[docs]class Spectra(object): """ Contains a summary of the number of passing and failing tests that cover each line in a given project. """ @staticmethod def from_coverage(coverage: TestSuiteCoverage) -> 'Spectra': # tally the number of times that each line is touched by a passing # or failing test tally_failing = {} # type: Dict[FileLine, int] tally_passing = {} # type: Dict[FileLine, int] for test in coverage.passing: for line in coverage[test].lines: tally_passing[line] = tally_passing.get(line, 0) + 1 for test in coverage.failing: for line in coverage[test].lines: tally_failing[line] = tally_failing.get(line, 0) + 1 return Spectra(len(coverage.passing), len(coverage.failing), FileLine.compactify(tally_passing), FileLine.compactify(tally_failing)) def __init__(self, num_passing: int, num_failing: int, tally_passing: Dict[str, Dict[int, int]], tally_failing: Dict[str, Dict[int, int]] ) -> None: self.__num_passing = num_passing self.__num_failing = num_failing self.__tally_passing = tally_passing self.__tally_failing = tally_failing
[docs] def __getitem__(self, line: FileLine) -> LineSpectra: """ Retrieves the spectra information for a given line. """ if not line.filename in self.__tally_passing: ep = 0 else: ep = self.__tally_passing[line.filename].get(line.num, 0) if not line.filename in self.__tally_failing: ef = 0 else: ef = self.__tally_failing[line.filename].get(line.num, 0) np = self.__num_passing - ep nf = self.__num_failing - ef return LineSpectra(ep, ef, np, nf)
[docs] def __iter__(self) -> Iterator[FileLine]: """ Returns an iterator over the source code lines that are represented in this spectra. """ passing_lines = \ set(FileLine.decompactify(self.__tally_passing).keys()) failing_lines = \ set(FileLine.decompactify(self.__tally_failing).keys()) lines = passing_lines.union(failing_lines) for line in lines: yield line
def __repr__(self) -> str: bfr = ["{}: {}".format(line, repr(self[line])) for line in self] return 'Spectra({})'.format('\n'.join(bfr)) def restricted_to_files(self, filenames: List[str] ) -> 'Spectra': """ Returns a variant of this spectra that only contains entries for lines that appear in any of the files whose name appear in the given list. """ tally_passing = \ {fn: entries for (fn, entries) in self.__tally_passing.items() \ if fn in filenames} tally_failing = \ {fn: entries for (fn, entries) in self.__tally_failing.items() \ if fn in filenames} return Spectra(self.__num_passing, self.__num_failing, tally_passing, tally_failing)