379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
| # -*- coding: utf-8 -*-
 | |
| from __future__ import absolute_import, division, print_function
 | |
| import os
 | |
| import py.path
 | |
| import pytest
 | |
| import sys
 | |
| import _pytest.pytester as pytester
 | |
| from _pytest.pytester import HookRecorder
 | |
| from _pytest.pytester import CwdSnapshot, SysModulesSnapshot, SysPathsSnapshot
 | |
| from _pytest.config import PytestPluginManager
 | |
| from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
 | |
| 
 | |
| 
 | |
| def test_make_hook_recorder(testdir):
 | |
|     item = testdir.getitem("def test_func(): pass")
 | |
|     recorder = testdir.make_hook_recorder(item.config.pluginmanager)
 | |
|     assert not recorder.getfailures()
 | |
| 
 | |
|     pytest.xfail("internal reportrecorder tests need refactoring")
 | |
| 
 | |
|     class rep(object):
 | |
|         excinfo = None
 | |
|         passed = False
 | |
|         failed = True
 | |
|         skipped = False
 | |
|         when = "call"
 | |
| 
 | |
|     recorder.hook.pytest_runtest_logreport(report=rep)
 | |
|     failures = recorder.getfailures()
 | |
|     assert failures == [rep]
 | |
|     failures = recorder.getfailures()
 | |
|     assert failures == [rep]
 | |
| 
 | |
|     class rep(object):
 | |
|         excinfo = None
 | |
|         passed = False
 | |
|         failed = False
 | |
|         skipped = True
 | |
|         when = "call"
 | |
|     rep.passed = False
 | |
|     rep.skipped = True
 | |
|     recorder.hook.pytest_runtest_logreport(report=rep)
 | |
| 
 | |
|     modcol = testdir.getmodulecol("")
 | |
|     rep = modcol.config.hook.pytest_make_collect_report(collector=modcol)
 | |
|     rep.passed = False
 | |
|     rep.failed = True
 | |
|     rep.skipped = False
 | |
|     recorder.hook.pytest_collectreport(report=rep)
 | |
| 
 | |
|     passed, skipped, failed = recorder.listoutcomes()
 | |
|     assert not passed and skipped and failed
 | |
| 
 | |
|     numpassed, numskipped, numfailed = recorder.countoutcomes()
 | |
|     assert numpassed == 0
 | |
|     assert numskipped == 1
 | |
|     assert numfailed == 1
 | |
|     assert len(recorder.getfailedcollections()) == 1
 | |
| 
 | |
|     recorder.unregister()
 | |
|     recorder.clear()
 | |
|     recorder.hook.pytest_runtest_logreport(report=rep)
 | |
|     pytest.raises(ValueError, "recorder.getfailures()")
 | |
| 
 | |
| 
 | |
| def test_parseconfig(testdir):
 | |
|     config1 = testdir.parseconfig()
 | |
|     config2 = testdir.parseconfig()
 | |
|     assert config2 != config1
 | |
|     assert config1 != pytest.config
 | |
| 
 | |
| 
 | |
