test_ok2/testing/test_tmpdir.py

441 lines
13 KiB
Python
Raw Normal View History

import os
import stat
import sys
import attr
2014-01-29 17:20:13 +08:00
import pytest
from _pytest import pathlib
from _pytest.pathlib import cleanup_numbered_dir
from _pytest.pathlib import create_cleanup_lock
from _pytest.pathlib import make_numbered_dir
from _pytest.pathlib import maybe_delete_a_numbered_dir
from _pytest.pathlib import on_rm_rf_error
from _pytest.pathlib import Path
from _pytest.pathlib import register_cleanup_lock_removal
from _pytest.pathlib import rm_rf
from _pytest.tmpdir import get_user
from _pytest.tmpdir import TempdirFactory
from _pytest.tmpdir import TempPathFactory
def test_tmpdir_fixture(testdir):
p = testdir.copy_example("tmpdir/tmpdir_fixture.py")
results = testdir.runpytest(p)
results.stdout.fnmatch_lines(["*1 passed*"])
@attr.s
2019-06-03 06:32:00 +08:00
class FakeConfig:
basetemp = attr.ib()
@property
def trace(self):
return self
def get(self, key):
return lambda *k: None
@property
def option(self):
return self
2019-06-03 06:32:00 +08:00
class TestTempdirHandler:
def test_mktemp(self, tmp_path):
config = FakeConfig(tmp_path)
t = TempdirFactory(TempPathFactory.from_config(config))
tmp = t.mktemp("world")
assert tmp.relto(t.getbasetemp()) == "world0"
tmp = t.mktemp("this")
assert tmp.relto(t.getbasetemp()).startswith("this")
tmp2 = t.mktemp("this")
assert tmp2.relto(t.getbasetemp()).startswith("this")
assert tmp2 != tmp
def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch):
"""#4425"""
monkeypatch.chdir(tmp_path)
config = FakeConfig("hello")
t = TempPathFactory.from_config(config)
assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve()
2019-06-03 06:32:00 +08:00
class TestConfigTmpdir:
def test_getbasetemp_custom_removes_old(self, testdir):
mytemp = testdir.tmpdir.join("xyz")
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_1(tmpdir):
pass
2018-05-23 22:48:46 +08:00
"""
)
testdir.runpytest(p, "--basetemp=%s" % mytemp)
mytemp.check()
mytemp.ensure("hello")
2018-05-23 22:48:46 +08:00
testdir.runpytest(p, "--basetemp=%s" % mytemp)
mytemp.check()
assert not mytemp.join("hello").check()
testdata = [
("mypath", True),
("/mypath1", False),
("./mypath1", True),
("../mypath3", False),
("../../mypath4", False),
("mypath5/..", False),
("mypath6/../mypath6", True),
("mypath7/../mypath7/..", False),
]
@pytest.mark.parametrize("basename, is_ok", testdata)
def test_mktemp(testdir, basename, is_ok):
mytemp = testdir.tmpdir.mkdir("mytemp")
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_abs_path(tmpdir_factory):
tmpdir_factory.mktemp('{}', numbered=False)
""".format(
basename
)
2018-05-23 22:48:46 +08:00
)
2019-06-30 23:56:27 +08:00
result = testdir.runpytest(p, "--basetemp=%s" % mytemp)
if is_ok:
assert result.ret == 0
assert mytemp.join(basename).check()
else:
assert result.ret == 1
result.stdout.fnmatch_lines("*ValueError*")
def test_tmpdir_always_is_realpath(testdir):
# the reason why tmpdir should be a realpath is that
# when you cd to it and do "os.getcwd()" you will anyway
# get the realpath. Using the symlinked path can thus
# easily result in path-inequality
# XXX if that proves to be a problem, consider using
# os.environ["PWD"]
realtemp = testdir.tmpdir.mkdir("myrealtemp")
linktemp = testdir.tmpdir.join("symlinktemp")
attempt_symlink_to(linktemp, str(realtemp))
2018-05-23 22:48:46 +08:00
p = testdir.makepyfile(
"""
def test_1(tmpdir):
import os
assert os.path.realpath(str(tmpdir)) == str(tmpdir)
2018-05-23 22:48:46 +08:00
"""
)
result = testdir.runpytest("-s", p, "--basetemp=%s/bt" % linktemp)
assert not result.ret
def test_tmp_path_always_is_realpath(testdir, monkeypatch):
# for reasoning see: test_tmpdir_always_is_realpath test-case
realtemp = testdir.tmpdir.mkdir("myrealtemp")
linktemp = testdir.tmpdir.join("symlinktemp")
attempt_symlink_to(linktemp, str(realtemp))
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(linktemp))
testdir.makepyfile(
"""
def test_1(tmp_path):
assert tmp_path.resolve() == tmp_path
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
def test_tmpdir_too_long_on_parametrization(testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.mark.parametrize("arg", ["1"*1000])
def test_some(arg, tmpdir):
tmpdir.ensure("hello")
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
def test_tmpdir_factory(testdir):
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
import pytest
@pytest.fixture(scope='session')
def session_dir(tmpdir_factory):
return tmpdir_factory.mktemp('data', numbered=False)
def test_some(session_dir):
2018-09-20 00:17:10 +08:00
assert session_dir.isdir()
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
def test_tmpdir_fallback_tox_env(testdir, monkeypatch):
"""Test that tmpdir works even if environment variables required by getpass
module are missing (#1010).
"""
2018-05-23 22:48:46 +08:00
monkeypatch.delenv("USER", raising=False)
monkeypatch.delenv("USERNAME", raising=False)
testdir.makepyfile(
"""
def test_some(tmpdir):
assert tmpdir.isdir()
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
2015-09-30 03:00:12 +08:00
@pytest.fixture
def break_getuser(monkeypatch):
2018-05-23 22:48:46 +08:00
monkeypatch.setattr("os.getuid", lambda: -1)
2015-09-30 03:00:12 +08:00
# taken from python 2.7/3.4
2018-05-23 22:48:46 +08:00
for envvar in ("LOGNAME", "USER", "LNAME", "USERNAME"):
2015-09-30 03:00:12 +08:00
monkeypatch.delenv(envvar, raising=False)
@pytest.mark.usefixtures("break_getuser")
2018-05-23 22:48:46 +08:00
@pytest.mark.skipif(sys.platform.startswith("win"), reason="no os.getuid on windows")
2015-09-30 03:00:12 +08:00
def test_tmpdir_fallback_uid_not_found(testdir):
"""Test that tmpdir works even if the current process's user id does not
correspond to a valid user.
"""
2018-05-23 22:48:46 +08:00
testdir.makepyfile(
"""
def test_some(tmpdir):
assert tmpdir.isdir()
2018-05-23 22:48:46 +08:00
"""
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
2015-09-30 03:00:12 +08:00
@pytest.mark.usefixtures("break_getuser")
2018-05-23 22:48:46 +08:00
@pytest.mark.skipif(sys.platform.startswith("win"), reason="no os.getuid on windows")
2015-09-30 03:00:12 +08:00
def test_get_user_uid_not_found():
"""Test that get_user() function works even if the current process's
user id does not correspond to a valid user (e.g. running pytest in a
Docker container with 'docker run -u'.
"""
assert get_user() is None
2018-05-23 22:48:46 +08:00
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="win only")
def test_get_user(monkeypatch):
"""Test that get_user() function works even if environment variables
required by getpass module are missing from the environment on Windows
(#1010).
"""
2018-05-23 22:48:46 +08:00
monkeypatch.delenv("USER", raising=False)
monkeypatch.delenv("USERNAME", raising=False)
assert get_user() is None
2018-09-20 22:10:33 +08:00
2019-06-03 06:32:00 +08:00
class TestNumberedDir:
2018-09-20 22:10:33 +08:00
PREFIX = "fun-"
def test_make(self, tmp_path):
for i in range(10):
d = make_numbered_dir(root=tmp_path, prefix=self.PREFIX)
assert d.name.startswith(self.PREFIX)
assert d.name.endswith(str(i))
symlink = tmp_path.joinpath(self.PREFIX + "current")
if symlink.exists():
# unix
assert symlink.is_symlink()
assert symlink.resolve() == d.resolve()
2018-09-20 22:10:33 +08:00
def test_cleanup_lock_create(self, tmp_path):
d = tmp_path.joinpath("test")
d.mkdir()
lockfile = create_cleanup_lock(d)
with pytest.raises(OSError, match="cannot create lockfile in .*"):
2018-09-20 22:10:33 +08:00
create_cleanup_lock(d)
lockfile.unlink()
def test_lock_register_cleanup_removal(self, tmp_path):
lock = create_cleanup_lock(tmp_path)
registry = []
register_cleanup_lock_removal(lock, register=registry.append)
2019-11-17 01:53:29 +08:00
(cleanup_func,) = registry
2018-09-20 22:10:33 +08:00
assert lock.is_file()
cleanup_func(original_pid="intentionally_different")
assert lock.is_file()
cleanup_func()
assert not lock.exists()
cleanup_func()
assert not lock.exists()
def _do_cleanup(self, tmp_path):
2018-09-20 22:10:33 +08:00
self.test_make(tmp_path)
cleanup_numbered_dir(
2018-09-29 04:06:43 +08:00
root=tmp_path,
prefix=self.PREFIX,
keep=2,
consider_lock_dead_if_created_before=0,
2018-09-20 22:10:33 +08:00
)
def test_cleanup_keep(self, tmp_path):
self._do_cleanup(tmp_path)
a, b = (x for x in tmp_path.iterdir() if not x.is_symlink())
2018-09-20 22:10:33 +08:00
print(a, b)
2018-09-27 02:41:33 +08:00
def test_cleanup_locked(self, tmp_path):
p = make_numbered_dir(root=tmp_path, prefix=self.PREFIX)
2018-09-27 02:41:33 +08:00
create_cleanup_lock(p)
2018-09-29 04:06:43 +08:00
assert not pathlib.ensure_deletable(
2018-09-29 04:06:43 +08:00
p, consider_lock_dead_if_created_before=p.stat().st_mtime - 1
)
assert pathlib.ensure_deletable(
2018-09-29 04:06:43 +08:00
p, consider_lock_dead_if_created_before=p.stat().st_mtime + 1
)
2018-10-01 22:39:24 +08:00
def test_cleanup_ignores_symlink(self, tmp_path):
the_symlink = tmp_path / (self.PREFIX + "current")
attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5"))
self._do_cleanup(tmp_path)
def test_removal_accepts_lock(self, tmp_path):
folder = make_numbered_dir(root=tmp_path, prefix=self.PREFIX)
create_cleanup_lock(folder)
maybe_delete_a_numbered_dir(folder)
assert folder.is_dir()
class TestRmRf:
def test_rm_rf(self, tmp_path):
2018-10-01 22:39:24 +08:00
adir = tmp_path / "adir"
adir.mkdir()
rm_rf(adir)
2018-10-01 22:39:24 +08:00
assert not adir.exists()
adir.mkdir()
afile = adir / "afile"
afile.write_bytes(b"aa")
rm_rf(adir)
2018-10-01 22:39:24 +08:00
assert not adir.exists()
def test_rm_rf_with_read_only_file(self, tmp_path):
"""Ensure rm_rf can remove directories with read-only files in them (#5524)"""
fn = tmp_path / "dir/foo.txt"
fn.parent.mkdir()
fn.touch()
self.chmod_r(fn)
rm_rf(fn.parent)
assert not fn.parent.is_dir()
def chmod_r(self, path):
mode = os.stat(str(path)).st_mode
os.chmod(str(path), mode & ~stat.S_IWRITE)
def test_rm_rf_with_read_only_directory(self, tmp_path):
"""Ensure rm_rf can remove read-only directories (#5524)"""
adir = tmp_path / "dir"
adir.mkdir()
(adir / "foo.txt").touch()
self.chmod_r(adir)
rm_rf(adir)
assert not adir.is_dir()
def test_on_rm_rf_error(self, tmp_path):
adir = tmp_path / "dir"
adir.mkdir()
fn = adir / "foo.txt"
fn.touch()
self.chmod_r(fn)
# unknown exception
with pytest.warns(pytest.PytestWarning):
exc_info = (None, RuntimeError(), None)
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file()
# we ignore FileNotFoundError
exc_info = (None, FileNotFoundError(), None)
assert not on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
# unknown function
2019-10-28 19:34:40 +08:00
with pytest.warns(
pytest.PytestWarning,
match=r"^\(rm_rf\) unknown function None when removing .*foo.txt:\nNone: ",
):
exc_info = (None, PermissionError(), None)
on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file()
# ignored function
with pytest.warns(None) as warninfo:
exc_info = (None, PermissionError(), None)
on_rm_rf_error(os.open, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file()
assert not [x.message for x in warninfo]
exc_info = (None, PermissionError(), None)
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
assert not fn.is_file()
def attempt_symlink_to(path, to_path):
"""Try to make a symlink from "path" to "to_path", skipping in case this platform
does not support it or we don't have sufficient privileges (common on Windows)."""
try:
Path(path).symlink_to(Path(to_path))
except OSError:
pytest.skip("could not create symbolic link")
def test_tmpdir_equals_tmp_path(tmpdir, tmp_path):
assert Path(tmpdir) == tmp_path
def test_basetemp_with_read_only_files(testdir):
"""Integration test for #5524"""
testdir.makepyfile(
"""
import os
import stat
def test(tmp_path):
fn = tmp_path / 'foo.txt'
fn.write_text('hello')
mode = os.stat(str(fn)).st_mode
os.chmod(str(fn), mode & ~stat.S_IREAD)
"""
)
result = testdir.runpytest("--basetemp=tmp")
assert result.ret == 0
# running a second time and ensure we don't crash
result = testdir.runpytest("--basetemp=tmp")
assert result.ret == 0