diff --git a/tests/doctor/devenv/checks/bad_check.py b/tests/doctor/checks/bad_check.py similarity index 100% rename from tests/doctor/devenv/checks/bad_check.py rename to tests/doctor/checks/bad_check.py diff --git a/tests/doctor/devenv/checks/bad_fix.py b/tests/doctor/checks/bad_fix.py similarity index 100% rename from tests/doctor/devenv/checks/bad_fix.py rename to tests/doctor/checks/bad_fix.py diff --git a/tests/doctor/devenv/checks/broken_check.py b/tests/doctor/checks/broken_check.py similarity index 100% rename from tests/doctor/devenv/checks/broken_check.py rename to tests/doctor/checks/broken_check.py diff --git a/tests/doctor/devenv/checks/broken_fix.py b/tests/doctor/checks/broken_fix.py similarity index 100% rename from tests/doctor/devenv/checks/broken_fix.py rename to tests/doctor/checks/broken_fix.py diff --git a/tests/doctor/devenv/checks/failing_check.py b/tests/doctor/checks/failing_check.py similarity index 100% rename from tests/doctor/devenv/checks/failing_check.py rename to tests/doctor/checks/failing_check.py diff --git a/tests/doctor/devenv/checks/failing_check_with_msg.py b/tests/doctor/checks/failing_check_with_msg.py similarity index 100% rename from tests/doctor/devenv/checks/failing_check_with_msg.py rename to tests/doctor/checks/failing_check_with_msg.py diff --git a/tests/doctor/devenv/checks/no_check.py b/tests/doctor/checks/no_check.py similarity index 100% rename from tests/doctor/devenv/checks/no_check.py rename to tests/doctor/checks/no_check.py diff --git a/tests/doctor/devenv/checks/no_name.py b/tests/doctor/checks/no_name.py similarity index 100% rename from tests/doctor/devenv/checks/no_name.py rename to tests/doctor/checks/no_name.py diff --git a/tests/doctor/devenv/checks/no_tags.py b/tests/doctor/checks/no_tags.py similarity index 100% rename from tests/doctor/devenv/checks/no_tags.py rename to tests/doctor/checks/no_tags.py diff --git a/tests/doctor/devenv/checks/passing_check.py b/tests/doctor/checks/passing_check.py similarity index 100% rename from tests/doctor/devenv/checks/passing_check.py rename to tests/doctor/checks/passing_check.py diff --git a/tests/doctor/test_attempt_fix.py b/tests/doctor/test_attempt_fix.py index 8cf749cd..c7967ee0 100644 --- a/tests/doctor/test_attempt_fix.py +++ b/tests/doctor/test_attempt_fix.py @@ -1,25 +1,37 @@ from __future__ import annotations +import os from concurrent.futures import ThreadPoolExecutor from devenv import doctor -from tests.doctor.devenv.checks import broken_fix -from tests.doctor.devenv.checks import failing_check -from tests.doctor.devenv.checks import failing_check_with_msg -from tests.doctor.devenv.checks import passing_check +from tests.utils import import_module_from_file + +here = os.path.join(os.path.dirname(__file__)) def test_attempt_fix_success() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + check = doctor.Check(passing_check) assert doctor.attempt_fix(check, ThreadPoolExecutor()) == (True, "") def test_attempt_fix_failure() -> None: + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + check = doctor.Check(failing_check) assert doctor.attempt_fix(check, ThreadPoolExecutor()) == (False, "") def test_attempt_fix_failure_with_msg() -> None: + failing_check_with_msg = import_module_from_file( + f"{here}/checks/failing_check_with_msg.py", "failing_check_with_msg" + ) + check = doctor.Check(failing_check_with_msg) assert doctor.attempt_fix(check, ThreadPoolExecutor()) == ( False, @@ -28,6 +40,10 @@ def test_attempt_fix_failure_with_msg() -> None: def test_attempt_fix_broken_fix() -> None: + broken_fix = import_module_from_file( + f"{here}/checks/broken_fix.py", "broken_fix" + ) + check = doctor.Check(broken_fix) assert doctor.attempt_fix(check, ThreadPoolExecutor()) == ( False, diff --git a/tests/doctor/test_filter_failing_checks.py b/tests/doctor/test_filter_failing_checks.py index 92a68d38..cb7cc06f 100644 --- a/tests/doctor/test_filter_failing_checks.py +++ b/tests/doctor/test_filter_failing_checks.py @@ -1,8 +1,11 @@ from __future__ import annotations +import os + from devenv import doctor -from tests.doctor.devenv.checks import failing_check -from tests.doctor.devenv.checks import passing_check +from tests.utils import import_module_from_file + +here = os.path.join(os.path.dirname(__file__)) def test_filter_failing_checks_no_checks() -> None: @@ -10,16 +13,31 @@ def test_filter_failing_checks_no_checks() -> None: def test_filter_failing_checks_one_passing_check() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + check = doctor.Check(passing_check) assert doctor.filter_failing_checks({check: (True, "")}) == [] def test_filter_failing_checks_one_failing_check() -> None: + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + check = doctor.Check(failing_check) assert doctor.filter_failing_checks({check: (False, "")}) == [check] def test_filter_failing_checks_one_passing_and_one_failing_check() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + first_check = doctor.Check(passing_check) second_check = doctor.Check(failing_check) assert doctor.filter_failing_checks( @@ -28,6 +46,13 @@ def test_filter_failing_checks_one_passing_and_one_failing_check() -> None: def test_filter_failing_checks_no_duplicate_checks() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + first_check = doctor.Check(passing_check) second_check = doctor.Check(failing_check) assert doctor.filter_failing_checks( diff --git a/tests/doctor/test_prompt_for_fix.py b/tests/doctor/test_prompt_for_fix.py index 73e84970..c90b21aa 100644 --- a/tests/doctor/test_prompt_for_fix.py +++ b/tests/doctor/test_prompt_for_fix.py @@ -1,12 +1,15 @@ from __future__ import annotations import builtins +import os from unittest import mock import pytest from devenv import doctor -from tests.doctor.devenv.checks import passing_check +from tests.utils import import_module_from_file + +here = os.path.join(os.path.dirname(__file__)) prompts_for_fix_yes: list[str] = [ @@ -35,6 +38,10 @@ @pytest.mark.parametrize("yes", prompts_for_fix_yes) def test_prompt_for_fix_yes(yes: str) -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + check = doctor.Check(passing_check) def fake_input(_: str) -> str: @@ -46,6 +53,10 @@ def fake_input(_: str) -> str: @pytest.mark.parametrize("no", prompts_for_fix_no) def test_prompt_for_fix_no(no: str) -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + check = doctor.Check(passing_check) def fake_input(_: str) -> str: diff --git a/tests/doctor/test_run_checks.py b/tests/doctor/test_run_checks.py index 8e8f4c97..c2cceb1c 100644 --- a/tests/doctor/test_run_checks.py +++ b/tests/doctor/test_run_checks.py @@ -1,14 +1,14 @@ from __future__ import annotations +import os from concurrent.futures import ThreadPoolExecutor import pytest from devenv import doctor -from tests.doctor.devenv.checks import broken_check -from tests.doctor.devenv.checks import failing_check -from tests.doctor.devenv.checks import failing_check_with_msg -from tests.doctor.devenv.checks import passing_check +from tests.utils import import_module_from_file + +here = os.path.join(os.path.dirname(__file__)) def test_run_checks_no_checks() -> None: @@ -16,6 +16,10 @@ def test_run_checks_no_checks() -> None: def test_run_checks_one_passing_check() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + check = doctor.Check(passing_check) assert doctor.run_checks([check], ThreadPoolExecutor()) == { check: (True, "") @@ -23,6 +27,10 @@ def test_run_checks_one_passing_check() -> None: def test_run_checks_one_failing_check() -> None: + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + check = doctor.Check(failing_check) assert doctor.run_checks([check], ThreadPoolExecutor()) == { check: (False, "") @@ -30,6 +38,10 @@ def test_run_checks_one_failing_check() -> None: def test_run_checks_one_failing_check_with_msg() -> None: + failing_check_with_msg = import_module_from_file( + f"{here}/checks/failing_check_with_msg.py", "failing_check_with_msg" + ) + check = doctor.Check(failing_check_with_msg) assert doctor.run_checks([check], ThreadPoolExecutor()) == { check: (False, "check failed") @@ -37,6 +49,13 @@ def test_run_checks_one_failing_check_with_msg() -> None: def test_run_checks_one_passing_and_one_failing_check() -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + first_check = doctor.Check(passing_check) second_check = doctor.Check(failing_check) assert doctor.run_checks( @@ -45,6 +64,13 @@ def test_run_checks_one_passing_and_one_failing_check() -> None: def test_run_checks_skip(capsys: pytest.CaptureFixture[str]) -> None: + passing_check = import_module_from_file( + f"{here}/checks/passing_check.py", "passing_check" + ) + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + first_check = doctor.Check(passing_check) second_check = doctor.Check(failing_check) assert doctor.run_checks( @@ -55,6 +81,13 @@ def test_run_checks_skip(capsys: pytest.CaptureFixture[str]) -> None: def test_run_checks_multiple_failing_checks() -> None: + failing_check = import_module_from_file( + f"{here}/checks/failing_check.py", "failing_check" + ) + failing_check_with_msg = import_module_from_file( + f"{here}/checks/failing_check_with_msg.py", "failing_check_with_msg" + ) + first_check = doctor.Check(failing_check) second_check = doctor.Check(failing_check_with_msg) assert doctor.run_checks( @@ -63,6 +96,10 @@ def test_run_checks_multiple_failing_checks() -> None: def test_run_checks_broken_check() -> None: + broken_check = import_module_from_file( + f"{here}/checks/broken_check.py", "broken_check" + ) + check = doctor.Check(broken_check) assert doctor.run_checks([check], ThreadPoolExecutor()) == { check: (False, "Check threw a runtime exception: division by zero") diff --git a/tests/utils.py b/tests/utils.py index 8bd41533..2a865470 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,9 +1,11 @@ from __future__ import annotations import contextlib +import importlib import os import pathlib from collections.abc import Generator +from types import ModuleType # TODO: replace with contextlib.chdir when we can use python3.11 @@ -15,3 +17,10 @@ def chdir(d: str | pathlib.Path) -> Generator[None, None, None]: yield finally: os.chdir(curdir) + + +def import_module_from_file(fp: str, name: str) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, fp) + module = importlib.util.module_from_spec(spec) # type: ignore + spec.loader.exec_module(module) # type: ignore + return module