diff options
-rw-r--r-- | coverage/backward.py | 6 | ||||
-rw-r--r-- | tests/coveragetest.py | 16 | ||||
-rw-r--r-- | tests/helpers.py | 27 | ||||
-rw-r--r-- | tests/mixins.py | 76 | ||||
-rw-r--r-- | tests/test_test_mixins.py | 27 |
5 files changed, 86 insertions, 66 deletions
diff --git a/coverage/backward.py b/coverage/backward.py index ac781ab9..779cd661 100644 --- a/coverage/backward.py +++ b/coverage/backward.py @@ -229,12 +229,6 @@ def format_local_datetime(dt): return dt.strftime('%Y-%m-%d %H:%M') -def invalidate_import_caches(): - """Invalidate any import caches that may or may not exist.""" - if importlib and hasattr(importlib, "invalidate_caches"): - importlib.invalidate_caches() - - def import_local_file(modname, modfile=None): """Import a local file as a module. diff --git a/tests/coveragetest.py b/tests/coveragetest.py index f08de798..65678d52 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -22,8 +22,8 @@ from coverage.backward import StringIO, import_local_file, string_class, shlex_q from coverage.cmdline import CoverageScript from tests.helpers import arcs_to_arcz_repr, arcz_to_arcs, assert_count_equal -from tests.helpers import run_command, SuperModuleCleaner -from tests.mixins import StdStreamCapturingMixin, TempDirMixin, PytestBase +from tests.helpers import run_command +from tests.mixins import PytestBase, StdStreamCapturingMixin, SysPathModulesMixin, TempDirMixin # Status returns for the command line. @@ -35,6 +35,7 @@ TESTS_DIR = os.path.dirname(__file__) class CoverageTest( StdStreamCapturingMixin, + SysPathModulesMixin, TempDirMixin, PytestBase, ): @@ -61,22 +62,11 @@ class CoverageTest( def setup_test(self): super(CoverageTest, self).setup_test() - self.module_cleaner = SuperModuleCleaner() - # Attributes for getting info about what happened. self.last_command_status = None self.last_command_output = None self.last_module_name = None - 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. - - """ - self.module_cleaner.clean_local_file_imports() - def start_import_stop(self, cov, modname, modfile=None): """Start coverage, import a file, then stop coverage. diff --git a/tests/helpers.py b/tests/helpers.py index 195fefde..d4dd33ea 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -8,15 +8,13 @@ import contextlib import glob import os import re -import shutil import subprocess import sys import mock -from unittest_mixins import ModuleCleaner from coverage import env -from coverage.backward import invalidate_import_caches, unicode_class +from coverage.backward import unicode_class from coverage.misc import output_encoding @@ -115,29 +113,6 @@ def remove_files(*patterns): os.remove(fname) -class SuperModuleCleaner(ModuleCleaner): - """Remember the state of sys.modules and restore it later.""" - - 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.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. - remove_files("*.pyc", "*$py.class") - if os.path.exists("__pycache__"): - shutil.rmtree("__pycache__") - - invalidate_import_caches() - - # Map chars to numbers for arcz_to_arcs _arcz_map = {'.': -1} _arcz_map.update(dict((c, ord(c) - ord('0')) for c in '123456789')) diff --git a/tests/mixins.py b/tests/mixins.py index 7492e90c..ef9cdb6f 100644 --- a/tests/mixins.py +++ b/tests/mixins.py @@ -9,12 +9,16 @@ Some of these are transitional while working toward pure-pytest style. import os import os.path +import shutil import sys import textwrap import pytest from coverage import env +from coverage.backward import importlib + +from tests.helpers import remove_files class PytestBase(object): @@ -78,26 +82,6 @@ class TempDirMixin(object): 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. @@ -138,6 +122,58 @@ class TempDirMixin(object): return filename +class SysPathModulesMixin: + """Auto-restore sys.path and the imported modules at the end of each test.""" + + @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.""" + self._old_modules = list(sys.modules) + try: + yield + finally: + self._cleanup_modules() + + def _cleanup_modules(self): + """Remove any new modules imported since our construction. + + This lets us import the same source files for more than one test, or + if called explicitly, within one test. + + """ + for m in [m for m in sys.modules if m not in self._old_modules]: + del sys.modules[m] + + 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._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. + remove_files("*.pyc", "*$py.class") + if os.path.exists("__pycache__"): + shutil.rmtree("__pycache__") + + if importlib and hasattr(importlib, "invalidate_caches"): + importlib.invalidate_caches() + + class StdStreamCapturingMixin: """ Adapter from the pytest capsys fixture to more convenient methods. diff --git a/tests/test_test_mixins.py b/tests/test_test_mixins.py index b8a3ac67..028a19fd 100644 --- a/tests/test_test_mixins.py +++ b/tests/test_test_mixins.py @@ -4,7 +4,11 @@ """Tests of code in tests/mixins.py""" -from tests.mixins import TempDirMixin +import pytest + +from coverage.backward import import_local_file + +from tests.mixins import TempDirMixin, SysPathModulesMixin class TempDirMixinTest(TempDirMixin): @@ -54,3 +58,24 @@ class TempDirMixinTest(TempDirMixin): with open("binary.dat", "rb") as f: data = f.read() assert data == b"\x99\x33\x66hello\0" + + +class SysPathModulessMixinTest(TempDirMixin, SysPathModulesMixin): + """Tests of SysPathModulesMixin.""" + + @pytest.mark.parametrize("val", [17, 42]) + def test_module_independence(self, val): + self.make_file("xyzzy.py", "A = {}".format(val)) + import xyzzy # pylint: disable=import-error + assert xyzzy.A == val + + def test_cleanup_and_reimport(self): + self.make_file("xyzzy.py", "A = 17") + xyzzy = import_local_file("xyzzy") + assert xyzzy.A == 17 + + self.clean_local_file_imports() + + self.make_file("xyzzy.py", "A = 42") + xyzzy = import_local_file("xyzzy") + assert xyzzy.A == 42 |