2024-01-28 21:12:42 +08:00
|
|
|
# mypy: allow-untyped-defs
|
2021-10-05 14:36:38 +08:00
|
|
|
import sys
|
|
|
|
from textwrap import dedent
|
|
|
|
from typing import Generator
|
|
|
|
from typing import List
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
from _pytest.pytester import Pytester
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
def file_structure(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
|
|
|
test_foo="""
|
|
|
|
from foo import foo
|
|
|
|
|
|
|
|
def test_foo():
|
|
|
|
assert foo() == 1
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
test_bar="""
|
|
|
|
from bar import bar
|
|
|
|
|
|
|
|
def test_bar():
|
|
|
|
assert bar() == 2
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
|
|
|
foo_py = pytester.mkdir("sub") / "foo.py"
|
|
|
|
content = dedent(
|
|
|
|
"""
|
|
|
|
def foo():
|
|
|
|
return 1
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
foo_py.write_text(content, encoding="utf-8")
|
|
|
|
|
|
|
|
bar_py = pytester.mkdir("sub2") / "bar.py"
|
|
|
|
content = dedent(
|
|
|
|
"""
|
|
|
|
def bar():
|
|
|
|
return 2
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
bar_py.write_text(content, encoding="utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
def test_one_dir(pytester: Pytester, file_structure) -> None:
|
|
|
|
pytester.makefile(".ini", pytest="[pytest]\npythonpath=sub\n")
|
|
|
|
result = pytester.runpytest("test_foo.py")
|
|
|
|
assert result.ret == 0
|
|
|
|
result.assert_outcomes(passed=1)
|
|
|
|
|
|
|
|
|
|
|
|
def test_two_dirs(pytester: Pytester, file_structure) -> None:
|
|
|
|
pytester.makefile(".ini", pytest="[pytest]\npythonpath=sub sub2\n")
|
|
|
|
result = pytester.runpytest("test_foo.py", "test_bar.py")
|
|
|
|
assert result.ret == 0
|
|
|
|
result.assert_outcomes(passed=2)
|
|
|
|
|
|
|
|
|
|
|
|
def test_module_not_found(pytester: Pytester, file_structure) -> None:
|
|
|
|
"""Without the pythonpath setting, the module should not be found."""
|
|
|
|
pytester.makefile(".ini", pytest="[pytest]\n")
|
|
|
|
result = pytester.runpytest("test_foo.py")
|
|
|
|
assert result.ret == pytest.ExitCode.INTERRUPTED
|
|
|
|
result.assert_outcomes(errors=1)
|
|
|
|
expected_error = "E ModuleNotFoundError: No module named 'foo'"
|
|
|
|
result.stdout.fnmatch_lines([expected_error])
|
|
|
|
|
|
|
|
|
|
|
|
def test_no_ini(pytester: Pytester, file_structure) -> None:
|
|
|
|
"""If no ini file, test should error."""
|
|
|
|
result = pytester.runpytest("test_foo.py")
|
|
|
|
assert result.ret == pytest.ExitCode.INTERRUPTED
|
|
|
|
result.assert_outcomes(errors=1)
|
|
|
|
expected_error = "E ModuleNotFoundError: No module named 'foo'"
|
|
|
|
result.stdout.fnmatch_lines([expected_error])
|
|
|
|
|
|
|
|
|
|
|
|
def test_clean_up(pytester: Pytester) -> None:
|
2022-02-09 15:58:38 +08:00
|
|
|
"""Test that the plugin cleans up after itself."""
|
2023-05-18 21:10:44 +08:00
|
|
|
# This is tough to test behaviorally because the cleanup really runs last.
|
2021-10-05 14:36:38 +08:00
|
|
|
# So the test make several implementation assumptions:
|
|
|
|
# - Cleanup is done in pytest_unconfigure().
|
2023-06-13 03:30:06 +08:00
|
|
|
# - Not a hook wrapper.
|
|
|
|
# So we can add a hook wrapper ourselves to test what it does.
|
2021-10-05 14:36:38 +08:00
|
|
|
pytester.makefile(".ini", pytest="[pytest]\npythonpath=I_SHALL_BE_REMOVED\n")
|
|
|
|
pytester.makepyfile(test_foo="""def test_foo(): pass""")
|
|
|
|
|
|
|
|
before: Optional[List[str]] = None
|
|
|
|
after: Optional[List[str]] = None
|
|
|
|
|
|
|
|
class Plugin:
|
2023-06-13 03:30:06 +08:00
|
|
|
@pytest.hookimpl(wrapper=True, tryfirst=True)
|
2021-10-05 14:36:38 +08:00
|
|
|
def pytest_unconfigure(self) -> Generator[None, None, None]:
|
|
|
|
nonlocal before, after
|
|
|
|
before = sys.path.copy()
|
2023-06-13 03:30:06 +08:00
|
|
|
try:
|
|
|
|
return (yield)
|
|
|
|
finally:
|
|
|
|
after = sys.path.copy()
|
2021-10-05 14:36:38 +08:00
|
|
|
|
|
|
|
result = pytester.runpytest_inprocess(plugins=[Plugin()])
|
|
|
|
assert result.ret == 0
|
|
|
|
|
|
|
|
assert before is not None
|
|
|
|
assert after is not None
|
|
|
|
assert any("I_SHALL_BE_REMOVED" in entry for entry in before)
|
|
|
|
assert not any("I_SHALL_BE_REMOVED" in entry for entry in after)
|