summaryrefslogtreecommitdiff
path: root/tests/coveragetest.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/coveragetest.py')
-rw-r--r--tests/coveragetest.py87
1 files changed, 58 insertions, 29 deletions
diff --git a/tests/coveragetest.py b/tests/coveragetest.py
index 5042c98..d79aee7 100644
--- a/tests/coveragetest.py
+++ b/tests/coveragetest.py
@@ -15,11 +15,12 @@ import sys
import coverage
from coverage.backunittest import TestCase
-from coverage.backward import StringIO, import_local_file, string_class
+from coverage.backward import StringIO, import_local_file, string_class, shlex_quote
from coverage.cmdline import CoverageScript
from coverage.debug import _TEST_NAME_FILE, DebugControl
from coverage.test_helpers import (
EnvironmentAwareMixin, StdStreamCapturingMixin, TempDirMixin,
+ DelayedAssertionMixin,
)
from nose.plugins.skip import SkipTest
@@ -35,6 +36,7 @@ class CoverageTest(
EnvironmentAwareMixin,
StdStreamCapturingMixin,
TempDirMixin,
+ DelayedAssertionMixin,
TestCase
):
"""A base class for coverage.py test cases."""
@@ -81,20 +83,22 @@ class CoverageTest(
if os.path.exists("__pycache__"):
shutil.rmtree("__pycache__")
- def import_local_file(self, modname):
+ def import_local_file(self, modname, modfile=None):
"""Import a local file as a module.
Opens a file in the current directory named `modname`.py, imports it
- as `modname`, and returns the module object.
+ as `modname`, and returns the module object. `modfile` is the file to
+ import if it isn't in the current directory.
"""
- return import_local_file(modname)
+ return import_local_file(modname, modfile)
- def start_import_stop(self, cov, modname):
+ def start_import_stop(self, cov, modname, modfile=None):
"""Start coverage, import a file, then stop coverage.
`cov` is started and stopped, with an `import_local_file` of
- `modname` in the middle.
+ `modname` in the middle. `modfile` is the file to import as `modname`
+ if it isn't in the current directory.
The imported module is returned.
@@ -102,7 +106,7 @@ class CoverageTest(
cov.start()
try: # pragma: nested
# Import the Python file, executing it.
- mod = self.import_local_file(modname)
+ mod = self.import_local_file(modname, modfile)
finally: # pragma: nested
# Stop coverage.py.
cov.stop()
@@ -161,7 +165,7 @@ class CoverageTest(
def check_coverage(
self, text, lines=None, missing="", report="",
excludes=None, partials="",
- arcz=None, arcz_missing=None, arcz_unpredicted=None,
+ arcz=None, arcz_missing="", arcz_unpredicted="",
arcs=None, arcs_missing=None, arcs_unpredicted=None,
):
"""Check the coverage measurement of `text`.
@@ -173,10 +177,11 @@ class CoverageTest(
of the measurement report.
For arc measurement, `arcz` is a string that can be decoded into arcs
- in the code (see `arcz_to_arcs` for the encoding scheme),
+ in the code (see `arcz_to_arcs` for the encoding scheme).
`arcz_missing` are the arcs that are not executed, and
- `arcs_unpredicted` are the arcs executed in the code, but not deducible
- from the code.
+ `arcz_unpredicted` are the arcs executed in the code, but not deducible
+ from the code. These last two default to "", meaning we explicitly
+ check that there are no missing or unpredicted arcs.
Returns the Coverage object, in case you want to poke at it some more.
@@ -189,14 +194,13 @@ class CoverageTest(
if arcs is None and arcz is not None:
arcs = self.arcz_to_arcs(arcz)
- if arcs_missing is None and arcz_missing is not None:
+ if arcs_missing is None:
arcs_missing = self.arcz_to_arcs(arcz_missing)
- if arcs_unpredicted is None and arcz_unpredicted is not None:
+ if arcs_unpredicted is None:
arcs_unpredicted = self.arcz_to_arcs(arcz_unpredicted)
- branch = any(x is not None for x in [arcs, arcs_missing, arcs_unpredicted])
# Start up coverage.py.
- cov = coverage.Coverage(branch=branch)
+ cov = coverage.Coverage(branch=True)
cov.erase()
for exc in excludes or []:
cov.exclude(exc)
@@ -236,15 +240,17 @@ class CoverageTest(
self.fail("None of the missing choices matched %r" % missing_formatted)
if arcs is not None:
- self.assert_equal_args(analysis.arc_possibilities(), arcs, "Possible arcs differ")
+ with self.delayed_assertions():
+ self.assert_equal_args(
+ analysis.arc_possibilities(), arcs,
+ "Possible arcs differ",
+ )
- if arcs_missing is not None:
self.assert_equal_args(
analysis.arcs_missing(), arcs_missing,
"Missing arcs differ"
)
- if arcs_unpredicted is not None:
self.assert_equal_args(
analysis.arcs_unpredicted(), arcs_unpredicted,
"Unpredicted arcs differ"
@@ -332,25 +338,48 @@ class CoverageTest(
ret_actual = script.command_line(shlex.split(args))
self.assertEqual(ret_actual, ret)
+ coverage_command = "coverage"
+
def run_command(self, cmd):
- """Run the command-line `cmd` in a sub-process, and print its output.
+ """Run the command-line `cmd` in a sub-process.
+
+ `cmd` is the command line to invoke in a sub-process. Returns the
+ combined content of `stdout` and `stderr` output streams from the
+ sub-process.
Use this when you need to test the process behavior of coverage.
Compare with `command_line`.
- Returns the process' stdout text.
+ Handles the following command name specially:
+
+ * "python" is replaced with the command name of the current
+ Python interpreter.
+
+ * "coverage" is replaced with the command name for the main
+ Coverage.py program.
"""
- # Running Python sub-processes can be tricky. Use the real name of our
- # own executable. So "python foo.py" might get executed as
- # "python3.3 foo.py". This is important because Python 3.x doesn't
- # install as "python", so you might get a Python 2 executable instead
- # if you don't use the executable's basename.
- if cmd.startswith("python "):
- cmd = os.path.basename(sys.executable) + cmd[6:]
-
- _, output = self.run_command_status(cmd)
+ split_commandline = cmd.split(" ", 1)
+ command_name = split_commandline[0]
+ command_args = split_commandline[1:]
+
+ if command_name == "python":
+ # Running a Python interpreter in a sub-processes can be tricky.
+ # Use the real name of our own executable. So "python foo.py" might
+ # get executed as "python3.3 foo.py". This is important because
+ # Python 3.x doesn't install as "python", so you might get a Python
+ # 2 executable instead if you don't use the executable's basename.
+ command_name = os.path.basename(sys.executable)
+
+ if command_name == "coverage":
+ # The invocation requests the Coverage.py program. Substitute the
+ # actual Coverage.py main command name.
+ command_name = self.coverage_command
+
+ full_commandline = " ".join([shlex_quote(command_name)] + command_args)
+
+ _, output = self.run_command_status(full_commandline)
return output
def run_command_status(self, cmd):