summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/backtest.py50
-rw-r--r--tests/backunittest.py26
-rw-r--r--tests/coveragetest.py318
-rw-r--r--tests/farm/annotate/annotate_dir.py4
-rw-r--r--tests/farm/annotate/run.py4
-rw-r--r--tests/farm/annotate/run_multi.py4
-rw-r--r--tests/farm/html/gold_x_xml/coverage.xml3
-rw-r--r--tests/farm/html/gold_y_xml_branch/coverage.xml3
-rw-r--r--tests/farm/html/run_a_xml_1.py5
-rw-r--r--tests/farm/html/run_a_xml_2.py5
-rw-r--r--tests/farm/html/run_unicode.py14
-rw-r--r--tests/farm/html/run_y_xml_branch.py5
-rw-r--r--tests/farm/html/src/coverage.xml3
-rw-r--r--tests/farm/run/run_chdir.py2
-rw-r--r--tests/farm/run/run_timid.py8
-rw-r--r--tests/farm/run/run_xxx.py4
-rw-r--r--tests/modules/pkg1/p1a.py2
-rw-r--r--tests/modules/plugins/__init__.py0
-rw-r--r--tests/modules/plugins/a_plugin.py6
-rw-r--r--tests/modules/plugins/another.py6
-rw-r--r--tests/test_api.py8
-rw-r--r--tests/test_backward.py6
-rw-r--r--tests/test_cmdline.py4
-rw-r--r--tests/test_codeunit.py28
-rw-r--r--tests/test_config.py168
-rw-r--r--tests/test_coroutine.py208
-rw-r--r--tests/test_coverage.py14
-rw-r--r--tests/test_data.py14
-rw-r--r--tests/test_execfile.py11
-rw-r--r--tests/test_farm.py66
-rw-r--r--tests/test_files.py77
-rw-r--r--tests/test_html.py42
-rw-r--r--tests/test_oddball.py66
-rw-r--r--tests/test_parser.py54
-rw-r--r--tests/test_phystokens.py5
-rw-r--r--tests/test_plugins.py217
-rw-r--r--tests/test_process.py94
-rw-r--r--tests/test_summary.py148
-rw-r--r--tests/test_templite.py70
-rw-r--r--tests/test_testing.py42
-rw-r--r--tests/test_xml.py7
41 files changed, 1184 insertions, 637 deletions
diff --git a/tests/backtest.py b/tests/backtest.py
index 89a25536..439493d1 100644
--- a/tests/backtest.py
+++ b/tests/backtest.py
@@ -4,41 +4,31 @@
# (Redefining built-in blah)
# The whole point of this file is to redefine built-ins, so shut up about it.
-import os
+import subprocess
-# Py2 and Py3 don't agree on how to run commands in a subprocess.
-try:
- import subprocess
-except ImportError:
- def run_command(cmd, status=0):
- """Run a command in a subprocess.
-
- Returns the exit status code and the combined stdout and stderr.
- """
- _, stdouterr = os.popen4(cmd)
- return status, stdouterr.read()
+# This isn't really a backward compatibility thing, should be moved into a
+# helpers file or something.
+def run_command(cmd):
+ """Run a command in a subprocess.
-else:
- def run_command(cmd, status=0):
- """Run a command in a subprocess.
+ Returns the exit status code and the combined stdout and stderr.
- Returns the exit status code and the combined stdout and stderr.
+ """
+ proc = subprocess.Popen(cmd, shell=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT
+ )
+ output, _ = proc.communicate()
+ status = proc.returncode # pylint: disable=E1101
- """
- proc = subprocess.Popen(cmd, shell=True,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT
- )
- output, _ = proc.communicate()
- status = proc.returncode # pylint: disable=E1101
+ # Get the output, and canonicalize it to strings with newlines.
+ if not isinstance(output, str):
+ output = output.decode('utf-8')
+ output = output.replace('\r', '')
- # Get the output, and canonicalize it to strings with newlines.
- if not isinstance(output, str):
- output = output.decode('utf-8')
- output = output.replace('\r', '')
+ return status, output
- return status, output
# No more execfile in Py3
try:
@@ -46,4 +36,6 @@ try:
except NameError:
def execfile(filename, globs):
"""A Python 3 implementation of execfile."""
- exec(compile(open(filename).read(), filename, 'exec'), globs)
+ with open(filename) as fobj:
+ code = fobj.read()
+ exec(compile(code, filename, 'exec'), globs)
diff --git a/tests/backunittest.py b/tests/backunittest.py
deleted file mode 100644
index ca741d37..00000000
--- a/tests/backunittest.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Implementations of unittest features from the future."""
-
-# Use unittest2 if it's available, otherwise unittest. This gives us
-# backported features for 2.6.
-try:
- import unittest2 as unittest # pylint: disable=F0401
-except ImportError:
- import unittest
-
-
-def _need(method):
- """Do we need to define our own `method` method?"""
- return not hasattr(unittest.TestCase, method)
-
-
-class TestCase(unittest.TestCase):
- """Just like unittest.TestCase, but with assert methods added.
-
- Designed to be compatible with 3.1 unittest. Methods are only defined if
- `unittest` doesn't have them.
-
- """
- if _need('assertSameElements'):
- def assertSameElements(self, s1, s2):
- """Assert that the two arguments are equal as sets."""
- self.assertEqual(set(s1), set(s2))
diff --git a/tests/coveragetest.py b/tests/coveragetest.py
index 0467d808..4053059f 100644
--- a/tests/coveragetest.py
+++ b/tests/coveragetest.py
@@ -1,47 +1,39 @@
"""Base test case class for coverage testing."""
-import glob, imp, os, random, shlex, shutil, sys, tempfile, textwrap
-import atexit, collections
+import glob, os, random, re, shlex, shutil, sys
import coverage
-from coverage.backward import StringIO, to_bytes
+from coverage.backunittest import TestCase
+from coverage.backward import StringIO, import_local_file
+from coverage.backward import importlib # pylint: disable=unused-import
from coverage.control import _TEST_NAME_FILE
-from tests.backtest import run_command
-from tests.backunittest import TestCase
-
-class Tee(object):
- """A file-like that writes to all the file-likes it has."""
-
- def __init__(self, *files):
- """Make a Tee that writes to all the files in `files.`"""
- self._files = files
- if hasattr(files[0], "encoding"):
- self.encoding = files[0].encoding
-
- def write(self, data):
- """Write `data` to all the files."""
- for f in self._files:
- f.write(data)
+from coverage.test_helpers import (
+ ModuleAwareMixin, SysPathAwareMixin, EnvironmentAwareMixin,
+ StdStreamCapturingMixin, TempDirMixin,
+)
- if 0:
- # Use this if you need to use a debugger, though it makes some tests
- # fail, I'm not sure why...
- def __getattr__(self, name):
- return getattr(self._files[0], name)
+from tests.backtest import run_command
# Status returns for the command line.
OK, ERR = 0, 1
-class CoverageTest(TestCase):
+class CoverageTest(
+ ModuleAwareMixin,
+ SysPathAwareMixin,
+ EnvironmentAwareMixin,
+ StdStreamCapturingMixin,
+ TempDirMixin,
+ TestCase
+):
"""A base class for Coverage test cases."""
- # Our own setting: most CoverageTests run in their own temp directory.
- run_in_temp_dir = True
-
# Standard unittest setting: show me diffs even if they are very long.
maxDiff = None
+ # Tell newer unittest implementations to print long helpful messages.
+ longMessage = True
+
def setUp(self):
super(CoverageTest, self).setUp()
@@ -50,151 +42,6 @@ class CoverageTest(TestCase):
f.write("%s_%s" % (self.__class__.__name__, self._testMethodName))
f.close()
- # Tell newer unittest implementations to print long helpful messages.
- self.longMessage = True
-
- # tearDown will restore the original sys.path
- self.old_syspath = sys.path[:]
-
- if self.run_in_temp_dir:
- # Create a temporary directory.
- self.noise = str(random.random())[2:]
- self.temp_root = os.path.join(tempfile.gettempdir(), 'test_cover')
- self.temp_dir = os.path.join(self.temp_root, self.noise)
- os.makedirs(self.temp_dir)
- self.old_dir = os.getcwd()
- os.chdir(self.temp_dir)
-
- # Modules should be importable from this temp directory. We don't
- # use '' because we make lots of different temp directories and
- # nose's caching importer can get confused. The full path prevents
- # problems.
- sys.path.insert(0, os.getcwd())
-
- # Keep a counter to make every call to check_coverage unique.
- self.n = 0
-
- # Record environment variables that we changed with set_environ.
- self.environ_undos = {}
-
- # Capture stdout and stderr so we can examine them in tests.
- # nose keeps stdout from littering the screen, so we can safely Tee it,
- # but it doesn't capture stderr, so we don't want to Tee stderr to the
- # real stderr, since it will interfere with our nice field of dots.
- self.old_stdout = sys.stdout
- self.captured_stdout = StringIO()
- sys.stdout = Tee(sys.stdout, self.captured_stdout)
- self.old_stderr = sys.stderr
- self.captured_stderr = StringIO()
- sys.stderr = self.captured_stderr
-
- # Record sys.modules here so we can restore it in tearDown.
- self.old_modules = dict(sys.modules)
-
- class_behavior = self.class_behavior()
- class_behavior.tests += 1
- class_behavior.test_method_made_any_files = False
- class_behavior.temp_dir = self.run_in_temp_dir
-
- def tearDown(self):
- super(CoverageTest, self).tearDown()
-
- # Restore the original sys.path.
- sys.path = self.old_syspath
-
- if self.run_in_temp_dir:
- # Get rid of the temporary directory.
- os.chdir(self.old_dir)
- shutil.rmtree(self.temp_root)
-
- # Restore the environment.
- self.undo_environ()
-
- # Restore stdout and stderr
- sys.stdout = self.old_stdout
- sys.stderr = self.old_stderr
-
- self.clean_modules()
-
- class_behavior = self.class_behavior()
- if class_behavior.test_method_made_any_files:
- class_behavior.tests_making_files += 1
-
- def clean_modules(self):
- """Remove any new modules imported during the test run.
-
- This lets us import the same source files for more than one test.
-
- """
- for m in [m for m in sys.modules if m not in self.old_modules]:
- del sys.modules[m]
-
- def set_environ(self, name, value):
- """Set an environment variable `name` to be `value`.
-
- The environment variable is set, and record is kept that it was set,
- so that `tearDown` can restore its original value.
-
- """
- if name not in self.environ_undos:
- self.environ_undos[name] = os.environ.get(name)
- os.environ[name] = value
-
- def original_environ(self, name, if_missing=None):
- """The environment variable `name` from when the test started."""
- if name in self.environ_undos:
- ret = self.environ_undos[name]
- else:
- ret = os.environ.get(name)
- if ret is None:
- ret = if_missing
- return ret
-
- def undo_environ(self):
- """Undo all the changes made by `set_environ`."""
- for name, value in self.environ_undos.items():
- if value is None:
- del os.environ[name]
- else:
- os.environ[name] = value
-
- def stdout(self):
- """Return the data written to stdout during the test."""
- return self.captured_stdout.getvalue()
-
- def stderr(self):
- """Return the data written to stderr during the test."""
- return self.captured_stderr.getvalue()
-
- def make_file(self, filename, text="", newline=None):
- """Create a temp file.
-
- `filename` is the path to the file, including directories if desired,
- and `text` is the content. If `newline` is provided, it is a string
- that will be used as the line endings in the created file.
-
- Returns the path to the file.
-
- """
- # Tests that call `make_file` should be run in a temp environment.
- assert self.run_in_temp_dir
- self.class_behavior().test_method_made_any_files = True
-
- text = textwrap.dedent(text)
- if newline:
- text = text.replace("\n", newline)
-
- # Make sure the directories are available.
- dirs, _ = os.path.split(filename)
- if dirs and not os.path.exists(dirs):
- os.makedirs(dirs)
-
- # Create the file.
- with open(filename, 'wb') as f:
- f.write(to_bytes(text))
-
- return filename
-
def clean_local_file_imports(self):
"""Clean up the results of calls to `import_local_file`.
@@ -203,7 +50,7 @@ class CoverageTest(TestCase):
"""
# So that we can re-import files, clean them out first.
- self.clean_modules()
+ self.cleanup_modules()
# Also have to clean out the .pyc file, since the timestamp
# resolution is only one second, a changed file might not be
# picked up.
@@ -219,18 +66,7 @@ class CoverageTest(TestCase):
as `modname`, and returns the module object.
"""
- modfile = modname + '.py'
-
- for suff in imp.get_suffixes():
- if suff[0] == '.py':
- break
-
- with open(modfile, 'r') as f:
- # pylint: disable=W0631
- # (Using possibly undefined loop variable 'suff')
- mod = imp.load_module(modname, f, modfile, suff)
-
- return mod
+ return import_local_file(modname)
def start_import_stop(self, cov, modname):
"""Start coverage, import a file, then stop coverage.
@@ -252,13 +88,7 @@ class CoverageTest(TestCase):
def get_module_name(self):
"""Return the module name to use for this test run."""
- # We append self.n because otherwise two calls in one test will use the
- # same filename and whether the test works or not depends on the
- # timestamps in the .pyc file, so it becomes random whether the second
- # call will use the compiled version of the first call's code or not!
- modname = 'coverage_test_' + self.noise + str(self.n)
- self.n += 1
- return modname
+ return 'coverage_test_' + str(random.random())[2:]
# Map chars to numbers for arcz_to_arcs
_arcz_map = {'.': -1}
@@ -363,19 +193,21 @@ class CoverageTest(TestCase):
if statements == line_list:
break
else:
- self.fail("None of the lines choices matched %r" %
- statements
+ self.fail(
+ "None of the lines choices matched %r" % statements
)
+ missing_formatted = analysis.missing_formatted()
if type(missing) == type(""):
- self.assertEqual(analysis.missing_formatted(), missing)
+ self.assertEqual(missing_formatted, missing)
else:
for missing_list in missing:
- if analysis.missing_formatted() == missing_list:
+ if missing_formatted == missing_list:
break
else:
- self.fail("None of the missing choices matched %r" %
- analysis.missing_formatted()
+ self.fail(
+ "None of the missing choices matched %r" %
+ missing_formatted
)
if arcs is not None:
@@ -410,17 +242,17 @@ class CoverageTest(TestCase):
"""Assert that `flist1` and `flist2` are the same set of file names."""
flist1_nice = [self.nice_file(f) for f in flist1]
flist2_nice = [self.nice_file(f) for f in flist2]
- self.assertSameElements(flist1_nice, flist2_nice)
+ self.assertCountEqual(flist1_nice, flist2_nice)
def assert_exists(self, fname):
"""Assert that `fname` is a file that exists."""
msg = "File %r should exist" % fname
- self.assert_(os.path.exists(fname), msg)
+ self.assertTrue(os.path.exists(fname), msg)
def assert_doesnt_exist(self, fname):
"""Assert that `fname` is a file that doesn't exist."""
msg = "File %r shouldn't exist" % fname
- self.assert_(not os.path.exists(fname), msg)
+ self.assertTrue(not os.path.exists(fname), msg)
def assert_starts_with(self, s, prefix, msg=None):
"""Assert that `s` starts with `prefix`."""
@@ -464,7 +296,7 @@ class CoverageTest(TestCase):
_, output = self.run_command_status(cmd)
return output
- def run_command_status(self, cmd, status=0):
+ def run_command_status(self, cmd):
"""Run the command-line `cmd` in a subprocess, and print its output.
Use this when you need to test the process behavior of coverage.
@@ -473,9 +305,6 @@ class CoverageTest(TestCase):
Returns a pair: the process' exit status and stdout text.
- The `status` argument is returned as the status on older Pythons where
- we can't get the actual exit status of the process.
-
"""
# Add our test modules directory to PYTHONPATH. I'm sure there's too
# much path munging here, but...
@@ -488,58 +317,31 @@ class CoverageTest(TestCase):
pypath += testmods + os.pathsep + zipfile
self.set_environ('PYTHONPATH', pypath)
- status, output = run_command(cmd, status=status)
+ status, output = run_command(cmd)
print(output)
return status, output
- # We run some tests in temporary directories, because they may need to make
- # files for the tests. But this is expensive, so we can change per-class
- # whether a temp dir is used or not. It's easy to forget to set that
- # option properly, so we track information about what the tests did, and
- # then report at the end of the process on test classes that were set
- # wrong.
-
- class ClassBehavior(object):
- """A value object to store per-class in CoverageTest."""
- def __init__(self):
- self.tests = 0
- self.temp_dir = True
- self.tests_making_files = 0
- self.test_method_made_any_files = False
-
- # Map from class to info about how it ran.
- class_behaviors = collections.defaultdict(ClassBehavior)
-
- @classmethod
- def report_on_class_behavior(cls):
- """Called at process exit to report on class behavior."""
- for test_class, behavior in cls.class_behaviors.items():
- if behavior.temp_dir and behavior.tests_making_files == 0:
- bad = "Inefficient"
- elif not behavior.temp_dir and behavior.tests_making_files > 0:
- bad = "Unsafe"
- else:
- bad = ""
-
- if bad:
- if behavior.temp_dir:
- where = "in a temp directory"
- else:
- where = "without a temp directory"
- print(
- "%s: %s ran %d tests, %d made files %s" % (
- bad,
- test_class.__name__,
- behavior.tests,
- behavior.tests_making_files,
- where,
- )
- )
-
- def class_behavior(self):
- """Get the ClassBehavior instance for this test."""
- return self.class_behaviors[self.__class__]
-
-
-# When the process ends, find out about bad classes.
-atexit.register(CoverageTest.report_on_class_behavior)
+ def report_from_command(self, cmd):
+ """Return the report from the `cmd`, with some convenience added."""
+ report = self.run_command(cmd).replace('\\', '/')
+ self.assertNotIn("error", report.lower())
+ return report
+
+ def report_lines(self, report):
+ """Return the lines of the report, as a list."""
+ lines = report.split('\n')
+ self.assertEqual(lines[-1], "")
+ return lines[:-1]
+
+ def line_count(self, report):
+ """How many lines are in `report`?"""
+ return len(self.report_lines(report))
+
+ def squeezed_lines(self, report):
+ """Return a list of the lines in report, with the spaces squeezed."""
+ lines = self.report_lines(report)
+ return [re.sub(r"\s+", " ", l.strip()) for l in lines]
+
+ def last_line_squeezed(self, report):
+ """Return the last line of `report` with the spaces squeezed down."""
+ return self.squeezed_lines(report)[-1]
diff --git a/tests/farm/annotate/annotate_dir.py b/tests/farm/annotate/annotate_dir.py
index 3e37f9ed..86c18cab 100644
--- a/tests/farm/annotate/annotate_dir.py
+++ b/tests/farm/annotate/annotate_dir.py
@@ -1,7 +1,7 @@
copy("src", "run")
run("""
- coverage -e -x multi.py
- coverage -a -d out_anno_dir
+ coverage run multi.py
+ coverage annotate -d out_anno_dir
""", rundir="run")
compare("run/out_anno_dir", "gold_anno_dir", "*,cover", left_extra=True)
clean("run")
diff --git a/tests/farm/annotate/run.py b/tests/farm/annotate/run.py
index c645f21c..236f401f 100644
--- a/tests/farm/annotate/run.py
+++ b/tests/farm/annotate/run.py
@@ -1,7 +1,7 @@
copy("src", "out")
run("""
- coverage -e -x white.py
- coverage -a white.py
+ coverage run white.py
+ coverage annotate white.py
""", rundir="out")
compare("out", "gold", "*,cover")
clean("out")
diff --git a/tests/farm/annotate/run_multi.py b/tests/farm/annotate/run_multi.py
index 4e8252ed..ef1e8238 100644
--- a/tests/farm/annotate/run_multi.py
+++ b/tests/farm/annotate/run_multi.py
@@ -1,7 +1,7 @@
copy("src", "out_multi")
run("""
- coverage -e -x multi.py
- coverage -a
+ coverage run multi.py
+ coverage annotate
""", rundir="out_multi")
compare("out_multi", "gold_multi", "*,cover")
clean("out_multi")
diff --git a/tests/farm/html/gold_x_xml/coverage.xml b/tests/farm/html/gold_x_xml/coverage.xml
index 912112f2..d5a8c442 100644
--- a/tests/farm/html/gold_x_xml/coverage.xml
+++ b/tests/farm/html/gold_x_xml/coverage.xml
@@ -3,6 +3,9 @@
SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
<coverage branch-rate="0" line-rate="0.6667" timestamp="1253972570431" version="3.1b1">
<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage/VER -->
+ <sources>
+ <source></source>
+ </sources>
<packages>
<package branch-rate="0" complexity="0" line-rate="0.6667" name="">
<classes>
diff --git a/tests/farm/html/gold_y_xml_branch/coverage.xml b/tests/farm/html/gold_y_xml_branch/coverage.xml
index ecbe0073..86e9e73c 100644
--- a/tests/farm/html/gold_y_xml_branch/coverage.xml
+++ b/tests/farm/html/gold_y_xml_branch/coverage.xml
@@ -3,6 +3,9 @@
SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
<coverage branch-rate="0.5" line-rate="0.8" timestamp="1259288252325" version="3.2b4">
<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage/VER -->
+ <sources>
+ <source></source>
+ </sources>
<packages>
<package branch-rate="0.5" complexity="0" line-rate="0.8" name="">
<classes>
diff --git a/tests/farm/html/run_a_xml_1.py b/tests/farm/html/run_a_xml_1.py
index 3d187023..83f8c86d 100644
--- a/tests/farm/html/run_a_xml_1.py
+++ b/tests/farm/html/run_a_xml_1.py
@@ -1,3 +1,5 @@
+source_path = None
+
def html_it():
"""Run coverage and make an XML report for a."""
import coverage
@@ -6,6 +8,8 @@ def html_it():
import a # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(a, outfile="../xml_1/coverage.xml")
+ global source_path
+ source_path = cov.file_locator.relative_dir.rstrip('/')
import os
if not os.path.exists("xml_1"):
@@ -16,6 +20,7 @@ runfunc(html_it, rundir="src")
compare("gold_x_xml", "xml_1", scrubs=[
(r' timestamp="\d+"', ' timestamp="TIMESTAMP"'),
(r' version="[-.\w]+"', ' version="VERSION"'),
+ (r'<source>\s*.*?\s*</source>', '<source>%s</source>' % source_path),
(r'/code/coverage/?[-.\w]*', '/code/coverage/VER'),
])
clean("xml_1")
diff --git a/tests/farm/html/run_a_xml_2.py b/tests/farm/html/run_a_xml_2.py
index 53691ead..6dd44225 100644
--- a/tests/farm/html/run_a_xml_2.py
+++ b/tests/farm/html/run_a_xml_2.py
@@ -1,3 +1,5 @@
+source_path = None
+
def html_it():
"""Run coverage and make an XML report for a."""
import coverage
@@ -6,6 +8,8 @@ def html_it():
import a # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(a)
+ global source_path
+ source_path = cov.file_locator.relative_dir.rstrip('/')
import os
if not os.path.exists("xml_2"):
@@ -16,6 +20,7 @@ runfunc(html_it, rundir="src")
compare("gold_x_xml", "xml_2", scrubs=[
(r' timestamp="\d+"', ' timestamp="TIMESTAMP"'),
(r' version="[-.\w]+"', ' version="VERSION"'),
+ (r'<source>\s*.*?\s*</source>', '<source>%s</source>' % source_path),
(r'/code/coverage/?[-.\w]*', '/code/coverage/VER'),
])
clean("xml_2")
diff --git a/tests/farm/html/run_unicode.py b/tests/farm/html/run_unicode.py
index cef26ee5..c8cb6c50 100644
--- a/tests/farm/html/run_unicode.py
+++ b/tests/farm/html/run_unicode.py
@@ -1,5 +1,3 @@
-import sys
-
def html_it():
"""Run coverage and make an HTML report for unicode.py."""
import coverage
@@ -18,13 +16,9 @@ contains("html_unicode/unicode.html",
"<span class='str'>&quot;&#654;d&#729;&#477;b&#592;&#633;&#477;&#652;o&#596;&quot;</span>",
)
-if sys.maxunicode == 65535:
- contains("html_unicode/unicode.html",
- "<span class='str'>&quot;db40,dd00: x&#56128;&#56576;&quot;</span>",
- )
-else:
- contains("html_unicode/unicode.html",
- "<span class='str'>&quot;db40,dd00: x&#917760;&quot;</span>",
- )
+contains_any("html_unicode/unicode.html",
+ "<span class='str'>&quot;db40,dd00: x&#56128;&#56576;&quot;</span>",
+ "<span class='str'>&quot;db40,dd00: x&#917760;&quot;</span>",
+ )
clean("html_unicode")
diff --git a/tests/farm/html/run_y_xml_branch.py b/tests/farm/html/run_y_xml_branch.py
index 88a2e44e..9ae9a9f0 100644
--- a/tests/farm/html/run_y_xml_branch.py
+++ b/tests/farm/html/run_y_xml_branch.py
@@ -1,3 +1,5 @@
+source_path = None
+
def xml_it():
"""Run coverage and make an XML report for y."""
import coverage
@@ -6,6 +8,8 @@ def xml_it():
import y # pragma: nested
cov.stop() # pragma: nested
cov.xml_report(y, outfile="../xml_branch/coverage.xml")
+ global source_path
+ source_path = cov.file_locator.relative_dir.rstrip('/')
import os
if not os.path.exists("xml_branch"):
@@ -16,6 +20,7 @@ runfunc(xml_it, rundir="src")
compare("gold_y_xml_branch", "xml_branch", scrubs=[
(r' timestamp="\d+"', ' timestamp="TIMESTAMP"'),
(r' version="[-.\w]+"', ' version="VERSION"'),
+ (r'<source>\s*.*?\s*</source>', '<source>%s</source>' % source_path),
(r'/code/coverage/?[-.\w]*', '/code/coverage/VER'),
])
clean("xml_branch")
diff --git a/tests/farm/html/src/coverage.xml b/tests/farm/html/src/coverage.xml
index 128cf750..e20cdaec 100644
--- a/tests/farm/html/src/coverage.xml
+++ b/tests/farm/html/src/coverage.xml
@@ -3,6 +3,9 @@
SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
<coverage branch-rate="0.0" line-rate="0.666666666667" timestamp="1263087779313" version="3.3a1">
<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage -->
+ <sources>
+ <source></source>
+ </sources>
<packages>
<package branch-rate="0.0" complexity="0.0" line-rate="0.666666666667" name="">
<classes>
diff --git a/tests/farm/run/run_chdir.py b/tests/farm/run/run_chdir.py
index f459f500..367cd0ad 100644
--- a/tests/farm/run/run_chdir.py
+++ b/tests/farm/run/run_chdir.py
@@ -1,7 +1,7 @@
copy("src", "out")
run("""
coverage run chdir.py
- coverage -r
+ coverage report
""", rundir="out", outfile="stdout.txt")
contains("out/stdout.txt",
"Line One",
diff --git a/tests/farm/run/run_timid.py b/tests/farm/run/run_timid.py
index ce78fff1..d4e69a46 100644
--- a/tests/farm/run/run_timid.py
+++ b/tests/farm/run/run_timid.py
@@ -17,8 +17,8 @@ if os.environ.get('COVERAGE_COVERAGE', ''):
copy("src", "out")
run("""
python showtrace.py none
- coverage -e -x showtrace.py regular
- coverage -e -x --timid showtrace.py timid
+ coverage run showtrace.py regular
+ coverage run --timid showtrace.py timid
""", rundir="out", outfile="showtraceout.txt")
# When running without coverage, no trace function
@@ -42,8 +42,8 @@ old_opts = os.environ.get('COVERAGE_OPTIONS')
os.environ['COVERAGE_OPTIONS'] = '--timid'
run("""
- coverage -e -x showtrace.py regular
- coverage -e -x --timid showtrace.py timid
+ coverage run showtrace.py regular
+ coverage run --timid showtrace.py timid
""", rundir="out", outfile="showtraceout.txt")
contains("out/showtraceout.txt",
diff --git a/tests/farm/run/run_xxx.py b/tests/farm/run/run_xxx.py
index 19e94a42..6fedc934 100644
--- a/tests/farm/run/run_xxx.py
+++ b/tests/farm/run/run_xxx.py
@@ -1,7 +1,7 @@
copy("src", "out")
run("""
- coverage -e -x xxx
- coverage -r
+ coverage run xxx
+ coverage report
""", rundir="out", outfile="stdout.txt")
contains("out/stdout.txt",
"xxx: 3 4 0 7",
diff --git a/tests/modules/pkg1/p1a.py b/tests/modules/pkg1/p1a.py
index be5fcdd3..337add49 100644
--- a/tests/modules/pkg1/p1a.py
+++ b/tests/modules/pkg1/p1a.py
@@ -1,5 +1,5 @@
import os, sys
# Invoke functions in os and sys so we can see if we measure code there.
-x = sys.getcheckinterval()
+x = sys.getfilesystemencoding()
y = os.getcwd()
diff --git a/tests/modules/plugins/__init__.py b/tests/modules/plugins/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/modules/plugins/__init__.py
diff --git a/tests/modules/plugins/a_plugin.py b/tests/modules/plugins/a_plugin.py
new file mode 100644
index 00000000..2ff84dac
--- /dev/null
+++ b/tests/modules/plugins/a_plugin.py
@@ -0,0 +1,6 @@
+"""A plugin for tests to reference."""
+
+from coverage import CoveragePlugin
+
+class Plugin(CoveragePlugin):
+ pass
diff --git a/tests/modules/plugins/another.py b/tests/modules/plugins/another.py
new file mode 100644
index 00000000..2ff84dac
--- /dev/null
+++ b/tests/modules/plugins/another.py
@@ -0,0 +1,6 @@
+"""A plugin for tests to reference."""
+
+from coverage import CoveragePlugin
+
+class Plugin(CoveragePlugin):
+ pass
diff --git a/tests/test_api.py b/tests/test_api.py
index 097947d2..31bfc57f 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -100,7 +100,7 @@ class ApiTest(CoverageTest):
"""Assert that the files here are `files`, ignoring the usual junk."""
here = os.listdir(".")
here = self.clean_files(here, ["*.pyc", "__pycache__"])
- self.assertSameElements(here, files)
+ self.assertCountEqual(here, files)
def test_unexecuted_file(self):
cov = coverage.coverage()
@@ -221,7 +221,7 @@ class ApiTest(CoverageTest):
self.assertEqual(cov.get_exclude_list(), ["foo"])
cov.exclude("bar")
self.assertEqual(cov.get_exclude_list(), ["foo", "bar"])
- self.assertEqual(cov._exclude_regex('exclude'), "(foo)|(bar)")
+ self.assertEqual(cov._exclude_regex('exclude'), "(?:foo)|(?:bar)")
cov.clear_exclude()
self.assertEqual(cov.get_exclude_list(), [])
@@ -233,7 +233,9 @@ class ApiTest(CoverageTest):
self.assertEqual(cov.get_exclude_list(which='partial'), ["foo"])
cov.exclude("bar", which='partial')
self.assertEqual(cov.get_exclude_list(which='partial'), ["foo", "bar"])
- self.assertEqual(cov._exclude_regex(which='partial'), "(foo)|(bar)")
+ self.assertEqual(
+ cov._exclude_regex(which='partial'), "(?:foo)|(?:bar)"
+ )
cov.clear_exclude(which='partial')
self.assertEqual(cov.get_exclude_list(which='partial'), [])
diff --git a/tests/test_backward.py b/tests/test_backward.py
index e98017ae..09803ba7 100644
--- a/tests/test_backward.py
+++ b/tests/test_backward.py
@@ -1,18 +1,16 @@
"""Tests that our version shims in backward.py are working."""
+from coverage.backunittest import TestCase
from coverage.backward import iitems, binary_bytes, byte_to_int, bytes_to_ints
-from tests.backunittest import TestCase
class BackwardTest(TestCase):
"""Tests of things from backward.py."""
- run_in_temp_dir = False
-
def test_iitems(self):
d = {'a': 1, 'b': 2, 'c': 3}
items = [('a', 1), ('b', 2), ('c', 3)]
- self.assertSameElements(list(iitems(d)), items)
+ self.assertCountEqual(list(iitems(d)), items)
def test_binary_bytes(self):
byte_values = [0, 255, 17, 23, 42, 57]
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index 99bae516..08f7937a 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -72,7 +72,7 @@ class CmdLineTest(CoverageTest):
code = re.sub(r"(?m)^\.", "m2.", code)
m2 = self.model_object()
code_obj = compile(code, "<code>", "exec")
- eval(code_obj, globals(), { 'm2': m2 })
+ eval(code_obj, globals(), { 'm2': m2 }) # pylint: disable=eval-used
# Many of our functions take a lot of arguments, and cmdline.py
# calls them with many. But most of them are just the defaults, which
@@ -754,7 +754,7 @@ class CmdMainTest(CoverageTest):
self.assertEqual(err[-2], 'Exception: oh noes!')
def test_internalraise(self):
- with self.assertRaisesRegexp(ValueError, "coverage is broken"):
+ with self.assertRaisesRegex(ValueError, "coverage is broken"):
coverage.cmdline.main(['internalraise'])
def test_exit(self):
diff --git a/tests/test_codeunit.py b/tests/test_codeunit.py
index e4912e11..fe82ea1c 100644
--- a/tests/test_codeunit.py
+++ b/tests/test_codeunit.py
@@ -31,9 +31,9 @@ class CodeUnitTest(CoverageTest):
self.assertEqual(acu[0].flat_rootname(), "aa_afile")
self.assertEqual(bcu[0].flat_rootname(), "aa_bb_bfile")
self.assertEqual(ccu[0].flat_rootname(), "aa_bb_cc_cfile")
- self.assertEqual(acu[0].source_file().read(), "# afile.py\n")
- self.assertEqual(bcu[0].source_file().read(), "# bfile.py\n")
- self.assertEqual(ccu[0].source_file().read(), "# cfile.py\n")
+ self.assertEqual(acu[0].source(), "# afile.py\n")
+ self.assertEqual(bcu[0].source(), "# bfile.py\n")
+ self.assertEqual(ccu[0].source(), "# cfile.py\n")
def test_odd_filenames(self):
acu = code_unit_factory("aa/afile.odd.py", FileLocator())
@@ -45,9 +45,9 @@ class CodeUnitTest(CoverageTest):
self.assertEqual(acu[0].flat_rootname(), "aa_afile_odd")
self.assertEqual(bcu[0].flat_rootname(), "aa_bb_bfile_odd")
self.assertEqual(b2cu[0].flat_rootname(), "aa_bb_odd_bfile")
- self.assertEqual(acu[0].source_file().read(), "# afile.odd.py\n")
- self.assertEqual(bcu[0].source_file().read(), "# bfile.odd.py\n")
- self.assertEqual(b2cu[0].source_file().read(), "# bfile.py\n")
+ self.assertEqual(acu[0].source(), "# afile.odd.py\n")
+ self.assertEqual(bcu[0].source(), "# bfile.odd.py\n")
+ self.assertEqual(b2cu[0].source(), "# bfile.py\n")
def test_modules(self):
import aa, aa.bb, aa.bb.cc
@@ -58,9 +58,9 @@ class CodeUnitTest(CoverageTest):
self.assertEqual(cu[0].flat_rootname(), "aa")
self.assertEqual(cu[1].flat_rootname(), "aa_bb")
self.assertEqual(cu[2].flat_rootname(), "aa_bb_cc")
- self.assertEqual(cu[0].source_file().read(), "# aa\n")
- self.assertEqual(cu[1].source_file().read(), "# bb\n")
- self.assertEqual(cu[2].source_file().read(), "") # yes, empty
+ self.assertEqual(cu[0].source(), "# aa\n")
+ self.assertEqual(cu[1].source(), "# bb\n")
+ self.assertEqual(cu[2].source(), "") # yes, empty
def test_module_files(self):
import aa.afile, aa.bb.bfile, aa.bb.cc.cfile
@@ -72,9 +72,9 @@ class CodeUnitTest(CoverageTest):
self.assertEqual(cu[0].flat_rootname(), "aa_afile")
self.assertEqual(cu[1].flat_rootname(), "aa_bb_bfile")
self.assertEqual(cu[2].flat_rootname(), "aa_bb_cc_cfile")
- self.assertEqual(cu[0].source_file().read(), "# afile.py\n")
- self.assertEqual(cu[1].source_file().read(), "# bfile.py\n")
- self.assertEqual(cu[2].source_file().read(), "# cfile.py\n")
+ self.assertEqual(cu[0].source(), "# afile.py\n")
+ self.assertEqual(cu[1].source(), "# bfile.py\n")
+ self.assertEqual(cu[2].source(), "# cfile.py\n")
def test_comparison(self):
acu = code_unit_factory("aa/afile.py", FileLocator())[0]
@@ -97,7 +97,7 @@ class CodeUnitTest(CoverageTest):
self.assert_doesnt_exist(egg1.__file__)
cu = code_unit_factory([egg1, egg1.egg1], FileLocator())
- self.assertEqual(cu[0].source_file().read(), "")
- self.assertEqual(cu[1].source_file().read().split("\n")[0],
+ self.assertEqual(cu[0].source(), "")
+ self.assertEqual(cu[1].source().split("\n")[0],
"# My egg file!"
)
diff --git a/tests/test_config.py b/tests/test_config.py
index 7fa31208..bf84423d 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
"""Test the config file handling for coverage.py"""
+import sys, os
+
import coverage
from coverage.misc import CoverageException
@@ -125,58 +127,84 @@ class ConfigTest(CoverageTest):
class ConfigFileTest(CoverageTest):
"""Tests of the config file settings in particular."""
- def test_config_file_settings(self):
- # This sample file tries to use lots of variation of syntax...
- self.make_file(".coveragerc", """\
- # This is a settings file for coverage.py
- [run]
- timid = yes
- data_file = something_or_other.dat
- branch = 1
- cover_pylib = TRUE
- parallel = on
- include = a/ , b/
-
- [report]
- ; these settings affect reporting.
- exclude_lines =
- if 0:
-
- pragma:?\\s+no cover
- another_tab
-
- ignore_errors = TRUE
- omit =
- one, another, some_more,
- yet_more
- precision = 3
-
- partial_branches =
- pragma:?\\s+no branch
- partial_branches_always =
- if 0:
- while True:
-
- show_missing= TruE
-
- [html]
-
- directory = c:\\tricky\\dir.somewhere
- extra_css=something/extra.css
- title = Title & nums # nums!
- [xml]
- output=mycov.xml
-
- [paths]
- source =
- .
- /home/ned/src/
-
- other = other, /home/ned/other, c:\\Ned\\etc
-
- """)
- cov = coverage.coverage()
-
+ def setUp(self):
+ super(ConfigFileTest, self).setUp()
+ # Parent class saves and restores sys.path, we can just modify it.
+ # Add modules to the path so we can import plugins.
+ sys.path.append(self.nice_file(os.path.dirname(__file__), 'modules'))
+
+ # This sample file tries to use lots of variation of syntax...
+ # The {section} placeholder lets us nest these settings in another file.
+ LOTSA_SETTINGS = """\
+ # This is a settings file for coverage.py
+ [{section}run]
+ timid = yes
+ data_file = something_or_other.dat
+ branch = 1
+ cover_pylib = TRUE
+ parallel = on
+ include = a/ , b/
+ plugins =
+ plugins.a_plugin
+ plugins.another
+
+ [{section}report]
+ ; these settings affect reporting.
+ exclude_lines =
+ if 0:
+
+ pragma:?\\s+no cover
+ another_tab
+
+ ignore_errors = TRUE
+ omit =
+ one, another, some_more,
+ yet_more
+ precision = 3
+
+ partial_branches =
+ pragma:?\\s+no branch
+ partial_branches_always =
+ if 0:
+ while True:
+
+ show_missing= TruE
+
+ [{section}html]
+
+ directory = c:\\tricky\\dir.somewhere
+ extra_css=something/extra.css
+ title = Title & nums # nums!
+ [{section}xml]
+ output=mycov.xml
+
+ [{section}paths]
+ source =
+ .
+ /home/ned/src/
+
+ other = other, /home/ned/other, c:\\Ned\\etc
+
+ [{section}plugins.a_plugin]
+ hello = world
+ ; comments still work.
+ names = Jane/John/Jenny
+ """
+
+ # Just some sample setup.cfg text from the docs.
+ SETUP_CFG = """\
+ [bdist_rpm]
+ release = 1
+ packager = Jane Packager <janep@pysoft.com>
+ doc_files = CHANGES.txt
+ README.txt
+ USAGE.txt
+ doc/
+ examples/
+ """
+
+ def assert_config_settings_are_correct(self, cov):
+ """Check that `cov` has all the settings from LOTSA_SETTINGS."""
self.assertTrue(cov.config.timid)
self.assertEqual(cov.config.data_file, "something_or_other.dat")
self.assertTrue(cov.config.branch)
@@ -199,6 +227,9 @@ class ConfigFileTest(CoverageTest):
self.assertEqual(cov.config.partial_always_list,
["if 0:", "while True:"]
)
+ self.assertEqual(cov.config.plugins,
+ ["plugins.a_plugin", "plugins.another"]
+ )
self.assertTrue(cov.config.show_missing)
self.assertEqual(cov.config.html_dir, r"c:\tricky\dir.somewhere")
self.assertEqual(cov.config.extra_css, "something/extra.css")
@@ -211,8 +242,39 @@ class ConfigFileTest(CoverageTest):
'other': ['other', '/home/ned/other', 'c:\\Ned\\etc']
})
+ self.assertEqual(cov.config.get_plugin_options("plugins.a_plugin"), {
+ 'hello': 'world',
+ 'names': 'Jane/John/Jenny',
+ })
+ self.assertEqual(cov.config.get_plugin_options("plugins.another"), {})
+
+ def test_config_file_settings(self):
+ self.make_file(".coveragerc", self.LOTSA_SETTINGS.format(section=""))
+ cov = coverage.coverage()
+ self.assert_config_settings_are_correct(cov)
+
+ def test_config_file_settings_in_setupcfg(self):
+ nested = self.LOTSA_SETTINGS.format(section="coverage:")
+ self.make_file("setup.cfg", nested + "\n" + self.SETUP_CFG)
+ cov = coverage.coverage()
+ self.assert_config_settings_are_correct(cov)
+
+ def test_setupcfg_only_if_not_coveragerc(self):
+ self.make_file(".coveragerc", """\
+ [run]
+ include = foo
+ """)
+ self.make_file("setup.cfg", """\
+ [run]
+ omit = bar
+ branch = true
+ """)
+ cov = coverage.coverage()
+ self.assertEqual(cov.config.include, ["foo"])
+ self.assertEqual(cov.config.omit, None)
+ self.assertEqual(cov.config.branch, False)
+
def test_one(self):
- # This sample file tries to use lots of variation of syntax...
self.make_file(".coveragerc", """\
[html]
title = tabblo & «ταБЬℓσ» # numbers
diff --git a/tests/test_coroutine.py b/tests/test_coroutine.py
new file mode 100644
index 00000000..4abdd6f6
--- /dev/null
+++ b/tests/test_coroutine.py
@@ -0,0 +1,208 @@
+"""Tests for coroutining."""
+
+import os, os.path, sys, threading
+
+import coverage
+
+from tests.coveragetest import CoverageTest
+
+
+# These libraries aren't always available, we'll skip tests if they aren't.
+
+try:
+ import eventlet # pylint: disable=import-error
+except ImportError:
+ eventlet = None
+
+try:
+ import gevent # pylint: disable=import-error
+except ImportError:
+ gevent = None
+
+try:
+ import greenlet # pylint: disable=import-error
+except ImportError:
+ greenlet = None
+
+# Are we running with the C tracer or not?
+C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
+
+
+def line_count(s):
+ """How many non-blank non-comment lines are in `s`?"""
+ def code_line(l):
+ """Is this a code line? Not blank, and not a full-line comment."""
+ return l.strip() and not l.strip().startswith('#')
+ return sum(1 for l in s.splitlines() if code_line(l))
+
+
+class CoroutineTest(CoverageTest):
+ """Tests of the coroutine support in coverage.py."""
+
+ LIMIT = 1000
+
+ # The code common to all the concurrency models.
+ COMMON = """
+ class Producer(threading.Thread):
+ def __init__(self, q):
+ threading.Thread.__init__(self)
+ self.q = q
+
+ def run(self):
+ for i in range({LIMIT}):
+ self.q.put(i)
+ self.q.put(None)
+
+ class Consumer(threading.Thread):
+ def __init__(self, q):
+ threading.Thread.__init__(self)
+ self.q = q
+
+ def run(self):
+ sum = 0
+ while True:
+ i = self.q.get()
+ if i is None:
+ print(sum)
+ break
+ sum += i
+
+ q = queue.Queue()
+ c = Consumer(q)
+ p = Producer(q)
+ c.start()
+ p.start()
+
+ p.join()
+ c.join()
+ """.format(LIMIT=LIMIT)
+
+ # Import the things to use threads.
+ if sys.version_info < (3, 0):
+ THREAD = """\
+ import threading
+ import Queue as queue
+ """ + COMMON
+ else:
+ THREAD = """\
+ import threading
+ import queue
+ """ + COMMON
+
+ # Import the things to use eventlet.
+ EVENTLET = """\
+ import eventlet.green.threading as threading
+ import eventlet.queue as queue
+ """ + COMMON
+
+ # Import the things to use gevent.
+ GEVENT = """\
+ from gevent import monkey
+ monkey.patch_thread()
+ import threading
+ import gevent.queue as queue
+ """ + COMMON
+
+ # Uncomplicated code that doesn't use any of the coroutining stuff, to test
+ # the simple case under each of the regimes.
+ SIMPLE = """\
+ total = 0
+ for i in range({LIMIT}):
+ total += i
+ print(total)
+ """.format(LIMIT=LIMIT)
+
+ def try_some_code(self, code, coroutine, the_module, expected_out=None):
+ """Run some coroutine testing code and see that it was all covered.
+
+ `code` is the Python code to execute. `coroutine` is the name of the
+ coroutine regime to test it under. `the_module` is the imported module
+ that must be available for this to work at all. `expected_out` is the
+ text we expect the code to produce.
+
+ """
+
+ self.make_file("try_it.py", code)
+
+ cmd = "coverage run --coroutine=%s try_it.py" % coroutine
+ out = self.run_command(cmd)
+
+ if not the_module:
+ # We don't even have the underlying module installed, we expect
+ # coverage to alert us to this fact.
+ expected_out = (
+ "Couldn't trace with coroutine=%s, "
+ "the module isn't installed.\n" % coroutine
+ )
+ self.assertEqual(out, expected_out)
+ elif C_TRACER or coroutine == "thread":
+ # We can fully measure the code if we are using the C tracer, which
+ # can support all the coroutining, or if we are using threads.
+ if expected_out is None:
+ expected_out = "%d\n" % (sum(range(self.LIMIT)))
+ self.assertEqual(out, expected_out)
+
+ # Read the coverage file and see that try_it.py has all its lines
+ # executed.
+ data = coverage.CoverageData()
+ data.read_file(".coverage")
+
+ # If the test fails, it's helpful to see this info:
+ fname = os.path.abspath("try_it.py")
+ linenos = data.executed_lines(fname).keys()
+ print("{0}: {1}".format(len(linenos), linenos))
+ print_simple_annotation(code, linenos)
+
+ lines = line_count(code)
+ self.assertEqual(data.summary()['try_it.py'], lines)
+ else:
+ expected_out = (
+ "Can't support coroutine=%s with PyTracer, "
+ "only threads are supported\n" % coroutine
+ )
+ self.assertEqual(out, expected_out)
+
+ def test_threads(self):
+ self.try_some_code(self.THREAD, "thread", threading)
+
+ def test_threads_simple_code(self):
+ self.try_some_code(self.SIMPLE, "thread", threading)
+
+ def test_eventlet(self):
+ self.try_some_code(self.EVENTLET, "eventlet", eventlet)
+
+ def test_eventlet_simple_code(self):
+ self.try_some_code(self.SIMPLE, "eventlet", eventlet)
+
+ def test_gevent(self):
+ self.try_some_code(self.GEVENT, "gevent", gevent)
+
+ def test_gevent_simple_code(self):
+ self.try_some_code(self.SIMPLE, "gevent", gevent)
+
+ def test_greenlet(self):
+ GREENLET = """\
+ from greenlet import greenlet
+
+ def test1(x, y):
+ z = gr2.switch(x+y)
+ print(z)
+
+ def test2(u):
+ print(u)
+ gr1.switch(42)
+
+ gr1 = greenlet(test1)
+ gr2 = greenlet(test2)
+ gr1.switch("hello", " world")
+ """
+ self.try_some_code(GREENLET, "greenlet", greenlet, "hello world\n42\n")
+
+ def test_greenlet_simple_code(self):
+ self.try_some_code(self.SIMPLE, "greenlet", greenlet)
+
+
+def print_simple_annotation(code, linenos):
+ """Print the lines in `code` with X for each line number in `linenos`."""
+ for lineno, line in enumerate(code.splitlines(), start=1):
+ print(" {0} {1}".format("X" if lineno in linenos else " ", line))
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index 33f644fa..565fa4e1 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -46,7 +46,7 @@ class TestCoverageTest(CoverageTest):
def test_failed_coverage(self):
# If the lines are wrong, the message shows right and wrong.
- with self.assertRaisesRegexp(AssertionError, r"\[1, 2] != \[1]"):
+ with self.assertRaisesRegex(AssertionError, r"\[1, 2] != \[1]"):
self.check_coverage("""\
a = 1
b = 2
@@ -55,7 +55,7 @@ class TestCoverageTest(CoverageTest):
)
# If the list of lines possibilities is wrong, the msg shows right.
msg = r"None of the lines choices matched \[1, 2]"
- with self.assertRaisesRegexp(AssertionError, msg):
+ with self.assertRaisesRegex(AssertionError, msg):
self.check_coverage("""\
a = 1
b = 2
@@ -63,7 +63,7 @@ class TestCoverageTest(CoverageTest):
([1], [2])
)
# If the missing lines are wrong, the message shows right and wrong.
- with self.assertRaisesRegexp(AssertionError, r"'3' != '37'"):
+ with self.assertRaisesRegex(AssertionError, r"'3' != '37'"):
self.check_coverage("""\
a = 1
if a == 2:
@@ -74,7 +74,7 @@ class TestCoverageTest(CoverageTest):
)
# If the missing lines possibilities are wrong, the msg shows right.
msg = r"None of the missing choices matched '3'"
- with self.assertRaisesRegexp(AssertionError, msg):
+ with self.assertRaisesRegex(AssertionError, msg):
self.check_coverage("""\
a = 1
if a == 2:
@@ -1671,7 +1671,7 @@ class ReportingTest(CoverageTest):
def test_no_data_to_report_on_annotate(self):
# Reporting with no data produces a nice message and no output dir.
- with self.assertRaisesRegexp(CoverageException, "No data to report."):
+ with self.assertRaisesRegex(CoverageException, "No data to report."):
self.command_line("annotate -d ann")
self.assert_doesnt_exist("ann")
@@ -1681,12 +1681,12 @@ class ReportingTest(CoverageTest):
def test_no_data_to_report_on_html(self):
# Reporting with no data produces a nice message and no output dir.
- with self.assertRaisesRegexp(CoverageException, "No data to report."):
+ with self.assertRaisesRegex(CoverageException, "No data to report."):
self.command_line("html -d htmlcov")
self.assert_doesnt_exist("htmlcov")
def test_no_data_to_report_on_xml(self):
# Reporting with no data produces a nice message.
- with self.assertRaisesRegexp(CoverageException, "No data to report."):
+ with self.assertRaisesRegex(CoverageException, "No data to report."):
self.command_line("xml")
self.assert_doesnt_exist("coverage.xml")
diff --git a/tests/test_data.py b/tests/test_data.py
index 31578f26..b048fd18 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -33,7 +33,7 @@ class DataTest(CoverageTest):
def assert_measured_files(self, covdata, measured):
"""Check that `covdata`'s measured files are `measured`."""
- self.assertSameElements(covdata.measured_files(), measured)
+ self.assertCountEqual(covdata.measured_files(), measured)
def test_reading_empty(self):
covdata = CoverageData()
@@ -96,9 +96,9 @@ class DataTest(CoverageTest):
data = pickle.load(fdata)
lines = data['lines']
- self.assertSameElements(lines.keys(), MEASURED_FILES_1)
- self.assertSameElements(lines['a.py'], A_PY_LINES_1)
- self.assertSameElements(lines['b.py'], B_PY_LINES_1)
+ self.assertCountEqual(lines.keys(), MEASURED_FILES_1)
+ self.assertCountEqual(lines['a.py'], A_PY_LINES_1)
+ self.assertCountEqual(lines['b.py'], B_PY_LINES_1)
# If not measuring branches, there's no arcs entry.
self.assertEqual(data.get('arcs', 'not there'), 'not there')
@@ -111,10 +111,10 @@ class DataTest(CoverageTest):
with open(".coverage", 'rb') as fdata:
data = pickle.load(fdata)
- self.assertSameElements(data['lines'].keys(), [])
+ self.assertCountEqual(data['lines'].keys(), [])
arcs = data['arcs']
- self.assertSameElements(arcs['x.py'], X_PY_ARCS_3)
- self.assertSameElements(arcs['y.py'], Y_PY_ARCS_3)
+ self.assertCountEqual(arcs['x.py'], X_PY_ARCS_3)
+ self.assertCountEqual(arcs['y.py'], Y_PY_ARCS_3)
def test_combining_with_aliases(self):
covdata1 = CoverageData()
diff --git a/tests/test_execfile.py b/tests/test_execfile.py
index 7cd8ac4e..69616e84 100644
--- a/tests/test_execfile.py
+++ b/tests/test_execfile.py
@@ -16,7 +16,7 @@ class RunFileTest(CoverageTest):
def test_run_python_file(self):
tryfile = os.path.join(here, "try_execfile.py")
run_python_file(tryfile, [tryfile, "arg1", "arg2"])
- mod_globs = eval(self.stdout())
+ mod_globs = eval(self.stdout()) # pylint: disable=eval-used
# The file should think it is __main__
self.assertEqual(mod_globs['__name__'], "__main__")
@@ -118,11 +118,11 @@ class RunPycFileTest(CoverageTest):
fpyc.write(binary_bytes([0x2a, 0xeb, 0x0d, 0x0a]))
fpyc.close()
- with self.assertRaisesRegexp(NoCode, "Bad magic number in .pyc file"):
+ with self.assertRaisesRegex(NoCode, "Bad magic number in .pyc file"):
run_python_file(pycfile, [pycfile])
def test_no_such_pyc_file(self):
- with self.assertRaisesRegexp(NoCode, "No file to run: 'xyzzy.pyc'"):
+ with self.assertRaisesRegex(NoCode, "No file to run: 'xyzzy.pyc'"):
run_python_file("xyzzy.pyc", [])
@@ -138,22 +138,27 @@ class RunModuleTest(CoverageTest):
def test_runmod1(self):
run_python_module("runmod1", ["runmod1", "hello"])
+ self.assertEqual(self.stderr(), "")
self.assertEqual(self.stdout(), "runmod1: passed hello\n")
def test_runmod2(self):
run_python_module("pkg1.runmod2", ["runmod2", "hello"])
+ self.assertEqual(self.stderr(), "")
self.assertEqual(self.stdout(), "runmod2: passed hello\n")
def test_runmod3(self):
run_python_module("pkg1.sub.runmod3", ["runmod3", "hello"])
+ self.assertEqual(self.stderr(), "")
self.assertEqual(self.stdout(), "runmod3: passed hello\n")
def test_pkg1_main(self):
run_python_module("pkg1", ["pkg1", "hello"])
+ self.assertEqual(self.stderr(), "")
self.assertEqual(self.stdout(), "pkg1.__main__: passed hello\n")
def test_pkg1_sub_main(self):
run_python_module("pkg1.sub", ["pkg1.sub", "hello"])
+ self.assertEqual(self.stderr(), "")
self.assertEqual(self.stdout(), "pkg1.sub.__main__: passed hello\n")
def test_no_such_module(self):
diff --git a/tests/test_farm.py b/tests/test_farm.py
index c86983e5..47f9b7b7 100644
--- a/tests/test_farm.py
+++ b/tests/test_farm.py
@@ -15,6 +15,10 @@ def test_farm(clean_only=False):
yield (case,)
+# "rU" was deprecated in 3.4
+READ_MODE = "rU" if sys.version_info < (3, 4) else "r"
+
+
class FarmTestCase(object):
"""A test case from the farm tree.
@@ -22,8 +26,8 @@ class FarmTestCase(object):
copy("src", "out")
run('''
- coverage -x white.py
- coverage -a white.py
+ coverage run white.py
+ coverage annotate white.py
''', rundir="out")
compare("out", "gold", "*,cover")
clean("out")
@@ -75,7 +79,8 @@ class FarmTestCase(object):
# Prepare a dictionary of globals for the run.py files to use.
fns = """
- copy run runfunc compare contains doesnt_contain clean skip
+ copy run runfunc clean skip
+ compare contains contains_any doesnt_contain
""".split()
if self.clean_only:
glo = dict((fn, self.noop) for fn in fns)
@@ -238,8 +243,10 @@ class FarmTestCase(object):
# guide for size comparison.
wrong_size = []
for f in diff_files:
- left = open(os.path.join(dir1, f), "rb").read()
- right = open(os.path.join(dir2, f), "rb").read()
+ with open(os.path.join(dir1, f), "rb") as fobj:
+ left = fobj.read()
+ with open(os.path.join(dir2, f), "rb") as fobj:
+ right = fobj.read()
size_l, size_r = len(left), len(right)
big, little = max(size_l, size_r), min(size_l, size_r)
if (big - little) / float(little) > size_within/100.0:
@@ -256,14 +263,18 @@ class FarmTestCase(object):
# ourselves.
text_diff = []
for f in diff_files:
- left = open(os.path.join(dir1, f), "rU").readlines()
- right = open(os.path.join(dir2, f), "rU").readlines()
+ with open(os.path.join(dir1, f), READ_MODE) as fobj:
+ left = fobj.read()
+ with open(os.path.join(dir2, f), READ_MODE) as fobj:
+ right = fobj.read()
if scrubs:
left = self._scrub(left, scrubs)
right = self._scrub(right, scrubs)
if left != right:
text_diff.append(f)
- print("".join(list(difflib.Differ().compare(left, right))))
+ left = left.splitlines()
+ right = right.splitlines()
+ print("\n".join(difflib.Differ().compare(left, right)))
assert not text_diff, "Files differ: %s" % text_diff
if not left_extra:
@@ -271,19 +282,16 @@ class FarmTestCase(object):
if not right_extra:
assert not right_only, "Files in %s only: %s" % (dir2, right_only)
- def _scrub(self, strlist, scrubs):
- """Scrub uninteresting data from the strings in `strlist`.
+ def _scrub(self, strdata, scrubs):
+ """Scrub uninteresting data from the payload in `strdata`.
- `scrubs is a list of (find, replace) pairs of regexes that are used on
- each string in `strlist`. A list of scrubbed strings is returned.
+ `scrubs` is a list of (find, replace) pairs of regexes that are used on
+ `strdata`. A string is returned.
"""
- scrubbed = []
- for s in strlist:
- for rgx_find, rgx_replace in scrubs:
- s = re.sub(rgx_find, rgx_replace, s)
- scrubbed.append(s)
- return scrubbed
+ for rgx_find, rgx_replace in scrubs:
+ strdata = re.sub(rgx_find, rgx_replace, strdata)
+ return strdata
def contains(self, filename, *strlist):
"""Check that the file contains all of a list of strings.
@@ -292,10 +300,27 @@ class FarmTestCase(object):
missing in `filename`.
"""
- text = open(filename, "r").read()
+ with open(filename, "r") as fobj:
+ text = fobj.read()
for s in strlist:
assert s in text, "Missing content in %s: %r" % (filename, s)
+ def contains_any(self, filename, *strlist):
+ """Check that the file contains at least one of a list of strings.
+
+ An assert will be raised if none of the arguments in `strlist` is in
+ `filename`.
+
+ """
+ with open(filename, "r") as fobj:
+ text = fobj.read()
+ for s in strlist:
+ if s in text:
+ return
+ assert False, "Missing content in %s: %r [1 of %d]" % (
+ filename, strlist[0], len(strlist),
+ )
+
def doesnt_contain(self, filename, *strlist):
"""Check that the file contains none of a list of strings.
@@ -303,7 +328,8 @@ class FarmTestCase(object):
`filename`.
"""
- text = open(filename, "r").read()
+ with open(filename, "r") as fobj:
+ text = fobj.read()
for s in strlist:
assert s not in text, "Forbidden content in %s: %r" % (filename, s)
diff --git a/tests/test_files.py b/tests/test_files.py
index 85c0ac7b..648c76a9 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -50,41 +50,62 @@ class FileLocatorTest(CoverageTest):
class MatcherTest(CoverageTest):
"""Tests of file matchers."""
+ def setUp(self):
+ super(MatcherTest, self).setUp()
+ self.fl = FileLocator()
+
+ def assertMatches(self, matcher, filepath, matches):
+ """The `matcher` should agree with `matches` about `filepath`."""
+ canonical = self.fl.canonical_filename(filepath)
+ self.assertEqual(
+ matcher.match(canonical), matches,
+ "File %s should have matched as %s" % (filepath, matches)
+ )
+
def test_tree_matcher(self):
- file1 = self.make_file("sub/file1.py")
- file2 = self.make_file("sub/file2.c")
- file3 = self.make_file("sub2/file3.h")
- file4 = self.make_file("sub3/file4.py")
- file5 = self.make_file("sub3/file5.c")
+ matches_to_try = [
+ (self.make_file("sub/file1.py"), True),
+ (self.make_file("sub/file2.c"), True),
+ (self.make_file("sub2/file3.h"), False),
+ (self.make_file("sub3/file4.py"), True),
+ (self.make_file("sub3/file5.c"), False),
+ ]
fl = FileLocator()
trees = [
fl.canonical_filename("sub"),
- fl.canonical_filename(file4),
+ fl.canonical_filename("sub3/file4.py"),
]
tm = TreeMatcher(trees)
- self.assertTrue(tm.match(fl.canonical_filename(file1)))
- self.assertTrue(tm.match(fl.canonical_filename(file2)))
- self.assertFalse(tm.match(fl.canonical_filename(file3)))
- self.assertTrue(tm.match(fl.canonical_filename(file4)))
- self.assertFalse(tm.match(fl.canonical_filename(file5)))
-
self.assertEqual(tm.info(), trees)
+ for filepath, matches in matches_to_try:
+ self.assertMatches(tm, filepath, matches)
def test_fnmatch_matcher(self):
- file1 = self.make_file("sub/file1.py")
- file2 = self.make_file("sub/file2.c")
- file3 = self.make_file("sub2/file3.h")
- file4 = self.make_file("sub3/file4.py")
- file5 = self.make_file("sub3/file5.c")
- fl = FileLocator()
+ matches_to_try = [
+ (self.make_file("sub/file1.py"), True),
+ (self.make_file("sub/file2.c"), False),
+ (self.make_file("sub2/file3.h"), True),
+ (self.make_file("sub3/file4.py"), True),
+ (self.make_file("sub3/file5.c"), False),
+ ]
fnm = FnmatchMatcher(["*.py", "*/sub2/*"])
- self.assertTrue(fnm.match(fl.canonical_filename(file1)))
- self.assertFalse(fnm.match(fl.canonical_filename(file2)))
- self.assertTrue(fnm.match(fl.canonical_filename(file3)))
- self.assertTrue(fnm.match(fl.canonical_filename(file4)))
- self.assertFalse(fnm.match(fl.canonical_filename(file5)))
-
self.assertEqual(fnm.info(), ["*.py", "*/sub2/*"])
+ for filepath, matches in matches_to_try:
+ self.assertMatches(fnm, filepath, matches)
+
+ def test_fnmatch_matcher_overload(self):
+ fnm = FnmatchMatcher(["*x%03d*.txt" % i for i in range(500)])
+ self.assertMatches(fnm, "x007foo.txt", True)
+ self.assertMatches(fnm, "x123foo.txt", True)
+ self.assertMatches(fnm, "x798bar.txt", False)
+
+ def test_fnmatch_windows_paths(self):
+ # We should be able to match Windows paths even if we are running on
+ # a non-Windows OS.
+ fnm = FnmatchMatcher(["*/foo.py"])
+ self.assertMatches(fnm, r"dir\foo.py", True)
+ fnm = FnmatchMatcher([r"*\foo.py"])
+ self.assertMatches(fnm, r"dir\foo.py", True)
class PathAliasesTest(CoverageTest):
@@ -124,11 +145,11 @@ class PathAliasesTest(CoverageTest):
def test_cant_have_wildcard_at_end(self):
aliases = PathAliases()
msg = "Pattern must not end with wildcards."
- with self.assertRaisesRegexp(CoverageException, msg):
+ with self.assertRaisesRegex(CoverageException, msg):
aliases.add("/ned/home/*", "fooey")
- with self.assertRaisesRegexp(CoverageException, msg):
+ with self.assertRaisesRegex(CoverageException, msg):
aliases.add("/ned/home/*/", "fooey")
- with self.assertRaisesRegexp(CoverageException, msg):
+ with self.assertRaisesRegex(CoverageException, msg):
aliases.add("/ned/home/*/*/", "fooey")
def test_no_accidental_munging(self):
@@ -170,7 +191,7 @@ class RelativePathAliasesTest(CoverageTest):
aliases.add(d, '/the/source')
the_file = os.path.join(d, 'a.py')
the_file = os.path.expanduser(the_file)
- the_file = os.path.abspath(the_file)
+ the_file = os.path.abspath(os.path.realpath(the_file))
assert '~' not in the_file # to be sure the test is pure.
self.assertEqual(aliases.map(the_file), '/the/source/a.py')
diff --git a/tests/test_html.py b/tests/test_html.py
index 41859382..8e43e7cf 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Tests that HTML generation is awesome."""
-import os.path, re
+import os.path, re, sys
import coverage
import coverage.html
from coverage.misc import CoverageException, NotPython, NoSource
@@ -42,6 +42,13 @@ class HtmlTestHelpers(CoverageTest):
os.remove("htmlcov/helper1.html")
os.remove("htmlcov/helper2.html")
+ def get_html_report_content(self, module):
+ """Return the content of the HTML report for `module`."""
+ filename = module.replace(".py", ".html").replace("/", "_")
+ filename = os.path.join("htmlcov", filename)
+ with open(filename) as f:
+ return f.read()
+
class HtmlDeltaTest(HtmlTestHelpers, CoverageTest):
"""Tests of the HTML delta speed-ups."""
@@ -208,7 +215,7 @@ class HtmlTitleTest(HtmlTestHelpers, CoverageTest):
)
-class HtmlWithUnparsableFilesTest(CoverageTest):
+class HtmlWithUnparsableFilesTest(HtmlTestHelpers, CoverageTest):
"""Test the behavior when measuring unparsable files."""
def test_dotpy_not_python(self):
@@ -217,7 +224,7 @@ class HtmlWithUnparsableFilesTest(CoverageTest):
self.start_import_stop(cov, "innocuous")
self.make_file("innocuous.py", "<h1>This isn't python!</h1>")
msg = "Couldn't parse '.*innocuous.py' as Python source: .* at line 1"
- with self.assertRaisesRegexp(NotPython, msg):
+ with self.assertRaisesRegex(NotPython, msg):
cov.html_report()
def test_dotpy_not_python_ignored(self):
@@ -267,6 +274,31 @@ class HtmlWithUnparsableFilesTest(CoverageTest):
cov.html_report()
self.assert_exists("htmlcov/index.html")
+ def test_decode_error(self):
+ # imp.load_module won't load a file with an undecodable character
+ # in a comment, though Python will run them. So we'll change the
+ # file after running.
+ self.make_file("main.py", "import sub.not_ascii")
+ self.make_file("sub/__init__.py")
+ self.make_file("sub/not_ascii.py", """\
+ a = 1 # Isn't this great?!
+ """)
+ cov = coverage.coverage()
+ self.start_import_stop(cov, "main")
+
+ # Create the undecodable version of the file.
+ self.make_file("sub/not_ascii.py", """\
+ a = 1 # Isn't this great?\xcb!
+ """)
+ cov.html_report()
+
+ html_report = self.get_html_report_content("sub/not_ascii.py")
+ if sys.version_info < (3, 0):
+ expected = "# Isn&#39;t this great?&#65533;!"
+ else:
+ expected = "# Isn&#39;t this great?&#203;!"
+ self.assertIn(expected, html_report)
+
class HtmlTest(CoverageTest):
"""Moar HTML tests."""
@@ -283,7 +315,7 @@ class HtmlTest(CoverageTest):
missing_file = os.path.join(self.temp_dir, "sub", "another.py")
missing_file = os.path.realpath(missing_file)
msg = "(?i)No source for code: '%s'" % re.escape(missing_file)
- with self.assertRaisesRegexp(NoSource, msg):
+ with self.assertRaisesRegex(NoSource, msg):
cov.html_report()
class HtmlStaticFileTest(CoverageTest):
@@ -340,5 +372,5 @@ class HtmlStaticFileTest(CoverageTest):
cov = coverage.coverage()
self.start_import_stop(cov, "main")
msg = "Couldn't find static file '.*'"
- with self.assertRaisesRegexp(CoverageException, msg):
+ with self.assertRaisesRegex(CoverageException, msg):
cov.html_report()
diff --git a/tests/test_oddball.py b/tests/test_oddball.py
index 786ede94..47f492f6 100644
--- a/tests/test_oddball.py
+++ b/tests/test_oddball.py
@@ -116,9 +116,8 @@ class RecursionTest(CoverageTest):
self.assertEqual(statements, [1,2,3,5,7,8,9,10,11])
self.assertEqual(missing, expected_missing)
- # We can get a warning about the stackoverflow effect on the tracing
- # function only if we have sys.gettrace
- if pytrace and hasattr(sys, "gettrace"):
+ # Get a warning about the stackoverflow effect on the tracing function.
+ if pytrace:
self.assertEqual(cov._warnings,
["Trace function changed, measurement is likely wrong: None"]
)
@@ -368,35 +367,34 @@ class DoctestTest(CoverageTest):
[1,11,12,14,16,17], "")
-if hasattr(sys, 'gettrace'):
- class GettraceTest(CoverageTest):
- """Tests that we work properly with `sys.gettrace()`."""
- def test_round_trip(self):
- self.check_coverage('''\
- import sys
- def foo(n):
- return 3*n
- def bar(n):
- return 5*n
- a = foo(6)
+class GettraceTest(CoverageTest):
+ """Tests that we work properly with `sys.gettrace()`."""
+ def test_round_trip(self):
+ self.check_coverage('''\
+ import sys
+ def foo(n):
+ return 3*n
+ def bar(n):
+ return 5*n
+ a = foo(6)
+ sys.settrace(sys.gettrace())
+ a = bar(8)
+ ''',
+ [1,2,3,4,5,6,7,8], "")
+
+ def test_multi_layers(self):
+ self.check_coverage('''\
+ import sys
+ def level1():
+ a = 3
+ level2()
+ b = 5
+ def level2():
+ c = 7
sys.settrace(sys.gettrace())
- a = bar(8)
- ''',
- [1,2,3,4,5,6,7,8], "")
-
- def test_multi_layers(self):
- self.check_coverage('''\
- import sys
- def level1():
- a = 3
- level2()
- b = 5
- def level2():
- c = 7
- sys.settrace(sys.gettrace())
- d = 9
- e = 10
- level1()
- f = 12
- ''',
- [1,2,3,4,5,6,7,8,9,10,11,12], "")
+ d = 9
+ e = 10
+ level1()
+ f = 12
+ ''',
+ [1,2,3,4,5,6,7,8,9,10,11,12], "")
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 80773c74..a392ea03 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -2,23 +2,23 @@
import textwrap
from tests.coveragetest import CoverageTest
-from coverage.parser import CodeParser
+from coverage.parser import PythonParser
-class ParserTest(CoverageTest):
- """Tests for Coverage.py's code parsing."""
+class PythonParserTest(CoverageTest):
+ """Tests for Coverage.py's Python code parsing."""
run_in_temp_dir = False
def parse_source(self, text):
- """Parse `text` as source, and return the `CodeParser` used."""
+ """Parse `text` as source, and return the `PythonParser` used."""
text = textwrap.dedent(text)
- cp = CodeParser(text=text, exclude="nocover")
- cp.parse_source()
- return cp
+ parser = PythonParser(text=text, exclude="nocover")
+ parser.parse_source()
+ return parser
def test_exit_counts(self):
- cp = self.parse_source("""\
+ parser = self.parse_source("""\
# check some basic branch counting
class Foo:
def foo(self, a):
@@ -30,12 +30,12 @@ class ParserTest(CoverageTest):
class Bar:
pass
""")
- self.assertEqual(cp.exit_counts(), {
+ self.assertEqual(parser.exit_counts(), {
2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1
})
def test_try_except(self):
- cp = self.parse_source("""\
+ parser = self.parse_source("""\
try:
a = 2
except ValueError:
@@ -46,12 +46,12 @@ class ParserTest(CoverageTest):
a = 8
b = 9
""")
- self.assertEqual(cp.exit_counts(), {
+ self.assertEqual(parser.exit_counts(), {
1: 1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:1, 8:1, 9:1
})
def test_excluded_classes(self):
- cp = self.parse_source("""\
+ parser = self.parse_source("""\
class Foo:
def __init__(self):
pass
@@ -60,20 +60,20 @@ class ParserTest(CoverageTest):
class Bar:
pass
""")
- self.assertEqual(cp.exit_counts(), {
+ self.assertEqual(parser.exit_counts(), {
1:0, 2:1, 3:1
})
def test_missing_branch_to_excluded_code(self):
- cp = self.parse_source("""\
+ parser = self.parse_source("""\
if fooey:
a = 2
else: # nocover
a = 4
b = 5
""")
- self.assertEqual(cp.exit_counts(), { 1:1, 2:1, 5:1 })
- cp = self.parse_source("""\
+ self.assertEqual(parser.exit_counts(), { 1:1, 2:1, 5:1 })
+ parser = self.parse_source("""\
def foo():
if fooey:
a = 3
@@ -81,8 +81,8 @@ class ParserTest(CoverageTest):
a = 5
b = 6
""")
- self.assertEqual(cp.exit_counts(), { 1:1, 2:2, 3:1, 5:1, 6:1 })
- cp = self.parse_source("""\
+ self.assertEqual(parser.exit_counts(), { 1:1, 2:2, 3:1, 5:1, 6:1 })
+ parser = self.parse_source("""\
def foo():
if fooey:
a = 3
@@ -90,17 +90,17 @@ class ParserTest(CoverageTest):
a = 5
b = 6
""")
- self.assertEqual(cp.exit_counts(), { 1:1, 2:1, 3:1, 6:1 })
+ self.assertEqual(parser.exit_counts(), { 1:1, 2:1, 3:1, 6:1 })
class ParserFileTest(CoverageTest):
"""Tests for Coverage.py's code parsing from files."""
def parse_file(self, filename):
- """Parse `text` as source, and return the `CodeParser` used."""
- cp = CodeParser(filename=filename, exclude="nocover")
- cp.parse_source()
- return cp
+ """Parse `text` as source, and return the `PythonParser` used."""
+ parser = PythonParser(filename=filename, exclude="nocover")
+ parser.parse_source()
+ return parser
def test_line_endings(self):
text = """\
@@ -120,12 +120,12 @@ class ParserFileTest(CoverageTest):
for fname, newline in name_endings:
fname = fname + ".py"
self.make_file(fname, text, newline=newline)
- cp = self.parse_file(fname)
- self.assertEqual(cp.exit_counts(), counts)
+ parser = self.parse_file(fname)
+ self.assertEqual(parser.exit_counts(), counts)
def test_encoding(self):
self.make_file("encoded.py", """\
coverage = "\xe7\xf6v\xear\xe3g\xe9"
""")
- cp = self.parse_file("encoded.py")
- cp.exit_counts()
+ parser = self.parse_file("encoded.py")
+ parser.exit_counts() # TODO: This value should be tested!
diff --git a/tests/test_phystokens.py b/tests/test_phystokens.py
index e15400b6..4755c167 100644
--- a/tests/test_phystokens.py
+++ b/tests/test_phystokens.py
@@ -97,6 +97,11 @@ if sys.version_info < (3, 0):
source = "# This Python file uses this encoding: utf-8\n"
self.assertEqual(source_encoding(source), 'utf-8')
+ def test_detect_source_encoding_not_in_comment(self):
+ # Should not detect anything here
+ source = 'def parse(src, encoding=None):\n pass'
+ self.assertEqual(source_encoding(source), 'ascii')
+
def test_detect_source_encoding_on_second_line(self):
# A coding declaration should be found despite a first blank line.
source = "\n# coding=cp850\n\n"
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
new file mode 100644
index 00000000..9c5a037d
--- /dev/null
+++ b/tests/test_plugins.py
@@ -0,0 +1,217 @@
+"""Tests for plugins."""
+
+import os.path
+
+import coverage
+from coverage.codeunit import CodeUnit
+from coverage.parser import CodeParser
+from coverage.plugin import Plugins, overrides
+
+from tests.coveragetest import CoverageTest
+
+
+class FakeConfig(object):
+ """A fake config for use in tests."""
+
+ def __init__(self, plugin, options):
+ self.plugin = plugin
+ self.options = options
+ self.asked_for = []
+
+ def get_plugin_options(self, module):
+ """Just return the options for `module` if this is the right module."""
+ self.asked_for.append(module)
+ if module == self.plugin:
+ return self.options
+ else:
+ return {}
+
+
+class PluginUnitTest(CoverageTest):
+ """Test Plugins.load_plugins directly."""
+
+ def test_importing_and_configuring(self):
+ self.make_file("plugin1.py", """\
+ from coverage import CoveragePlugin
+
+ class Plugin(CoveragePlugin):
+ def __init__(self, options):
+ super(Plugin, self).__init__(options)
+ self.this_is = "me"
+ """)
+
+ config = FakeConfig("plugin1", {'a':'hello'})
+ plugins = list(Plugins.load_plugins(["plugin1"], config))
+
+ self.assertEqual(len(plugins), 1)
+ self.assertEqual(plugins[0].this_is, "me")
+ self.assertEqual(plugins[0].options, {'a':'hello'})
+ self.assertEqual(config.asked_for, ['plugin1'])
+
+ def test_importing_and_configuring_more_than_one(self):
+ self.make_file("plugin1.py", """\
+ from coverage import CoveragePlugin
+
+ class Plugin(CoveragePlugin):
+ def __init__(self, options):
+ super(Plugin, self).__init__(options)
+ self.this_is = "me"
+ """)
+ self.make_file("plugin2.py", """\
+ from coverage import CoveragePlugin
+
+ class Plugin(CoveragePlugin):
+ pass
+ """)
+
+ config = FakeConfig("plugin1", {'a':'hello'})
+ plugins = list(Plugins.load_plugins(["plugin1", "plugin2"], config))
+
+ self.assertEqual(len(plugins), 2)
+ self.assertEqual(plugins[0].this_is, "me")
+ self.assertEqual(plugins[0].options, {'a':'hello'})
+ self.assertEqual(plugins[1].options, {})
+ self.assertEqual(config.asked_for, ['plugin1', 'plugin2'])
+
+ def test_cant_import(self):
+ with self.assertRaises(ImportError):
+ _ = Plugins.load_plugins(["plugin_not_there"], None)
+
+ def test_ok_to_not_define_plugin(self):
+ self.make_file("plugin2.py", """\
+ from coverage import CoveragePlugin
+
+ Nothing = 0
+ """)
+ plugins = list(Plugins.load_plugins(["plugin2"], None))
+ self.assertEqual(plugins, [])
+
+
+class PluginTest(CoverageTest):
+ """Test plugins through the Coverage class."""
+
+ def test_plugin_imported(self):
+ # Prove that a plugin will be imported.
+ self.make_file("my_plugin.py", """\
+ with open("evidence.out", "w") as f:
+ f.write("we are here!")
+ """)
+
+ self.assert_doesnt_exist("evidence.out")
+ _ = coverage.Coverage(plugins=["my_plugin"])
+
+ with open("evidence.out") as f:
+ self.assertEqual(f.read(), "we are here!")
+
+ def test_missing_plugin_raises_import_error(self):
+ # Prove that a missing plugin will raise an ImportError.
+ with self.assertRaises(ImportError):
+ cov = coverage.Coverage(plugins=["does_not_exist_woijwoicweo"])
+ cov.start()
+
+ def test_bad_plugin_isnt_hidden(self):
+ # Prove that a plugin with an error in it will raise the error.
+ self.make_file("plugin_over_zero.py", """\
+ 1/0
+ """)
+ with self.assertRaises(ZeroDivisionError):
+ _ = coverage.Coverage(plugins=["plugin_over_zero"])
+
+ def test_importing_myself(self):
+ self.make_file("simple.py", """\
+ import try_xyz
+ a = 1
+ b = 2
+ """)
+ self.make_file("try_xyz.py", """\
+ c = 3
+ d = 4
+ """)
+
+ cov = coverage.Coverage(plugins=["tests.test_plugins"])
+
+ # Import the python file, executing it.
+ self.start_import_stop(cov, "simple")
+
+ _, statements, missing, _ = cov.analysis("simple.py")
+ self.assertEqual(statements, [1,2,3])
+ self.assertEqual(missing, [])
+ _, statements, _, _ = cov.analysis("/src/try_ABC.zz")
+ self.assertEqual(statements, [105, 106, 107, 205, 206, 207])
+
+
+class Plugin(coverage.CoveragePlugin):
+ def trace_judge(self, disp):
+ if "xyz.py" in disp.original_filename:
+ disp.trace = True
+ disp.source_filename = os.path.join(
+ "/src",
+ os.path.basename(
+ disp.original_filename.replace("xyz.py", "ABC.zz")
+ )
+ )
+
+ def line_number_range(self, frame):
+ lineno = frame.f_lineno
+ return lineno*100+5, lineno*100+7
+
+ def code_unit_class(self, filename):
+ return PluginCodeUnit
+
+class PluginCodeUnit(CodeUnit):
+ def get_parser(self, exclude=None):
+ return PluginParser()
+
+class PluginParser(CodeParser):
+ def parse_source(self):
+ return set([105, 106, 107, 205, 206, 207]), set([])
+
+
+class OverridesTest(CoverageTest):
+ """Test plugins.py:overrides."""
+
+ run_in_temp_dir = False
+
+ def test_overrides(self):
+ class SomeBase(object):
+ """Base class, two base methods."""
+ def method1(self):
+ pass
+
+ def method2(self):
+ pass
+
+ class Derived1(SomeBase):
+ """Simple single inheritance."""
+ def method1(self):
+ pass
+
+ self.assertTrue(overrides(Derived1(), "method1", SomeBase))
+ self.assertFalse(overrides(Derived1(), "method2", SomeBase))
+
+ class FurtherDerived1(Derived1):
+ """Derive again from Derived1, inherit its method1."""
+ pass
+
+ self.assertTrue(overrides(FurtherDerived1(), "method1", SomeBase))
+ self.assertFalse(overrides(FurtherDerived1(), "method2", SomeBase))
+
+ class FurtherDerived2(Derived1):
+ """Override the overridden method."""
+ def method1(self):
+ pass
+
+ self.assertTrue(overrides(FurtherDerived2(), "method1", SomeBase))
+ self.assertFalse(overrides(FurtherDerived2(), "method2", SomeBase))
+
+ class Mixin(object):
+ """A mixin that overrides method1."""
+ def method1(self):
+ pass
+
+ class Derived2(Mixin, SomeBase):
+ """A class that gets the method from the mixin."""
+ pass
+
+ self.assertTrue(overrides(Derived2(), "method1", SomeBase))
+ self.assertFalse(overrides(Derived2(), "method2", SomeBase))
diff --git a/tests/test_process.py b/tests/test_process.py
index fa4759a8..3a0980dc 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -26,7 +26,7 @@ class ProcessTest(CoverageTest):
""")
self.assert_doesnt_exist(".coverage")
- self.run_command("coverage -x mycode.py")
+ self.run_command("coverage run mycode.py")
self.assert_exists(".coverage")
def test_environment(self):
@@ -39,7 +39,7 @@ class ProcessTest(CoverageTest):
""")
self.assert_doesnt_exist(".coverage")
- out = self.run_command("coverage -x mycode.py")
+ out = self.run_command("coverage run mycode.py")
self.assert_exists(".coverage")
self.assertEqual(out, 'done\n')
@@ -55,11 +55,11 @@ class ProcessTest(CoverageTest):
print('done')
""")
- out = self.run_command("coverage -x -p b_or_c.py b")
+ out = self.run_command("coverage run -p b_or_c.py b")
self.assertEqual(out, 'done\n')
self.assert_doesnt_exist(".coverage")
- out = self.run_command("coverage -x -p b_or_c.py c")
+ out = self.run_command("coverage run -p b_or_c.py c")
self.assertEqual(out, 'done\n')
self.assert_doesnt_exist(".coverage")
@@ -67,7 +67,7 @@ class ProcessTest(CoverageTest):
self.assertEqual(self.number_of_data_files(), 2)
# Combine the parallel coverage data files into .coverage .
- self.run_command("coverage -c")
+ self.run_command("coverage combine")
self.assert_exists(".coverage")
# After combining, there should be only the .coverage file.
@@ -91,23 +91,23 @@ class ProcessTest(CoverageTest):
print('done')
""")
- out = self.run_command("coverage -x -p b_or_c.py b")
+ out = self.run_command("coverage run -p b_or_c.py b")
self.assertEqual(out, 'done\n')
self.assert_doesnt_exist(".coverage")
self.assertEqual(self.number_of_data_files(), 1)
# Combine the (one) parallel coverage data file into .coverage .
- self.run_command("coverage -c")
+ self.run_command("coverage combine")
self.assert_exists(".coverage")
self.assertEqual(self.number_of_data_files(), 1)
- out = self.run_command("coverage -x -p b_or_c.py c")
+ out = self.run_command("coverage run --append -p b_or_c.py c")
self.assertEqual(out, 'done\n')
self.assert_exists(".coverage")
self.assertEqual(self.number_of_data_files(), 2)
# Combine the parallel coverage data files into .coverage .
- self.run_command("coverage -c")
+ self.run_command("coverage combine")
self.assert_exists(".coverage")
# After combining, there should be only the .coverage file.
@@ -229,7 +229,7 @@ class ProcessTest(CoverageTest):
self.run_command("coverage run fleeting.py")
os.remove("fleeting.py")
out = self.run_command("coverage html -d htmlcov")
- self.assertRegexpMatches(out, "No source for code: '.*fleeting.py'")
+ self.assertRegex(out, "No source for code: '.*fleeting.py'")
self.assertNotIn("Traceback", out)
# It happens that the code paths are different for *.py and other
@@ -240,14 +240,14 @@ class ProcessTest(CoverageTest):
self.run_command("coverage run fleeting")
os.remove("fleeting")
- status, out = self.run_command_status("coverage html -d htmlcov", 1)
- self.assertRegexpMatches(out, "No source for code: '.*fleeting'")
+ status, out = self.run_command_status("coverage html -d htmlcov")
+ self.assertRegex(out, "No source for code: '.*fleeting'")
self.assertNotIn("Traceback", out)
self.assertEqual(status, 1)
def test_running_missing_file(self):
- status, out = self.run_command_status("coverage run xyzzy.py", 1)
- self.assertRegexpMatches(out, "No file to run: .*xyzzy.py")
+ status, out = self.run_command_status("coverage run xyzzy.py")
+ self.assertRegex(out, "No file to run: .*xyzzy.py")
self.assertNotIn("raceback", out)
self.assertNotIn("rror", out)
self.assertEqual(status, 1)
@@ -265,7 +265,7 @@ class ProcessTest(CoverageTest):
# The important thing is for "coverage run" and "python" to report the
# same traceback.
- status, out = self.run_command_status("coverage run throw.py", 1)
+ status, out = self.run_command_status("coverage run throw.py")
out2 = self.run_command("python throw.py")
if '__pypy__' in sys.builtin_module_names:
# Pypy has an extra frame in the traceback for some reason
@@ -294,8 +294,8 @@ class ProcessTest(CoverageTest):
# The important thing is for "coverage run" and "python" to have the
# same output. No traceback.
- status, out = self.run_command_status("coverage run exit.py", 17)
- status2, out2 = self.run_command_status("python exit.py", 17)
+ status, out = self.run_command_status("coverage run exit.py")
+ status2, out2 = self.run_command_status("python exit.py")
self.assertMultiLineEqual(out, out2)
self.assertMultiLineEqual(out, "about to exit..\n")
self.assertEqual(status, status2)
@@ -310,8 +310,8 @@ class ProcessTest(CoverageTest):
f1()
""")
- status, out = self.run_command_status("coverage run exit_none.py", 0)
- status2, out2 = self.run_command_status("python exit_none.py", 0)
+ status, out = self.run_command_status("coverage run exit_none.py")
+ status2, out2 = self.run_command_status("python exit_none.py")
self.assertMultiLineEqual(out, out2)
self.assertMultiLineEqual(out, "about to exit quietly..\n")
self.assertEqual(status, status2)
@@ -378,7 +378,7 @@ class ProcessTest(CoverageTest):
self.assertEqual(self.number_of_data_files(), 2)
# Combine the parallel coverage data files into .coverage .
- self.run_command("coverage -c")
+ self.run_command("coverage combine")
self.assert_exists(".coverage")
# After combining, there should be only the .coverage file.
@@ -470,8 +470,7 @@ class ProcessTest(CoverageTest):
self.assertIn("Hello\n", out)
self.assertIn("Goodbye\n", out)
- if hasattr(sys, "gettrace"):
- self.assertIn("Trace function changed", out)
+ self.assertIn("Trace function changed", out)
if sys.version_info >= (3, 0): # This only works on 3.x for now.
# It only works with the C tracer,
@@ -502,6 +501,18 @@ class ProcessTest(CoverageTest):
# about 5.
self.assertGreater(data.summary()['os.py'], 50)
+ def test_deprecation_warnings(self):
+ # Test that coverage doesn't trigger deprecation warnings.
+ # https://bitbucket.org/ned/coveragepy/issue/305/pendingdeprecationwarning-the-imp-module
+ self.make_file("allok.py", """\
+ import warnings
+ warnings.simplefilter('default')
+ import coverage
+ print("No warnings!")
+ """)
+ out = self.run_command("python allok.py")
+ self.assertEqual(out, "No warnings!\n")
+
class AliasedCommandTest(CoverageTest):
"""Tests of the version-specific command aliases."""
@@ -556,32 +567,47 @@ class FailUnderTest(CoverageTest):
def setUp(self):
super(FailUnderTest, self).setUp()
- self.make_file("fifty.py", """\
- # I have 50% coverage!
+ self.make_file("forty_two_plus.py", """\
+ # I have 42.857% (3/7) coverage!
a = 1
- if a > 2:
- b = 3
- c = 4
+ b = 2
+ if a > 3:
+ b = 4
+ c = 5
+ d = 6
+ e = 7
""")
- st, _ = self.run_command_status("coverage run fifty.py", 0)
+ st, _ = self.run_command_status("coverage run forty_two_plus.py")
+ self.assertEqual(st, 0)
+ st, out = self.run_command_status("coverage report")
self.assertEqual(st, 0)
+ self.assertEqual(
+ self.last_line_squeezed(out),
+ "forty_two_plus 7 4 43%"
+ )
def test_report(self):
- st, _ = self.run_command_status("coverage report --fail-under=50", 0)
+ st, _ = self.run_command_status("coverage report --fail-under=42")
+ self.assertEqual(st, 0)
+ st, _ = self.run_command_status("coverage report --fail-under=43")
self.assertEqual(st, 0)
- st, _ = self.run_command_status("coverage report --fail-under=51", 2)
+ st, _ = self.run_command_status("coverage report --fail-under=44")
self.assertEqual(st, 2)
def test_html_report(self):
- st, _ = self.run_command_status("coverage html --fail-under=50", 0)
+ st, _ = self.run_command_status("coverage html --fail-under=42")
self.assertEqual(st, 0)
- st, _ = self.run_command_status("coverage html --fail-under=51", 2)
+ st, _ = self.run_command_status("coverage html --fail-under=43")
+ self.assertEqual(st, 0)
+ st, _ = self.run_command_status("coverage html --fail-under=44")
self.assertEqual(st, 2)
def test_xml_report(self):
- st, _ = self.run_command_status("coverage xml --fail-under=50", 0)
+ st, _ = self.run_command_status("coverage xml --fail-under=42")
+ self.assertEqual(st, 0)
+ st, _ = self.run_command_status("coverage xml --fail-under=43")
self.assertEqual(st, 0)
- st, _ = self.run_command_status("coverage xml --fail-under=51", 2)
+ st, _ = self.run_command_status("coverage xml --fail-under=44")
self.assertEqual(st, 2)
diff --git a/tests/test_summary.py b/tests/test_summary.py
index 29167bf8..7bd1c496 100644
--- a/tests/test_summary.py
+++ b/tests/test_summary.py
@@ -21,26 +21,10 @@ class SummaryTest(CoverageTest):
# Parent class saves and restores sys.path, we can just modify it.
sys.path.append(self.nice_file(os.path.dirname(__file__), 'modules'))
- def report_from_command(self, cmd):
- """Return the report from the `cmd`, with some convenience added."""
- report = self.run_command(cmd).replace('\\', '/')
- self.assertNotIn("error", report.lower())
- return report
-
- def line_count(self, report):
- """How many lines are in `report`?"""
- self.assertEqual(report.split('\n')[-1], "")
- return len(report.split('\n')) - 1
-
- def last_line_squeezed(self, report):
- """Return the last line of `report` with the spaces squeezed down."""
- last_line = report.split('\n')[-2]
- return re.sub(r"\s+", " ", last_line)
-
def test_report(self):
- out = self.run_command("coverage -x mycode.py")
+ out = self.run_command("coverage run mycode.py")
self.assertEqual(out, 'done\n')
- report = self.report_from_command("coverage -r")
+ report = self.report_from_command("coverage report")
# Name Stmts Miss Cover
# ---------------------------------------------------------------------
@@ -58,8 +42,24 @@ class SummaryTest(CoverageTest):
def test_report_just_one(self):
# Try reporting just one module
- self.run_command("coverage -x mycode.py")
- report = self.report_from_command("coverage -r mycode.py")
+ self.run_command("coverage run mycode.py")
+ report = self.report_from_command("coverage report mycode.py")
+
+ # Name Stmts Miss Cover
+ # ----------------------------
+ # mycode 4 0 100%
+
+ self.assertEqual(self.line_count(report), 3)
+ self.assertNotIn("/coverage/", report)
+ self.assertNotIn("/tests/modules/covmod1 ", report)
+ self.assertNotIn("/tests/zipmods.zip/covmodzip1 ", report)
+ self.assertIn("mycode ", report)
+ self.assertEqual(self.last_line_squeezed(report), "mycode 4 0 100%")
+
+ def test_report_wildcard(self):
+ # Try reporting using wildcards to get the modules.
+ self.run_command("coverage run mycode.py")
+ report = self.report_from_command("coverage report my*.py")
# Name Stmts Miss Cover
# ----------------------------
@@ -75,8 +75,10 @@ class SummaryTest(CoverageTest):
def test_report_omitting(self):
# Try reporting while omitting some modules
prefix = os.path.split(__file__)[0]
- self.run_command("coverage -x mycode.py")
- report = self.report_from_command("coverage -r -o '%s/*'" % prefix)
+ self.run_command("coverage run mycode.py")
+ report = self.report_from_command(
+ "coverage report --omit '%s/*'" % prefix
+ )
# Name Stmts Miss Cover
# ----------------------------
@@ -126,13 +128,109 @@ class SummaryTest(CoverageTest):
self.assertEqual(self.last_line_squeezed(report),
"mybranch 5 0 2 1 86%")
+ def test_report_show_missing(self):
+ self.make_file("mymissing.py", """\
+ def missing(x, y):
+ if x:
+ print("x")
+ return x
+ if y:
+ print("y")
+ try:
+ print("z")
+ 1/0
+ print("Never!")
+ except ZeroDivisionError:
+ pass
+ return x
+ missing(0, 1)
+ """)
+ out = self.run_command("coverage run mymissing.py")
+ self.assertEqual(out, 'y\nz\n')
+ report = self.report_from_command("coverage report --show-missing")
+
+ # Name Stmts Miss Cover Missing
+ # -----------------------------------------
+ # mymissing 14 3 79% 3-4, 10
+
+ self.assertEqual(self.line_count(report), 3)
+ self.assertIn("mymissing ", report)
+ self.assertEqual(self.last_line_squeezed(report),
+ "mymissing 14 3 79% 3-4, 10")
+
+ def test_report_show_missing_branches(self):
+ self.make_file("mybranch.py", """\
+ def branch(x, y):
+ if x:
+ print("x")
+ if y:
+ print("y")
+ return x
+ branch(1, 1)
+ """)
+ out = self.run_command("coverage run --branch mybranch.py")
+ self.assertEqual(out, 'x\ny\n')
+ report = self.report_from_command("coverage report --show-missing")
+
+ # Name Stmts Miss Branch BrMiss Cover Missing
+ # -------------------------------------------------------
+ # mybranch 7 0 4 2 82% 2->4, 4->6
+
+ self.assertEqual(self.line_count(report), 3)
+ self.assertIn("mybranch ", report)
+ self.assertEqual(self.last_line_squeezed(report),
+ "mybranch 7 0 4 2 82% 2->4, 4->6")
+
+ def test_report_show_missing_branches_and_lines(self):
+ self.make_file("main.py", """\
+ import mybranch
+ """)
+ self.make_file("mybranch.py", """\
+ def branch(x, y, z):
+ if x:
+ print("x")
+ if y:
+ print("y")
+ if z:
+ if x and y:
+ print("z")
+ return x
+ branch(1, 1, 0)
+ """)
+ out = self.run_command("coverage run --branch main.py")
+ self.assertEqual(out, 'x\ny\n')
+ report = self.report_from_command("coverage report --show-missing")
+
+ # pylint: disable=C0301
+ # Name Stmts Miss Branch BrMiss Cover Missing
+ # -------------------------------------------------------
+ # main 1 0 0 0 100%
+ # mybranch 10 2 8 5 61% 7-8, 2->4, 4->6
+ # -------------------------------------------------------
+ # TOTAL 11 2 8 5 63%
+
+ self.assertEqual(self.line_count(report), 6)
+ squeezed = self.squeezed_lines(report)
+ self.assertEqual(
+ squeezed[2],
+ "main 1 0 0 0 100%"
+ )
+ self.assertEqual(
+ squeezed[3],
+ "mybranch 10 2 8 5 61% 7-8, 2->4, 4->6"
+ )
+ self.assertEqual(
+ squeezed[5],
+ "TOTAL 11 2 8 5 63%"
+ )
+
def test_dotpy_not_python(self):
# We run a .py file, and when reporting, we can't parse it as Python.
# We should get an error message in the report.
self.run_command("coverage run mycode.py")
self.make_file("mycode.py", "This isn't python at all!")
- report = self.report_from_command("coverage -r mycode.py")
+ report = self.report_from_command("coverage report mycode.py")
# pylint: disable=C0301
# Name Stmts Miss Cover
@@ -155,7 +253,7 @@ class SummaryTest(CoverageTest):
# but we've said to ignore errors, so there's no error reported.
self.run_command("coverage run mycode.py")
self.make_file("mycode.py", "This isn't python at all!")
- report = self.report_from_command("coverage -r -i mycode.py")
+ report = self.report_from_command("coverage report -i mycode.py")
# Name Stmts Miss Cover
# ----------------------------
@@ -171,7 +269,7 @@ class SummaryTest(CoverageTest):
self.run_command("coverage run mycode.html")
# Before reporting, change it to be an HTML file.
self.make_file("mycode.html", "<h1>This isn't python at all!</h1>")
- report = self.report_from_command("coverage -r mycode.html")
+ report = self.report_from_command("coverage report mycode.html")
# Name Stmts Miss Cover
# ----------------------------
diff --git a/tests/test_templite.py b/tests/test_templite.py
index 48e53ab4..a4667a62 100644
--- a/tests/test_templite.py
+++ b/tests/test_templite.py
@@ -1,6 +1,7 @@
"""Tests for coverage.templite."""
-from coverage.templite import Templite
+import re
+from coverage.templite import Templite, TempliteSyntaxError
from tests.coveragetest import CoverageTest
# pylint: disable=W0612,E1101
@@ -23,9 +24,23 @@ class TempliteTest(CoverageTest):
run_in_temp_dir = False
- def try_render(self, text, ctx, result):
- """Render `text` through `ctx`, and it had better be `result`."""
- self.assertEqual(Templite(text).render(ctx), result)
+ def try_render(self, text, ctx=None, result=None):
+ """Render `text` through `ctx`, and it had better be `result`.
+
+ Result defaults to None so we can shorten the calls where we expect
+ an exception and never get to the result comparison.
+ """
+ actual = Templite(text).render(ctx or {})
+ if result:
+ self.assertEqual(actual, result)
+
+ def assertSynErr(self, msg):
+ """Assert that a `TempliteSyntaxError` will happen.
+
+ A context manager, and the message should be `msg`.
+ """
+ pat = "^" + re.escape(msg) + "$"
+ return self.assertRaisesRegex(TempliteSyntaxError, pat)
def test_passthrough(self):
# Strings without variables are passed through unchanged.
@@ -42,7 +57,7 @@ class TempliteTest(CoverageTest):
def test_undefined_variables(self):
# Using undefined names is an error.
with self.assertRaises(Exception):
- self.try_render("Hi, {{name}}!", {}, "xyz")
+ self.try_render("Hi, {{name}}!")
def test_pipes(self):
# Variables can be filtered with pipes.
@@ -225,15 +240,42 @@ class TempliteTest(CoverageTest):
"Hey {{foo.bar.baz}} there", {'foo': None}, "Hey ??? there"
)
+ def test_bad_names(self):
+ with self.assertSynErr("Not a valid name: 'var%&!@'"):
+ self.try_render("Wat: {{ var%&!@ }}")
+ with self.assertSynErr("Not a valid name: 'filter%&!@'"):
+ self.try_render("Wat: {{ foo|filter%&!@ }}")
+ with self.assertSynErr("Not a valid name: '@'"):
+ self.try_render("Wat: {% for @ in x %}{% endfor %}")
+
def test_bogus_tag_syntax(self):
- msg = "Don't understand tag: 'bogus'"
- with self.assertRaisesRegexp(SyntaxError, msg):
- self.try_render("Huh: {% bogus %}!!{% endbogus %}??", {}, "")
+ with self.assertSynErr("Don't understand tag: 'bogus'"):
+ self.try_render("Huh: {% bogus %}!!{% endbogus %}??")
+
+ def test_malformed_if(self):
+ with self.assertSynErr("Don't understand if: '{% if %}'"):
+ self.try_render("Buh? {% if %}hi!{% endif %}")
+ with self.assertSynErr("Don't understand if: '{% if this or that %}'"):
+ self.try_render("Buh? {% if this or that %}hi!{% endif %}")
+
+ def test_malformed_for(self):
+ with self.assertSynErr("Don't understand for: '{% for %}'"):
+ self.try_render("Weird: {% for %}loop{% endfor %}")
+ with self.assertSynErr("Don't understand for: '{% for x from y %}'"):
+ self.try_render("Weird: {% for x from y %}loop{% endfor %}")
+ with self.assertSynErr("Don't understand for: '{% for x, y in z %}'"):
+ self.try_render("Weird: {% for x, y in z %}loop{% endfor %}")
def test_bad_nesting(self):
- msg = "Unmatched action tag: 'if'"
- with self.assertRaisesRegexp(SyntaxError, msg):
- self.try_render("{% if x %}X", {}, "")
- msg = "Mismatched end tag: 'for'"
- with self.assertRaisesRegexp(SyntaxError, msg):
- self.try_render("{% if x %}X{% endfor %}", {}, "")
+ with self.assertSynErr("Unmatched action tag: 'if'"):
+ self.try_render("{% if x %}X")
+ with self.assertSynErr("Mismatched end tag: 'for'"):
+ self.try_render("{% if x %}X{% endfor %}")
+ with self.assertSynErr("Too many ends: '{% endif %}'"):
+ self.try_render("{% if x %}{% endif %}{% endif %}")
+
+ def test_malformed_end(self):
+ with self.assertSynErr("Don't understand end: '{% end if %}'"):
+ self.try_render("{% if x %}X{% end if %}")
+ with self.assertSynErr("Don't understand end: '{% endif now %}'"):
+ self.try_render("{% if x %}X{% endif now %}")
diff --git a/tests/test_testing.py b/tests/test_testing.py
index a89a59a9..4a19098f 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -2,45 +2,44 @@
"""Tests that our test infrastructure is really working!"""
import os, sys
+from coverage.backunittest import TestCase
from coverage.backward import to_bytes
-from tests.backunittest import TestCase
-from tests.coveragetest import CoverageTest
+from tests.coveragetest import TempDirMixin, CoverageTest
class TestingTest(TestCase):
"""Tests of helper methods on `backunittest.TestCase`."""
- run_in_temp_dir = False
-
- def test_assert_same_elements(self):
- self.assertSameElements(set(), set())
- self.assertSameElements(set([1,2,3]), set([3,1,2]))
+ def test_assert_count_equal(self):
+ self.assertCountEqual(set(), set())
+ self.assertCountEqual(set([1,2,3]), set([3,1,2]))
with self.assertRaises(AssertionError):
- self.assertSameElements(set([1,2,3]), set())
+ self.assertCountEqual(set([1,2,3]), set())
with self.assertRaises(AssertionError):
- self.assertSameElements(set([1,2,3]), set([4,5,6]))
+ self.assertCountEqual(set([1,2,3]), set([4,5,6]))
-class CoverageTestTest(CoverageTest):
- """Test the methods in `CoverageTest`."""
+class TempDirMixinTest(TempDirMixin, TestCase):
+ """Test the methods in TempDirMixin."""
def file_text(self, fname):
"""Return the text read from a file."""
- return open(fname, "rb").read().decode('ascii')
+ with open(fname, "rb") as f:
+ return f.read().decode('ascii')
def test_make_file(self):
# A simple file.
self.make_file("fooey.boo", "Hello there")
- self.assertEqual(open("fooey.boo").read(), "Hello there")
+ self.assertEqual(self.file_text("fooey.boo"), "Hello there")
# A file in a sub-directory
self.make_file("sub/another.txt", "Another")
- self.assertEqual(open("sub/another.txt").read(), "Another")
+ self.assertEqual(self.file_text("sub/another.txt"), "Another")
# A second file in that sub-directory
self.make_file("sub/second.txt", "Second")
- self.assertEqual(open("sub/second.txt").read(), "Second")
+ self.assertEqual(self.file_text("sub/second.txt"), "Second")
# A deeper directory
self.make_file("sub/deeper/evenmore/third.txt")
- self.assertEqual(open("sub/deeper/evenmore/third.txt").read(), "")
+ self.assertEqual(self.file_text("sub/deeper/evenmore/third.txt"), "")
def test_make_file_newline(self):
self.make_file("unix.txt", "Hello\n")
@@ -52,10 +51,13 @@ class CoverageTestTest(CoverageTest):
def test_make_file_non_ascii(self):
self.make_file("unicode.txt", "tabblo: «ταБЬℓσ»")
- self.assertEqual(
- open("unicode.txt", "rb").read(),
- to_bytes("tabblo: «ταБЬℓσ»")
- )
+ with open("unicode.txt", "rb") as f:
+ text = f.read()
+ self.assertEqual(text, to_bytes("tabblo: «ταБЬℓσ»"))
+
+
+class CoverageTestTest(CoverageTest):
+ """Test the methods in `CoverageTest`."""
def test_file_exists(self):
self.make_file("whoville.txt", "We are here!")
diff --git a/tests/test_xml.py b/tests/test_xml.py
index 0801bad3..37ada3cb 100644
--- a/tests/test_xml.py
+++ b/tests/test_xml.py
@@ -26,6 +26,13 @@ class XmlReportTest(CoverageTest):
self.assert_doesnt_exist("coverage.xml")
self.assert_exists("put_it_there.xml")
+ def test_config_file_directory_does_not_exist(self):
+ self.run_mycode()
+ self.run_command("coverage xml -o nonexistent/put_it_there.xml")
+ self.assert_doesnt_exist("coverage.xml")
+ self.assert_doesnt_exist("put_it_there.xml")
+ self.assert_exists("nonexistent/put_it_there.xml")
+
def test_config_affects_xml_placement(self):
self.run_mycode()
self.make_file(".coveragerc", "[xml]\noutput = xml.out\n")