"""run_uniformity.py の単体テスト.
仕様(実装サマリー):
- load_roi_config: ROI 設定読み込み
- 正常なキーで ROI 辞書が返る
- 存在しないキーで None が返る
- ファイル不在で FileNotFoundError
テスト方針 (GUIDE_08_テスト方針.md):
- pytest を使用
- tmp_path フィクスチャを利用
"""
import json
from pathlib import Path
import pytest
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
from src.config import load_roi_config
# ---------------------------------------------------------------------------
# load_roi_config テスト
# ---------------------------------------------------------------------------
class TestLoadRoiConfig:
"""load_roi_config 関数のテスト群."""
@pytest.fixture
def roi_config_file(self, tmp_path: Path) -> Path:
"""テスト用 ROI 設定 JSON ファイルを生成して返す fixture."""
config = {
"smtias": {
"whiteboard": {"x": 100, "y": 200, "width": 400, "height": 300}
},
"tias": {
"whiteboard": {"x": 50, "y": 80, "width": 600, "height": 400}
},
}
config_path = tmp_path / "roi_config.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
return config_path
def test_returns_roi_dict_for_valid_key(self, roi_config_file: Path) -> None:
"""正常: 存在するキーで ROI 辞書が返ること."""
result = load_roi_config(str(roi_config_file), "smtias.whiteboard")
assert result == {"x": 100, "y": 200, "width": 400, "height": 300}
def test_returns_roi_dict_for_another_valid_key(self, roi_config_file: Path) -> None:
"""正常: 別の存在するキーで ROI 辞書が返ること."""
result = load_roi_config(str(roi_config_file), "tias.whiteboard")
assert result == {"x": 50, "y": 80, "width": 600, "height": 400}
def test_returns_none_for_nonexistent_key(self, roi_config_file: Path) -> None:
"""正常: 存在しないキーで None が返ること."""
result = load_roi_config(str(roi_config_file), "nonexistent.key")
assert result is None
def test_returns_none_for_partial_key(self, roi_config_file: Path) -> None:
"""正常: ネストの途中で存在しないキーでも None が返ること."""
result = load_roi_config(str(roi_config_file), "smtias.nonexistent")
assert result is None
def test_raises_file_not_found_for_missing_config(self, tmp_path: Path) -> None:
"""異常: 設定ファイルが存在しない場合に FileNotFoundError が送出されること."""
missing_path = str(tmp_path / "no_such_config.json")
with pytest.raises(FileNotFoundError):
load_roi_config(missing_path, "smtias.whiteboard")
def test_file_not_found_error_contains_path(self, tmp_path: Path) -> None:
"""異常: FileNotFoundError のメッセージにファイルパスが含まれること."""
missing_path = str(tmp_path / "missing_roi.json")
with pytest.raises(FileNotFoundError, match="missing_roi.json"):
load_roi_config(missing_path, "smtias.whiteboard")
def test_returns_dict_type_for_valid_key(self, roi_config_file: Path) -> None:
"""正常: 戻り値が dict 型であること."""
result = load_roi_config(str(roi_config_file), "smtias.whiteboard")
assert isinstance(result, dict)
def test_returned_roi_has_required_keys(self, roi_config_file: Path) -> None:
"""正常: 返された ROI が x, y, width, height キーを持つこと."""
result = load_roi_config(str(roi_config_file), "smtias.whiteboard")
assert result is not None
for key in ("x", "y", "width", "height"):
assert key in result, f"ROI に '{key}' キーが存在しない"
def test_returns_none_for_empty_dot_split_key(self, tmp_path: Path) -> None:
"""エッジケース: 設定に存在しないトップレベルキーで None が返ること."""
config = {"smtias": {"whiteboard": {"x": 0, "y": 0, "width": 10, "height": 10}}}
config_path = tmp_path / "config.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
result = load_roi_config(str(config_path), "unknown_top.sub")
assert result is None
def test_returns_none_when_leaf_value_is_not_dict(self, tmp_path: Path) -> None:
"""エッジケース: 指定キーの値が dict でない場合に None が返ること."""
# キーの値が辞書ではなくスカラー値の場合
config = {"smtias": {"whiteboard": "not_a_dict"}}
config_path = tmp_path / "config.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
result = load_roi_config(str(config_path), "smtias.whiteboard")
assert result is None