"""版本隔离守卫:实例目录与内容根必须同级,实例态不得回流进内容根。 权威口径(`mcview/lib/versions.cjs:59/61`): contentRoot = BASE/store/- 共享只读:jar / libraries / assets instanceDir = BASE/instances/ 实例独占:mods / config / options / saves runtimeDir = BASE/runtime/ 实例独占:X display / screenshots / logs 为什么这条值得单独一个文件 -------------------------- 它**不需要 JVM**(纯路径断言,毫秒级),所以必须落在默认测试里 —— 放进行 `--with-render` 才跑的套件等于没守卫(大多数人不会加那个参数)。 ★ 这条曾经真的被踩过:渲染联测把实例写成 `/instances/`, 于是「一个版本的内容根」里长出了实例态。而版本隔离要防的崩因正是 「多实例共用 mods」—— 内层目录会把它重新引回来,而那条路径**看起来完全合法**。 所以检查的不是「路径能不能用」,而是「它违反了哪条分层」。 """ import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) BASE = Path("/home/newqqagent/mcview") STORE = BASE / "store" INSTANCES = BASE / "instances" RUNTIME = BASE / "runtime" # 测试会真正使用的实例(新增测试实例时加到这里,守卫才会管到它) TEST_INSTANCES = ["test-mcfold", "exp-vanilla-1.20.1", "exp-loader-1201"] def test_three_layers_are_siblings(): """三层(store / instances / runtime)是 BASE 下的**兄弟**,不是嵌套。""" for d in (STORE, INSTANCES, RUNTIME): assert d.parent == BASE, f"{d} 应在 {BASE} 直下,实际父目录 {d.parent}" assert STORE != INSTANCES and INSTANCES != RUNTIME print(f" ✓ 三层同级({STORE.name}/ · {INSTANCES.name}/ · {RUNTIME.name}/)") def test_no_instance_state_inside_content_roots(): """★ 内容根里不得出现实例态(instances/ 或独占文件)。 这是本条守卫的核心:内容根是**共享只读**的,一旦有实例独占物落进来, 下一个实例就会读到它 —— 而那正是「共用 mods」类崩因的开端。 """ if not STORE.is_dir(): print(" - 跳过(无 store/)") return offenders = [] for root in sorted(p for p in STORE.iterdir() if p.is_dir()): if (root / "instances").exists(): offenders.append(f"{root}/instances/(内容根里嵌套了实例层)") # 实例独占物(凡出现即说明有实例把内容根当成自己的家) for name in ("mods", "config", "options.txt", "saves"): p = root / name if p.exists(): offenders.append(f"{p}(实例独占物落在内容根)") assert not offenders, "内容根被实例化:\n - " + "\n - ".join(offenders) print(f" ✓ 内容根干净({len(list(STORE.iterdir()))} 个版本目录)") def test_test_instances_live_in_instances_layer(): """测试用的实例目录必须落在 instances/ 层,且不在任何内容根下。""" missing = [] for name in TEST_INSTANCES: p = INSTANCES / name if not p.exists(): missing.append(name) continue assert STORE not in p.parents, f"实例落在了内容根里:{p}" # 缺目录不算失败(测试会自建),但已存在的必须位置正确 print(f" ✓ 测试实例归位({len(TEST_INSTANCES) - len(missing)}/{len(TEST_INSTANCES)} 已建)") def test_content_root_has_no_shared_instance_config(): """内容根里的 options.txt / mods 是「共用」的直接证据,必须为空。 ★ 与上一条的区别:上一条查「存在」,这条查「内容」—— 空目录也可能只是没写进去,所以要看是否真有文件。 """ if not STORE.is_dir(): print(" - 跳过(无 store/)") return bad = [] for root in sorted(p for p in STORE.iterdir() if p.is_dir()): for name in ("mods", "config"): d = root / name if d.is_dir() and any(d.iterdir()): bad.append(f"{d}({len(list(d.iterdir()))} 项)") assert not bad, "共享内容根里有实例配置(会让多实例互相污染):\n - " + "\n - ".join(bad) print(" ✓ 内容根无实例配置") if __name__ == "__main__": tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")] failed = [] for t in tests: try: t() except AssertionError as e: failed.append(f"{t.__name__}: {e}") print(f" ✗ {t.__name__}: {e}") except Exception as e: failed.append(f"{t.__name__}: {e!r}") print(f" ✗ {t.__name__}: {e!r}") print() if failed: print(f"版本隔离守卫: {len(tests)-len(failed)}/{len(tests)} 通过") sys.exit(1) print(f"版本隔离守卫: {len(tests)}/{len(tests)} 全部通过")