# -*- coding: utf-8 -*- r'''验证卸载模块的逻辑(不依赖提权、不依赖真的装一遍)。 为什么单独测这个: 安装包的整体周期测试需要管理员权限,而这个环境里有几个 杀不掉的僵尸卸载器进程挡着。但**卸载逻辑本身**可以直接调, 不需要装、不需要提权。 要验证的三件事(都是这轮修的真 bug): 1. _kill_running 不能用 taskkill /IM 把自己也杀掉 —— 那样后面的删自启/删数据全都不执行 2. run() 必须真的调到 _remove_autostart 3. run(keep_dir=True) 不能安排自删(会和 Inno 卸载器抢目录) ''' from __future__ import annotations import os import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") sys.path.insert(0, str(ROOT / "windows")) import lanremote.uninstall as uni # noqa: E402 passed = 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: print(" 一、_kill_running 不会杀自己") source = (ROOT / "windows" / "lanremote" / "uninstall.py").read_text( encoding="utf-8") check("用了 PID 排除(/FI \"PID ne\")", 'PID ne' in source and "os.getpid()" in source, "没找到排除自己的逻辑") check("没有裸的 /IM ykxmz.exe", '"/IM", exe' in source and 'PID ne' in source, "taskkill 参数形态不对") # 直接模拟一下 os.environ.setdefault("YKXMZ_DATA", str(ROOT / "_build" / "_uninst_test")) result = uni._kill_running() print(" _kill_running() -> %s" % result) check("调用后本进程还活着", True) # 能走到这里就说明没被杀 print() print(" 二、run() 会依次做三件事") calls = [] originals = { "_kill_running": uni._kill_running, "_remove_autostart": uni._remove_autostart, "_remove_data": uni._remove_data, "_schedule_self_delete": uni._schedule_self_delete, } def spy(name, value): def wrapper(*args, **kwargs): calls.append(name) if callable(value): return value(*args, **kwargs) return value return wrapper try: uni._kill_running = spy("kill", originals["_kill_running"]) uni._remove_autostart = spy("autostart", lambda: "(测试替身)") uni._remove_data = spy("data", lambda: "(测试替身)") uni._schedule_self_delete = spy("selfdel", lambda: "(测试替身)") text = uni.run(keep_data=True) # keep_data 免得真删数据 print(" run() 返回:") for line in text.splitlines(): print(" " + line) check("调了 _kill_running", "kill" in calls, calls) check("调了 _remove_autostart(这一步以前会被自己的 taskkill 干掉)", "autostart" in calls, calls) check("调了 _schedule_self_delete", "selfdel" in calls, calls) print() print(" 三、keep_dir=True 时不安排自删") calls.clear() uni.run(keep_data=True, keep_dir=True) check("调了 _kill_running", "kill" in calls, calls) check("调了 _remove_autostart", "autostart" in calls, calls) check("**没有**安排自删(避免和 Inno 卸载器抢目录)", "selfdel" not in calls, calls) finally: uni._kill_running = originals["_kill_running"] uni._remove_autostart = originals["_remove_autostart"] uni._remove_data = originals["_remove_data"] uni._schedule_self_delete = originals["_schedule_self_delete"] print() print(" 四、_schedule_self_delete 重定向了子进程 stdio") check("Popen 用了 DEVNULL 重定向", "_QUIET" in source and "DEVNULL" in source, "没重定向的话 capture_output 会等管道 EOF,表现为卡死") check("用 ping 而不是 timeout 做延迟", "ping 127.0.0.1" in source, "timeout 在 DETACHED_PROCESS 下没控制台,会立刻失败") print() print("=" * 54) print(" 通过 %d 项,失败 %d 项" % (passed, failed)) return 0 if failed == 0 else 1 if __name__ == "__main__": sys.exit(main())