diff options
Diffstat (limited to 'tests/coveragetest.py')
| -rw-r--r-- | tests/coveragetest.py | 87 |
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): |
