"""scene/schema.py の load_scene_yaml 関数の仕様ベーステスト.
仕様出典: docs/04_SPEC/SPEC_02_シーン定義仕様.md
モック方針: ファイル I/O はモックせず tmp_path フィクスチャで実ファイルを使う (GUIDE_08)
"""
from pathlib import Path
import pytest
import yaml
from pydantic import ValidationError
from sm_tias_lightsim.scene import SceneConfig, load_scene_yaml
@pytest.fixture
def valid_yaml_content() -> str:
"""load_scene_yaml テスト用の有効な YAML 文字列."""
return """\
name: loader_test_scene
geometry:
meshes:
- id: housing
path: configs/meshes/housing.stl
unit: mm
material: mat_diffuse
materials:
mat_diffuse:
type: diffuse
reflectance: 0.5
lights:
- id: led_top
type: point
position: [0.0, 0.0, 50.0]
intensity: [1.0, 1.0, 1.0]
profile: lambertian
sensor:
type: perspective
position: [0.0, 0.0, 100.0]
target: [0.0, 0.0, 0.0]
up: [0.0, 1.0, 0.0]
fov_deg: 60.0
film:
width: 1920
height: 1080
render:
variant: llvm_ad_rgb
integrator: path
max_depth: 8
spp: 256
"""
def test_load_scene_yaml_returns_scene_config(tmp_path, valid_yaml_content):
"""正常な YAML ファイルから SceneConfig インスタンスが返ること.
仕様: load_scene_yaml(path) -> SceneConfig (SPEC_02 バリデーション節)
"""
yaml_file = tmp_path / "scene.yaml"
yaml_file.write_text(valid_yaml_content, encoding="utf-8")
config = load_scene_yaml(yaml_file)
assert isinstance(config, SceneConfig)
assert config.name == "loader_test_scene"
def test_load_scene_yaml_raises_file_not_found(tmp_path):
"""存在しないパスを渡したとき FileNotFoundError が発生すること.
仕様: FileNotFoundError: ファイルが存在しない場合 (schema.py docstring)
"""
nonexistent = tmp_path / "does_not_exist.yaml"
with pytest.raises(FileNotFoundError):
load_scene_yaml(nonexistent)
def test_load_scene_yaml_raises_yaml_error(tmp_path):
"""不正な YAML 構文のファイルを渡したとき yaml.YAMLError 系の例外が発生すること.
仕様: yaml.YAMLError: YAML 構文エラー (schema.py docstring)
"""
invalid_yaml = tmp_path / "invalid.yaml"
# タブ文字を含む不正な YAML
invalid_yaml.write_text("name: test\n\t invalid_indent: [unclosed", encoding="utf-8")
with pytest.raises(yaml.YAMLError):
load_scene_yaml(invalid_yaml)
def test_load_scene_yaml_raises_validation_error(tmp_path):
"""構文は正しいが必須キーが欠落した YAML で ValidationError が発生すること.
仕様: pydantic.ValidationError: スキーマ違反 (schema.py docstring)
"""
# name が欠落した構文的に正しい YAML
missing_name_yaml = """\
geometry:
meshes:
- id: housing
path: configs/meshes/housing.stl
unit: mm
material: mat_diffuse
materials:
mat_diffuse:
type: diffuse
reflectance: 0.5
lights:
- id: led_top
type: point
position: [0.0, 0.0, 50.0]
intensity: 1.0
profile: lambertian
sensor:
type: perspective
position: [0.0, 0.0, 100.0]
target: [0.0, 0.0, 0.0]
up: [0.0, 1.0, 0.0]
fov_deg: 60.0
film:
width: 1920
height: 1080
"""
yaml_file = tmp_path / "missing_name.yaml"
yaml_file.write_text(missing_name_yaml, encoding="utf-8")
with pytest.raises(ValidationError):
load_scene_yaml(yaml_file)
def test_load_scene_yaml_accepts_pathlib_path(tmp_path, valid_yaml_content):
"""Path オブジェクトを引数として受け付けること.
仕様: load_scene_yaml(path: str | Path) (schema.py シグネチャ)
"""
yaml_file = tmp_path / "scene.yaml"
yaml_file.write_text(valid_yaml_content, encoding="utf-8")
# Path オブジェクトを渡す
config = load_scene_yaml(Path(yaml_file))
assert isinstance(config, SceneConfig)
def test_load_scene_yaml_accepts_str_path(tmp_path, valid_yaml_content):
"""文字列パスを引数として受け付けること.
仕様: load_scene_yaml(path: str | Path) (schema.py シグネチャ)
"""
yaml_file = tmp_path / "scene.yaml"
yaml_file.write_text(valid_yaml_content, encoding="utf-8")
# 文字列パスを渡す
config = load_scene_yaml(str(yaml_file))
assert isinstance(config, SceneConfig)