"""P1 交付回归:benchmark manifest/真值隔离、纯时间奖励与 project 兼容层。 这些测试不训练神经网络,只验证评测入口和接口契约;重训练属于 smoke 手测。 """ from __future__ import annotations import importlib.util import sys from pathlib import Path import pytest np = pytest.importorskip("numpy") pytest.importorskip("referee") from mcm26b.config import ProblemConfig def _benchmark_module(): path = Path(__file__).parents[1] / "scripts" / "benchmark_hrl.py" spec = importlib.util.spec_from_file_location("benchmark_hrl_p1", path) module = importlib.util.module_from_spec(spec) assert spec.loader is not None sys.modules[spec.name] = module try: spec.loader.exec_module(module) finally: pass return module def test_manifest_roundtrip_and_truth_summary(): module = _benchmark_module() manifest = module.make_default_manifest( problem=3, scenes=2, sources=3, directional=0, start_seed=123, config=ProblemConfig()) scenes = module.scenes_from_manifest(manifest) assert [scene.scene_id for scene in scenes] == ["scene-123", "scene-124"] assert all(scene.truth_channels for scene in scenes) assert all(1 <= channel <= 20 for scene in scenes for channel in scene.truth_channels) def test_benchmark_heuristic_is_valid_and_truth_isolated(tmp_path): module = _benchmark_module() manifest = module.make_default_manifest( problem=3, scenes=1, sources=3, directional=0, start_seed=777, config=ProblemConfig()) scene = module.scenes_from_manifest(manifest)[0] run_dir = tmp_path / "run" result = module.run_scene_variant( scene, "hrl_heuristic", config=ProblemConfig(), hrl_model=None, run_dir=run_dir) row = result["row"] assert row["valid"] is True assert row["cleared_count"] == len(scene.truth_channels) == 3 assert row["cleared_matches_truth"] is True assert row["no_source_declared_absent"] is True assert (run_dir / "session.jsonl").exists() # manifest/run summary 中不出现策略可消费的 simulator.sources 真值对象。 assert "sources" not in row def test_time_only_reward_has_no_per_source_shaping(): from mcm26b.hrl.hierarchical_env import HRLReward reward = HRLReward.time_only() assert reward.clear == 0.0 assert reward.detect == 0.0 assert reward.info_weight == 0.0 assert reward.time_scale == 100.0 assert reward.failure >= 3600.0 @pytest.mark.skipif(importlib.util.find_spec("gymnasium") is None, reason="RL 依赖组未安装") def test_project_compat_layer_reexports_real_classes(): from mcm26b.hrl.hierarchical_env import HierarchicalEnv # project 是仓库根部的兼容层:pytest 经 rootdir 把仓库根放进 sys.path, # 而单独 lint tests/ 时 pylint 的 sys.path 不含仓库根,故此处局部豁免。 # pylint: disable=import-error from project.env.local_env import HierarchicalEnv as CompatEnv from project.env.simulator_client import SimulatorClient from project.policies.high_level_ppo import HighLevelPolicy assert CompatEnv is HierarchicalEnv assert SimulatorClient.__module__ == "mcm26b.protocol.client" assert HighLevelPolicy.__module__ == "mcm26b.hrl.policy"