Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial run of a py.test collector for the existing klippy .test format #503

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
[project]
name = "Kalico"

version = "0.12.1"

requires-python = ">=3.9"
dependencies = [
"cffi==1.15.1",
"greenlet==2.0.2 ; python_version < '3.12'",
"greenlet==3.0.3 ; python_version >= '3.12'",
"Jinja2==3.1.4",
"markupsafe==2.1.5",
"numpy~=2.0 ; python_version=='3.9'",
"numpy~=2.2 ; python_version>='3.10'",
"pyserial==3.4",
"python-can==3.3.4",
]

[project.urls]
homepage = "https://kalico.gg/"
source = "https://github.com/KalicoCrew/kalico"
documentation = "https://docs.kalico.gg/"
issues = "https://github.com/KalicoCrew/kalico/issues"
funding = "https://docs.kalico.gg/Sponsors.html"

[tool.ruff]
line-length = 80
indent-width = 4
exclude = [
".github",
".history",
"config",
"docs",
"lib",
"src"
]
exclude = [".github", ".history", "config", "docs", "lib", "src"]

[tool.ruff.lint]
ignore = [
Expand All @@ -21,3 +39,15 @@ ignore = [
"F841", # Local variable name is assigned to but never used
"E721", # Do not compare types, use 'isinstance()'
]

[tool.uv]
dev-dependencies = [
"pytest-xdist>=3.6.1",
"pytest>=8.3.4",
"ruff>=0.8.4",
]

[tool.pytest.ini_options]
testpaths = [
'test',
]
11 changes: 3 additions & 8 deletions scripts/ci-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ fi
# Verify klippy host software
######################################################################

start_test klippy "Test klippy import (Python3)"
# I'm leaving this with klippy/klippy.py so we test that compatibility
$PYTHON klippy/klippy.py --import-test
finish_test klippy "Test klippy import (Python3)"

start_test klippy "Test invoke klippy (Python3)"
$PYTHON scripts/test_klippy.py -d ${DICTDIR} test/klippy/*.test
finish_test klippy "Test invoke klippy (Python3)"
start_test klippy "py.test suite"
py.test --dictdir "${DICTDIR}"
finish_test klippy "py.test suite"
21 changes: 12 additions & 9 deletions scripts/klippy-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# This file describes the Python virtualenv package requirements for
# the Klipper host software (Klippy). These package requirements are
# typically installed via the command:
# pip install -r klippy-requirements.txt
# This file was autogenerated by uv via the following command:
# uv export -o scripts/klippy-requirements.txt --no-dev --no-hashes
aenum==3.1.15
cffi==1.15.1
greenlet==2.0.2 ; python_full_version < '3.12'
greenlet==3.0.3 ; python_full_version >= '3.12'
jinja2==3.1.4
markupsafe==2.1.5
numpy==2.0.2 ; python_full_version < '3.10'
numpy==2.2.0 ; python_full_version >= '3.10'
pycparser==2.21
pyserial==3.4
greenlet==2.0.2 ; python_version < '3.12'
greenlet==3.0.3 ; python_version >= '3.12'
Jinja2==3.1.4
python-can==3.3.4
markupsafe==2.1.5
numpy==1.26.4
windows-curses==2.4.0 ; platform_system == 'Windows'
wrapt==1.16.0
15 changes: 12 additions & 3 deletions scripts/requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# This file describes the Python virtualenv package requirements for
# klipper CI checks and local development.
ruff==0.7.1
# This file was autogenerated by uv via the following command:
# uv export -o scripts/requirements_dev.txt --only-dev --no-hashes
colorama==0.4.6 ; sys_platform == 'win32'
exceptiongroup==1.2.2 ; python_full_version < '3.11'
execnet==2.1.1
iniconfig==2.0.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.4
pytest-xdist==3.6.1
ruff==0.8.4
tomli==2.2.1 ; python_full_version < '3.11'
157 changes: 157 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from __future__ import annotations

import pytest
import pathlib
import subprocess
import sys
import tempfile
import shutil


@pytest.fixture(autouse=True)
def _ensure_klippy_imports():
root = pathlib.Path().parent.parent.parent
if str(root) not in sys.path:
sys.path.insert(0, str(root))


def pytest_addoption(parser):
parser.addoption("--dictdir", action="store", default="dict")


def pytest_collect_file(parent, file_path):
if file_path.suffix == ".test":
return KlippyTest.from_parent(parent, path=file_path)


class KlippyTest(pytest.File):
def relative_path(self, *parts, root=None):
if not root:
root = self.path.parent
return root.joinpath(*parts).resolve().relative_to(self.path.cwd())

def collect(self):
dict_path = pathlib.Path.cwd() / self.config.getoption("dictdir")

with self.path.open("r", encoding="utf-8") as test_file:
multi_test = False
should_fail = False
gcode = []
config_file = None
dictionaries = []

for line in test_file:
parts = line.strip().split()

if not parts or line.strip().startswith("#"):
continue

elif parts[0] == "SHOULD_FAIL":
should_fail = True

elif parts[0] == "GCODE":
with self.relative_path(parts[1]).open(
"r", encoding="utf-8"
) as fp:
gcode = fp.readlines()

elif parts[0] == "DICTIONARY":
dictionaries = [
str(self.relative_path(parts[1], root=dict_path))
]
for mcu_path in parts[2:]:
mcu, fname = mcu_path.split("=", maxsplit=1)
dictionaries.append(
f"{mcu}={self.relative_path(fname, root=dict_path)}"
)

elif parts[0] == "CONFIG":
if config_file and not multi_test:
multi_test = True
yield KlippyTestItem.from_parent(
self,
name=str(config_file),
gcode=gcode,
config_file=config_file,
dictionaries=list(dictionaries),
should_fail=should_fail,
)

config_file = self.relative_path(parts[1])

if multi_test:
yield KlippyTestItem.from_parent(
self,
name=str(config_file),
gcode=gcode,
config_file=config_file,
dictionaries=list(dictionaries),
should_fail=should_fail,
)

else:
gcode.append(line.strip())

if not multi_test:
yield KlippyTestItem.from_parent(
self,
name=str(config_file),
gcode=gcode,
config_file=config_file,
dictionaries=list(dictionaries),
should_fail=should_fail,
)


class KlippyTestItem(pytest.Item):
def __init__(
self,
*,
config_file: pathlib.Path,
dictionaries: list[str],
gcode: list[str],
should_fail: bool = False,
**kwargs,
):
super().__init__(**kwargs)

self.config_file = config_file
self.dictionaries = dictionaries
self.gcode = gcode

if should_fail:
self.add_marker(pytest.mark.xfail)

def setup(self):
self.tmp_dir = pathlib.Path(tempfile.mkdtemp())

def teardown(self):
shutil.rmtree(self.tmp_dir, ignore_errors=True)

def runtest(self):
gcode_file = self.tmp_dir.joinpath("_test_.gcode")
output_file = self.tmp_dir.joinpath("_test_.output")

gcode_file.write_text("\n".join(self.gcode))

args = [sys.executable, "-m", "klippy", str(self.config_file)]
args.extend(["-i", str(gcode_file)])
args.extend(["-o", str(output_file)])
args.extend(["-v"])

for df in self.dictionaries:
args.extend(["-d", df])

subprocess.run(args, check=True, text=True, stderr=subprocess.STDOUT)

def repr_failure(self, excinfo, style=None):
if isinstance(excinfo.value, subprocess.CalledProcessError):
return "\n".join(
[
f"Error in {self.name}",
f" Config File: {self.config_file}",
f" Dictionaries: {', '.join(self.dictionaries)}",
]
)

return super().repr_failure(excinfo=excinfo, style=style)
Empty file added test/klippy/conftest.py
Empty file.
8 changes: 8 additions & 0 deletions test/test_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest


def test_import_extra():
from klippy import import_test

with pytest.raises(SystemExit):
import_test()
Loading
Loading