Skip to content

Commit

Permalink
Upgrade to using PyTest 7 (#738)
Browse files Browse the repository at this point in the history
Fixes #660
  • Loading branch information
chrisrink10 authored Dec 27, 2023
1 parent 67188c4 commit 190edf1
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added rudimentary support for `clojure.stacktrace` with `print-cause-trace` (part of #721)
* Added support for `bytes` literals using a `#b` prefix (#732)

### Changed
* Basilisp now supports PyTest 7.0+ (#660)

### Fixed
* Fix issue with `case` evaluating all of its clauses expressions (#699)
* Fix issue with relative paths dropping their first character on MS-Windows (#703)
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ python-dateutil = "^2.8.1"
readerwriterlock = "^1.0.8"

astor = { version = "^0.8.1", python = "<3.9", optional = true }
pytest = { version = "^6.2.5", optional = true }
pytest = { version = "^7.0.0", optional = true }
pygments = { version = "^2.9.0", optional = true }

[tool.poetry.dev-dependencies]
black = "*"
docutils = "*"
isort = "*"
pygments = "*"
pytest = "^6.2.5"
pytest = "^7.0.0"
pytest-pycharm = "*"
sphinx = "*"
sphinx-rtd-theme = "*"
Expand Down
76 changes: 53 additions & 23 deletions src/basilisp/contrib/pytest/testrunner.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import importlib.util
import inspect
import os
import traceback
from pathlib import Path
from types import GeneratorType
from typing import Callable, Iterable, Iterator, Optional, Tuple

import py
import pytest
from _pytest.config import Config
from _pytest.main import Session

from basilisp import main as basilisp
from basilisp.lang import keyword as kw
Expand All @@ -26,11 +26,11 @@ def pytest_configure(config):
basilisp.bootstrap("basilisp.test")


def pytest_collect_file(parent, path):
def pytest_collect_file(file_path: Path, path, parent):
"""Primary PyTest hook to identify Basilisp test files."""
if path.ext == ".lpy":
if path.basename.startswith("test_") or path.purebasename.endswith("_test"):
return BasilispFile.from_parent(parent, fspath=path)
if file_path.suffix == ".lpy":
if file_path.name.startswith("test_") or file_path.name.endswith("_test"):
return BasilispFile.from_parent(parent, fspath=path, path=file_path)
return None


Expand Down Expand Up @@ -135,18 +135,43 @@ def teardown(self) -> None:
self._teardowns = ()


def _is_package(path: Path) -> bool:
"""Return `True` if the given path refers to a Python or Basilisp package."""
_, _, files = next(os.walk(path))
for file in files:
if file in {"__init__.lpy", "__init__.py"} or file.endswith(".lpy"):
return True
return False


def _get_fully_qualified_module_name(file: Path) -> str:
"""Return the fully qualified module name (from the import root) for a module given
its location.
This works by traversing up the filesystem looking for the top-most package. From
there, we derive a Python module name referring to the given module path."""
top = None
for p in file.parents:
if _is_package(p):
top = p
else:
break

if top is None or top == file.parent:
return file.stem

root = top.parent
elems = list(file.with_suffix("").relative_to(root).parts)
if elems[-1] == "__init__":
elems.pop()
return ".".join(elems)


class BasilispFile(pytest.File):
"""Files represent a test module in Python or a test namespace in Basilisp."""

def __init__( # pylint: disable=too-many-arguments
self,
fspath: py.path.local,
parent=None,
config: Optional[Config] = None,
session: Optional["Session"] = None,
nodeid: Optional[str] = None,
) -> None:
super().__init__(fspath, parent, config, session, nodeid)
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self._fixture_manager: Optional[FixtureManager] = None

@staticmethod
Expand Down Expand Up @@ -192,16 +217,21 @@ def teardown(self) -> None:
assert self._fixture_manager is not None
self._fixture_manager.teardown()

def _import_module(self) -> runtime.BasilispModule:
modname = _get_fully_qualified_module_name(self.path)
module = importlib.import_module(modname)
assert isinstance(module, runtime.BasilispModule)
return module

def collect(self):
"""Collect all of the tests in the namespace (module) given.
"""Collect all tests from the namespace (module) given.
Basilisp's test runner imports the namespace which will (as a side effect)
collect all of the test functions in a namespace (represented by `deftest`
forms in Basilisp). BasilispFile.collect fetches those test functions and
generates BasilispTestItems for PyTest to run the tests."""
filename = self.fspath.basename
module = self.fspath.pyimport()
assert isinstance(module, runtime.BasilispModule)
collect the test functions in a namespace (represented by `deftest` forms in
Basilisp). BasilispFile.collect fetches those test functions and generates
BasilispTestItems for PyTest to run the tests."""
filename = self.path.name
module = self._import_module()
ns = module.__basilisp_namespace__
once_fixtures, each_fixtures = self._collected_fixtures(ns)
self._fixture_manager = FixtureManager(once_fixtures)
Expand Down
10 changes: 4 additions & 6 deletions tests/basilisp/importer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from unittest.mock import patch

import pytest
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import Testdir

from basilisp import importer as importer
from basilisp.lang import runtime as runtime
Expand All @@ -35,7 +33,7 @@ def test_hook_imports():
assert 1 == importer_counter()


def test_demunged_import(testdir: Testdir):
def test_demunged_import(pytester: pytest.Pytester):
with TemporaryDirectory() as tmpdir:
tmp_module = os.path.join(
tmpdir, "long__AMP__namespace_name__PLUS__with___LT__punctuation__GT__.lpy"
Expand Down Expand Up @@ -85,19 +83,19 @@ def _ns_and_module(filename: str) -> Tuple[str, str]:

class TestImporter:
@pytest.fixture
def do_cache_namespaces(self, monkeypatch):
def do_cache_namespaces(self, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv(importer._NO_CACHE_ENVVAR, "false")

@pytest.fixture
def do_not_cache_namespaces(self, monkeypatch):
def do_not_cache_namespaces(self, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv(importer._NO_CACHE_ENVVAR, "true")

@pytest.fixture
def module_cache(self):
return {name: module for name, module in sys.modules.items()}

@pytest.fixture
def module_dir(self, monkeypatch: MonkeyPatch, module_cache):
def module_dir(self, monkeypatch: pytest.MonkeyPatch, module_cache):
with TemporaryDirectory() as tmpdir:
cwd = os.getcwd()
monkeypatch.chdir(tmpdir)
Expand Down
3 changes: 3 additions & 0 deletions tests/basilisp/testrunner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def run_result(self, pytester: Pytester) -> RunResult:
(ex-info "This test will count as an error." {})))
"""
pytester.makefile(".lpy", test_testrunner=code)
pytester.syspathinsert()
yield pytester.runpytest()
runtime.Namespace.remove(sym.symbol("test-testrunner"))

Expand Down Expand Up @@ -165,6 +166,7 @@ def test_fixtures(pytester: Pytester):
(is false))
"""
pytester.makefile(".lpy", test_fixtures=code)
pytester.syspathinsert()
result: pytester.RunResult = pytester.runpytest()
result.assert_outcomes(passed=1, failed=1)

Expand Down Expand Up @@ -216,5 +218,6 @@ def test_fixtures_with_errors(
(is false))
"""
pytester.makefile(".lpy", test_fixtures_with_errors=code)
pytester.syspathinsert()
result: pytester.RunResult = pytester.runpytest()
result.assert_outcomes(passed=passes, failed=failures, errors=errors)
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ setenv =
BASILISP_DO_NOT_CACHE_NAMESPACES = true
deps =
coverage[toml]
pytest~=6.2.5
pytest >=7.0.0,<8.0.0
pygments
commands =
coverage run \
Expand Down

0 comments on commit 190edf1

Please sign in to comment.