# -*- coding: utf-8 -*- """验证回退(撤销)系统。 要点: * 可回退的操作执行后会自动进回退列表 * 回退能把状态真的改回去 * **不可回退的操作(关机、删文件、杀进程)不该被记录** —— 记了会给用户"还能退回去"的错觉,反而危险 """ import sys import tempfile import time from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) from lanremote.actions import Actions # noqa: E402 from lanremote.config import Store # noqa: E402 from lanremote.features import Features # noqa: E402 from lanremote.headless import HeadlessUI # noqa: E402 passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + " " + str(detail)) def main() -> int: store = Store(Path(tempfile.mkdtemp())) ui = HeadlessUI(store) features = Features(Actions(ui, store)) ui.root print("一、三个回退功能都在") defs = features.defs() names = {d["name"] for d in defs} for required in ("undo_last", "undo_all", "undo_list"): check("声明了 " + required, required in names) check("功能总数 > 200", len(defs) > 200, len(defs)) print() print("二、初始没有可回退的") check("undo_list 说没有", "没有" in features.run("undo_list", {}), features.run("undo_list", {})) check("undo_last 说没有", "没有" in features.run("undo_last", {}), features.run("undo_last", {})) print() print("三、做几个可回退的操作") features.run("shuffle_play", {"on": True}) check("开关被记为可回退", len(features._undo_stack()) == 1, len(features._undo_stack())) features.run("loop_play", {"on": True}) features.run("big_text", {"text": "回退测试"}) check("三步都记上了", len(features._undo_stack()) == 3, len(features._undo_stack())) listing = features.run("undo_list", {}) check("undo_list 列了三步", "3 步" in listing, listing) check("看得到操作描述", "回退测试" in listing or "随机播放" in listing, listing) print() print("四、开关的状态确实是开着的,回退要能关掉") check("shuffle_play 现在是开的", store.get("shuffle_play") is True, store.get("shuffle_play")) check("loop_play 现在是开的", store.get("loop_play") is True, store.get("loop_play")) # 最近一步是 big_text,先退它 result = features.run("undo_last", {}) check("回退上一步成功", "已回退" in result, result) check("回退后列表少一条", len(features._undo_stack()) == 2, len(features._undo_stack())) # 再退两步,应该把两个开关关掉 features.run("undo_last", {}) features.run("undo_last", {}) check("shuffle_play 被关回去了", store.get("shuffle_play") is False, store.get("shuffle_play")) check("loop_play 被关回去了", store.get("loop_play") is False, store.get("loop_play")) check("列表清空了", len(features._undo_stack()) == 0, len(features._undo_stack())) print() print("五、undo_all 批量回退") features.run("shuffle_play", {"on": True}) features.run("loop_play", {"on": True}) features.run("big_text", {"text": "批量测试"}) check("攒了 3 步", len(features._undo_stack()) == 3, len(features._undo_stack())) result = features.run("undo_all", {}) check("undo_all 说回退了 3 步", "3 步" in result, result) check("开关都回去了", store.get("shuffle_play") is False and store.get("loop_play") is False, f"{store.get('shuffle_play')}/{store.get('loop_play')}") check("列表空了", len(features._undo_stack()) == 0, len(features._undo_stack())) print() print("六、撤不回来的操作不该被记录") # 这些都是破坏性的 / 不可逆的 for name in ("kill_games", "clean_temp", "lock_now", "net_reset"): try: features.run(name, {}) except Exception: # noqa: BLE001 pass check("破坏性操作没有进回退列表", len(features._undo_stack()) == 0, len(features._undo_stack())) check("IRREVERSIBLE 里有 kill_games", "kill_games" in Features.IRREVERSIBLE) check("IRREVERSIBLE 里有 delete_file", "delete_file" in Features.IRREVERSIBLE) check("IRREVERSIBLE 里有 shutdown", "shutdown" in Features.IRREVERSIBLE) print() print("七、回退不会来回横跳") features.run("shuffle_play", {"on": True}) stack_before = len(features._undo_stack()) features.run("undo_last", {}) check("回退后不会又记一条新的", len(features._undo_stack()) == stack_before - 1, f"{stack_before} -> {len(features._undo_stack())}") print() print("八、全部 toggle 都能记录回退") toggles = [d for d in defs if d.get("kind") == "toggle"] # 别写死数量 —— 加功能时这里会假失败(relay_saver 就踩过一次)。 # 真正要保证的是:开关数量合理,且**每一个**都能回退。 check("开关数量合理(>= 15)", len(toggles) >= 15, len(toggles)) check("开关类都算可回退", all(d["name"] not in Features.IRREVERSIBLE for d in toggles), str([d["name"] for d in toggles if d["name"] in Features.IRREVERSIBLE])) ui.stop() print() print("=" * 60) print(f"通过 {passed} 项,失败 {failed} 项") return 0 if failed == 0 else 1 if __name__ == "__main__": sys.exit(main())