Skip to content
Draft
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
2 changes: 2 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from ml_peg import models

pytest_plugins = ["pytester"]


def pytest_addoption(parser):
"""Add flag to run tests for extra MLIPs."""
Expand Down
111 changes: 111 additions & 0 deletions ml_peg/analysis/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Configure pytest for analysis."""

from __future__ import annotations

import pytest
from pytest import CallInfo, Item, Parser

from ml_peg.calcs.utils import completion


def pytest_addoption(parser: Parser) -> None:
"""
Add custom CLI inputs to pytest.

Parameters
----------
parser
Pytest parser object.
"""
parser.addoption(
"--force-analysis",
action="store_true",
default=False,
help="Run analysis even if it previously completed",
)


def pytest_runtest_setup(item: Item) -> None:
"""
Skip analysis that previously completed with identical inputs.

Unlike calculations, analysis aggregates all models into shared outputs,
so completion is tracked per test rather than per model. Modules missing
any of MODELS, CALC_PATH or OUT_PATH always run and keep no marker.

Parameters
----------
item
Pytest test item.
"""
module = getattr(item, "module", None)
models = getattr(module, "MODELS", None)
calc_path = getattr(module, "CALC_PATH", None)
out_path = getattr(module, "OUT_PATH", None)
if models is None or calc_path is None or out_path is None:
return

completion.clear_data_files()
item._analysis_fingerprint = completion.analysis_fingerprint(
item.path.parent, models, calc_path
)
item._analysis_out_path = out_path

# Empty model name: analysis markers live directly in OUT_PATH, not per model
if not item.config.getoption("--force-analysis") and completion.is_complete(
out_path, "", item.name, item._analysis_fingerprint
):
pytest.skip("Analysis previously completed. Use --force-analysis to re-run.")

# The analysis will run: drop any previous marker entry so a failed or
# interrupted run cannot leave outputs masked as complete
completion.unmark_complete(out_path, "", item.name)


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item: Item, call: CallInfo):
"""
Record whether the test call phase passed.

Needed as pytest does not expose the test outcome to
pytest_runtest_teardown, which must only mark analysis as completed if
the test passed.

Parameters
----------
item
Pytest test item.
call
Result of the test phase that just ran.

Yields
------
Result
Hook wrapper result holding the test report.
"""
outcome = yield
report = outcome.get_result()
if report.when == "call" and report.passed:
item._analysis_passed = True


def pytest_runtest_teardown(item: Item) -> None:
"""
Mark completed analysis.

Parameters
----------
item
Pytest test item.
"""
fingerprint = getattr(item, "_analysis_fingerprint", None)
if fingerprint is None or not getattr(item, "_analysis_passed", False):
return

completion.mark_complete(
item._analysis_out_path,
"",
item.name,
fingerprint,
completion.used_data_files(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -973,8 +973,8 @@ def run_iron_properties(model_name: str, model: Any) -> None:


@pytest.mark.slow
@pytest.mark.parametrize("model_name", MODELS)
def test_iron_properties(model_name: str) -> None:
@pytest.mark.parametrize("mlip", MODELS.items())
def test_iron_properties(mlip: tuple[str, Any]) -> None:
"""
Run iron properties benchmark for each registered model.

Expand All @@ -983,7 +983,8 @@ def test_iron_properties(model_name: str) -> None:

Parameters
----------
model_name
Name of the model to evaluate.
mlip
Name of the model to evaluate and the model itself.
"""
run_iron_properties(model_name, MODELS[model_name])
model_name, model = mlip
run_iron_properties(model_name, model)
Loading