summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt4
-rw-r--r--coverage/parser.py14
-rw-r--r--tests/test_parser.py32
3 files changed, 42 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 0ab34232..ddafc9de 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -47,6 +47,10 @@ Latest
- Made some PyPy-specific tweaks to improve speed under PyPy. Thanks, Alex
Gaynor.
+- In some cases, with a file with a missing final newline, coverage would
+ count statements incorrectly. This is now fixed, closing `issue 293`_.
+
+.. _issue 293: https://bitbucket.org/ned/coveragepy/issue/293/number-of-statement-detection-wrong-if-no
.. _issue 314: https://bitbucket.org/ned/coveragepy/issue/314/fail_under-param-not-working-in-coveragerc
.. _issue 328: https://bitbucket.org/ned/coveragepy/issue/328/misbehavior-in-run-source
.. _issue 334: https://bitbucket.org/ned/coveragepy/issue/334/pragma-not-recognized-if-tab-character
diff --git a/coverage/parser.py b/coverage/parser.py
index ae36a738..ef2ee5b8 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -49,9 +49,13 @@ class PythonParser(CodeParser):
"No source for code: '%s': %s" % (self.filename, err)
)
- # Scrap the BOM if it exists.
- if self.text and ord(self.text[0]) == 0xfeff:
- self.text = self.text[1:]
+ if self.text:
+ # Scrap the BOM if it exists.
+ if ord(self.text[0]) == 0xfeff:
+ self.text = self.text[1:]
+ # Python source should always have a final newline.
+ if self.text[-1] != "\n":
+ self.text += "\n"
self.exclude = exclude
@@ -346,9 +350,7 @@ class ByteParser(object):
self.text = text
try:
- # Python 2.3 and 2.4 don't like partial last lines, so be sure
- # the text ends nicely for them.
- self.code = compile(text + '\n', filename, "exec")
+ self.code = compile(text, filename, "exec")
except SyntaxError as synerr:
raise NotPython(
"Couldn't parse '%s' as Python source: '%s' at line %d" %
diff --git a/tests/test_parser.py b/tests/test_parser.py
index a392ea03..244d4c70 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -99,7 +99,7 @@ class ParserFileTest(CoverageTest):
def parse_file(self, filename):
"""Parse `text` as source, and return the `PythonParser` used."""
parser = PythonParser(filename=filename, exclude="nocover")
- parser.parse_source()
+ self.statements, self.excluded = parser.parse_source()
return parser
def test_line_endings(self):
@@ -128,4 +128,32 @@ class ParserFileTest(CoverageTest):
coverage = "\xe7\xf6v\xear\xe3g\xe9"
""")
parser = self.parse_file("encoded.py")
- parser.exit_counts() # TODO: This value should be tested!
+ self.assertEqual(parser.exit_counts(), {1: 1})
+
+ def test_missing_line_ending(self):
+ # Test that the set of statements is the same even if a final
+ # multi-line statement has no final newline.
+ # https://bitbucket.org/ned/coveragepy/issue/293
+
+ self.make_file("normal.py", """\
+ out, err = subprocess.Popen(
+ [sys.executable, '-c', 'pass'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+ """)
+
+ self.parse_file("normal.py")
+ self.assertEqual(self.statements, set([1]))
+
+ self.make_file("abrupt.py", """\
+ out, err = subprocess.Popen(
+ [sys.executable, '-c', 'pass'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()""") # no final newline.
+
+ # Double-check that some test helper wasn't being helpful.
+ with open("abrupt.py") as f:
+ self.assertEqual(f.read()[-1], ")")
+
+ self.parse_file("abrupt.py")
+ self.assertEqual(self.statements, set([1]))