|
1 | 1 | """Unit tests for fit_engine/baseline.py - single and batched baseline evaluation.""" |
| 2 | +import builtins |
| 3 | +from pathlib import Path |
| 4 | + |
2 | 5 | import numpy as np |
3 | 6 | import pytest |
4 | 7 |
|
5 | 8 | from spectroview.fit_engine.baseline import ( |
6 | | - eval_baseline, eval_baseline_batch, get_baseline_method_meta, |
| 9 | + BaselineEvaluationError, eval_baseline, eval_baseline_batch, |
| 10 | + get_baseline_method_meta, |
7 | 11 | ) |
8 | 12 |
|
9 | 13 |
|
@@ -86,6 +90,46 @@ def test_order_capped_by_number_of_points(self, x, linear_y): |
86 | 90 | np.testing.assert_allclose(baseline, linear_y, atol=1e-6) |
87 | 91 |
|
88 | 92 |
|
| 93 | +class TestEvalBaselineAutomatic: |
| 94 | + @pytest.mark.parametrize("mode", ["airpls", "arpls", "asls", "modpoly"]) |
| 95 | + def test_supported_method_returns_a_real_baseline(self, x, mode): |
| 96 | + background = 8.0 + 0.02 * x + 0.0005 * (x - 50.0) ** 2 |
| 97 | + peak = 30.0 * np.exp(-0.5 * ((x - 55.0) / 4.0) ** 2) |
| 98 | + y = background + peak |
| 99 | + config = {"mode": mode, "coef": 5.0, "order_max": 2} |
| 100 | + |
| 101 | + baseline = eval_baseline(x, y, config) |
| 102 | + |
| 103 | + assert baseline.shape == y.shape |
| 104 | + assert np.all(np.isfinite(baseline)) |
| 105 | + assert np.any(baseline != 0) |
| 106 | + |
| 107 | + def test_missing_pybaselines_is_not_silently_treated_as_zero( |
| 108 | + self, x, linear_y, monkeypatch): |
| 109 | + real_import = builtins.__import__ |
| 110 | + |
| 111 | + def reject_pybaselines(name, *args, **kwargs): |
| 112 | + if name == "pybaselines" or name.startswith("pybaselines."): |
| 113 | + raise ModuleNotFoundError("No module named 'pybaselines'") |
| 114 | + return real_import(name, *args, **kwargs) |
| 115 | + |
| 116 | + monkeypatch.setattr(builtins, "__import__", reject_pybaselines) |
| 117 | + |
| 118 | + with pytest.raises(BaselineEvaluationError, match="pybaselines"): |
| 119 | + eval_baseline(x, linear_y, {"mode": "airpls", "coef": 5.0}) |
| 120 | + |
| 121 | + def test_pybaselines_is_declared_as_a_direct_dependency(self): |
| 122 | + project_root = Path(__file__).parents[3] |
| 123 | + pyproject = (project_root / "pyproject.toml").read_text(encoding="utf-8") |
| 124 | + requirements = (project_root / "requirements.txt").read_text(encoding="utf-8") |
| 125 | + |
| 126 | + assert '"pybaselines ' in pyproject |
| 127 | + assert any( |
| 128 | + line.lower().startswith("pybaselines") |
| 129 | + for line in requirements.splitlines() |
| 130 | + ) |
| 131 | + |
| 132 | + |
89 | 133 | class TestEvalBaselineBatchMatchesPerSpectrumLoop: |
90 | 134 | """eval_baseline_batch has a fully-vectorized fast path for Linear and |
91 | 135 | Polynomial; it must match calling eval_baseline() row-by-row.""" |
|
0 commit comments