diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/html.py | 3 | ||||
-rw-r--r-- | coverage/parser.py | 28 | ||||
-rw-r--r-- | coverage/plugin.py | 8 | ||||
-rw-r--r-- | coverage/python.py | 4 |
4 files changed, 33 insertions, 10 deletions
diff --git a/coverage/html.py b/coverage/html.py index 9471db93..d4fb7516 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -190,6 +190,7 @@ class HtmlReporter(Reporter): if self.has_arcs: missing_branch_arcs = analysis.missing_branch_arcs() + arcs_executed = analysis.arcs_executed() # These classes determine which lines are highlighted by default. c_run = "run hide_run" @@ -219,7 +220,7 @@ class HtmlReporter(Reporter): shorts.append("exit") else: shorts.append(b) - longs.append(fr.missing_arc_description(lineno, b)) + longs.append(fr.missing_arc_description(lineno, b, arcs_executed)) # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. short_fmt = "%s ↛ %s" diff --git a/coverage/parser.py b/coverage/parser.py index f4dd02d4..8fb5d89b 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -21,8 +21,12 @@ from coverage.phystokens import compile_unicode, generate_tokens, neuter_encodin class PythonParser(object): - """Parse code to find executable lines, excluded lines, etc.""" + """Parse code to find executable lines, excluded lines, etc. + This information is all based on static analysis: no code execution is + involved. + + """ @contract(text='unicode|None') def __init__(self, text=None, filename=None, exclude=None): """ @@ -304,11 +308,23 @@ class PythonParser(object): return exit_counts - def missing_arc_description(self, start, end): + def missing_arc_description(self, start, end, executed_arcs=None): """Provide an English sentence describing a missing arc.""" if self._missing_arc_fragments is None: self._analyze_ast() + actual_start = start + + if ( + executed_arcs and + end < 0 and end == -start and + (end, start) not in executed_arcs and + (end, start) in self._missing_arc_fragments + ): + # It's a one-line callable, and we never even started it, + # and we have a message about not starting it. + start, end = end, start + fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)]) msgs = [] @@ -325,9 +341,9 @@ class PythonParser(object): emsg = "didn't jump to line {lineno}" emsg = emsg.format(lineno=end) - msg = "line {start} {emsg}".format(start=start, emsg=emsg) + msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg) if smsg is not None: - msg += ", because {smsg}".format(smsg=smsg.format(lineno=start)) + msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start)) msgs.append(msg) @@ -939,10 +955,10 @@ class AstArcAnalyzer(object): """A function to make methods for online callable _code_object__ methods.""" def _code_object__oneline_callable(self, node): start = self.line_for_node(node) - self.add_arc(-start, start) + self.add_arc(-start, start, None, "didn't run the {0} on line {1}".format(noun, start)) self.add_arc( start, -start, None, - "didn't run the {0} on line {1}".format(noun, start), + "didn't finish the {0} on line {1}".format(noun, start), ) return _code_object__oneline_callable diff --git a/coverage/plugin.py b/coverage/plugin.py index 97d9c16e..a7b95466 100644 --- a/coverage/plugin.py +++ b/coverage/plugin.py @@ -329,9 +329,15 @@ class FileReporter(object): """ return {} - def missing_arc_description(self, start, end): + def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument """Provide an English sentence describing a missing arc. + The `start` and `end` arguments are the line numbers of the missing + arc. Negative numbers indicate entering or exiting code objects. + + The `executed_arcs` argument is a set of line number pairs, the arcs + that were executed in this file. + By default, this simply returns the string "Line {start} didn't jump to {end}". diff --git a/coverage/python.py b/coverage/python.py index 0d2fb3b4..0cd2181c 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -165,8 +165,8 @@ class PythonFileReporter(FileReporter): def exit_counts(self): return self.parser.exit_counts() - def missing_arc_description(self, start, end): - return self.parser.missing_arc_description(start, end) + def missing_arc_description(self, start, end, executed_arcs=None): + return self.parser.missing_arc_description(start, end, executed_arcs) @contract(returns='unicode') def source(self): |