summaryrefslogtreecommitdiff
path: root/tests/mixins.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/mixins.py')
-rw-r--r--tests/mixins.py131
1 files changed, 122 insertions, 9 deletions
diff --git a/tests/mixins.py b/tests/mixins.py
index 5abaa759..8fe0690b 100644
--- a/tests/mixins.py
+++ b/tests/mixins.py
@@ -8,30 +8,143 @@ Some of these are transitional while working toward pure-pytest style.
"""
import functools
+import os
+import os.path
+import sys
import types
+import textwrap
import unittest
import pytest
+from coverage import env
from coverage.misc import StopEverything
-class EnvironmentAwareMixin:
- """
- Adapter from pytst monkeypatch fixture to our environment variable methods.
- """
+class PytestBase(object):
+ """A base class to connect to pytest in a test class hierarchy."""
+
@pytest.fixture(autouse=True)
- def _monkeypatch(self, monkeypatch):
- """Get the monkeypatch fixture for our methods to use."""
- self._envpatcher = monkeypatch
+ def connect_to_pytest(self, request, monkeypatch):
+ """Captures pytest facilities for use by other test helpers."""
+ # pylint: disable=attribute-defined-outside-init
+ self._pytest_request = request
+ self._monkeypatch = monkeypatch
+ self.setup_test()
+
+ # Can't call this setUp or setup because pytest sniffs out unittest and
+ # nosetest special names, and does things with them.
+ # https://github.com/pytest-dev/pytest/issues/8424
+ def setup_test(self):
+ """Per-test initialization. Override this as you wish."""
+ pass
+
+ def addCleanup(self, fn, *args):
+ """Like unittest's addCleanup: code to call when the test is done."""
+ self._pytest_request.addfinalizer(lambda: fn(*args))
def set_environ(self, name, value):
"""Set an environment variable `name` to be `value`."""
- self._envpatcher.setenv(name, value)
+ self._monkeypatch.setenv(name, value)
def del_environ(self, name):
"""Delete an environment variable, unless we set it."""
- self._envpatcher.delenv(name)
+ self._monkeypatch.delenv(name)
+
+
+class TempDirMixin(object):
+ """Provides temp dir and data file helpers for tests."""
+
+ # Our own setting: most of these tests run in their own temp directory.
+ # Set this to False in your subclass if you don't want a temp directory
+ # created.
+ run_in_temp_dir = True
+
+ # Set this if you aren't creating any files with make_file, but still want
+ # the temp directory. This will stop the test behavior checker from
+ # complaining.
+ no_files_in_temp_dir = False
+
+ @pytest.fixture(autouse=True)
+ def _temp_dir(self, tmpdir_factory):
+ """Create a temp dir for the tests, if they want it."""
+ old_dir = None
+ if self.run_in_temp_dir:
+ tmpdir = tmpdir_factory.mktemp("")
+ self.temp_dir = str(tmpdir)
+ old_dir = os.getcwd()
+ tmpdir.chdir()
+
+ # 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())
+
+ try:
+ yield None
+ finally:
+ if old_dir is not None:
+ os.chdir(old_dir)
+
+ @pytest.fixture(autouse=True)
+ def _save_sys_path(self):
+ """Restore sys.path at the end of each test."""
+ old_syspath = sys.path[:]
+ try:
+ yield
+ finally:
+ sys.path = old_syspath
+
+ @pytest.fixture(autouse=True)
+ def _module_saving(self):
+ """Remove modules we imported during the test."""
+ old_modules = list(sys.modules)
+ try:
+ yield
+ finally:
+ added_modules = [m for m in sys.modules if m not in old_modules]
+ for m in added_modules:
+ del sys.modules[m]
+
+ def make_file(self, filename, text="", bytes=b"", newline=None):
+ """Create a file for testing.
+
+ `filename` is the relative path to the file, including directories if
+ desired, which will be created if need be.
+
+ `text` is the content to create in the file, a native string (bytes in
+ Python 2, unicode in Python 3), or `bytes` are the bytes to write.
+
+ If `newline` is provided, it is a string that will be used as the line
+ endings in the created file, otherwise the line endings are as provided
+ in `text`.
+
+ Returns `filename`.
+
+ """
+ # pylint: disable=redefined-builtin # bytes
+ if bytes:
+ data = bytes
+ else:
+ text = textwrap.dedent(text)
+ if newline:
+ text = text.replace("\n", newline)
+ if env.PY3:
+ data = text.encode('utf8')
+ else:
+ data = text
+
+ # 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(data)
+
+ return filename
def convert_skip_exceptions(method):