| def test_testdir_runs_with_plugin(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         pytest_plugins = "pytester"
 | |
|         def test_hello(testdir):
 | |
|             assert 1
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.assert_outcomes(passed=1)
 | |
| 
 | |
| 
 | |
| def make_holder():
 | |
|     class apiclass(object):
 | |
|         def pytest_xyz(self, arg):
 | |
|             "x"
 | |
| 
 | |
|         def pytest_xyz_noarg(self):
 | |
|             "x"
 | |
| 
 | |
|     apimod = type(os)('api')
 | |
| 
 | |
|     def pytest_xyz(arg):
 | |
|         "x"
 | |
| 
 | |
|     def pytest_xyz_noarg():
 | |
|         "x"
 | |
| 
 | |
|     apimod.pytest_xyz = pytest_xyz
 | |
|     apimod.pytest_xyz_noarg = pytest_xyz_noarg
 | |
|     return apiclass, apimod
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("holder", make_holder())
 | |
| def test_hookrecorder_basic(holder):
 | |
|     pm = PytestPluginManager()
 | |
|     pm.addhooks(holder)
 | |
|     rec = HookRecorder(pm)
 | |
|     pm.hook.pytest_xyz(arg=123)
 | |
|     call = rec.popcall("pytest_xyz")
 | |
|     assert call.arg == 123
 | |
|     assert call._name == "pytest_xyz"
 | |
|     pytest.raises(pytest.fail.Exception, "rec.popcall('abc')")
 | |
|     pm.hook.pytest_xyz_noarg()
 | |
|     call = rec.popcall("pytest_xyz_noarg")
 | |
|     assert call._name == "pytest_xyz_noarg"
 | |
| 
 | |
| 
 | |
| def test_makepyfile_unicode(testdir):
 | |
|     global unichr
 | |
|     try:
 | |
|         unichr(65)
 | |
|     except NameError:
 | |
|         unichr = chr
 | |
|     testdir.makepyfile(unichr(0xfffd))
 | |
| 
 | |
| 
 | |
| def test_makepyfile_utf8(testdir):
 | |
|     """Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
 | |
|     utf8_contents = u"""
 | |
|         def setup_function(function):
 | |
|             mixed_encoding = u'São Paulo'
 | |
|     """.encode('utf-8')
 | |
|     p = testdir.makepyfile(utf8_contents)
 | |
|     assert u"mixed_encoding = u'São Paulo'".encode('utf-8') in p.read('rb')
 | |
| 
 | |
| 
 | |
| class TestInlineRunModulesCleanup(object):
 | |
|     def test_inline_run_test_module_not_cleaned_up(self, testdir):
 | |
|         test_mod = testdir.makepyfile("def test_foo(): assert True")
 | |
|         result = testdir.inline_run(str(test_mod))
 | |
|         assert result.ret == EXIT_OK
 | |
|         # rewrite module, now test should fail if module was re-imported
 | |
|         test_mod.write("def test_foo(): assert False")
 | |
|         result2 = testdir.inline_run(str(test_mod))
 | |
|         assert result2.ret == EXIT_TESTSFAILED
 | |
| 
 | |
|     def spy_factory(self):
 | |
|         class SysModulesSnapshotSpy(object):
 | |
|             instances = []
 | |
| 
 | |
|             def __init__(self, preserve=None):
 | |
|                 SysModulesSnapshotSpy.instances.append(self)
 | |
|                 self._spy_restore_count = 0
 | |
|                 self._spy_preserve = preserve
 | |
|                 self.__snapshot = SysModulesSnapshot(preserve=preserve)
 | |
| 
 | |
|             def restore(self):
 | |
|                 self._spy_restore_count += 1
 | |
|                 return self.__snapshot.restore()
 | |
|         return SysModulesSnapshotSpy
 | |
| 
 | |
|     def test_inline_run_taking_and_restoring_a_sys_modules_snapshot(
 | |
|             self, testdir, monkeypatch):
 | |
|         spy_factory = self.spy_factory()
 | |
|         monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
 | |
|         original = dict(sys.modules)
 | |
|         testdir.syspathinsert()
 | |
|         testdir.makepyfile(import1="# you son of a silly person")
 | |
|         testdir.makepyfile(import2="# my hovercraft is full of eels")
 | |
|         test_mod = testdir.makepyfile("""
 | |
|             import import1
 | |
|             def test_foo(): import import2""")
 | |
|         testdir.inline_run(str(test_mod))
 | |
|         assert len(spy_factory.instances) == 1
 | |
|         spy = spy_factory.instances[0]
 | |
|         assert spy._spy_restore_count == 1
 | |
|         assert sys.modules == original
 | |
|         assert all(sys.modules[x] is original[x] for x in sys.modules)
 | |
| 
 | |
|     def test_inline_run_sys_modules_snapshot_restore_preserving_modules(
 | |
|             self, testdir, monkeypatch):
 | |
|         spy_factory = self.spy_factory()
 | |
|         monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
 | |
|         test_mod = testdir.makepyfile("def test_foo(): pass")
 | |
|         testdir.inline_run(str(test_mod))
 | |
|         spy = spy_factory.instances[0]
 | |
|         assert not spy._spy_preserve("black_knight")
 | |
|         assert spy._spy_preserve("zope")
 | |
|         assert spy._spy_preserve("zope.interface")
 | |
|         assert spy._spy_preserve("zopelicious")
 | |
| 
 | |
|     def test_external_test_module_imports_not_cleaned_up(self, testdir):
 | |
|         testdir.syspathinsert()
 | |
|         testdir.makepyfile(imported="data = 'you son of a silly person'")
 | |
|         import imported
 | |
|         test_mod = testdir.makepyfile("""
 | |
|             def test_foo():
 | |
|                 import imported
 | |
|                 imported.data = 42""")
 | |
|         testdir.inline_run(str(test_mod))
 | |
|         assert imported.data == 42
 | |
| 
 | |
| 
 | |
| def test_inline_run_clean_sys_paths(testdir):
 | |
|     def test_sys_path_change_cleanup(self, testdir):
 | |
|         test_path1 = testdir.tmpdir.join("boink1").strpath
 | |
|         test_path2 = testdir.tmpdir.join("boink2").strpath
 | |
|         test_path3 = testdir.tmpdir.join("boink3").strpath
 | |
|         sys.path.append(test_path1)
 | |
|         sys.meta_path.append(test_path1)
 | |
|         original_path = list(sys.path)
 | |
|         original_meta_path = list(sys.meta_path)
 | |
|         test_mod = testdir.makepyfile("""
 | |
|             import sys
 | |
|             sys.path.append({:test_path2})
 | |
|             sys.meta_path.append({:test_path2})
 | |
|             def test_foo():
 | |
|                 sys.path.append({:test_path3})
 | |
|                 sys.meta_path.append({:test_path3})""".format(locals()))
 | |
|         testdir.inline_run(str(test_mod))
 | |
|         assert sys.path == original_path
 | |
|         assert sys.meta_path == original_meta_path
 | |
| 
 | |
|     def spy_factory(self):
 | |
|         class SysPathsSnapshotSpy(object):
 | |
|             instances = []
 | |
| 
 | |
|             def __init__(self):
 | |
|                 SysPathsSnapshotSpy.instances.append(self)
 | |
|                 self._spy_restore_count = 0
 | |
|                 self.__snapshot = SysPathsSnapshot()
 | |
| 
 | |
|             def restore(self):
 | |
|                 self._spy_restore_count += 1
 | |
|                 return self.__snapshot.restore()
 | |
|         return SysPathsSnapshotSpy
 | |
| 
 | |
|     def test_inline_run_taking_and_restoring_a_sys_paths_snapshot(
 | |
|             self, testdir, monkeypatch):
 | |
|         spy_factory = self.spy_factory()
 | |
|         monkeypatch.setattr(pytester, "SysPathsSnapshot", spy_factory)
 | |
|         test_mod = testdir.makepyfile("def test_foo(): pass")
 | |
|         testdir.inline_run(str(test_mod))
 | |
|         assert len(spy_factory.instances) == 1
 | |
|         spy = spy_factory.instances[0]
 | |
|         assert spy._spy_restore_count == 1
 | |
| 
 | |
| 
 | |
| def test_assert_outcomes_after_pytest_error(testdir):
 | |
|     testdir.makepyfile("def test_foo(): assert True")
 | |
| 
 | |
|     result = testdir.runpytest('--unexpected-argument')
 | |
|     with pytest.raises(ValueError, message="Pytest terminal report not found"):
 | |
|         result.assert_outcomes(passed=0)
 | |
| 
 | |
| 
 | |
| def test_cwd_snapshot(tmpdir):
 | |
|     foo = tmpdir.ensure('foo', dir=1)
 | |
|     bar = tmpdir.ensure('bar', dir=1)
 | |
|     foo.chdir()
 | |
|     snapshot = CwdSnapshot()
 | |
|     bar.chdir()
 | |
|     assert py.path.local() == bar
 | |
|     snapshot.restore()
 | |
|     assert py.path.local() == foo
 | |
| 
 | |
| 
 | |
| class TestSysModulesSnapshot(object):
 | |
|     key = 'my-test-module'
 | |
| 
 | |
|     def test_remove_added(self):
 | |
|         original = dict(sys.modules)
 | |
|         assert self.key not in sys.modules
 | |
|         snapshot = SysModulesSnapshot()
 | |
|         sys.modules[self.key] = 'something'
 | |
|         assert self.key in sys.modules
 | |
|         snapshot.restore()
 | |
|         assert sys.modules == original
 | |
| 
 | |
|     def test_add_removed(self, monkeypatch):
 | |
|         assert self.key not in sys.modules
 | |
|         monkeypatch.setitem(sys.modules, self.key, 'something')
 | |
|         assert self.key in sys.modules
 | |
|         original = dict(sys.modules)
 | |
|         snapshot = SysModulesSnapshot()
 | |
|         del sys.modules[self.key]
 | |
|         assert self.key not in sys.modules
 | |
|         snapshot.restore()
 | |
|         assert sys.modules == original
 | |
| 
 | |
|     def test_restore_reloaded(self, monkeypatch):
 | |
|         assert self.key not in sys.modules
 | |
|         monkeypatch.setitem(sys.modules, self.key, 'something')
 | |
|         assert self.key in sys.modules
 | |
|         original = dict(sys.modules)
 | |
|         snapshot = SysModulesSnapshot()
 | |
|         sys.modules[self.key] = 'something else'
 | |
|         snapshot.restore()
 | |
|         assert sys.modules == original
 | |
| 
 | |
|     def test_preserve_modules(self, monkeypatch):
 | |
|         key = [self.key + str(i) for i in range(3)]
 | |
|         assert not any(k in sys.modules for k in key)
 | |
|         for i, k in enumerate(key):
 | |
|             monkeypatch.setitem(sys.modules, k, 'something' + str(i))
 | |
|         original = dict(sys.modules)
 | |
| 
 | |
|         def preserve(name):
 | |
|             return name in (key[0], key[1], 'some-other-key')
 | |
| 
 | |
|         snapshot = SysModulesSnapshot(preserve=preserve)
 | |
|         sys.modules[key[0]] = original[key[0]] = 'something else0'
 | |
|         sys.modules[key[1]] = original[key[1]] = 'something else1'
 | |
|         sys.modules[key[2]] = 'something else2'
 | |
|         snapshot.restore()
 | |
|         assert sys.modules == original
 | |
| 
 | |
|     def test_preserve_container(self, monkeypatch):
 | |
|         original = dict(sys.modules)
 | |
|         assert self.key not in original
 | |
|         replacement = dict(sys.modules)
 | |
|         replacement[self.key] = 'life of brian'
 | |
|         snapshot = SysModulesSnapshot()
 | |
|         monkeypatch.setattr(sys, 'modules', replacement)
 | |
|         snapshot.restore()
 | |
|         assert sys.modules is replacement
 | |
|         assert sys.modules == original
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize('path_type', ('path', 'meta_path'))
 | |
| class TestSysPathsSnapshot(object):
 | |
|     other_path = {
 | |
|         'path': 'meta_path',
 | |
|         'meta_path': 'path'}
 | |
| 
 | |
|     @staticmethod
 | |
|     def path(n):
 | |
|         return 'my-dirty-little-secret-' + str(n)
 | |
| 
 | |
|     def test_restore(self, monkeypatch, path_type):
 | |
|         other_path_type = self.other_path[path_type]
 | |
|         for i in range(10):
 | |
|             assert self.path(i) not in getattr(sys, path_type)
 | |
|         sys_path = [self.path(i) for i in range(6)]
 | |
|         monkeypatch.setattr(sys, path_type, sys_path)
 | |
|         original = list(sys_path)
 | |
|         original_other = list(getattr(sys, other_path_type))
 | |
|         snapshot = SysPathsSnapshot()
 | |
|         transformation = {
 | |
|             'source': (0, 1, 2,    3, 4, 5),
 | |
|             'target': (   6, 2, 9, 7,    5, 8)}  # noqa: E201
 | |
|         assert sys_path == [self.path(x) for x in transformation['source']]
 | |
|         sys_path[1] = self.path(6)
 | |
|         sys_path[3] = self.path(7)
 | |
|         sys_path.append(self.path(8))
 | |
|         del sys_path[4]
 | |
|         sys_path[3:3] = [self.path(9)]
 | |
|         del sys_path[0]
 | |
|         assert sys_path == [self.path(x) for x in transformation['target']]
 | |
|         snapshot.restore()
 | |
|         assert getattr(sys, path_type) is sys_path
 | |
|         assert getattr(sys, path_type) == original
 | |
|         assert getattr(sys, other_path_type) == original_other
 | |
| 
 | |
|     def test_preserve_container(self, monkeypatch, path_type):
 | |
|         other_path_type = self.other_path[path_type]
 | |
|         original_data = list(getattr(sys, path_type))
 | |
|         original_other = getattr(sys, other_path_type)
 | |
|         original_other_data = list(original_other)
 | |
|         new = []
 | |
|         snapshot = SysPathsSnapshot()
 | |
|         monkeypatch.setattr(sys, path_type, new)
 | |
|         snapshot.restore()
 | |
|         assert getattr(sys, path_type) is new
 | |
|         assert getattr(sys, path_type) == original_data
 | |
|         assert getattr(sys, other_path_type) is original_other
 | |
|         assert getattr(sys, other_path_type) == original_other_data
 |