# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function import os import sys import time import py.path import _pytest.pytester as pytester import pytest from _pytest.config import PytestPluginManager from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.main import EXIT_OK from _pytest.main import EXIT_TESTSFAILED from _pytest.pytester import CwdSnapshot from _pytest.pytester import HookRecorder from _pytest.pytester import SysModulesSnapshot from _pytest.pytester import SysPathsSnapshot 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 test_runresult_assertion_on_xfail(testdir): testdir.makepyfile( """ import pytest pytest_plugins = "pytester" @pytest.mark.xfail def test_potato(): assert False """ ) result = testdir.runpytest() result.assert_outcomes(xfailed=1) assert result.ret == 0 def test_runresult_assertion_on_xpassed(testdir): testdir.makepyfile( """ import pytest pytest_plugins = "pytester" @pytest.mark.xfail def test_potato(): assert True """ ) result = testdir.runpytest() result.assert_outcomes(xpassed=1) assert result.ret == 0 def test_runresult_repr(): from _pytest.pytester import RunResult assert ( repr( RunResult(ret="ret", outlines=[""], errlines=["some", "errors"], duration=1) ) == "" ) def test_xpassed_with_strict_is_considered_a_failure(testdir): testdir.makepyfile( """ import pytest pytest_plugins = "pytester" @pytest.mark.xfail(strict=True) def test_potato(): assert True """ ) result = testdir.runpytest() result.assert_outcomes(failed=1) assert result.ret != 0 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.add_hookspecs(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_assert_outcomes_after_pytest_error(testdir): testdir.makepyfile("def test_foo(): assert True") result = testdir.runpytest("--unexpected-argument") with pytest.raises(ValueError, match="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)} 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 def test_testdir_subprocess(testdir): testfile = testdir.makepyfile("def test_one(): pass") assert testdir.runpytest_subprocess(testfile).ret == 0 def test_unicode_args(testdir): result = testdir.runpytest("-k", u"💩") assert result.ret == EXIT_NOTESTSCOLLECTED def test_testdir_run_no_timeout(testdir): testfile = testdir.makepyfile("def test_no_timeout(): pass") assert testdir.runpytest_subprocess(testfile).ret == EXIT_OK def test_testdir_run_with_timeout(testdir): testfile = testdir.makepyfile("def test_no_timeout(): pass") timeout = 120 start = time.time() result = testdir.runpytest_subprocess(testfile, timeout=timeout) end = time.time() duration = end - start assert result.ret == EXIT_OK assert duration < timeout def test_testdir_run_timeout_expires(testdir): testfile = testdir.makepyfile( """ import time def test_timeout(): time.sleep(10)""" ) with pytest.raises(testdir.TimeoutExpired): testdir.runpytest_subprocess(testfile, timeout=1)