"""解耦守卫:评测机(referee)与参赛队实现(mcm26b)互不依赖。 双方只通过附件2 定义的 HTTP 协议耦合;本测试固化三条边界: 1. 运行时零依赖——导入参赛队全部运行时模块不会拉起 referee; 2. 源码零引用——referee 包内不出现任何 mcm26b 字样; 3. 规则不漂移——两侧独立持有的题面常量、字段表、计时模型必须一致 (105/111/194/199 锚点)。 """ import re import subprocess import sys from pathlib import Path from mcm26b.config import ProblemConfig ROOT = Path(__file__).resolve().parents[1] REFEREE_SRC = ROOT / "referee" / "src" / "referee" IMPORT_LINE = re.compile(r"^\s*(from\s+|import\s+)mcm26b\b", re.MULTILINE) def test_runtime_code_never_imports_referee(): code = ( "import sys\n" "import mcm26b, mcm26b.cli, mcm26b.config\n" "import mcm26b.protocol, mcm26b.geometry, mcm26b.belief\n" "import mcm26b.coverage, mcm26b.planner, mcm26b.evaluation\n" "assert 'referee' not in sys.modules, '运行时代码不得导入 referee'\n" "print('clean')\n" ) result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=ROOT) assert result.returncode == 0, result.stderr assert "clean" in result.stdout def test_referee_source_has_no_mcm26b_import(): offenders = [str(path) for path in REFEREE_SRC.rglob("*.py") if IMPORT_LINE.search(path.read_text(encoding="utf-8"))] assert offenders == [], f"referee 包不得依赖参赛队代码: {offenders}" def test_spec_constants_do_not_drift(): from referee import CostSpec as RCost, FieldSpec as RField field, cost = ProblemConfig().field, ProblemConfig().cost rf, rc = RField(), RCost() pairs = [ (field.omega_radius_m, rf.omega_radius_m), (field.channel_min, rf.channel_min), (field.channel_max, rf.channel_max), (field.source_count_min, rf.source_count_min), (field.source_count_max, rf.source_count_max), (field.r_eff_min_m, rf.r_eff_min_m), (field.r_eff_max_m, rf.r_eff_max_m), (field.near_radius_m, rf.near_radius_m), (field.clear_radius_m, rf.clear_radius_m), (field.bearing_error_half_deg, rf.bearing_error_half_deg), (field.bearing_decimals, rf.bearing_decimals), (field.coord_abs_limit_m, rf.coord_abs_limit_m), (field.directional_half_angle_deg, rf.directional_half_angle_deg), (field.max_virtual_duration_s, rf.max_virtual_duration_s), (field.max_real_duration_s, rf.max_real_duration_s), (field.test_window_s, rf.test_window_s), (cost.speed_mps, rc.speed_mps), (cost.measure_s, rc.measure_s), (cost.switch_s, rc.switch_s), (cost.clear_success_s, rc.clear_success_s), (cost.clear_fail_s, rc.clear_fail_s), ] drifted = [(a, b) for a, b in pairs if a != b] assert drifted == [], f"题面常量漂移: {drifted}" def test_rules_tables_do_not_drift(): from mcm26b.protocol import rules as client_rules from referee import rules as server_rules assert set(client_rules.ACTION_FIELDS) == set(server_rules.ACTION_FIELDS) assert set(client_rules.COMMON_FIELDS) == set(server_rules.COMMON_FIELDS) assert set(client_rules.POSITION_FIELDS) == set(server_rules.POSITION_FIELDS) assert client_rules.MEASURE_RESULTS == server_rules.MEASURE_RESULTS assert client_rules.CLEAR_RESULTS == server_rules.CLEAR_RESULTS assert client_rules.EXIT_USER == server_rules.EXIT_USER assert client_rules.BASE_RESPONSE_FIELDS == server_rules.BASE_RESPONSE_FIELDS assert client_rules.STATUS_OK == server_rules.STATUS_OK assert client_rules.STATUS_BAD_REQUEST == server_rules.STATUS_BAD_REQUEST assert client_rules.STATUS_CONFLICT == server_rules.STATUS_CONFLICT def test_timing_models_agree_on_attachment_example(): """同一指令序列在两侧计时模型下总耗时一致(附件示例 105/111/194/199)。""" from mcm26b.protocol.timing import clear_cost as client_clear from mcm26b.protocol.timing import measure_cost as client_measure from referee.timing import clear_cost as server_clear from referee.timing import measure_cost as server_measure steps = [ ("m", (0.0, 0.0), (300.0, 400.0), 1, 1), ("m", (300.0, 400.0), (300.0, 400.0), 1, 2), ("c", (300.0, 400.0), (300.0, 0.0), None, None), ("m", (300.0, 0.0), (300.0, 0.0), 2, 2), ] expected = [105, 6, 83, 5] # 单步耗时(累计 105/111/194/199) for step, total in zip(steps, expected): if step[0] == "m": _, prev, pos, prev_ch, ch = step client = client_measure(prev, pos, prev_ch, ch) server = server_measure(prev, pos, prev_ch, ch) else: _, prev, pos, _, _ = step client = client_clear(prev, pos, success=False) server = server_clear(prev, pos, success=False) assert client.total_us == server.total_us assert client.total_s == total