87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
"""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"
|