summaryrefslogtreecommitdiff
path: root/tests/test_summary.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_summary.py')
-rw-r--r--tests/test_summary.py339
1 files changed, 269 insertions, 70 deletions
diff --git a/tests/test_summary.py b/tests/test_summary.py
index 6e7dbeba..5ba00389 100644
--- a/tests/test_summary.py
+++ b/tests/test_summary.py
@@ -1,3 +1,7 @@
+# coding: utf8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
"""Test text-based summary reporting for coverage.py"""
import glob
@@ -10,6 +14,11 @@ import sys
import coverage
from coverage import env
from coverage.backward import StringIO
+from coverage.config import CoverageConfig
+from coverage.control import Coverage
+from coverage.data import CoverageData
+from coverage.misc import CoverageException, output_encoding
+from coverage.summary import SummaryReporter
from tests.coveragetest import CoverageTest
@@ -42,16 +51,16 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Cover
# ------------------------------------------------------------------
# c:/ned/coverage/tests/modules/covmod1.py 2 0 100%
- # c:/ned/coverage/tests/zipmods.zip/covmodzip1.py 2 0 100%
+ # c:/ned/coverage/tests/zipmods.zip/covmodzip1.py 3 0 100%
# mycode.py 4 0 100%
# ------------------------------------------------------------------
- # TOTAL 8 0 100%
+ # TOTAL 9 0 100%
self.assertNotIn("/coverage/__init__/", report)
self.assertIn("/tests/modules/covmod1.py ", report)
self.assertIn("/tests/zipmods.zip/covmodzip1.py ", report)
self.assertIn("mycode.py ", report)
- self.assertEqual(self.last_line_squeezed(report), "TOTAL 8 0 100%")
+ self.assertEqual(self.last_line_squeezed(report), "TOTAL 9 0 100%")
def test_report_just_one(self):
# Try reporting just one module
@@ -91,9 +100,7 @@ class SummaryTest(CoverageTest):
# Try reporting while omitting some modules
self.make_mycode()
self.run_command("coverage run mycode.py")
- report = self.report_from_command(
- "coverage report --omit '%s/*'" % HERE
- )
+ report = self.report_from_command("coverage report --omit '%s/*'" % HERE)
# Name Stmts Miss Cover
# -------------------------------
@@ -141,8 +148,7 @@ class SummaryTest(CoverageTest):
self.assertEqual(self.line_count(report), 3)
self.assertIn("mybranch.py ", report)
- self.assertEqual(self.last_line_squeezed(report),
- "mybranch.py 5 0 2 1 86%")
+ self.assertEqual(self.last_line_squeezed(report), "mybranch.py 5 0 2 1 86%")
def test_report_show_missing(self):
self.make_file("mymissing.py", """\
@@ -171,8 +177,7 @@ class SummaryTest(CoverageTest):
self.assertEqual(self.line_count(report), 3)
self.assertIn("mymissing.py ", report)
- self.assertEqual(self.last_line_squeezed(report),
- "mymissing.py 14 3 79% 3-4, 10")
+ self.assertEqual(self.last_line_squeezed(report), "mymissing.py 14 3 79% 3-4, 10")
def test_report_show_missing_branches(self):
self.make_file("mybranch.py", """\
@@ -181,7 +186,6 @@ class SummaryTest(CoverageTest):
print("x")
if y:
print("y")
- return x
branch(1, 1)
""")
out = self.run_command("coverage run --branch mybranch.py")
@@ -190,12 +194,11 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Branch BrPart Cover Missing
# ----------------------------------------------------------
- # mybranch.py 7 0 4 2 82% 2->4, 4->6
+ # mybranch.py 6 0 4 2 80% 2->4, 4->exit
self.assertEqual(self.line_count(report), 3)
self.assertIn("mybranch.py ", report)
- self.assertEqual(self.last_line_squeezed(report),
- "mybranch.py 7 0 4 2 82% 2->4, 4->6")
+ self.assertEqual(self.last_line_squeezed(report), "mybranch.py 6 0 4 2 80% 2->4, 4->exit")
def test_report_show_missing_branches_and_lines(self):
self.make_file("main.py", """\
@@ -216,28 +219,17 @@ class SummaryTest(CoverageTest):
out = self.run_command("coverage run --branch main.py")
self.assertEqual(out, 'x\ny\n')
report = self.report_from_command("coverage report --show-missing")
-
- # Name Stmts Miss Branch BrPart Cover Missing
- # -------------------------------------------------------
- # main.py 1 0 0 0 100%
- # mybranch.py 10 2 8 3 61% 7-8, 2->4, 4->6, 6->7
- # -------------------------------------------------------
- # TOTAL 11 2 8 3 63%
-
- self.assertEqual(self.line_count(report), 6)
- squeezed = self.squeezed_lines(report)
- self.assertEqual(
- squeezed[2],
- "main.py 1 0 0 0 100%"
- )
- self.assertEqual(
- squeezed[3],
- "mybranch.py 10 2 8 3 61% 7-8, 2->4, 4->6, 6->7"
- )
- self.assertEqual(
- squeezed[5],
- "TOTAL 11 2 8 3 63%"
- )
+ report_lines = report.splitlines()
+
+ expected = [
+ 'Name Stmts Miss Branch BrPart Cover Missing',
+ '---------------------------------------------------------',
+ 'main.py 1 0 0 0 100%',
+ 'mybranch.py 10 2 8 3 61% 7-8, 2->4, 4->6, 6->7',
+ '---------------------------------------------------------',
+ 'TOTAL 11 2 8 3 63%',
+ ]
+ self.assertEqual(report_lines, expected)
def test_report_skip_covered_no_branches(self):
self.make_file("main.py", """
@@ -253,19 +245,26 @@ class SummaryTest(CoverageTest):
""")
out = self.run_command("coverage run main.py")
self.assertEqual(out, "z\n")
- report = self.report_from_command("coverage report --skip-covered")
+ report = self.report_from_command("coverage report --skip-covered --fail-under=70")
# Name Stmts Miss Cover
# ------------------------------------
# not_covered.py 2 1 50%
+ # ------------------------------------
+ # TOTAL 6 1 83%
+ #
+ # 1 file skipped due to complete coverage.
- self.assertEqual(self.line_count(report), 3, report)
+ self.assertEqual(self.line_count(report), 7, report)
squeezed = self.squeezed_lines(report)
self.assertEqual(squeezed[2], "not_covered.py 2 1 50%")
+ self.assertEqual(squeezed[4], "TOTAL 6 1 83%")
+ self.assertEqual(squeezed[6], "1 file skipped due to complete coverage.")
+ self.assertEqual(self.last_command_status, 0)
def test_report_skip_covered_branches(self):
self.make_file("main.py", """
- import not_covered
+ import not_covered, covered
def normal(z):
if z:
@@ -279,6 +278,11 @@ class SummaryTest(CoverageTest):
print("n")
not_covered(True)
""")
+ self.make_file("covered.py", """
+ def foo():
+ pass
+ foo()
+ """)
out = self.run_command("coverage run --branch main.py")
self.assertEqual(out, "n\nz\n")
report = self.report_from_command("coverage report --skip-covered")
@@ -286,10 +290,16 @@ class SummaryTest(CoverageTest):
# Name Stmts Miss Branch BrPart Cover
# --------------------------------------------------
# not_covered.py 4 0 2 1 83%
+ # --------------------------------------------------
+ # TOTAL 13 0 4 1 94%
+ #
+ # 2 files skipped due to complete coverage.
- self.assertEqual(self.line_count(report), 3, report)
+ self.assertEqual(self.line_count(report), 7, report)
squeezed = self.squeezed_lines(report)
self.assertEqual(squeezed[2], "not_covered.py 4 0 2 1 83%")
+ self.assertEqual(squeezed[4], "TOTAL 13 0 4 1 94%")
+ self.assertEqual(squeezed[6], "2 files skipped due to complete coverage.")
def test_report_skip_covered_branches_with_totals(self):
self.make_file("main.py", """
@@ -321,13 +331,67 @@ class SummaryTest(CoverageTest):
# also_not_run.py 2 1 0 0 50%
# not_covered.py 4 0 2 1 83%
# --------------------------------------------------
- # TOTAL 6 1 2 1 75%
+ # TOTAL 13 1 4 1 88%
+ #
+ # 1 file skipped due to complete coverage.
- self.assertEqual(self.line_count(report), 6, report)
+ self.assertEqual(self.line_count(report), 8, report)
squeezed = self.squeezed_lines(report)
self.assertEqual(squeezed[2], "also_not_run.py 2 1 0 0 50%")
self.assertEqual(squeezed[3], "not_covered.py 4 0 2 1 83%")
- self.assertEqual(squeezed[5], "TOTAL 6 1 2 1 75%")
+ self.assertEqual(squeezed[5], "TOTAL 13 1 4 1 88%")
+ self.assertEqual(squeezed[7], "1 file skipped due to complete coverage.")
+
+ def test_report_skip_covered_all_files_covered(self):
+ self.make_file("main.py", """
+ def foo():
+ pass
+ foo()
+ """)
+ out = self.run_command("coverage run --branch main.py")
+ self.assertEqual(out, "")
+ report = self.report_from_command("coverage report --skip-covered")
+
+ # Name Stmts Miss Branch BrPart Cover
+ # -------------------------------------------
+ #
+ # 1 file skipped due to complete coverage.
+
+ self.assertEqual(self.line_count(report), 4, report)
+ squeezed = self.squeezed_lines(report)
+ self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.")
+
+ def test_report_skip_covered_longfilename(self):
+ self.make_file("long_______________filename.py", """
+ def foo():
+ pass
+ foo()
+ """)
+ out = self.run_command("coverage run --branch long_______________filename.py")
+ self.assertEqual(out, "")
+ report = self.report_from_command("coverage report --skip-covered")
+
+ # Name Stmts Miss Branch BrPart Cover
+ # -----------------------------------------
+ #
+ # 1 file skipped due to complete coverage.
+
+ self.assertEqual(self.line_count(report), 4, report)
+ lines = self.report_lines(report)
+ self.assertEqual(lines[0], "Name Stmts Miss Branch BrPart Cover")
+ squeezed = self.squeezed_lines(report)
+ self.assertEqual(squeezed[3], "1 file skipped due to complete coverage.")
+
+ def test_report_skip_covered_no_data(self):
+ report = self.report_from_command("coverage report --skip-covered")
+
+ # Name Stmts Miss Branch BrPart Cover
+ # -------------------------------------------
+ # No data to report.
+
+ self.assertEqual(self.line_count(report), 3, report)
+ squeezed = self.squeezed_lines(report)
+ self.assertEqual(squeezed[2], "No data to report.")
def test_dotpy_not_python(self):
# We run a .py file, and when reporting, we can't parse it as Python.
@@ -338,22 +402,48 @@ class SummaryTest(CoverageTest):
self.make_file("mycode.py", "This isn't python at all!")
report = self.report_from_command("coverage report mycode.py")
- # pylint: disable=line-too-long
+ # mycode NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1
+ # Name Stmts Miss Cover
+ # ----------------------------
+ # No data to report.
+
+ errmsg = self.squeezed_lines(report)[0]
+ # The actual file name varies run to run.
+ errmsg = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", errmsg)
+ # The actual error message varies version to version
+ errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg)
+ self.assertEqual(
+ errmsg,
+ "mycode.py NotPython: Couldn't parse 'mycode.py' as Python source: 'error' at line 1"
+ )
+
+ def test_accenteddotpy_not_python(self):
+ # We run a .py file with a non-ascii name, and when reporting, we can't
+ # parse it as Python. We should get an error message in the report.
+
+ self.make_file(u"accented\xe2.py", "print('accented')")
+ self.run_command(u"coverage run accented\xe2.py")
+ self.make_file(u"accented\xe2.py", "This isn't python at all!")
+ report = self.report_from_command(u"coverage report accented\xe2.py")
+
+ # xxxx NotPython: Couldn't parse '...' as Python source: 'invalid syntax' at line 1
# Name Stmts Miss Cover
# ----------------------------
- # mycode NotPython: Couldn't parse '/tmp/test_cover/63354509363/mycode.py' as Python source: 'invalid syntax' at line 1
# No data to report.
- last = self.squeezed_lines(report)[-2]
+ errmsg = self.squeezed_lines(report)[0]
# The actual file name varies run to run.
- last = re.sub(r"parse '.*mycode.py", "parse 'mycode.py", last)
+ errmsg = re.sub(r"parse '.*(accented.*?\.py)", r"parse '\1", errmsg)
# The actual error message varies version to version
- last = re.sub(r": '.*' at", ": 'error' at", last)
- self.assertEqual(last,
- "mycode.py NotPython: "
- "Couldn't parse 'mycode.py' as Python source: "
- "'error' at line 1"
- )
+ errmsg = re.sub(r": '.*' at", ": 'error' at", errmsg)
+ expected = (
+ u"accented\xe2.py NotPython: "
+ u"Couldn't parse 'accented\xe2.py' as Python source: 'error' at line 1"
+ )
+ if env.PY2:
+ # pylint: disable=redefined-variable-type
+ expected = expected.encode(output_encoding())
+ self.assertEqual(errmsg, expected)
def test_dotpy_not_python_ignored(self):
# We run a .py file, and when reporting, we can't parse it as Python,
@@ -407,18 +497,18 @@ class SummaryTest(CoverageTest):
self.make_file("main.py", """\
print("y")
""")
- cov = coverage.coverage(branch=True, source=["."])
+ cov = coverage.Coverage(branch=True, source=["."])
cov.start()
- import main # pragma: nested # pylint: disable=import-error,unused-variable
+ import main # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
report = self.get_report(cov).splitlines()
self.assertIn("mybranch.py 5 5 2 0 0%", report)
def run_TheCode_and_report_it(self):
"""A helper for the next few tests."""
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
- import TheCode # pragma: nested # pylint: disable=import-error,unused-variable
+ import TheCode # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
return self.get_report(cov)
@@ -441,7 +531,7 @@ class SummaryTest(CoverageTest):
def test_pyw_files(self):
if not env.WINDOWS:
- self.skip(".pyw files are only on Windows.")
+ self.skipTest(".pyw files are only on Windows.")
# https://bitbucket.org/ned/coveragepy/issue/261
self.make_file("start.pyw", """\
@@ -451,9 +541,9 @@ class SummaryTest(CoverageTest):
self.make_file("mod.pyw", """\
print("In mod.pyw")
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
- import start # pragma: nested # pylint: disable=import-error,unused-variable
+ import start # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
report = self.get_report(cov)
@@ -471,9 +561,9 @@ class SummaryTest(CoverageTest):
py_compile.compile("mod.py")
# Run the program.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
- import main # pragma: nested # pylint: disable=import-error,unused-variable
+ import main # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
report = self.get_report(cov).splitlines()
@@ -482,7 +572,7 @@ class SummaryTest(CoverageTest):
def test_missing_py_file_during_run(self):
# PyPy2 doesn't run bare .pyc files.
if env.PYPY and env.PY2:
- self.skip("PyPy2 doesn't run bare .pyc files")
+ self.skipTest("PyPy2 doesn't run bare .pyc files")
# Create two Python files.
self.make_file("mod.py", "a = 1\n")
@@ -501,9 +591,9 @@ class SummaryTest(CoverageTest):
os.rename(pycs[0], "mod.pyc")
# Run the program.
- cov = coverage.coverage()
+ cov = coverage.Coverage()
cov.start()
- import main # pragma: nested # pylint: disable=import-error,unused-variable
+ import main # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
# Put back the missing Python file.
@@ -529,9 +619,9 @@ class SummaryTest2(CoverageTest):
def test_empty_files(self):
# Shows that empty files like __init__.py are listed as having zero
# statements, not one statement.
- cov = coverage.coverage()
+ cov = coverage.Coverage(branch=True)
cov.start()
- import usepkgs # pragma: nested # pylint: disable=import-error,unused-variable
+ import usepkgs # pragma: nested # pylint: disable=import-error, unused-variable
cov.stop() # pragma: nested
repout = StringIO()
@@ -539,8 +629,8 @@ class SummaryTest2(CoverageTest):
report = repout.getvalue().replace('\\', '/')
report = re.sub(r"\s+", " ", report)
- self.assertIn("tests/modules/pkg1/__init__.py 1 0 100%", report)
- self.assertIn("tests/modules/pkg2/__init__.py 0 0 100%", report)
+ self.assertIn("tests/modules/pkg1/__init__.py 2 0 0 0 100%", report)
+ self.assertIn("tests/modules/pkg2/__init__.py 0 0 0 0 100%", report)
class ReportingReturnValueTest(CoverageTest):
@@ -558,7 +648,7 @@ class ReportingReturnValueTest(CoverageTest):
g = 7
""")
- cov = coverage.coverage()
+ cov = coverage.Coverage()
self.start_import_stop(cov, "doit")
return cov
@@ -576,3 +666,112 @@ class ReportingReturnValueTest(CoverageTest):
cov = self.run_coverage()
val = cov.xml_report(include="*/doit.py")
self.assertAlmostEqual(val, 85.7, 1)
+
+
+class TestSummaryReporterConfiguration(CoverageTest):
+ """Tests of SummaryReporter."""
+
+ run_in_temp_dir = False
+
+ # We just need some readable files to work with. These will do.
+ HERE = os.path.dirname(__file__)
+
+ LINES_1 = {
+ os.path.join(HERE, "test_api.py"): dict.fromkeys(range(300)),
+ os.path.join(HERE, "test_backward.py"): dict.fromkeys(range(20)),
+ os.path.join(HERE, "test_coverage.py"): dict.fromkeys(range(15)),
+ }
+
+ def get_coverage_data(self, lines):
+ """Get a CoverageData object that includes the requested lines."""
+ data = CoverageData()
+ data.add_lines(lines)
+ return data
+
+ def get_summary_text(self, coverage_data, options):
+ """Get text output from the SummaryReporter."""
+ cov = Coverage()
+ cov.start()
+ cov.stop() # pragma: nested
+ cov.data = coverage_data
+ printer = SummaryReporter(cov, options)
+ destination = StringIO()
+ printer.report([], destination)
+ return destination.getvalue()
+
+ def test_test_data(self):
+ # We use our own test files as test data. Check that our assumptions
+ # about them are still valid. We want the three columns of numbers to
+ # sort in three different orders.
+ data = self.get_coverage_data(self.LINES_1)
+ report = self.get_summary_text(data, CoverageConfig())
+ print(report)
+ # Name Stmts Miss Cover
+ # --------------------------------------------
+ # tests/test_api.py 339 155 54%
+ # tests/test_backward.py 13 3 77%
+ # tests/test_coverage.py 234 228 3%
+ # --------------------------------------------
+ # TOTAL 586 386 34%
+
+ lines = report.splitlines()[2:-2]
+ self.assertEqual(len(lines), 3)
+ nums = [list(map(int, l.replace('%', '').split()[1:])) for l in lines]
+ # [
+ # [339, 155, 54],
+ # [ 13, 3, 77],
+ # [234, 228, 3]
+ # ]
+ self.assertTrue(nums[1][0] < nums[2][0] < nums[0][0])
+ self.assertTrue(nums[1][1] < nums[0][1] < nums[2][1])
+ self.assertTrue(nums[2][2] < nums[0][2] < nums[1][2])
+
+ def test_defaults(self):
+ """Run the report with no configuration options."""
+ data = self.get_coverage_data(self.LINES_1)
+ opts = CoverageConfig()
+ report = self.get_summary_text(data, opts)
+ self.assertNotIn('Missing', report)
+ self.assertNotIn('Branch', report)
+
+ def test_print_missing(self):
+ """Run the report printing the missing lines."""
+ data = self.get_coverage_data(self.LINES_1)
+ opts = CoverageConfig()
+ opts.from_args(show_missing=True)
+ report = self.get_summary_text(data, opts)
+ self.assertIn('Missing', report)
+ self.assertNotIn('Branch', report)
+
+ def assert_ordering(self, text, *words):
+ """Assert that the `words` appear in order in `text`."""
+ indexes = list(map(text.find, words))
+ self.assertEqual(
+ indexes, sorted(indexes),
+ "The words %r don't appear in order in %r" % (words, text)
+ )
+
+ def test_sort_report_by_stmts(self):
+ # Sort the text report by the Stmts column.
+ data = self.get_coverage_data(self.LINES_1)
+ opts = CoverageConfig()
+ opts.from_args(sort='Stmts')
+ report = self.get_summary_text(data, opts)
+ self.assert_ordering(report, "test_backward.py", "test_coverage.py", "test_api.py")
+
+ def test_sort_report_by_cover(self):
+ # Sort the text report by the Cover column.
+ data = self.get_coverage_data(self.LINES_1)
+ opts = CoverageConfig()
+ opts.from_args(sort='Cover')
+ report = self.get_summary_text(data, opts)
+ self.assert_ordering(report, "test_coverage.py", "test_api.py", "test_backward.py")
+
+ def test_sort_report_by_invalid_option(self):
+ # Sort the text report by a nonsense column.
+ data = self.get_coverage_data(self.LINES_1)
+ opts = CoverageConfig()
+ opts.from_args(sort='Xyzzy')
+ msg = "Invalid sorting option: 'Xyzzy'"
+ with self.assertRaisesRegex(CoverageException, msg):
+ self.get_summary_text(data, opts)