Newer
Older
SmTIAS-LightSim / tests / scene / test_schema_render_seed.py
"""render.seed(レンダの乱数シード)の仕様ベーステスト.

仕様出典:
- docs/04_SPEC/SPEC_02_シーン定義仕様.md「render」セクション(レンダ設定の YAML スキーマ.
  seed は本実装で追加された新フィールドで,仕様書側は Phase 4 の更新対象)
- docs/02_ENV/ENV_04_開発コマンド.md「研究室サーバでの実行」
  (同じ YAML・同じ seed でも読み込み直すと画素は変わるが,指標はほぼ一致する.
  ノイズの実現を意図的に変えたいときに seed を振る)

検証する仕様:
- seed は省略可能(既定 0)=既存 YAML が書き換え無しで読める(後方互換)
- seed は 0 以上の整数のみ(負値は ValidationError)
- YAML に書いた値がそのまま SceneConfig に載る
"""

from __future__ import annotations

from pathlib import Path

import pytest
import yaml
from pydantic import ValidationError

from sm_tias_lightsim.scene.schema import RenderConfig, SceneConfig, load_scene_yaml

MINIMAL_YAML = Path(__file__).parent.parent / "fixtures" / "minimal.yaml"


# ---------------------------------------------------------------------------
# 既定値・後方互換
# ---------------------------------------------------------------------------


def test_seed_defaults_to_zero():
    """seed を省略すると 0 になること(既定値)."""
    assert RenderConfig().seed == 0


def test_existing_yaml_without_seed_is_still_valid():
    """seed を書いていない既存 YAML がそのまま読め,seed=0 になること(後方互換)."""
    config = load_scene_yaml(MINIMAL_YAML)
    assert config.render.seed == 0


def test_seed_omission_does_not_change_other_render_fields():
    """seed の追加で他の render 設定の既定値が変わっていないこと."""
    render = RenderConfig()
    assert render.variant == "llvm_ad_rgb"
    assert render.integrator == "path"
    assert render.max_depth == 8
    assert render.spp == 256


# ---------------------------------------------------------------------------
# 正常系: 値の指定
# ---------------------------------------------------------------------------


@pytest.mark.parametrize("seed", [0, 1, 2, 42, 12345])
def test_seed_accepts_non_negative_integers(seed):
    """0 以上の整数はそのまま保持されること."""
    assert RenderConfig(seed=seed).seed == seed


def test_seed_from_yaml_is_loaded(tmp_path: Path):
    """YAML の render.seed が SceneConfig に反映されること."""
    data = yaml.safe_load(MINIMAL_YAML.read_text(encoding="utf-8"))
    data["render"]["seed"] = 7
    # メッシュの相対パスを保つため,YAML はフィクスチャと同じディレクトリ基準で解決する
    yaml_path = tmp_path / "scene_with_seed.yaml"
    yaml_path.write_text(yaml.safe_dump(data, allow_unicode=True), encoding="utf-8")
    config = SceneConfig.model_validate(yaml.safe_load(yaml_path.read_text(encoding="utf-8")))
    assert config.render.seed == 7


# ---------------------------------------------------------------------------
# 異常系
# ---------------------------------------------------------------------------


@pytest.mark.parametrize("seed", [-1, -42])
def test_negative_seed_raises_validation_error(seed):
    """負の seed は ValidationError になること(ge=0)."""
    with pytest.raises(ValidationError):
        RenderConfig(seed=seed)


def test_non_integer_seed_raises_validation_error():
    """整数に解釈できない seed は ValidationError になること."""
    with pytest.raises(ValidationError):
        RenderConfig(seed="abc")


def test_float_seed_with_fraction_raises_validation_error():
    """小数の seed は ValidationError になること(整数フィールド)."""
    with pytest.raises(ValidationError):
        RenderConfig(seed=1.5)