test_ok2/testing/test_pytester.py

850 lines
27 KiB
Python

import os
import subprocess
import sys
import time
from pathlib import Path
from types import ModuleType
from typing import List
import _pytest.pytester as pytester_mod
import pytest
from _pytest.config import ExitCode
from _pytest.config import PytestPluginManager
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import CwdSnapshot
from _pytest.pytester import HookRecorder
from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester
from _pytest.pytester import SysModulesSnapshot
from _pytest.pytester import SysPathsSnapshot
from _pytest.pytester import Testdir
def test_make_hook_recorder(pytester: Pytester) -> None:
item = pytester.getitem("def test_func(): pass")
recorder = pytester.make_hook_recorder(item.config.pluginmanager)
assert not recorder.getfailures()
# (The silly condition is to fool mypy that the code below this is reachable)
if 1 + 1 == 2:
pytest.xfail("internal reportrecorder tests need refactoring")
class rep:
excinfo = None
passed = False
failed = True
skipped = False
when = "call"
recorder.hook.pytest_runtest_logreport(report=rep) # type: ignore[attr-defined]
failures = recorder.getfailures()
assert failures == [rep] # type: ignore[comparison-overlap]
failures = recorder.getfailures()
assert failures == [rep] # type: ignore[comparison-overlap]
class rep2:
excinfo = None
passed = False
failed = False
skipped = True
when = "call"
rep2.passed = False
rep2.skipped = True
recorder.hook.pytest_runtest_logreport(report=rep2) # type: ignore[attr-defined]
modcol = pytester.getmodulecol("")
rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol)
rep3.passed = False
rep3.failed = True
rep3.skipped = False
recorder.hook.pytest_collectreport(report=rep3) # type: ignore[attr-defined]
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() # type: ignore[attr-defined]
recorder.clear()
recorder.hook.pytest_runtest_logreport(report=rep3) # type: ignore[attr-defined]
pytest.raises(ValueError, recorder.getfailures)
def test_parseconfig(pytester: Pytester) -> None:
config1 = pytester.parseconfig()
config2 = pytester.parseconfig()
assert config2 is not config1
def test_pytester_runs_with_plugin(pytester: Pytester) -> None:
pytester.makepyfile(
"""
pytest_plugins = "pytester"
def test_hello(pytester):
assert 1
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_pytester_with_doctest(pytester: Pytester) -> None:
"""Check that pytester can be used within doctests.
It used to use `request.function`, which is `None` with doctests."""
pytester.makepyfile(
**{
"sub/t-doctest.py": """
'''
>>> import os
>>> pytester = getfixture("pytester")
>>> str(pytester.makepyfile("content")).replace(os.sep, '/')
'.../basetemp/sub.t-doctest0/sub.py'
'''
""",
"sub/__init__.py": "",
}
)
result = pytester.runpytest(
"-p", "pytester", "--doctest-modules", "sub/t-doctest.py"
)
assert result.ret == 0
def test_runresult_assertion_on_xfail(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest
pytest_plugins = "pytester"
@pytest.mark.xfail
def test_potato():
assert False
"""
)
result = pytester.runpytest()
result.assert_outcomes(xfailed=1)
assert result.ret == 0
def test_runresult_assertion_on_xpassed(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest
pytest_plugins = "pytester"
@pytest.mark.xfail
def test_potato():
assert True
"""
)
result = pytester.runpytest()
result.assert_outcomes(xpassed=1)
assert result.ret == 0
def test_xpassed_with_strict_is_considered_a_failure(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest
pytest_plugins = "pytester"
@pytest.mark.xfail(strict=True)
def test_potato():
assert True
"""
)
result = pytester.runpytest()
result.assert_outcomes(failed=1)
assert result.ret != 0
def make_holder():
class apiclass:
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 # type: ignore
apimod.pytest_xyz_noarg = pytest_xyz_noarg # type: ignore
return apiclass, apimod
@pytest.mark.parametrize("holder", make_holder())
def test_hookrecorder_basic(holder) -> None:
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(pytester: Pytester) -> None:
pytester.makepyfile(chr(0xFFFD))
def test_makepyfile_utf8(pytester: Pytester) -> None:
"""Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
utf8_contents = """
def setup_function(function):
mixed_encoding = 'São Paulo'
""".encode()
p = pytester.makepyfile(utf8_contents)
assert "mixed_encoding = 'São Paulo'".encode() in p.read_bytes()
class TestInlineRunModulesCleanup:
def test_inline_run_test_module_not_cleaned_up(self, pytester: Pytester) -> None:
test_mod = pytester.makepyfile("def test_foo(): assert True")
result = pytester.inline_run(str(test_mod))
assert result.ret == ExitCode.OK
# rewrite module, now test should fail if module was re-imported
test_mod.write_text("def test_foo(): assert False")
result2 = pytester.inline_run(str(test_mod))
assert result2.ret == ExitCode.TESTS_FAILED
def spy_factory(self):
class SysModulesSnapshotSpy:
instances: List["SysModulesSnapshotSpy"] = [] # noqa: F821
def __init__(self, preserve=None) -> 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, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None:
spy_factory = self.spy_factory()
monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory)
pytester.syspathinsert()
original = dict(sys.modules)
pytester.makepyfile(import1="# you son of a silly person")
pytester.makepyfile(import2="# my hovercraft is full of eels")
test_mod = pytester.makepyfile(
"""
import import1
def test_foo(): import import2"""
)
pytester.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, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None:
spy_factory = self.spy_factory()
monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory)
test_mod = pytester.makepyfile("def test_foo(): pass")
pytester.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, pytester: Pytester
) -> None:
pytester.syspathinsert()
pytester.makepyfile(imported="data = 'you son of a silly person'")
import imported
test_mod = pytester.makepyfile(
"""
def test_foo():
import imported
imported.data = 42"""
)
pytester.inline_run(str(test_mod))
assert imported.data == 42
def test_assert_outcomes_after_pytest_error(pytester: Pytester) -> None:
pytester.makepyfile("def test_foo(): assert True")
result = pytester.runpytest("--unexpected-argument")
with pytest.raises(ValueError, match="Pytest terminal summary report not found"):
result.assert_outcomes(passed=0)
def test_cwd_snapshot(pytester: Pytester) -> None:
foo = pytester.mkdir("foo")
bar = pytester.mkdir("bar")
os.chdir(foo)
snapshot = CwdSnapshot()
os.chdir(bar)
assert Path().absolute() == bar
snapshot.restore()
assert Path().absolute() == foo
class TestSysModulesSnapshot:
key = "my-test-module"
def test_remove_added(self) -> None:
original = dict(sys.modules)
assert self.key not in sys.modules
snapshot = SysModulesSnapshot()
sys.modules[self.key] = ModuleType("something")
assert self.key in sys.modules
snapshot.restore()
assert sys.modules == original
def test_add_removed(self, monkeypatch: MonkeyPatch) -> None:
assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, ModuleType("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: MonkeyPatch) -> None:
assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, ModuleType("something"))
assert self.key in sys.modules
original = dict(sys.modules)
snapshot = SysModulesSnapshot()
sys.modules[self.key] = ModuleType("something else")
snapshot.restore()
assert sys.modules == original
def test_preserve_modules(self, monkeypatch: MonkeyPatch) -> None:
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):
mod = ModuleType("something" + str(i))
monkeypatch.setitem(sys.modules, k, mod)
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]] = ModuleType("something else0")
sys.modules[key[1]] = original[key[1]] = ModuleType("something else1")
sys.modules[key[2]] = ModuleType("something else2")
snapshot.restore()
assert sys.modules == original
def test_preserve_container(self, monkeypatch: MonkeyPatch) -> None:
original = dict(sys.modules)
assert self.key not in original
replacement = dict(sys.modules)
replacement[self.key] = ModuleType("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:
other_path = {"path": "meta_path", "meta_path": "path"}
@staticmethod
def path(n: int) -> str:
return "my-dirty-little-secret-" + str(n)
def test_restore(self, monkeypatch: MonkeyPatch, path_type) -> None:
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: MonkeyPatch, path_type) -> None:
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: List[object] = []
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_pytester_subprocess(pytester: Pytester) -> None:
testfile = pytester.makepyfile("def test_one(): pass")
assert pytester.runpytest_subprocess(testfile).ret == 0
def test_pytester_subprocess_via_runpytest_arg(pytester: Pytester) -> None:
testfile = pytester.makepyfile(
"""
def test_pytester_subprocess(pytester):
import os
testfile = pytester.makepyfile(
\"""
import os
def test_one():
assert {} != os.getpid()
\""".format(os.getpid())
)
assert pytester.runpytest(testfile).ret == 0
"""
)
result = pytester.runpytest_inprocess(
"-p", "pytester", "--runpytest", "subprocess", testfile
)
assert result.ret == 0
def test_unicode_args(pytester: Pytester) -> None:
result = pytester.runpytest("-k", "אבג")
assert result.ret == ExitCode.NO_TESTS_COLLECTED
def test_pytester_run_no_timeout(pytester: Pytester) -> None:
testfile = pytester.makepyfile("def test_no_timeout(): pass")
assert pytester.runpytest_subprocess(testfile).ret == ExitCode.OK
def test_pytester_run_with_timeout(pytester: Pytester) -> None:
testfile = pytester.makepyfile("def test_no_timeout(): pass")
timeout = 120
start = time.time()
result = pytester.runpytest_subprocess(testfile, timeout=timeout)
end = time.time()
duration = end - start
assert result.ret == ExitCode.OK
assert duration < timeout
def test_pytester_run_timeout_expires(pytester: Pytester) -> None:
testfile = pytester.makepyfile(
"""
import time
def test_timeout():
time.sleep(10)"""
)
with pytest.raises(pytester.TimeoutExpired):
pytester.runpytest_subprocess(testfile, timeout=1)
def test_linematcher_with_nonlist() -> None:
"""Test LineMatcher with regard to passing in a set (accidentally)."""
from _pytest._code.source import Source
lm = LineMatcher([])
with pytest.raises(TypeError, match="invalid type for lines2: set"):
lm.fnmatch_lines(set()) # type: ignore[arg-type]
with pytest.raises(TypeError, match="invalid type for lines2: dict"):
lm.fnmatch_lines({}) # type: ignore[arg-type]
with pytest.raises(TypeError, match="invalid type for lines2: set"):
lm.re_match_lines(set()) # type: ignore[arg-type]
with pytest.raises(TypeError, match="invalid type for lines2: dict"):
lm.re_match_lines({}) # type: ignore[arg-type]
with pytest.raises(TypeError, match="invalid type for lines2: Source"):
lm.fnmatch_lines(Source()) # type: ignore[arg-type]
lm.fnmatch_lines([])
lm.fnmatch_lines(())
lm.fnmatch_lines("")
assert lm._getlines({}) == {} # type: ignore[arg-type,comparison-overlap]
assert lm._getlines(set()) == set() # type: ignore[arg-type,comparison-overlap]
assert lm._getlines(Source()) == []
assert lm._getlines(Source("pass\npass")) == ["pass", "pass"]
def test_linematcher_match_failure() -> None:
lm = LineMatcher(["foo", "foo", "bar"])
with pytest.raises(pytest.fail.Exception) as e:
lm.fnmatch_lines(["foo", "f*", "baz"])
assert e.value.msg is not None
assert e.value.msg.splitlines() == [
"exact match: 'foo'",
"fnmatch: 'f*'",
" with: 'foo'",
"nomatch: 'baz'",
" and: 'bar'",
"remains unmatched: 'baz'",
]
lm = LineMatcher(["foo", "foo", "bar"])
with pytest.raises(pytest.fail.Exception) as e:
lm.re_match_lines(["foo", "^f.*", "baz"])
assert e.value.msg is not None
assert e.value.msg.splitlines() == [
"exact match: 'foo'",
"re.match: '^f.*'",
" with: 'foo'",
" nomatch: 'baz'",
" and: 'bar'",
"remains unmatched: 'baz'",
]
def test_linematcher_consecutive() -> None:
lm = LineMatcher(["1", "", "2"])
with pytest.raises(pytest.fail.Exception) as excinfo:
lm.fnmatch_lines(["1", "2"], consecutive=True)
assert str(excinfo.value).splitlines() == [
"exact match: '1'",
"no consecutive match: '2'",
" with: ''",
]
lm.re_match_lines(["1", r"\d?", "2"], consecutive=True)
with pytest.raises(pytest.fail.Exception) as excinfo:
lm.re_match_lines(["1", r"\d", "2"], consecutive=True)
assert str(excinfo.value).splitlines() == [
"exact match: '1'",
r"no consecutive match: '\\d'",
" with: ''",
]
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
def test_linematcher_no_matching(function: str) -> None:
if function == "no_fnmatch_line":
good_pattern = "*.py OK*"
bad_pattern = "*X.py OK*"
else:
assert function == "no_re_match_line"
good_pattern = r".*py OK"
bad_pattern = r".*Xpy OK"
lm = LineMatcher(
[
"cachedir: .pytest_cache",
"collecting ... collected 1 item",
"",
"show_fixtures_per_test.py OK",
"=== elapsed 1s ===",
]
)
# check the function twice to ensure we don't accumulate the internal buffer
for i in range(2):
with pytest.raises(pytest.fail.Exception) as e:
func = getattr(lm, function)
func(good_pattern)
obtained = str(e.value).splitlines()
if function == "no_fnmatch_line":
assert obtained == [
f"nomatch: '{good_pattern}'",
" and: 'cachedir: .pytest_cache'",
" and: 'collecting ... collected 1 item'",
" and: ''",
f"fnmatch: '{good_pattern}'",
" with: 'show_fixtures_per_test.py OK'",
]
else:
assert obtained == [
f" nomatch: '{good_pattern}'",
" and: 'cachedir: .pytest_cache'",
" and: 'collecting ... collected 1 item'",
" and: ''",
f"re.match: '{good_pattern}'",
" with: 'show_fixtures_per_test.py OK'",
]
func = getattr(lm, function)
func(bad_pattern) # bad pattern does not match any line: passes
def test_linematcher_no_matching_after_match() -> None:
lm = LineMatcher(["1", "2", "3"])
lm.fnmatch_lines(["1", "3"])
with pytest.raises(pytest.fail.Exception) as e:
lm.no_fnmatch_line("*")
assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"]
def test_linematcher_string_api() -> None:
lm = LineMatcher(["foo", "bar"])
assert str(lm) == "foo\nbar"
def test_pytest_addopts_before_pytester(request, monkeypatch: MonkeyPatch) -> None:
orig = os.environ.get("PYTEST_ADDOPTS", None)
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
pytester: Pytester = request.getfixturevalue("pytester")
assert "PYTEST_ADDOPTS" not in os.environ
pytester._finalize()
assert os.environ.get("PYTEST_ADDOPTS") == "--orig-unused"
monkeypatch.undo()
assert os.environ.get("PYTEST_ADDOPTS") == orig
def test_run_stdin(pytester: Pytester) -> None:
with pytest.raises(pytester.TimeoutExpired):
pytester.run(
sys.executable,
"-c",
"import sys, time; time.sleep(1); print(sys.stdin.read())",
stdin=subprocess.PIPE,
timeout=0.1,
)
with pytest.raises(pytester.TimeoutExpired):
result = pytester.run(
sys.executable,
"-c",
"import sys, time; time.sleep(1); print(sys.stdin.read())",
stdin=b"input\n2ndline",
timeout=0.1,
)
result = pytester.run(
sys.executable,
"-c",
"import sys; print(sys.stdin.read())",
stdin=b"input\n2ndline",
)
assert result.stdout.lines == ["input", "2ndline"]
assert result.stderr.str() == ""
assert result.ret == 0
def test_popen_stdin_pipe(pytester: Pytester) -> None:
proc = pytester.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
)
stdin = b"input\n2ndline"
stdout, stderr = proc.communicate(input=stdin)
assert stdout.decode("utf8").splitlines() == ["input", "2ndline"]
assert stderr == b""
assert proc.returncode == 0
def test_popen_stdin_bytes(pytester: Pytester) -> None:
proc = pytester.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=b"input\n2ndline",
)
stdout, stderr = proc.communicate()
assert stdout.decode("utf8").splitlines() == ["input", "2ndline"]
assert stderr == b""
assert proc.returncode == 0
def test_popen_default_stdin_stderr_and_stdin_None(pytester: Pytester) -> None:
# stdout, stderr default to pipes,
# stdin can be None to not close the pipe, avoiding
# "ValueError: flush of closed file" with `communicate()`.
#
# Wraps the test to make it not hang when run with "-s".
p1 = pytester.makepyfile(
'''
import sys
def test_inner(pytester):
p1 = pytester.makepyfile(
"""
import sys
print(sys.stdin.read()) # empty
print('stdout')
sys.stderr.write('stderr')
"""
)
proc = pytester.popen([sys.executable, str(p1)], stdin=None)
stdout, stderr = proc.communicate(b"ignored")
assert stdout.splitlines() == [b"", b"stdout"]
assert stderr.splitlines() == [b"stderr"]
assert proc.returncode == 0
'''
)
result = pytester.runpytest("-p", "pytester", str(p1))
assert result.ret == 0
def test_spawn_uses_tmphome(pytester: Pytester) -> None:
tmphome = str(pytester.path)
assert os.environ.get("HOME") == tmphome
pytester._monkeypatch.setenv("CUSTOMENV", "42")
p1 = pytester.makepyfile(
"""
import os
def test():
assert os.environ["HOME"] == {tmphome!r}
assert os.environ["CUSTOMENV"] == "42"
""".format(
tmphome=tmphome
)
)
child = pytester.spawn_pytest(str(p1))
out = child.read()
assert child.wait() == 0, out.decode("utf8")
def test_run_result_repr() -> None:
outlines = ["some", "normal", "output"]
errlines = ["some", "nasty", "errors", "happened"]
# known exit code
r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5)
assert (
repr(r) == "<RunResult ret=ExitCode.TESTS_FAILED len(stdout.lines)=3"
" len(stderr.lines)=4 duration=0.50s>"
)
# unknown exit code: just the number
r = pytester_mod.RunResult(99, outlines, errlines, duration=0.5)
assert (
repr(r) == "<RunResult ret=99 len(stdout.lines)=3"
" len(stderr.lines)=4 duration=0.50s>"
)
def test_pytester_outcomes_with_multiple_errors(pytester: Pytester) -> None:
p1 = pytester.makepyfile(
"""
import pytest
@pytest.fixture
def bad_fixture():
raise Exception("bad")
def test_error1(bad_fixture):
pass
def test_error2(bad_fixture):
pass
"""
)
result = pytester.runpytest(str(p1))
result.assert_outcomes(errors=2)
assert result.parseoutcomes() == {"errors": 2}
def test_parse_summary_line_always_plural() -> None:
"""Parsing summaries always returns plural nouns (#6505)"""
lines = [
"some output 1",
"some output 2",
"======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====",
"done.",
]
assert pytester_mod.RunResult.parse_summary_nouns(lines) == {
"errors": 1,
"failed": 1,
"passed": 1,
"warnings": 1,
}
lines = [
"some output 1",
"some output 2",
"======= 1 failed, 1 passed, 2 warnings, 2 errors in 0.13s ====",
"done.",
]
assert pytester_mod.RunResult.parse_summary_nouns(lines) == {
"errors": 2,
"failed": 1,
"passed": 1,
"warnings": 2,
}
def test_makefile_joins_absolute_path(pytester: Pytester) -> None:
absfile = pytester.path / "absfile"
p1 = pytester.makepyfile(**{str(absfile): ""})
assert str(p1) == str(pytester.path / "absfile.py")
def test_testtmproot(testdir) -> None:
"""Check test_tmproot is a py.path attribute for backward compatibility."""
assert testdir.test_tmproot.check(dir=1)
def test_testdir_makefile_dot_prefixes_extension_silently(
testdir: Testdir,
) -> None:
"""For backwards compat #8192"""
p1 = testdir.makefile("foo.bar", "")
assert ".foo.bar" in str(p1)
def test_pytester_makefile_dot_prefixes_extension_with_warning(
pytester: Pytester,
) -> None:
with pytest.raises(
ValueError,
match="pytester.makefile expects a file extension, try .foo.bar instead of foo.bar",
):
pytester.makefile("foo.bar", "")
def test_testdir_makefile_ext_none_raises_type_error(testdir) -> None:
"""For backwards compat #8192"""
with pytest.raises(TypeError):
testdir.makefile(None, "")
def test_testdir_makefile_ext_empty_string_makes_file(testdir) -> None:
"""For backwards compat #8192"""
p1 = testdir.makefile("", "")
assert "test_testdir_makefile" in str(p1)