summaryrefslogtreecommitdiff
path: root/test/coveragetest.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/coveragetest.py')
-rw-r--r--test/coveragetest.py465
1 files changed, 0 insertions, 465 deletions
diff --git a/test/coveragetest.py b/test/coveragetest.py
deleted file mode 100644
index 6f6217aa..00000000
--- a/test/coveragetest.py
+++ /dev/null
@@ -1,465 +0,0 @@
-"""Base test case class for coverage testing."""
-
-import glob, imp, os, random, shlex, shutil, sys, tempfile, textwrap
-
-import coverage
-from coverage.backward import sorted, StringIO # pylint: disable=W0622
-from coverage.backward import to_bytes
-from coverage.control import _TEST_NAME_FILE
-from test.backtest import run_command
-from test.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)
-
- 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)
-
-
-# Status returns for the command line.
-OK, ERR = 0, 1
-
-class CoverageTest(TestCase):
- """A base class for Coverage test cases."""
-
- run_in_temp_dir = True
-
- def setUp(self):
- super(CoverageTest, self).setUp()
-
- if _TEST_NAME_FILE:
- f = open(_TEST_NAME_FILE, "w")
- 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.
- sys.path.insert(0, '')
-
- # 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)
-
- 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()
-
- 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
- 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.
- f = open(filename, 'wb')
- try:
- f.write(to_bytes(text))
- finally:
- f.close()
-
- return filename
-
- def clean_local_file_imports(self):
- """Clean up the results of calls to `import_local_file`.
-
- Use this if you need to `import_local_file` the same file twice in
- one test.
-
- """
- # So that we can re-import files, clean them out first.
- self.clean_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.
- for pyc in glob.glob('*.pyc'):
- os.remove(pyc)
- if os.path.exists("__pycache__"):
- shutil.rmtree("__pycache__")
-
- def import_local_file(self, modname):
- """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.
-
- """
- modfile = modname + '.py'
- f = open(modfile, 'r')
-
- for suff in imp.get_suffixes():
- if suff[0] == '.py':
- break
- try:
- # pylint: disable=W0631
- # (Using possibly undefined loop variable 'suff')
- mod = imp.load_module(modname, f, modfile, suff)
- finally:
- f.close()
- return mod
-
- def start_import_stop(self, cov, modname):
- """Start coverage, import a file, then stop coverage.
-
- `cov` is started and stopped, with an `import_local_file` of
- `modname` in the middle.
-
- The imported module is returned.
-
- """
- cov.start()
- try: # pragma: nested
- # Import the python file, executing it.
- mod = self.import_local_file(modname)
- finally: # pragma: nested
- # Stop Coverage.
- cov.stop()
- return mod
-
- 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
-
- # Map chars to numbers for arcz_to_arcs
- _arcz_map = {'.': -1}
- _arcz_map.update(dict([(c, ord(c)-ord('0')) for c in '123456789']))
- _arcz_map.update(dict(
- [(c, 10+ord(c)-ord('A')) for c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
- ))
-
- def arcz_to_arcs(self, arcz):
- """Convert a compact textual representation of arcs to a list of pairs.
-
- The text has space-separated pairs of letters. Period is -1, 1-9 are
- 1-9, A-Z are 10 through 36. The resulting list is sorted regardless of
- the order of the input pairs.
-
- ".1 12 2." --> [(-1,1), (1,2), (2,-1)]
-
- Minus signs can be included in the pairs:
-
- "-11, 12, 2-5" --> [(-1,1), (1,2), (2,-5)]
-
- """
- arcs = []
- for pair in arcz.split():
- asgn = bsgn = 1
- if len(pair) == 2:
- a,b = pair
- else:
- assert len(pair) == 3
- if pair[0] == '-':
- _,a,b = pair
- asgn = -1
- else:
- assert pair[1] == '-'
- a,_,b = pair
- bsgn = -1
- arcs.append((asgn*self._arcz_map[a], bsgn*self._arcz_map[b]))
- return sorted(arcs)
-
- def assertEqualArcs(self, a1, a2, msg=None):
- """Assert that the arc lists `a1` and `a2` are equal."""
- # Make them into multi-line strings so we can see what's going wrong.
- s1 = "\n".join([repr(a) for a in a1]) + "\n"
- s2 = "\n".join([repr(a) for a in a2]) + "\n"
- self.assertMultiLineEqual(s1, s2, msg)
-
- def check_coverage(self, text, lines=None, missing="", report="",
- excludes=None, partials="",
- arcz=None, arcz_missing="", arcz_unpredicted=""):
- """Check the coverage measurement of `text`.
-
- The source `text` is run and measured. `lines` are the line numbers
- that are executable, or a list of possible line numbers, any of which
- could match. `missing` are the lines not executed, `excludes` are
- regexes to match against for excluding lines, and `report` is the text
- 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),
- `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.
-
- """
- # We write the code into a file so that we can import it.
- # Coverage wants to deal with things as modules with file names.
- modname = self.get_module_name()
-
- self.make_file(modname+".py", text)
-
- arcs = arcs_missing = arcs_unpredicted = None
- if arcz is not None:
- arcs = self.arcz_to_arcs(arcz)
- arcs_missing = self.arcz_to_arcs(arcz_missing or "")
- arcs_unpredicted = self.arcz_to_arcs(arcz_unpredicted or "")
-
- # Start up Coverage.
- cov = coverage.coverage(branch=(arcs_missing is not None))
- cov.erase()
- for exc in excludes or []:
- cov.exclude(exc)
- for par in partials or []:
- cov.exclude(par, which='partial')
-
- mod = self.start_import_stop(cov, modname)
-
- # Clean up our side effects
- del sys.modules[modname]
-
- # Get the analysis results, and check that they are right.
- analysis = cov._analyze(mod)
- if lines is not None:
- if type(lines[0]) == type(1):
- # lines is just a list of numbers, it must match the statements
- # found in the code.
- self.assertEqual(analysis.statements, lines)
- else:
- # lines is a list of possible line number lists, one of them
- # must match.
- for line_list in lines:
- if analysis.statements == line_list:
- break
- else:
- self.fail("None of the lines choices matched %r" %
- analysis.statements
- )
-
- if type(missing) == type(""):
- self.assertEqual(analysis.missing_formatted(), missing)
- else:
- for missing_list in missing:
- if analysis.missing_formatted() == missing_list:
- break
- else:
- self.fail("None of the missing choices matched %r" %
- analysis.missing_formatted()
- )
-
- if arcs is not None:
- self.assertEqualArcs(
- analysis.arc_possibilities(), arcs, "Possible arcs differ"
- )
-
- if arcs_missing is not None:
- self.assertEqualArcs(
- analysis.arcs_missing(), arcs_missing,
- "Missing arcs differ"
- )
-
- if arcs_unpredicted is not None:
- self.assertEqualArcs(
- analysis.arcs_unpredicted(), arcs_unpredicted,
- "Unpredicted arcs differ"
- )
-
- if report:
- frep = StringIO()
- cov.report(mod, file=frep)
- rep = " ".join(frep.getvalue().split("\n")[2].split()[1:])
- self.assertEqual(report, rep)
-
- def nice_file(self, *fparts):
- """Canonicalize the filename composed of the parts in `fparts`."""
- fname = os.path.join(*fparts)
- return os.path.normcase(os.path.abspath(os.path.realpath(fname)))
-
- def assert_same_files(self, flist1, flist2):
- """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)
-
- 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)
-
- 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)
-
- def command_line(self, args, ret=OK, _covpkg=None):
- """Run `args` through the command line.
-
- Use this when you want to run the full coverage machinery, but in the
- current process. Exceptions may be thrown from deep in the code.
- Asserts that `ret` is returned by `CoverageScript.command_line`.
-
- Compare with `run_command`.
-
- Returns None.
-
- """
- script = coverage.CoverageScript(_covpkg=_covpkg)
- ret_actual = script.command_line(shlex.split(args))
- self.assertEqual(ret_actual, ret)
-
- def run_command(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.
-
- Compare with `command_line`.
-
- Returns the process' stdout text.
-
- """
- _, output = self.run_command_status(cmd)
- return output
-
- def run_command_status(self, cmd, status=0):
- """Run the command-line `cmd` in a subprocess, and print its output.
-
- Use this when you need to test the process behavior of coverage.
-
- Compare with `command_line`.
-
- 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...
- here = os.path.dirname(self.nice_file(coverage.__file__, ".."))
- testmods = self.nice_file(here, 'test/modules')
- zipfile = self.nice_file(here, 'test/zipmods.zip')
- pypath = os.getenv('PYTHONPATH', '')
- if pypath:
- pypath += os.pathsep
- pypath += testmods + os.pathsep + zipfile
- self.set_environ('PYTHONPATH', pypath)
-
- status, output = run_command(cmd, status=status)
- print(output)
- return status, output