"""sm-evaluate の --no-figures オプションの仕様ベーステスト(統合テスト).
仕様出典:
- docs/04_SPEC/SPEC_04_評価仕様.md「出力ファイル仕様」(既定は metrics.csv + figures/ 4 種)
- docs/02_ENV/ENV_04_開発コマンド.md(sm-evaluate は PNG / luminance.npy を評価し
metrics.csv + figures/ を出力する)
- 実装サマリー: --no-figures を付けると figures/ を作らず描画もせず,
サマリーの figures/ 行も出さない(指標だけ欲しいスイープ用)
検証する仕様:
- --no-figures なしは従来どおり(後方互換): metrics.csv + figures/ 4 種
- --no-figures ありは metrics.csv のみ.終了コードは 0
- どちらでも metrics.csv の内容は同一(描画は指標に影響しない)
テスト方針(testing.md): subprocess ではなく main() を直接呼ぶ.tmp_path で実ファイルを使う.
"""
from __future__ import annotations
import matplotlib # noqa: I001
# headless 環境でも動くよう Agg バックエンドを pyplot より前に設定する
matplotlib.use("Agg")
from pathlib import Path
import numpy
import pytest
from sm_tias_lightsim.cli.evaluate import main
_FIGURE_FILES = ("luminance_map.png", "histogram.png", "radial_profile.png", "zone_map.png")
def _make_inputs(tmp_path: Path, seed: int = 0) -> tuple[Path, Path]:
"""合成輝度 .npy と評価 YAML を書き出してパスを返す(seed 固定)."""
rng = numpy.random.RandomState(seed)
luminance = numpy.full((128, 128), 100.0) + rng.normal(0, 5, (128, 128))
npy_path = tmp_path / "luminance.npy"
numpy.save(npy_path, luminance)
config_path = tmp_path / "eval_config.yaml"
config_path.write_text(
"""\
roi:
type: image_pixel
x: 16
y: 16
width: 96
height: 96
zones:
enabled: true
boundaries: [0.33, 0.66]
metrics:
cov: true
std: true
max_min: true
radial_profile: true
""",
encoding="utf-8",
)
return npy_path, config_path
def _run(npy: Path, config: Path, out_dir: Path, *extra: str) -> int:
return main(
["--image", str(npy), "--config", str(config), "--output", str(out_dir), *extra]
)
def test_no_figures_returns_0(tmp_path: Path):
"""--no-figures 付きでも正常終了(0)すること."""
npy, config = _make_inputs(tmp_path, seed=0)
assert _run(npy, config, tmp_path / "out", "--no-figures") == 0
def test_no_figures_creates_metrics_csv(tmp_path: Path):
"""--no-figures でも metrics.csv は出力されること."""
npy, config = _make_inputs(tmp_path, seed=1)
out_dir = tmp_path / "out"
_run(npy, config, out_dir, "--no-figures")
assert (out_dir / "metrics.csv").is_file()
def test_no_figures_does_not_create_figures_dir(tmp_path: Path):
"""--no-figures のとき figures/ ディレクトリが作られないこと."""
npy, config = _make_inputs(tmp_path, seed=2)
out_dir = tmp_path / "out"
_run(npy, config, out_dir, "--no-figures")
assert not (out_dir / "figures").exists()
def test_no_figures_writes_no_png(tmp_path: Path):
"""--no-figures のとき PNG が 1 つも出力されないこと."""
npy, config = _make_inputs(tmp_path, seed=3)
out_dir = tmp_path / "out"
_run(npy, config, out_dir, "--no-figures")
assert list(out_dir.rglob("*.png")) == []
def test_no_figures_summary_has_no_figures_line(tmp_path: Path, capsys):
"""--no-figures のとき標準出力のサマリーに figures/ 行が出ないこと."""
npy, config = _make_inputs(tmp_path, seed=4)
_run(npy, config, tmp_path / "out", "--no-figures")
out = capsys.readouterr().out
assert "=== 均一性評価サマリー ===" in out
assert "metrics.csv" in out
assert "figures/" not in out
def test_default_still_creates_figures(tmp_path: Path):
"""--no-figures を付けなければ従来どおり figures/ 4 種が出ること(後方互換)."""
npy, config = _make_inputs(tmp_path, seed=5)
out_dir = tmp_path / "out"
assert _run(npy, config, out_dir) == 0
for name in _FIGURE_FILES:
assert (out_dir / "figures" / name).is_file(), name
def test_metrics_csv_identical_with_and_without_figures(tmp_path: Path):
"""--no-figures の有無で metrics.csv の内容が一致すること."""
npy, config = _make_inputs(tmp_path, seed=6)
with_dir = tmp_path / "with"
without_dir = tmp_path / "without"
_run(npy, config, with_dir)
_run(npy, config, without_dir, "--no-figures")
assert (with_dir / "metrics.csv").read_text(encoding="utf-8") == (
without_dir / "metrics.csv"
).read_text(encoding="utf-8")
def test_help_mentions_no_figures(capsys):
"""--help に --no-figures が説明されていること."""
with pytest.raises(SystemExit) as exc:
main(["--help"])
assert exc.value.code == 0
assert "--no-figures" in capsys.readouterr().out
def test_no_figures_error_path_still_returns_1(tmp_path: Path, capsys):
"""--no-figures 付きでも異常時は「エラー: 」を stderr に出して 1 を返すこと."""
_, config = _make_inputs(tmp_path, seed=7)
result = _run(tmp_path / "missing.npy", config, tmp_path / "out", "--no-figures")
assert result == 1
assert "エラー: " in capsys.readouterr().err