Merge pull request #8168 from antonblr/testdir-to-pytester-incr2

tests: Migrate to pytester - final update
This commit is contained in:
Ran Benita 2020-12-19 13:32:20 +02:00 committed by GitHub
commit 89dcfbf293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 697 additions and 576 deletions

View File

@ -528,7 +528,7 @@ class FSCollector(Collector):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath) return self.session.gethookproxy(fspath)
def isinitpath(self, path: py.path.local) -> bool: def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.isinitpath(path) return self.session.isinitpath(path)

View File

@ -660,7 +660,7 @@ class Package(Module):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath) return self.session.gethookproxy(fspath)
def isinitpath(self, path: py.path.local) -> bool: def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.isinitpath(path) return self.session.isinitpath(path)

View File

@ -5,24 +5,23 @@ from unittest import mock
import pytest import pytest
from _pytest import deprecated from _pytest import deprecated
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.pytester import Testdir
@pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore @pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore
# false positive due to dynamic attribute # false positive due to dynamic attribute
def test_pytest_collect_module_deprecated(attribute): def test_pytest_collect_module_deprecated(attribute) -> None:
with pytest.warns(DeprecationWarning, match=attribute): with pytest.warns(DeprecationWarning, match=attribute):
getattr(pytest.collect, attribute) getattr(pytest.collect, attribute)
@pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS)) @pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS))
@pytest.mark.filterwarnings("default") @pytest.mark.filterwarnings("default")
def test_external_plugins_integrated(testdir, plugin): def test_external_plugins_integrated(pytester: Pytester, plugin) -> None:
testdir.syspathinsert() pytester.syspathinsert()
testdir.makepyfile(**{plugin: ""}) pytester.makepyfile(**{plugin: ""})
with pytest.warns(pytest.PytestConfigWarning): with pytest.warns(pytest.PytestConfigWarning):
testdir.parseconfig("-p", plugin) pytester.parseconfig("-p", plugin)
def test_fillfuncargs_is_deprecated() -> None: def test_fillfuncargs_is_deprecated() -> None:
@ -49,32 +48,32 @@ def test_fillfixtures_is_deprecated() -> None:
_pytest.fixtures.fillfixtures(mock.Mock()) _pytest.fixtures.fillfixtures(mock.Mock())
def test_minus_k_dash_is_deprecated(testdir) -> None: def test_minus_k_dash_is_deprecated(pytester: Pytester) -> None:
threepass = testdir.makepyfile( threepass = pytester.makepyfile(
test_threepass=""" test_threepass="""
def test_one(): assert 1 def test_one(): assert 1
def test_two(): assert 1 def test_two(): assert 1
def test_three(): assert 1 def test_three(): assert 1
""" """
) )
result = testdir.runpytest("-k=-test_two", threepass) result = pytester.runpytest("-k=-test_two", threepass)
result.stdout.fnmatch_lines(["*The `-k '-expr'` syntax*deprecated*"]) result.stdout.fnmatch_lines(["*The `-k '-expr'` syntax*deprecated*"])
def test_minus_k_colon_is_deprecated(testdir) -> None: def test_minus_k_colon_is_deprecated(pytester: Pytester) -> None:
threepass = testdir.makepyfile( threepass = pytester.makepyfile(
test_threepass=""" test_threepass="""
def test_one(): assert 1 def test_one(): assert 1
def test_two(): assert 1 def test_two(): assert 1
def test_three(): assert 1 def test_three(): assert 1
""" """
) )
result = testdir.runpytest("-k", "test_two:", threepass) result = pytester.runpytest("-k", "test_two:", threepass)
result.stdout.fnmatch_lines(["*The `-k 'expr:'` syntax*deprecated*"]) result.stdout.fnmatch_lines(["*The `-k 'expr:'` syntax*deprecated*"])
def test_fscollector_gethookproxy_isinitpath(testdir: Testdir) -> None: def test_fscollector_gethookproxy_isinitpath(pytester: Pytester) -> None:
module = testdir.getmodulecol( module = pytester.getmodulecol(
""" """
def test_foo(): pass def test_foo(): pass
""", """,
@ -85,16 +84,16 @@ def test_fscollector_gethookproxy_isinitpath(testdir: Testdir) -> None:
assert isinstance(package, pytest.Package) assert isinstance(package, pytest.Package)
with pytest.warns(pytest.PytestDeprecationWarning, match="gethookproxy"): with pytest.warns(pytest.PytestDeprecationWarning, match="gethookproxy"):
package.gethookproxy(testdir.tmpdir) package.gethookproxy(pytester.path)
with pytest.warns(pytest.PytestDeprecationWarning, match="isinitpath"): with pytest.warns(pytest.PytestDeprecationWarning, match="isinitpath"):
package.isinitpath(testdir.tmpdir) package.isinitpath(pytester.path)
# The methods on Session are *not* deprecated. # The methods on Session are *not* deprecated.
session = module.session session = module.session
with warnings.catch_warnings(record=True) as rec: with warnings.catch_warnings(record=True) as rec:
session.gethookproxy(testdir.tmpdir) session.gethookproxy(pytester.path)
session.isinitpath(testdir.tmpdir) session.isinitpath(pytester.path)
assert len(rec) == 0 assert len(rec) == 0

View File

@ -1050,7 +1050,7 @@ class TestLastFailed:
class TestNewFirst: class TestNewFirst:
def test_newfirst_usecase(self, pytester: Pytester, testdir) -> None: def test_newfirst_usecase(self, pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(
**{ **{
"test_1/test_1.py": """ "test_1/test_1.py": """

View File

@ -6,6 +6,8 @@ import textwrap
from pathlib import Path from pathlib import Path
from typing import List from typing import List
import py.path
import pytest import pytest
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
@ -16,7 +18,6 @@ from _pytest.nodes import Item
from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import symlink_or_skip
from _pytest.pytester import HookRecorder from _pytest.pytester import HookRecorder
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.pytester import Testdir
def ensure_file(file_path: Path) -> Path: def ensure_file(file_path: Path) -> Path:
@ -206,15 +207,17 @@ class TestCollectFS:
"Activate.ps1", "Activate.ps1",
), ),
) )
def test__in_venv(self, testdir: Testdir, fname: str) -> None: def test__in_venv(self, pytester: Pytester, fname: str) -> None:
"""Directly test the virtual env detection function""" """Directly test the virtual env detection function"""
bindir = "Scripts" if sys.platform.startswith("win") else "bin" bindir = "Scripts" if sys.platform.startswith("win") else "bin"
# no bin/activate, not a virtualenv # no bin/activate, not a virtualenv
base_path = testdir.tmpdir.mkdir("venv") base_path = pytester.mkdir("venv")
assert _in_venv(base_path) is False assert _in_venv(py.path.local(base_path)) is False
# with bin/activate, totally a virtualenv # with bin/activate, totally a virtualenv
base_path.ensure(bindir, fname) bin_path = base_path.joinpath(bindir)
assert _in_venv(base_path) is True bin_path.mkdir()
bin_path.joinpath(fname).touch()
assert _in_venv(py.path.local(base_path)) is True
def test_custom_norecursedirs(self, pytester: Pytester) -> None: def test_custom_norecursedirs(self, pytester: Pytester) -> None:
pytester.makeini( pytester.makeini(
@ -264,7 +267,7 @@ class TestCollectFS:
class TestCollectPluginHookRelay: class TestCollectPluginHookRelay:
def test_pytest_collect_file(self, testdir: Testdir) -> None: def test_pytest_collect_file(self, pytester: Pytester) -> None:
wascalled = [] wascalled = []
class Plugin: class Plugin:
@ -273,8 +276,8 @@ class TestCollectPluginHookRelay:
# Ignore hidden files, e.g. .testmondata. # Ignore hidden files, e.g. .testmondata.
wascalled.append(path) wascalled.append(path)
testdir.makefile(".abc", "xyz") pytester.makefile(".abc", "xyz")
pytest.main(testdir.tmpdir, plugins=[Plugin()]) pytest.main(py.path.local(pytester.path), plugins=[Plugin()])
assert len(wascalled) == 1 assert len(wascalled) == 1
assert wascalled[0].ext == ".abc" assert wascalled[0].ext == ".abc"
@ -1336,7 +1339,7 @@ def test_does_not_put_src_on_path(pytester: Pytester) -> None:
assert result.ret == ExitCode.OK assert result.ret == ExitCode.OK
def test_fscollector_from_parent(testdir: Testdir, request: FixtureRequest) -> None: def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) -> None:
"""Ensure File.from_parent can forward custom arguments to the constructor. """Ensure File.from_parent can forward custom arguments to the constructor.
Context: https://github.com/pytest-dev/pytest-cpp/pull/47 Context: https://github.com/pytest-dev/pytest-cpp/pull/47
@ -1352,7 +1355,7 @@ def test_fscollector_from_parent(testdir: Testdir, request: FixtureRequest) -> N
return super().from_parent(parent=parent, fspath=fspath, x=x) return super().from_parent(parent=parent, fspath=fspath, x=x)
collector = MyCollector.from_parent( collector = MyCollector.from_parent(
parent=request.session, fspath=testdir.tmpdir / "foo", x=10 parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10
) )
assert collector.x == 10 assert collector.x == 10

View File

@ -24,8 +24,8 @@ _ENVIRON_PYTHONBREAKPOINT = os.environ.get("PYTHONBREAKPOINT", "")
def pdb_env(request): def pdb_env(request):
if "pytester" in request.fixturenames: if "pytester" in request.fixturenames:
# Disable pdb++ with inner tests. # Disable pdb++ with inner tests.
pytester = request.getfixturevalue("testdir") pytester = request.getfixturevalue("pytester")
pytester.monkeypatch.setenv("PDBPP_HIJACK_PDB", "0") pytester._monkeypatch.setenv("PDBPP_HIJACK_PDB", "0")
def runpdb_and_get_report(pytester: Pytester, source: str): def runpdb_and_get_report(pytester: Pytester, source: str):

View File

@ -4,51 +4,62 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import cast from typing import cast
from typing import List from typing import List
from typing import Optional
from typing import Tuple from typing import Tuple
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Union
from xml.dom import minidom from xml.dom import minidom
import py
import xmlschema import xmlschema
import pytest import pytest
from _pytest.config import Config from _pytest.config import Config
from _pytest.junitxml import bin_xml_escape from _pytest.junitxml import bin_xml_escape
from _pytest.junitxml import LogXML from _pytest.junitxml import LogXML
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import Pytester
from _pytest.pytester import RunResult
from _pytest.reports import BaseReport from _pytest.reports import BaseReport
from _pytest.reports import TestReport from _pytest.reports import TestReport
from _pytest.store import Store from _pytest.store import Store
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def schema(): def schema() -> xmlschema.XMLSchema:
"""Return an xmlschema.XMLSchema object for the junit-10.xsd file.""" """Return an xmlschema.XMLSchema object for the junit-10.xsd file."""
fn = Path(__file__).parent / "example_scripts/junit-10.xsd" fn = Path(__file__).parent / "example_scripts/junit-10.xsd"
with fn.open() as f: with fn.open() as f:
return xmlschema.XMLSchema(f) return xmlschema.XMLSchema(f)
class RunAndParse:
def __init__(self, pytester: Pytester, schema: xmlschema.XMLSchema) -> None:
self.pytester = pytester
self.schema = schema
def __call__(
self, *args: Union[str, "os.PathLike[str]"], family: Optional[str] = "xunit1"
) -> Tuple[RunResult, "DomNode"]:
if family:
args = ("-o", "junit_family=" + family) + args
xml_path = self.pytester.path.joinpath("junit.xml")
result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args)
if family == "xunit2":
with xml_path.open() as f:
self.schema.validate(f)
xmldoc = minidom.parse(str(xml_path))
return result, DomNode(xmldoc)
@pytest.fixture @pytest.fixture
def run_and_parse(testdir, schema): def run_and_parse(pytester: Pytester, schema: xmlschema.XMLSchema) -> RunAndParse:
"""Fixture that returns a function that can be used to execute pytest and """Fixture that returns a function that can be used to execute pytest and
return the parsed ``DomNode`` of the root xml node. return the parsed ``DomNode`` of the root xml node.
The ``family`` parameter is used to configure the ``junit_family`` of the written report. The ``family`` parameter is used to configure the ``junit_family`` of the written report.
"xunit2" is also automatically validated against the schema. "xunit2" is also automatically validated against the schema.
""" """
return RunAndParse(pytester, schema)
def run(*args, family="xunit1"):
if family:
args = ("-o", "junit_family=" + family) + args
xml_path = testdir.tmpdir.join("junit.xml")
result = testdir.runpytest("--junitxml=%s" % xml_path, *args)
if family == "xunit2":
with xml_path.open() as f:
schema.validate(f)
xmldoc = minidom.parse(str(xml_path))
return result, DomNode(xmldoc)
return run
def assert_attr(node, **kwargs): def assert_attr(node, **kwargs):
@ -130,8 +141,10 @@ parametrize_families = pytest.mark.parametrize("xunit_family", ["xunit1", "xunit
class TestPython: class TestPython:
@parametrize_families @parametrize_families
def test_summing_simple(self, testdir, run_and_parse, xunit_family): def test_summing_simple(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
def test_pass(): def test_pass():
@ -154,8 +167,10 @@ class TestPython:
node.assert_attr(name="pytest", errors=0, failures=1, skipped=2, tests=5) node.assert_attr(name="pytest", errors=0, failures=1, skipped=2, tests=5)
@parametrize_families @parametrize_families
def test_summing_simple_with_errors(self, testdir, run_and_parse, xunit_family): def test_summing_simple_with_errors(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.fixture @pytest.fixture
@ -181,8 +196,10 @@ class TestPython:
node.assert_attr(name="pytest", errors=1, failures=2, skipped=1, tests=5) node.assert_attr(name="pytest", errors=1, failures=2, skipped=1, tests=5)
@parametrize_families @parametrize_families
def test_hostname_in_xml(self, testdir, run_and_parse, xunit_family): def test_hostname_in_xml(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
@ -193,8 +210,10 @@ class TestPython:
node.assert_attr(hostname=platform.node()) node.assert_attr(hostname=platform.node())
@parametrize_families @parametrize_families
def test_timestamp_in_xml(self, testdir, run_and_parse, xunit_family): def test_timestamp_in_xml(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
@ -206,8 +225,10 @@ class TestPython:
timestamp = datetime.strptime(node["timestamp"], "%Y-%m-%dT%H:%M:%S.%f") timestamp = datetime.strptime(node["timestamp"], "%Y-%m-%dT%H:%M:%S.%f")
assert start_time <= timestamp < datetime.now() assert start_time <= timestamp < datetime.now()
def test_timing_function(self, testdir, run_and_parse, mock_timing): def test_timing_function(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, mock_timing
) -> None:
pytester.makepyfile(
""" """
from _pytest import timing from _pytest import timing
def setup_module(): def setup_module():
@ -226,8 +247,12 @@ class TestPython:
@pytest.mark.parametrize("duration_report", ["call", "total"]) @pytest.mark.parametrize("duration_report", ["call", "total"])
def test_junit_duration_report( def test_junit_duration_report(
self, testdir, monkeypatch, duration_report, run_and_parse self,
): pytester: Pytester,
monkeypatch: MonkeyPatch,
duration_report: str,
run_and_parse: RunAndParse,
) -> None:
# mock LogXML.node_reporter so it always sets a known duration to each test report object # mock LogXML.node_reporter so it always sets a known duration to each test report object
original_node_reporter = LogXML.node_reporter original_node_reporter = LogXML.node_reporter
@ -239,7 +264,7 @@ class TestPython:
monkeypatch.setattr(LogXML, "node_reporter", node_reporter_wrapper) monkeypatch.setattr(LogXML, "node_reporter", node_reporter_wrapper)
testdir.makepyfile( pytester.makepyfile(
""" """
def test_foo(): def test_foo():
pass pass
@ -256,8 +281,10 @@ class TestPython:
assert val == 1.0 assert val == 1.0
@parametrize_families @parametrize_families
def test_setup_error(self, testdir, run_and_parse, xunit_family): def test_setup_error(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@ -279,8 +306,10 @@ class TestPython:
assert "ValueError" in fnode.toxml() assert "ValueError" in fnode.toxml()
@parametrize_families @parametrize_families
def test_teardown_error(self, testdir, run_and_parse, xunit_family): def test_teardown_error(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@ -302,8 +331,10 @@ class TestPython:
assert "ValueError" in fnode.toxml() assert "ValueError" in fnode.toxml()
@parametrize_families @parametrize_families
def test_call_failure_teardown_error(self, testdir, run_and_parse, xunit_family): def test_call_failure_teardown_error(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@ -331,8 +362,10 @@ class TestPython:
) )
@parametrize_families @parametrize_families
def test_skip_contains_name_reason(self, testdir, run_and_parse, xunit_family): def test_skip_contains_name_reason(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
def test_skip(): def test_skip():
@ -349,8 +382,10 @@ class TestPython:
snode.assert_attr(type="pytest.skip", message="hello23") snode.assert_attr(type="pytest.skip", message="hello23")
@parametrize_families @parametrize_families
def test_mark_skip_contains_name_reason(self, testdir, run_and_parse, xunit_family): def test_mark_skip_contains_name_reason(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.skip(reason="hello24") @pytest.mark.skip(reason="hello24")
@ -371,9 +406,9 @@ class TestPython:
@parametrize_families @parametrize_families
def test_mark_skipif_contains_name_reason( def test_mark_skipif_contains_name_reason(
self, testdir, run_and_parse, xunit_family self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
): ) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
GLOBAL_CONDITION = True GLOBAL_CONDITION = True
@ -395,9 +430,9 @@ class TestPython:
@parametrize_families @parametrize_families
def test_mark_skip_doesnt_capture_output( def test_mark_skip_doesnt_capture_output(
self, testdir, run_and_parse, xunit_family self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
): ) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.skip(reason="foo") @pytest.mark.skip(reason="foo")
@ -411,8 +446,10 @@ class TestPython:
assert "bar!" not in node_xml assert "bar!" not in node_xml
@parametrize_families @parametrize_families
def test_classname_instance(self, testdir, run_and_parse, xunit_family): def test_classname_instance(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
class TestClass(object): class TestClass(object):
def test_method(self): def test_method(self):
@ -429,9 +466,11 @@ class TestPython:
) )
@parametrize_families @parametrize_families
def test_classname_nested_dir(self, testdir, run_and_parse, xunit_family): def test_classname_nested_dir(
p = testdir.tmpdir.ensure("sub", "test_hello.py") self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
p.write("def test_func(): 0/0") ) -> None:
p = pytester.mkdir("sub").joinpath("test_hello.py")
p.write_text("def test_func(): 0/0")
result, dom = run_and_parse(family=xunit_family) result, dom = run_and_parse(family=xunit_family)
assert result.ret assert result.ret
node = dom.find_first_by_tag("testsuite") node = dom.find_first_by_tag("testsuite")
@ -440,9 +479,11 @@ class TestPython:
tnode.assert_attr(classname="sub.test_hello", name="test_func") tnode.assert_attr(classname="sub.test_hello", name="test_func")
@parametrize_families @parametrize_families
def test_internal_error(self, testdir, run_and_parse, xunit_family): def test_internal_error(
testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0") self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
testdir.makepyfile("def test_function(): pass") ) -> None:
pytester.makeconftest("def pytest_runtest_protocol(): 0 / 0")
pytester.makepyfile("def test_function(): pass")
result, dom = run_and_parse(family=xunit_family) result, dom = run_and_parse(family=xunit_family)
assert result.ret assert result.ret
node = dom.find_first_by_tag("testsuite") node = dom.find_first_by_tag("testsuite")
@ -458,9 +499,13 @@ class TestPython:
) )
@parametrize_families @parametrize_families
def test_failure_function( def test_failure_function(
self, testdir, junit_logging, run_and_parse, xunit_family self,
): pytester: Pytester,
testdir.makepyfile( junit_logging,
run_and_parse: RunAndParse,
xunit_family,
) -> None:
pytester.makepyfile(
""" """
import logging import logging
import sys import sys
@ -521,8 +566,10 @@ class TestPython:
), "Found unexpected content: system-err" ), "Found unexpected content: system-err"
@parametrize_families @parametrize_families
def test_failure_verbose_message(self, testdir, run_and_parse, xunit_family): def test_failure_verbose_message(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import sys import sys
def test_fail(): def test_fail():
@ -536,8 +583,10 @@ class TestPython:
fnode.assert_attr(message="AssertionError: An error\nassert 0") fnode.assert_attr(message="AssertionError: An error\nassert 0")
@parametrize_families @parametrize_families
def test_failure_escape(self, testdir, run_and_parse, xunit_family): def test_failure_escape(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.parametrize('arg1', "<&'", ids="<&'") @pytest.mark.parametrize('arg1', "<&'", ids="<&'")
@ -564,8 +613,10 @@ class TestPython:
assert "%s\n" % char in text assert "%s\n" % char in text
@parametrize_families @parametrize_families
def test_junit_prefixing(self, testdir, run_and_parse, xunit_family): def test_junit_prefixing(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
def test_func(): def test_func():
assert 0 assert 0
@ -586,8 +637,10 @@ class TestPython:
) )
@parametrize_families @parametrize_families
def test_xfailure_function(self, testdir, run_and_parse, xunit_family): def test_xfailure_function(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
def test_xfail(): def test_xfail():
@ -604,8 +657,10 @@ class TestPython:
fnode.assert_attr(type="pytest.xfail", message="42") fnode.assert_attr(type="pytest.xfail", message="42")
@parametrize_families @parametrize_families
def test_xfailure_marker(self, testdir, run_and_parse, xunit_family): def test_xfailure_marker(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.xfail(reason="42") @pytest.mark.xfail(reason="42")
@ -625,8 +680,10 @@ class TestPython:
@pytest.mark.parametrize( @pytest.mark.parametrize(
"junit_logging", ["no", "log", "system-out", "system-err", "out-err", "all"] "junit_logging", ["no", "log", "system-out", "system-err", "out-err", "all"]
) )
def test_xfail_captures_output_once(self, testdir, junit_logging, run_and_parse): def test_xfail_captures_output_once(
testdir.makepyfile( self, pytester: Pytester, junit_logging: str, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
""" """
import sys import sys
import pytest import pytest
@ -652,8 +709,10 @@ class TestPython:
assert len(tnode.find_by_tag("system-out")) == 0 assert len(tnode.find_by_tag("system-out")) == 0
@parametrize_families @parametrize_families
def test_xfailure_xpass(self, testdir, run_and_parse, xunit_family): def test_xfailure_xpass(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.xfail @pytest.mark.xfail
@ -669,8 +728,10 @@ class TestPython:
tnode.assert_attr(classname="test_xfailure_xpass", name="test_xpass") tnode.assert_attr(classname="test_xfailure_xpass", name="test_xpass")
@parametrize_families @parametrize_families
def test_xfailure_xpass_strict(self, testdir, run_and_parse, xunit_family): def test_xfailure_xpass_strict(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.xfail(strict=True, reason="This needs to fail!") @pytest.mark.xfail(strict=True, reason="This needs to fail!")
@ -688,8 +749,10 @@ class TestPython:
fnode.assert_attr(message="[XPASS(strict)] This needs to fail!") fnode.assert_attr(message="[XPASS(strict)] This needs to fail!")
@parametrize_families @parametrize_families
def test_collect_error(self, testdir, run_and_parse, xunit_family): def test_collect_error(
testdir.makepyfile("syntax error") self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile("syntax error")
result, dom = run_and_parse(family=xunit_family) result, dom = run_and_parse(family=xunit_family)
assert result.ret assert result.ret
node = dom.find_first_by_tag("testsuite") node = dom.find_first_by_tag("testsuite")
@ -699,9 +762,9 @@ class TestPython:
fnode.assert_attr(message="collection failure") fnode.assert_attr(message="collection failure")
assert "SyntaxError" in fnode.toxml() assert "SyntaxError" in fnode.toxml()
def test_unicode(self, testdir, run_and_parse): def test_unicode(self, pytester: Pytester, run_and_parse: RunAndParse) -> None:
value = "hx\xc4\x85\xc4\x87\n" value = "hx\xc4\x85\xc4\x87\n"
testdir.makepyfile( pytester.makepyfile(
"""\ """\
# coding: latin1 # coding: latin1
def test_hello(): def test_hello():
@ -716,9 +779,11 @@ class TestPython:
fnode = tnode.find_first_by_tag("failure") fnode = tnode.find_first_by_tag("failure")
assert "hx" in fnode.toxml() assert "hx" in fnode.toxml()
def test_assertion_binchars(self, testdir, run_and_parse): def test_assertion_binchars(
self, pytester: Pytester, run_and_parse: RunAndParse
) -> None:
"""This test did fail when the escaping wasn't strict.""" """This test did fail when the escaping wasn't strict."""
testdir.makepyfile( pytester.makepyfile(
""" """
M1 = '\x01\x02\x03\x04' M1 = '\x01\x02\x03\x04'
@ -732,8 +797,10 @@ class TestPython:
print(dom.toxml()) print(dom.toxml())
@pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_pass_captures_stdout(self, testdir, run_and_parse, junit_logging): def test_pass_captures_stdout(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str
) -> None:
pytester.makepyfile(
""" """
def test_pass(): def test_pass():
print('hello-stdout') print('hello-stdout')
@ -753,8 +820,10 @@ class TestPython:
), "'hello-stdout' should be in system-out" ), "'hello-stdout' should be in system-out"
@pytest.mark.parametrize("junit_logging", ["no", "system-err"]) @pytest.mark.parametrize("junit_logging", ["no", "system-err"])
def test_pass_captures_stderr(self, testdir, run_and_parse, junit_logging): def test_pass_captures_stderr(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str
) -> None:
pytester.makepyfile(
""" """
import sys import sys
def test_pass(): def test_pass():
@ -775,8 +844,10 @@ class TestPython:
), "'hello-stderr' should be in system-err" ), "'hello-stderr' should be in system-err"
@pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_setup_error_captures_stdout(self, testdir, run_and_parse, junit_logging): def test_setup_error_captures_stdout(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@ -802,8 +873,10 @@ class TestPython:
), "'hello-stdout' should be in system-out" ), "'hello-stdout' should be in system-out"
@pytest.mark.parametrize("junit_logging", ["no", "system-err"]) @pytest.mark.parametrize("junit_logging", ["no", "system-err"])
def test_setup_error_captures_stderr(self, testdir, run_and_parse, junit_logging): def test_setup_error_captures_stderr(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str
) -> None:
pytester.makepyfile(
""" """
import sys import sys
import pytest import pytest
@ -830,8 +903,10 @@ class TestPython:
), "'hello-stderr' should be in system-err" ), "'hello-stderr' should be in system-err"
@pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_avoid_double_stdout(self, testdir, run_and_parse, junit_logging): def test_avoid_double_stdout(
testdir.makepyfile( self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str
) -> None:
pytester.makepyfile(
""" """
import sys import sys
import pytest import pytest
@ -858,7 +933,7 @@ class TestPython:
assert "hello-stdout teardown" in systemout.toxml() assert "hello-stdout teardown" in systemout.toxml()
def test_mangle_test_address(): def test_mangle_test_address() -> None:
from _pytest.junitxml import mangle_test_address from _pytest.junitxml import mangle_test_address
address = "::".join(["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"]) address = "::".join(["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"])
@ -866,7 +941,7 @@ def test_mangle_test_address():
assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"] assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"]
def test_dont_configure_on_workers(tmpdir) -> None: def test_dont_configure_on_workers(tmp_path: Path) -> None:
gotten: List[object] = [] gotten: List[object] = []
class FakeConfig: class FakeConfig:
@ -882,8 +957,8 @@ def test_dont_configure_on_workers(tmpdir) -> None:
return "pytest" return "pytest"
junitprefix = None junitprefix = None
# XXX: shouldn't need tmpdir ? # XXX: shouldn't need tmp_path ?
xmlpath = str(tmpdir.join("junix.xml")) xmlpath = str(tmp_path.joinpath("junix.xml"))
register = gotten.append register = gotten.append
fake_config = cast(Config, FakeConfig()) fake_config = cast(Config, FakeConfig())
@ -898,8 +973,10 @@ def test_dont_configure_on_workers(tmpdir) -> None:
class TestNonPython: class TestNonPython:
@parametrize_families @parametrize_families
def test_summing_simple(self, testdir, run_and_parse, xunit_family): def test_summing_simple(
testdir.makeconftest( self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makeconftest(
""" """
import pytest import pytest
def pytest_collect_file(path, parent): def pytest_collect_file(path, parent):
@ -912,7 +989,7 @@ class TestNonPython:
return "custom item runtest failed" return "custom item runtest failed"
""" """
) )
testdir.tmpdir.join("myfile.xyz").write("hello") pytester.path.joinpath("myfile.xyz").write_text("hello")
result, dom = run_and_parse(family=xunit_family) result, dom = run_and_parse(family=xunit_family)
assert result.ret assert result.ret
node = dom.find_first_by_tag("testsuite") node = dom.find_first_by_tag("testsuite")
@ -925,9 +1002,9 @@ class TestNonPython:
@pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_nullbyte(testdir, junit_logging): def test_nullbyte(pytester: Pytester, junit_logging: str) -> None:
# A null byte can not occur in XML (see section 2.2 of the spec) # A null byte can not occur in XML (see section 2.2 of the spec)
testdir.makepyfile( pytester.makepyfile(
""" """
import sys import sys
def test_print_nullbyte(): def test_print_nullbyte():
@ -936,9 +1013,9 @@ def test_nullbyte(testdir, junit_logging):
assert False assert False
""" """
) )
xmlf = testdir.tmpdir.join("junit.xml") xmlf = pytester.path.joinpath("junit.xml")
testdir.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
text = xmlf.read() text = xmlf.read_text()
assert "\x00" not in text assert "\x00" not in text
if junit_logging == "system-out": if junit_logging == "system-out":
assert "#x00" in text assert "#x00" in text
@ -947,9 +1024,9 @@ def test_nullbyte(testdir, junit_logging):
@pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_nullbyte_replace(testdir, junit_logging): def test_nullbyte_replace(pytester: Pytester, junit_logging: str) -> None:
# Check if the null byte gets replaced # Check if the null byte gets replaced
testdir.makepyfile( pytester.makepyfile(
""" """
import sys import sys
def test_print_nullbyte(): def test_print_nullbyte():
@ -958,16 +1035,16 @@ def test_nullbyte_replace(testdir, junit_logging):
assert False assert False
""" """
) )
xmlf = testdir.tmpdir.join("junit.xml") xmlf = pytester.path.joinpath("junit.xml")
testdir.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
text = xmlf.read() text = xmlf.read_text()
if junit_logging == "system-out": if junit_logging == "system-out":
assert "#x0" in text assert "#x0" in text
if junit_logging == "no": if junit_logging == "no":
assert "#x0" not in text assert "#x0" not in text
def test_invalid_xml_escape(): def test_invalid_xml_escape() -> None:
# Test some more invalid xml chars, the full range should be # Test some more invalid xml chars, the full range should be
# tested really but let's just test the edges of the ranges # tested really but let's just test the edges of the ranges
# instead. # instead.
@ -1003,52 +1080,54 @@ def test_invalid_xml_escape():
assert chr(i) == bin_xml_escape(chr(i)) assert chr(i) == bin_xml_escape(chr(i))
def test_logxml_path_expansion(tmpdir, monkeypatch): def test_logxml_path_expansion(tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
home_tilde = py.path.local(os.path.expanduser("~")).join("test.xml") home_tilde = Path(os.path.expanduser("~")).joinpath("test.xml")
xml_tilde = LogXML("~%stest.xml" % tmpdir.sep, None) xml_tilde = LogXML(Path("~", "test.xml"), None)
assert xml_tilde.logfile == home_tilde assert xml_tilde.logfile == str(home_tilde)
monkeypatch.setenv("HOME", str(tmpdir)) monkeypatch.setenv("HOME", str(tmp_path))
home_var = os.path.normpath(os.path.expandvars("$HOME/test.xml")) home_var = os.path.normpath(os.path.expandvars("$HOME/test.xml"))
xml_var = LogXML("$HOME%stest.xml" % tmpdir.sep, None) xml_var = LogXML(Path("$HOME", "test.xml"), None)
assert xml_var.logfile == home_var assert xml_var.logfile == str(home_var)
def test_logxml_changingdir(testdir): def test_logxml_changingdir(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
def test_func(): def test_func():
import os import os
os.chdir("a") os.chdir("a")
""" """
) )
testdir.tmpdir.mkdir("a") pytester.mkdir("a")
result = testdir.runpytest("--junitxml=a/x.xml") result = pytester.runpytest("--junitxml=a/x.xml")
assert result.ret == 0 assert result.ret == 0
assert testdir.tmpdir.join("a/x.xml").check() assert pytester.path.joinpath("a/x.xml").exists()
def test_logxml_makedir(testdir): def test_logxml_makedir(pytester: Pytester) -> None:
"""--junitxml should automatically create directories for the xml file""" """--junitxml should automatically create directories for the xml file"""
testdir.makepyfile( pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
""" """
) )
result = testdir.runpytest("--junitxml=path/to/results.xml") result = pytester.runpytest("--junitxml=path/to/results.xml")
assert result.ret == 0 assert result.ret == 0
assert testdir.tmpdir.join("path/to/results.xml").check() assert pytester.path.joinpath("path/to/results.xml").exists()
def test_logxml_check_isdir(testdir): def test_logxml_check_isdir(pytester: Pytester) -> None:
"""Give an error if --junit-xml is a directory (#2089)""" """Give an error if --junit-xml is a directory (#2089)"""
result = testdir.runpytest("--junit-xml=.") result = pytester.runpytest("--junit-xml=.")
result.stderr.fnmatch_lines(["*--junitxml must be a filename*"]) result.stderr.fnmatch_lines(["*--junitxml must be a filename*"])
def test_escaped_parametrized_names_xml(testdir, run_and_parse): def test_escaped_parametrized_names_xml(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
"""\ """\
import pytest import pytest
@pytest.mark.parametrize('char', ["\\x00"]) @pytest.mark.parametrize('char', ["\\x00"])
@ -1062,8 +1141,10 @@ def test_escaped_parametrized_names_xml(testdir, run_and_parse):
node.assert_attr(name="test_func[\\x00]") node.assert_attr(name="test_func[\\x00]")
def test_double_colon_split_function_issue469(testdir, run_and_parse): def test_double_colon_split_function_issue469(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.parametrize('param', ["double::colon"]) @pytest.mark.parametrize('param', ["double::colon"])
@ -1078,8 +1159,10 @@ def test_double_colon_split_function_issue469(testdir, run_and_parse):
node.assert_attr(name="test_func[double::colon]") node.assert_attr(name="test_func[double::colon]")
def test_double_colon_split_method_issue469(testdir, run_and_parse): def test_double_colon_split_method_issue469(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
class TestClass(object): class TestClass(object):
@ -1095,8 +1178,8 @@ def test_double_colon_split_method_issue469(testdir, run_and_parse):
node.assert_attr(name="test_func[double::colon]") node.assert_attr(name="test_func[double::colon]")
def test_unicode_issue368(testdir) -> None: def test_unicode_issue368(pytester: Pytester) -> None:
path = testdir.tmpdir.join("test.xml") path = pytester.path.joinpath("test.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
ustr = "ВНИ!" ustr = "ВНИ!"
@ -1125,8 +1208,8 @@ def test_unicode_issue368(testdir) -> None:
log.pytest_sessionfinish() log.pytest_sessionfinish()
def test_record_property(testdir, run_and_parse): def test_record_property(pytester: Pytester, run_and_parse: RunAndParse) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -1147,8 +1230,10 @@ def test_record_property(testdir, run_and_parse):
result.stdout.fnmatch_lines(["*= 1 passed in *"]) result.stdout.fnmatch_lines(["*= 1 passed in *"])
def test_record_property_same_name(testdir, run_and_parse): def test_record_property_same_name(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
""" """
def test_record_with_same_name(record_property): def test_record_with_same_name(record_property):
record_property("foo", "bar") record_property("foo", "bar")
@ -1165,8 +1250,10 @@ def test_record_property_same_name(testdir, run_and_parse):
@pytest.mark.parametrize("fixture_name", ["record_property", "record_xml_attribute"]) @pytest.mark.parametrize("fixture_name", ["record_property", "record_xml_attribute"])
def test_record_fixtures_without_junitxml(testdir, fixture_name): def test_record_fixtures_without_junitxml(
testdir.makepyfile( pytester: Pytester, fixture_name: str
) -> None:
pytester.makepyfile(
""" """
def test_record({fixture_name}): def test_record({fixture_name}):
{fixture_name}("foo", "bar") {fixture_name}("foo", "bar")
@ -1174,19 +1261,19 @@ def test_record_fixtures_without_junitxml(testdir, fixture_name):
fixture_name=fixture_name fixture_name=fixture_name
) )
) )
result = testdir.runpytest() result = pytester.runpytest()
assert result.ret == 0 assert result.ret == 0
@pytest.mark.filterwarnings("default") @pytest.mark.filterwarnings("default")
def test_record_attribute(testdir, run_and_parse): def test_record_attribute(pytester: Pytester, run_and_parse: RunAndParse) -> None:
testdir.makeini( pytester.makeini(
""" """
[pytest] [pytest]
junit_family = xunit1 junit_family = xunit1
""" """
) )
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -1209,15 +1296,17 @@ def test_record_attribute(testdir, run_and_parse):
@pytest.mark.filterwarnings("default") @pytest.mark.filterwarnings("default")
@pytest.mark.parametrize("fixture_name", ["record_xml_attribute", "record_property"]) @pytest.mark.parametrize("fixture_name", ["record_xml_attribute", "record_property"])
def test_record_fixtures_xunit2(testdir, fixture_name, run_and_parse): def test_record_fixtures_xunit2(
pytester: Pytester, fixture_name: str, run_and_parse: RunAndParse
) -> None:
"""Ensure record_xml_attribute and record_property drop values when outside of legacy family.""" """Ensure record_xml_attribute and record_property drop values when outside of legacy family."""
testdir.makeini( pytester.makeini(
""" """
[pytest] [pytest]
junit_family = xunit2 junit_family = xunit2
""" """
) )
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -1246,13 +1335,15 @@ def test_record_fixtures_xunit2(testdir, fixture_name, run_and_parse):
result.stdout.fnmatch_lines(expected_lines) result.stdout.fnmatch_lines(expected_lines)
def test_random_report_log_xdist(testdir, monkeypatch, run_and_parse): def test_random_report_log_xdist(
pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse: RunAndParse
) -> None:
"""`xdist` calls pytest_runtest_logreport as they are executed by the workers, """`xdist` calls pytest_runtest_logreport as they are executed by the workers,
with nodes from several nodes overlapping, so junitxml must cope with that with nodes from several nodes overlapping, so junitxml must cope with that
to produce correct reports (#1064).""" to produce correct reports (#1064)."""
pytest.importorskip("xdist") pytest.importorskip("xdist")
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False) monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest, time import pytest, time
@pytest.mark.parametrize('i', list(range(30))) @pytest.mark.parametrize('i', list(range(30)))
@ -1271,8 +1362,10 @@ def test_random_report_log_xdist(testdir, monkeypatch, run_and_parse):
@parametrize_families @parametrize_families
def test_root_testsuites_tag(testdir, run_and_parse, xunit_family): def test_root_testsuites_tag(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
def test_x(): def test_x():
pass pass
@ -1285,8 +1378,8 @@ def test_root_testsuites_tag(testdir, run_and_parse, xunit_family):
assert suite_node.tag == "testsuite" assert suite_node.tag == "testsuite"
def test_runs_twice(testdir, run_and_parse): def test_runs_twice(pytester: Pytester, run_and_parse: RunAndParse) -> None:
f = testdir.makepyfile( f = pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
@ -1299,10 +1392,12 @@ def test_runs_twice(testdir, run_and_parse):
assert first == second assert first == second
def test_runs_twice_xdist(testdir, run_and_parse): def test_runs_twice_xdist(
pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse: RunAndParse
) -> None:
pytest.importorskip("xdist") pytest.importorskip("xdist")
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
f = testdir.makepyfile( f = pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
@ -1315,9 +1410,9 @@ def test_runs_twice_xdist(testdir, run_and_parse):
assert first == second assert first == second
def test_fancy_items_regression(testdir, run_and_parse): def test_fancy_items_regression(pytester: Pytester, run_and_parse: RunAndParse) -> None:
# issue 1259 # issue 1259
testdir.makeconftest( pytester.makeconftest(
""" """
import pytest import pytest
class FunItem(pytest.Item): class FunItem(pytest.Item):
@ -1341,7 +1436,7 @@ def test_fancy_items_regression(testdir, run_and_parse):
""" """
) )
testdir.makepyfile( pytester.makepyfile(
""" """
def test_pass(): def test_pass():
pass pass
@ -1368,8 +1463,8 @@ def test_fancy_items_regression(testdir, run_and_parse):
@parametrize_families @parametrize_families
def test_global_properties(testdir, xunit_family) -> None: def test_global_properties(pytester: Pytester, xunit_family: str) -> None:
path = testdir.tmpdir.join("test_global_properties.xml") path = pytester.path.joinpath("test_global_properties.xml")
log = LogXML(str(path), None, family=xunit_family) log = LogXML(str(path), None, family=xunit_family)
class Report(BaseReport): class Report(BaseReport):
@ -1402,9 +1497,9 @@ def test_global_properties(testdir, xunit_family) -> None:
assert actual == expected assert actual == expected
def test_url_property(testdir) -> None: def test_url_property(pytester: Pytester) -> None:
test_url = "http://www.github.com/pytest-dev" test_url = "http://www.github.com/pytest-dev"
path = testdir.tmpdir.join("test_url_property.xml") path = pytester.path.joinpath("test_url_property.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
class Report(BaseReport): class Report(BaseReport):
@ -1429,8 +1524,10 @@ def test_url_property(testdir) -> None:
@parametrize_families @parametrize_families
def test_record_testsuite_property(testdir, run_and_parse, xunit_family): def test_record_testsuite_property(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
pytester.makepyfile(
""" """
def test_func1(record_testsuite_property): def test_func1(record_testsuite_property):
record_testsuite_property("stats", "all good") record_testsuite_property("stats", "all good")
@ -1449,27 +1546,29 @@ def test_record_testsuite_property(testdir, run_and_parse, xunit_family):
p2_node.assert_attr(name="stats", value="10") p2_node.assert_attr(name="stats", value="10")
def test_record_testsuite_property_junit_disabled(testdir): def test_record_testsuite_property_junit_disabled(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
def test_func1(record_testsuite_property): def test_func1(record_testsuite_property):
record_testsuite_property("stats", "all good") record_testsuite_property("stats", "all good")
""" """
) )
result = testdir.runpytest() result = pytester.runpytest()
assert result.ret == 0 assert result.ret == 0
@pytest.mark.parametrize("junit", [True, False]) @pytest.mark.parametrize("junit", [True, False])
def test_record_testsuite_property_type_checking(testdir, junit): def test_record_testsuite_property_type_checking(
testdir.makepyfile( pytester: Pytester, junit: bool
) -> None:
pytester.makepyfile(
""" """
def test_func1(record_testsuite_property): def test_func1(record_testsuite_property):
record_testsuite_property(1, 2) record_testsuite_property(1, 2)
""" """
) )
args = ("--junitxml=tests.xml",) if junit else () args = ("--junitxml=tests.xml",) if junit else ()
result = testdir.runpytest(*args) result = pytester.runpytest(*args)
assert result.ret == 1 assert result.ret == 1
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
["*TypeError: name parameter needs to be a string, but int given"] ["*TypeError: name parameter needs to be a string, but int given"]
@ -1478,9 +1577,11 @@ def test_record_testsuite_property_type_checking(testdir, junit):
@pytest.mark.parametrize("suite_name", ["my_suite", ""]) @pytest.mark.parametrize("suite_name", ["my_suite", ""])
@parametrize_families @parametrize_families
def test_set_suite_name(testdir, suite_name, run_and_parse, xunit_family): def test_set_suite_name(
pytester: Pytester, suite_name: str, run_and_parse: RunAndParse, xunit_family: str
) -> None:
if suite_name: if suite_name:
testdir.makeini( pytester.makeini(
""" """
[pytest] [pytest]
junit_suite_name={suite_name} junit_suite_name={suite_name}
@ -1492,7 +1593,7 @@ def test_set_suite_name(testdir, suite_name, run_and_parse, xunit_family):
expected = suite_name expected = suite_name
else: else:
expected = "pytest" expected = "pytest"
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -1506,8 +1607,10 @@ def test_set_suite_name(testdir, suite_name, run_and_parse, xunit_family):
node.assert_attr(name=expected) node.assert_attr(name=expected)
def test_escaped_skipreason_issue3533(testdir, run_and_parse): def test_escaped_skipreason_issue3533(
testdir.makepyfile( pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
""" """
import pytest import pytest
@pytest.mark.skip(reason='1 <> 2') @pytest.mark.skip(reason='1 <> 2')
@ -1524,9 +1627,9 @@ def test_escaped_skipreason_issue3533(testdir, run_and_parse):
@parametrize_families @parametrize_families
def test_logging_passing_tests_disabled_does_not_log_test_output( def test_logging_passing_tests_disabled_does_not_log_test_output(
testdir, run_and_parse, xunit_family pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
): ) -> None:
testdir.makeini( pytester.makeini(
""" """
[pytest] [pytest]
junit_log_passing_tests=False junit_log_passing_tests=False
@ -1536,7 +1639,7 @@ def test_logging_passing_tests_disabled_does_not_log_test_output(
family=xunit_family family=xunit_family
) )
) )
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
import logging import logging
@ -1558,9 +1661,12 @@ def test_logging_passing_tests_disabled_does_not_log_test_output(
@parametrize_families @parametrize_families
@pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"]) @pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"])
def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430(
testdir, junit_logging, run_and_parse, xunit_family pytester: Pytester,
): junit_logging: str,
testdir.makeini( run_and_parse: RunAndParse,
xunit_family: str,
) -> None:
pytester.makeini(
""" """
[pytest] [pytest]
junit_log_passing_tests=False junit_log_passing_tests=False
@ -1569,7 +1675,7 @@ def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430(
family=xunit_family family=xunit_family
) )
) )
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
import logging import logging

View File

@ -3,15 +3,14 @@ import subprocess
import sys import sys
import textwrap import textwrap
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from string import ascii_lowercase from string import ascii_lowercase
import py.path from _pytest.pytester import Pytester
from _pytest import pytester
@contextmanager @contextmanager
def subst_path_windows(filename): def subst_path_windows(filepath: Path):
for c in ascii_lowercase[7:]: # Create a subst drive from H-Z. for c in ascii_lowercase[7:]: # Create a subst drive from H-Z.
c += ":" c += ":"
if not os.path.exists(c): if not os.path.exists(c):
@ -20,14 +19,14 @@ def subst_path_windows(filename):
else: else:
raise AssertionError("Unable to find suitable drive letter for subst.") raise AssertionError("Unable to find suitable drive letter for subst.")
directory = filename.dirpath() directory = filepath.parent
basename = filename.basename basename = filepath.name
args = ["subst", drive, str(directory)] args = ["subst", drive, str(directory)]
subprocess.check_call(args) subprocess.check_call(args)
assert os.path.exists(drive) assert os.path.exists(drive)
try: try:
filename = py.path.local(drive) / basename filename = Path(drive, os.sep, basename)
yield filename yield filename
finally: finally:
args = ["subst", "/D", drive] args = ["subst", "/D", drive]
@ -35,9 +34,9 @@ def subst_path_windows(filename):
@contextmanager @contextmanager
def subst_path_linux(filename): def subst_path_linux(filepath: Path):
directory = filename.dirpath() directory = filepath.parent
basename = filename.basename basename = filepath.name
target = directory / ".." / "sub2" target = directory / ".." / "sub2"
os.symlink(str(directory), str(target), target_is_directory=True) os.symlink(str(directory), str(target), target_is_directory=True)
@ -49,11 +48,11 @@ def subst_path_linux(filename):
pass pass
def test_link_resolve(testdir: pytester.Testdir) -> None: def test_link_resolve(pytester: Pytester) -> None:
"""See: https://github.com/pytest-dev/pytest/issues/5965.""" """See: https://github.com/pytest-dev/pytest/issues/5965."""
sub1 = testdir.mkpydir("sub1") sub1 = pytester.mkpydir("sub1")
p = sub1.join("test_foo.py") p = sub1.joinpath("test_foo.py")
p.write( p.write_text(
textwrap.dedent( textwrap.dedent(
""" """
import pytest import pytest
@ -68,7 +67,7 @@ def test_link_resolve(testdir: pytester.Testdir) -> None:
subst = subst_path_windows subst = subst_path_windows
with subst(p) as subst_p: with subst(p) as subst_p:
result = testdir.runpytest(str(subst_p), "-v") result = pytester.runpytest(str(subst_p), "-v")
# i.e.: Make sure that the error is reported as a relative path, not as a # i.e.: Make sure that the error is reported as a relative path, not as a
# resolved path. # resolved path.
# See: https://github.com/pytest-dev/pytest/issues/5965 # See: https://github.com/pytest-dev/pytest/issues/5965

View File

@ -10,7 +10,6 @@ from _pytest.config import UsageError
from _pytest.main import resolve_collection_argument from _pytest.main import resolve_collection_argument
from _pytest.main import validate_basetemp from _pytest.main import validate_basetemp
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.pytester import Testdir
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -21,9 +20,9 @@ from _pytest.pytester import Testdir
pytest.param((False, SystemExit)), pytest.param((False, SystemExit)),
), ),
) )
def test_wrap_session_notify_exception(ret_exc, testdir): def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None:
returncode, exc = ret_exc returncode, exc = ret_exc
c1 = testdir.makeconftest( c1 = pytester.makeconftest(
""" """
import pytest import pytest
@ -38,7 +37,7 @@ def test_wrap_session_notify_exception(ret_exc, testdir):
returncode=returncode, exc=exc.__name__ returncode=returncode, exc=exc.__name__
) )
) )
result = testdir.runpytest() result = pytester.runpytest()
if returncode: if returncode:
assert result.ret == returncode assert result.ret == returncode
else: else:
@ -65,9 +64,9 @@ def test_wrap_session_notify_exception(ret_exc, testdir):
@pytest.mark.parametrize("returncode", (None, 42)) @pytest.mark.parametrize("returncode", (None, 42))
def test_wrap_session_exit_sessionfinish( def test_wrap_session_exit_sessionfinish(
returncode: Optional[int], testdir: Testdir returncode: Optional[int], pytester: Pytester
) -> None: ) -> None:
testdir.makeconftest( pytester.makeconftest(
""" """
import pytest import pytest
def pytest_sessionfinish(): def pytest_sessionfinish():
@ -76,7 +75,7 @@ def test_wrap_session_exit_sessionfinish(
returncode=returncode returncode=returncode
) )
) )
result = testdir.runpytest() result = pytester.runpytest()
if returncode: if returncode:
assert result.ret == returncode assert result.ret == returncode
else: else:
@ -101,8 +100,8 @@ def test_validate_basetemp_fails(tmp_path, basetemp, monkeypatch):
validate_basetemp(basetemp) validate_basetemp(basetemp)
def test_validate_basetemp_integration(testdir): def test_validate_basetemp_integration(pytester: Pytester) -> None:
result = testdir.runpytest("--basetemp=.") result = pytester.runpytest("--basetemp=.")
result.stderr.fnmatch_lines("*basetemp must not be*") result.stderr.fnmatch_lines("*basetemp must not be*")
@ -203,14 +202,14 @@ class TestResolveCollectionArgument:
) == (Path(os.path.abspath("src")), []) ) == (Path(os.path.abspath("src")), [])
def test_module_full_path_without_drive(testdir): def test_module_full_path_without_drive(pytester: Pytester) -> None:
"""Collect and run test using full path except for the drive letter (#7628). """Collect and run test using full path except for the drive letter (#7628).
Passing a full path without a drive letter would trigger a bug in py.path.local Passing a full path without a drive letter would trigger a bug in py.path.local
where it would keep the full path without the drive letter around, instead of resolving where it would keep the full path without the drive letter around, instead of resolving
to the full path, resulting in fixtures node ids not matching against test node ids correctly. to the full path, resulting in fixtures node ids not matching against test node ids correctly.
""" """
testdir.makepyfile( pytester.makepyfile(
**{ **{
"project/conftest.py": """ "project/conftest.py": """
import pytest import pytest
@ -220,7 +219,7 @@ def test_module_full_path_without_drive(testdir):
} }
) )
testdir.makepyfile( pytester.makepyfile(
**{ **{
"project/tests/dummy_test.py": """ "project/tests/dummy_test.py": """
def test(fix): def test(fix):
@ -228,12 +227,12 @@ def test_module_full_path_without_drive(testdir):
""" """
} }
) )
fn = testdir.tmpdir.join("project/tests/dummy_test.py") fn = pytester.path.joinpath("project/tests/dummy_test.py")
assert fn.isfile() assert fn.is_file()
drive, path = os.path.splitdrive(str(fn)) drive, path = os.path.splitdrive(str(fn))
result = testdir.runpytest(path, "-v") result = pytester.runpytest(path, "-v")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
[ [
os.path.join("project", "tests", "dummy_test.py") + "::test PASSED *", os.path.join("project", "tests", "dummy_test.py") + "::test PASSED *",

View File

@ -315,7 +315,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
shlex.quote(sys.executable) shlex.quote(sys.executable)
) )
) )
# alternative would be extended Testdir.{run(),_run(),popen()} to be able # alternative would be extended Pytester.{run(),_run(),popen()} to be able
# to handle a keyword argument env that replaces os.environ in popen or # to handle a keyword argument env that replaces os.environ in popen or
# extends the copy, advantage: could not forget to restore # extends the copy, advantage: could not forget to restore
monkeypatch.setenv("_ARGCOMPLETE", "1") monkeypatch.setenv("_ARGCOMPLETE", "1")

View File

@ -2,26 +2,26 @@ import os
import subprocess import subprocess
import sys import sys
import time import time
from pathlib import Path
from types import ModuleType
from typing import List from typing import List
import py.path import _pytest.pytester as pytester_mod
import _pytest.pytester as pytester
import pytest import pytest
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import CwdSnapshot from _pytest.pytester import CwdSnapshot
from _pytest.pytester import HookRecorder from _pytest.pytester import HookRecorder
from _pytest.pytester import LineMatcher from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
from _pytest.pytester import SysModulesSnapshot from _pytest.pytester import SysModulesSnapshot
from _pytest.pytester import SysPathsSnapshot from _pytest.pytester import SysPathsSnapshot
from _pytest.pytester import Testdir
def test_make_hook_recorder(testdir) -> None: def test_make_hook_recorder(pytester: Pytester) -> None:
item = testdir.getitem("def test_func(): pass") item = pytester.getitem("def test_func(): pass")
recorder = testdir.make_hook_recorder(item.config.pluginmanager) recorder = pytester.make_hook_recorder(item.config.pluginmanager)
assert not recorder.getfailures() assert not recorder.getfailures()
# (The silly condition is to fool mypy that the code below this is reachable) # (The silly condition is to fool mypy that the code below this is reachable)
@ -35,11 +35,11 @@ def test_make_hook_recorder(testdir) -> None:
skipped = False skipped = False
when = "call" when = "call"
recorder.hook.pytest_runtest_logreport(report=rep) recorder.hook.pytest_runtest_logreport(report=rep) # type: ignore[attr-defined]
failures = recorder.getfailures() failures = recorder.getfailures()
assert failures == [rep] assert failures == [rep] # type: ignore[comparison-overlap]
failures = recorder.getfailures() failures = recorder.getfailures()
assert failures == [rep] assert failures == [rep] # type: ignore[comparison-overlap]
class rep2: class rep2:
excinfo = None excinfo = None
@ -50,14 +50,14 @@ def test_make_hook_recorder(testdir) -> None:
rep2.passed = False rep2.passed = False
rep2.skipped = True rep2.skipped = True
recorder.hook.pytest_runtest_logreport(report=rep2) recorder.hook.pytest_runtest_logreport(report=rep2) # type: ignore[attr-defined]
modcol = testdir.getmodulecol("") modcol = pytester.getmodulecol("")
rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol) rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol)
rep3.passed = False rep3.passed = False
rep3.failed = True rep3.failed = True
rep3.skipped = False rep3.skipped = False
recorder.hook.pytest_collectreport(report=rep3) recorder.hook.pytest_collectreport(report=rep3) # type: ignore[attr-defined]
passed, skipped, failed = recorder.listoutcomes() passed, skipped, failed = recorder.listoutcomes()
assert not passed and skipped and failed assert not passed and skipped and failed
@ -68,55 +68,55 @@ def test_make_hook_recorder(testdir) -> None:
assert numfailed == 1 assert numfailed == 1
assert len(recorder.getfailedcollections()) == 1 assert len(recorder.getfailedcollections()) == 1
recorder.unregister() recorder.unregister() # type: ignore[attr-defined]
recorder.clear() recorder.clear()
recorder.hook.pytest_runtest_logreport(report=rep3) recorder.hook.pytest_runtest_logreport(report=rep3) # type: ignore[attr-defined]
pytest.raises(ValueError, recorder.getfailures) pytest.raises(ValueError, recorder.getfailures)
def test_parseconfig(testdir) -> None: def test_parseconfig(pytester: Pytester) -> None:
config1 = testdir.parseconfig() config1 = pytester.parseconfig()
config2 = testdir.parseconfig() config2 = pytester.parseconfig()
assert config2 is not config1 assert config2 is not config1
def test_testdir_runs_with_plugin(testdir) -> None: def test_pytester_runs_with_plugin(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
pytest_plugins = "pytester" pytest_plugins = "pytester"
def test_hello(testdir): def test_hello(pytester):
assert 1 assert 1
""" """
) )
result = testdir.runpytest() result = pytester.runpytest()
result.assert_outcomes(passed=1) result.assert_outcomes(passed=1)
def test_testdir_with_doctest(testdir): def test_pytester_with_doctest(pytester: Pytester) -> None:
"""Check that testdir can be used within doctests. """Check that pytester can be used within doctests.
It used to use `request.function`, which is `None` with doctests.""" It used to use `request.function`, which is `None` with doctests."""
testdir.makepyfile( pytester.makepyfile(
**{ **{
"sub/t-doctest.py": """ "sub/t-doctest.py": """
''' '''
>>> import os >>> import os
>>> testdir = getfixture("testdir") >>> pytester = getfixture("pytester")
>>> str(testdir.makepyfile("content")).replace(os.sep, '/') >>> str(pytester.makepyfile("content")).replace(os.sep, '/')
'.../basetemp/sub.t-doctest0/sub.py' '.../basetemp/sub.t-doctest0/sub.py'
''' '''
""", """,
"sub/__init__.py": "", "sub/__init__.py": "",
} }
) )
result = testdir.runpytest( result = pytester.runpytest(
"-p", "pytester", "--doctest-modules", "sub/t-doctest.py" "-p", "pytester", "--doctest-modules", "sub/t-doctest.py"
) )
assert result.ret == 0 assert result.ret == 0
def test_runresult_assertion_on_xfail(testdir) -> None: def test_runresult_assertion_on_xfail(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -127,13 +127,13 @@ def test_runresult_assertion_on_xfail(testdir) -> None:
assert False assert False
""" """
) )
result = testdir.runpytest() result = pytester.runpytest()
result.assert_outcomes(xfailed=1) result.assert_outcomes(xfailed=1)
assert result.ret == 0 assert result.ret == 0
def test_runresult_assertion_on_xpassed(testdir) -> None: def test_runresult_assertion_on_xpassed(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -144,13 +144,13 @@ def test_runresult_assertion_on_xpassed(testdir) -> None:
assert True assert True
""" """
) )
result = testdir.runpytest() result = pytester.runpytest()
result.assert_outcomes(xpassed=1) result.assert_outcomes(xpassed=1)
assert result.ret == 0 assert result.ret == 0
def test_xpassed_with_strict_is_considered_a_failure(testdir) -> None: def test_xpassed_with_strict_is_considered_a_failure(pytester: Pytester) -> None:
testdir.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -161,7 +161,7 @@ def test_xpassed_with_strict_is_considered_a_failure(testdir) -> None:
assert True assert True
""" """
) )
result = testdir.runpytest() result = pytester.runpytest()
result.assert_outcomes(failed=1) result.assert_outcomes(failed=1)
assert result.ret != 0 assert result.ret != 0
@ -202,28 +202,28 @@ def test_hookrecorder_basic(holder) -> None:
assert call._name == "pytest_xyz_noarg" assert call._name == "pytest_xyz_noarg"
def test_makepyfile_unicode(testdir) -> None: def test_makepyfile_unicode(pytester: Pytester) -> None:
testdir.makepyfile(chr(0xFFFD)) pytester.makepyfile(chr(0xFFFD))
def test_makepyfile_utf8(testdir) -> None: def test_makepyfile_utf8(pytester: Pytester) -> None:
"""Ensure makepyfile accepts utf-8 bytes as input (#2738)""" """Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
utf8_contents = """ utf8_contents = """
def setup_function(function): def setup_function(function):
mixed_encoding = 'São Paulo' mixed_encoding = 'São Paulo'
""".encode() """.encode()
p = testdir.makepyfile(utf8_contents) p = pytester.makepyfile(utf8_contents)
assert "mixed_encoding = 'São Paulo'".encode() in p.read("rb") assert "mixed_encoding = 'São Paulo'".encode() in p.read_bytes()
class TestInlineRunModulesCleanup: class TestInlineRunModulesCleanup:
def test_inline_run_test_module_not_cleaned_up(self, testdir) -> None: def test_inline_run_test_module_not_cleaned_up(self, pytester: Pytester) -> None:
test_mod = testdir.makepyfile("def test_foo(): assert True") test_mod = pytester.makepyfile("def test_foo(): assert True")
result = testdir.inline_run(str(test_mod)) result = pytester.inline_run(str(test_mod))
assert result.ret == ExitCode.OK assert result.ret == ExitCode.OK
# rewrite module, now test should fail if module was re-imported # rewrite module, now test should fail if module was re-imported
test_mod.write("def test_foo(): assert False") test_mod.write_text("def test_foo(): assert False")
result2 = testdir.inline_run(str(test_mod)) result2 = pytester.inline_run(str(test_mod))
assert result2.ret == ExitCode.TESTS_FAILED assert result2.ret == ExitCode.TESTS_FAILED
def spy_factory(self): def spy_factory(self):
@ -243,20 +243,20 @@ class TestInlineRunModulesCleanup:
return SysModulesSnapshotSpy return SysModulesSnapshotSpy
def test_inline_run_taking_and_restoring_a_sys_modules_snapshot( def test_inline_run_taking_and_restoring_a_sys_modules_snapshot(
self, testdir, monkeypatch self, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None: ) -> None:
spy_factory = self.spy_factory() spy_factory = self.spy_factory()
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory)
testdir.syspathinsert() pytester.syspathinsert()
original = dict(sys.modules) original = dict(sys.modules)
testdir.makepyfile(import1="# you son of a silly person") pytester.makepyfile(import1="# you son of a silly person")
testdir.makepyfile(import2="# my hovercraft is full of eels") pytester.makepyfile(import2="# my hovercraft is full of eels")
test_mod = testdir.makepyfile( test_mod = pytester.makepyfile(
""" """
import import1 import import1
def test_foo(): import import2""" def test_foo(): import import2"""
) )
testdir.inline_run(str(test_mod)) pytester.inline_run(str(test_mod))
assert len(spy_factory.instances) == 1 assert len(spy_factory.instances) == 1
spy = spy_factory.instances[0] spy = spy_factory.instances[0]
assert spy._spy_restore_count == 1 assert spy._spy_restore_count == 1
@ -264,51 +264,52 @@ class TestInlineRunModulesCleanup:
assert all(sys.modules[x] is original[x] for x in sys.modules) assert all(sys.modules[x] is original[x] for x in sys.modules)
def test_inline_run_sys_modules_snapshot_restore_preserving_modules( def test_inline_run_sys_modules_snapshot_restore_preserving_modules(
self, testdir, monkeypatch self, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None: ) -> None:
spy_factory = self.spy_factory() spy_factory = self.spy_factory()
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory)
test_mod = testdir.makepyfile("def test_foo(): pass") test_mod = pytester.makepyfile("def test_foo(): pass")
testdir.inline_run(str(test_mod)) pytester.inline_run(str(test_mod))
spy = spy_factory.instances[0] spy = spy_factory.instances[0]
assert not spy._spy_preserve("black_knight") assert not spy._spy_preserve("black_knight")
assert spy._spy_preserve("zope") assert spy._spy_preserve("zope")
assert spy._spy_preserve("zope.interface") assert spy._spy_preserve("zope.interface")
assert spy._spy_preserve("zopelicious") assert spy._spy_preserve("zopelicious")
def test_external_test_module_imports_not_cleaned_up(self, testdir) -> None: def test_external_test_module_imports_not_cleaned_up(
testdir.syspathinsert() self, pytester: Pytester
testdir.makepyfile(imported="data = 'you son of a silly person'") ) -> None:
pytester.syspathinsert()
pytester.makepyfile(imported="data = 'you son of a silly person'")
import imported import imported
test_mod = testdir.makepyfile( test_mod = pytester.makepyfile(
""" """
def test_foo(): def test_foo():
import imported import imported
imported.data = 42""" imported.data = 42"""
) )
testdir.inline_run(str(test_mod)) pytester.inline_run(str(test_mod))
assert imported.data == 42 assert imported.data == 42
def test_assert_outcomes_after_pytest_error(testdir) -> None: def test_assert_outcomes_after_pytest_error(pytester: Pytester) -> None:
testdir.makepyfile("def test_foo(): assert True") pytester.makepyfile("def test_foo(): assert True")
result = testdir.runpytest("--unexpected-argument") result = pytester.runpytest("--unexpected-argument")
with pytest.raises(ValueError, match="Pytest terminal summary report not found"): with pytest.raises(ValueError, match="Pytest terminal summary report not found"):
result.assert_outcomes(passed=0) result.assert_outcomes(passed=0)
def test_cwd_snapshot(testdir: Testdir) -> None: def test_cwd_snapshot(pytester: Pytester) -> None:
tmpdir = testdir.tmpdir foo = pytester.mkdir("foo")
foo = tmpdir.ensure("foo", dir=1) bar = pytester.mkdir("bar")
bar = tmpdir.ensure("bar", dir=1) os.chdir(foo)
foo.chdir()
snapshot = CwdSnapshot() snapshot = CwdSnapshot()
bar.chdir() os.chdir(bar)
assert py.path.local() == bar assert Path().absolute() == bar
snapshot.restore() snapshot.restore()
assert py.path.local() == foo assert Path().absolute() == foo
class TestSysModulesSnapshot: class TestSysModulesSnapshot:
@ -318,14 +319,14 @@ class TestSysModulesSnapshot:
original = dict(sys.modules) original = dict(sys.modules)
assert self.key not in sys.modules assert self.key not in sys.modules
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
sys.modules[self.key] = "something" # type: ignore sys.modules[self.key] = ModuleType("something")
assert self.key in sys.modules assert self.key in sys.modules
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_add_removed(self, monkeypatch) -> None: def test_add_removed(self, monkeypatch: MonkeyPatch) -> None:
assert self.key not in sys.modules assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, "something") monkeypatch.setitem(sys.modules, self.key, ModuleType("something"))
assert self.key in sys.modules assert self.key in sys.modules
original = dict(sys.modules) original = dict(sys.modules)
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
@ -334,38 +335,39 @@ class TestSysModulesSnapshot:
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_restore_reloaded(self, monkeypatch) -> None: def test_restore_reloaded(self, monkeypatch: MonkeyPatch) -> None:
assert self.key not in sys.modules assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, "something") monkeypatch.setitem(sys.modules, self.key, ModuleType("something"))
assert self.key in sys.modules assert self.key in sys.modules
original = dict(sys.modules) original = dict(sys.modules)
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
sys.modules[self.key] = "something else" # type: ignore sys.modules[self.key] = ModuleType("something else")
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_preserve_modules(self, monkeypatch) -> None: def test_preserve_modules(self, monkeypatch: MonkeyPatch) -> None:
key = [self.key + str(i) for i in range(3)] key = [self.key + str(i) for i in range(3)]
assert not any(k in sys.modules for k in key) assert not any(k in sys.modules for k in key)
for i, k in enumerate(key): for i, k in enumerate(key):
monkeypatch.setitem(sys.modules, k, "something" + str(i)) mod = ModuleType("something" + str(i))
monkeypatch.setitem(sys.modules, k, mod)
original = dict(sys.modules) original = dict(sys.modules)
def preserve(name): def preserve(name):
return name in (key[0], key[1], "some-other-key") return name in (key[0], key[1], "some-other-key")
snapshot = SysModulesSnapshot(preserve=preserve) snapshot = SysModulesSnapshot(preserve=preserve)
sys.modules[key[0]] = original[key[0]] = "something else0" # type: ignore sys.modules[key[0]] = original[key[0]] = ModuleType("something else0")
sys.modules[key[1]] = original[key[1]] = "something else1" # type: ignore sys.modules[key[1]] = original[key[1]] = ModuleType("something else1")
sys.modules[key[2]] = "something else2" # type: ignore sys.modules[key[2]] = ModuleType("something else2")
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_preserve_container(self, monkeypatch) -> None: def test_preserve_container(self, monkeypatch: MonkeyPatch) -> None:
original = dict(sys.modules) original = dict(sys.modules)
assert self.key not in original assert self.key not in original
replacement = dict(sys.modules) replacement = dict(sys.modules)
replacement[self.key] = "life of brian" # type: ignore replacement[self.key] = ModuleType("life of brian")
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
monkeypatch.setattr(sys, "modules", replacement) monkeypatch.setattr(sys, "modules", replacement)
snapshot.restore() snapshot.restore()
@ -381,7 +383,7 @@ class TestSysPathsSnapshot:
def path(n: int) -> str: def path(n: int) -> str:
return "my-dirty-little-secret-" + str(n) return "my-dirty-little-secret-" + str(n)
def test_restore(self, monkeypatch, path_type) -> None: def test_restore(self, monkeypatch: MonkeyPatch, path_type) -> None:
other_path_type = self.other_path[path_type] other_path_type = self.other_path[path_type]
for i in range(10): for i in range(10):
assert self.path(i) not in getattr(sys, path_type) assert self.path(i) not in getattr(sys, path_type)
@ -404,7 +406,7 @@ class TestSysPathsSnapshot:
assert getattr(sys, path_type) == original assert getattr(sys, path_type) == original
assert getattr(sys, other_path_type) == original_other assert getattr(sys, other_path_type) == original_other
def test_preserve_container(self, monkeypatch, path_type) -> None: def test_preserve_container(self, monkeypatch: MonkeyPatch, path_type) -> None:
other_path_type = self.other_path[path_type] other_path_type = self.other_path[path_type]
original_data = list(getattr(sys, path_type)) original_data = list(getattr(sys, path_type))
original_other = getattr(sys, other_path_type) original_other = getattr(sys, other_path_type)
@ -419,49 +421,49 @@ class TestSysPathsSnapshot:
assert getattr(sys, other_path_type) == original_other_data assert getattr(sys, other_path_type) == original_other_data
def test_testdir_subprocess(testdir) -> None: def test_pytester_subprocess(pytester: Pytester) -> None:
testfile = testdir.makepyfile("def test_one(): pass") testfile = pytester.makepyfile("def test_one(): pass")
assert testdir.runpytest_subprocess(testfile).ret == 0 assert pytester.runpytest_subprocess(testfile).ret == 0
def test_testdir_subprocess_via_runpytest_arg(testdir) -> None: def test_pytester_subprocess_via_runpytest_arg(pytester: Pytester) -> None:
testfile = testdir.makepyfile( testfile = pytester.makepyfile(
""" """
def test_testdir_subprocess(testdir): def test_pytester_subprocess(pytester):
import os import os
testfile = testdir.makepyfile( testfile = pytester.makepyfile(
\""" \"""
import os import os
def test_one(): def test_one():
assert {} != os.getpid() assert {} != os.getpid()
\""".format(os.getpid()) \""".format(os.getpid())
) )
assert testdir.runpytest(testfile).ret == 0 assert pytester.runpytest(testfile).ret == 0
""" """
) )
result = testdir.runpytest_subprocess( result = pytester.runpytest_inprocess(
"-p", "pytester", "--runpytest", "subprocess", testfile "-p", "pytester", "--runpytest", "subprocess", testfile
) )
assert result.ret == 0 assert result.ret == 0
def test_unicode_args(testdir) -> None: def test_unicode_args(pytester: Pytester) -> None:
result = testdir.runpytest("-k", "אבג") result = pytester.runpytest("-k", "אבג")
assert result.ret == ExitCode.NO_TESTS_COLLECTED assert result.ret == ExitCode.NO_TESTS_COLLECTED
def test_testdir_run_no_timeout(testdir) -> None: def test_pytester_run_no_timeout(pytester: Pytester) -> None:
testfile = testdir.makepyfile("def test_no_timeout(): pass") testfile = pytester.makepyfile("def test_no_timeout(): pass")
assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK assert pytester.runpytest_subprocess(testfile).ret == ExitCode.OK
def test_testdir_run_with_timeout(testdir) -> None: def test_pytester_run_with_timeout(pytester: Pytester) -> None:
testfile = testdir.makepyfile("def test_no_timeout(): pass") testfile = pytester.makepyfile("def test_no_timeout(): pass")
timeout = 120 timeout = 120
start = time.time() start = time.time()
result = testdir.runpytest_subprocess(testfile, timeout=timeout) result = pytester.runpytest_subprocess(testfile, timeout=timeout)
end = time.time() end = time.time()
duration = end - start duration = end - start
@ -469,16 +471,16 @@ def test_testdir_run_with_timeout(testdir) -> None:
assert duration < timeout assert duration < timeout
def test_testdir_run_timeout_expires(testdir) -> None: def test_pytester_run_timeout_expires(pytester: Pytester) -> None:
testfile = testdir.makepyfile( testfile = pytester.makepyfile(
""" """
import time import time
def test_timeout(): def test_timeout():
time.sleep(10)""" time.sleep(10)"""
) )
with pytest.raises(testdir.TimeoutExpired): with pytest.raises(pytester.TimeoutExpired):
testdir.runpytest_subprocess(testfile, timeout=1) pytester.runpytest_subprocess(testfile, timeout=1)
def test_linematcher_with_nonlist() -> None: def test_linematcher_with_nonlist() -> None:
@ -533,7 +535,7 @@ def test_linematcher_match_failure() -> None:
] ]
def test_linematcher_consecutive(): def test_linematcher_consecutive() -> None:
lm = LineMatcher(["1", "", "2"]) lm = LineMatcher(["1", "", "2"])
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(pytest.fail.Exception) as excinfo:
lm.fnmatch_lines(["1", "2"], consecutive=True) lm.fnmatch_lines(["1", "2"], consecutive=True)
@ -554,7 +556,7 @@ def test_linematcher_consecutive():
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"]) @pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
def test_linematcher_no_matching(function) -> None: def test_linematcher_no_matching(function: str) -> None:
if function == "no_fnmatch_line": if function == "no_fnmatch_line":
good_pattern = "*.py OK*" good_pattern = "*.py OK*"
bad_pattern = "*X.py OK*" bad_pattern = "*X.py OK*"
@ -615,7 +617,7 @@ def test_linematcher_string_api() -> None:
assert str(lm) == "foo\nbar" assert str(lm) == "foo\nbar"
def test_pytester_addopts_before_testdir(request, monkeypatch) -> None: def test_pytester_addopts_before_testdir(request, monkeypatch: MonkeyPatch) -> None:
orig = os.environ.get("PYTEST_ADDOPTS", None) orig = os.environ.get("PYTEST_ADDOPTS", None)
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused") monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
testdir = request.getfixturevalue("testdir") testdir = request.getfixturevalue("testdir")
@ -626,9 +628,9 @@ def test_pytester_addopts_before_testdir(request, monkeypatch) -> None:
assert os.environ.get("PYTEST_ADDOPTS") == orig assert os.environ.get("PYTEST_ADDOPTS") == orig
def test_run_stdin(testdir) -> None: def test_run_stdin(pytester: Pytester) -> None:
with pytest.raises(testdir.TimeoutExpired): with pytest.raises(pytester.TimeoutExpired):
testdir.run( pytester.run(
sys.executable, sys.executable,
"-c", "-c",
"import sys, time; time.sleep(1); print(sys.stdin.read())", "import sys, time; time.sleep(1); print(sys.stdin.read())",
@ -636,8 +638,8 @@ def test_run_stdin(testdir) -> None:
timeout=0.1, timeout=0.1,
) )
with pytest.raises(testdir.TimeoutExpired): with pytest.raises(pytester.TimeoutExpired):
result = testdir.run( result = pytester.run(
sys.executable, sys.executable,
"-c", "-c",
"import sys, time; time.sleep(1); print(sys.stdin.read())", "import sys, time; time.sleep(1); print(sys.stdin.read())",
@ -645,7 +647,7 @@ def test_run_stdin(testdir) -> None:
timeout=0.1, timeout=0.1,
) )
result = testdir.run( result = pytester.run(
sys.executable, sys.executable,
"-c", "-c",
"import sys; print(sys.stdin.read())", "import sys; print(sys.stdin.read())",
@ -656,8 +658,8 @@ def test_run_stdin(testdir) -> None:
assert result.ret == 0 assert result.ret == 0
def test_popen_stdin_pipe(testdir) -> None: def test_popen_stdin_pipe(pytester: Pytester) -> None:
proc = testdir.popen( proc = pytester.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], [sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@ -670,8 +672,8 @@ def test_popen_stdin_pipe(testdir) -> None:
assert proc.returncode == 0 assert proc.returncode == 0
def test_popen_stdin_bytes(testdir) -> None: def test_popen_stdin_bytes(pytester: Pytester) -> None:
proc = testdir.popen( proc = pytester.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], [sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@ -683,18 +685,18 @@ def test_popen_stdin_bytes(testdir) -> None:
assert proc.returncode == 0 assert proc.returncode == 0
def test_popen_default_stdin_stderr_and_stdin_None(testdir) -> None: def test_popen_default_stdin_stderr_and_stdin_None(pytester: Pytester) -> None:
# stdout, stderr default to pipes, # stdout, stderr default to pipes,
# stdin can be None to not close the pipe, avoiding # stdin can be None to not close the pipe, avoiding
# "ValueError: flush of closed file" with `communicate()`. # "ValueError: flush of closed file" with `communicate()`.
# #
# Wraps the test to make it not hang when run with "-s". # Wraps the test to make it not hang when run with "-s".
p1 = testdir.makepyfile( p1 = pytester.makepyfile(
''' '''
import sys import sys
def test_inner(testdir): def test_inner(pytester):
p1 = testdir.makepyfile( p1 = pytester.makepyfile(
""" """
import sys import sys
print(sys.stdin.read()) # empty print(sys.stdin.read()) # empty
@ -702,14 +704,14 @@ def test_popen_default_stdin_stderr_and_stdin_None(testdir) -> None:
sys.stderr.write('stderr') sys.stderr.write('stderr')
""" """
) )
proc = testdir.popen([sys.executable, str(p1)], stdin=None) proc = pytester.popen([sys.executable, str(p1)], stdin=None)
stdout, stderr = proc.communicate(b"ignored") stdout, stderr = proc.communicate(b"ignored")
assert stdout.splitlines() == [b"", b"stdout"] assert stdout.splitlines() == [b"", b"stdout"]
assert stderr.splitlines() == [b"stderr"] assert stderr.splitlines() == [b"stderr"]
assert proc.returncode == 0 assert proc.returncode == 0
''' '''
) )
result = testdir.runpytest("-p", "pytester", str(p1)) result = pytester.runpytest("-p", "pytester", str(p1))
assert result.ret == 0 assert result.ret == 0
@ -740,22 +742,22 @@ def test_run_result_repr() -> None:
errlines = ["some", "nasty", "errors", "happened"] errlines = ["some", "nasty", "errors", "happened"]
# known exit code # known exit code
r = pytester.RunResult(1, outlines, errlines, duration=0.5) r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5)
assert ( assert (
repr(r) == "<RunResult ret=ExitCode.TESTS_FAILED len(stdout.lines)=3" repr(r) == "<RunResult ret=ExitCode.TESTS_FAILED len(stdout.lines)=3"
" len(stderr.lines)=4 duration=0.50s>" " len(stderr.lines)=4 duration=0.50s>"
) )
# unknown exit code: just the number # unknown exit code: just the number
r = pytester.RunResult(99, outlines, errlines, duration=0.5) r = pytester_mod.RunResult(99, outlines, errlines, duration=0.5)
assert ( assert (
repr(r) == "<RunResult ret=99 len(stdout.lines)=3" repr(r) == "<RunResult ret=99 len(stdout.lines)=3"
" len(stderr.lines)=4 duration=0.50s>" " len(stderr.lines)=4 duration=0.50s>"
) )
def test_testdir_outcomes_with_multiple_errors(testdir): def test_pytester_outcomes_with_multiple_errors(pytester: Pytester) -> None:
p1 = testdir.makepyfile( p1 = pytester.makepyfile(
""" """
import pytest import pytest
@ -770,13 +772,13 @@ def test_testdir_outcomes_with_multiple_errors(testdir):
pass pass
""" """
) )
result = testdir.runpytest(str(p1)) result = pytester.runpytest(str(p1))
result.assert_outcomes(errors=2) result.assert_outcomes(errors=2)
assert result.parseoutcomes() == {"errors": 2} assert result.parseoutcomes() == {"errors": 2}
def test_parse_summary_line_always_plural(): def test_parse_summary_line_always_plural() -> None:
"""Parsing summaries always returns plural nouns (#6505)""" """Parsing summaries always returns plural nouns (#6505)"""
lines = [ lines = [
"some output 1", "some output 1",
@ -784,7 +786,7 @@ def test_parse_summary_line_always_plural():
"======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====", "======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====",
"done.", "done.",
] ]
assert pytester.RunResult.parse_summary_nouns(lines) == { assert pytester_mod.RunResult.parse_summary_nouns(lines) == {
"errors": 1, "errors": 1,
"failed": 1, "failed": 1,
"passed": 1, "passed": 1,
@ -797,7 +799,7 @@ def test_parse_summary_line_always_plural():
"======= 1 failed, 1 passed, 2 warnings, 2 errors in 0.13s ====", "======= 1 failed, 1 passed, 2 warnings, 2 errors in 0.13s ====",
"done.", "done.",
] ]
assert pytester.RunResult.parse_summary_nouns(lines) == { assert pytester_mod.RunResult.parse_summary_nouns(lines) == {
"errors": 2, "errors": 2,
"failed": 1, "failed": 1,
"passed": 1, "passed": 1,
@ -805,12 +807,12 @@ def test_parse_summary_line_always_plural():
} }
def test_makefile_joins_absolute_path(testdir: Testdir) -> None: def test_makefile_joins_absolute_path(pytester: Pytester) -> None:
absfile = testdir.tmpdir / "absfile" absfile = pytester.path / "absfile"
p1 = testdir.makepyfile(**{str(absfile): ""}) p1 = pytester.makepyfile(**{str(absfile): ""})
assert str(p1) == str(testdir.tmpdir / "absfile.py") assert str(p1) == str(pytester.path / "absfile.py")
def test_testtmproot(testdir): def test_testtmproot(testdir) -> None:
"""Check test_tmproot is a py.path attribute for backward compatibility.""" """Check test_tmproot is a py.path attribute for backward compatibility."""
assert testdir.test_tmproot.check(dir=1) assert testdir.test_tmproot.check(dir=1)

File diff suppressed because it is too large Load Diff