# -*- coding: utf-8 -*- """单测进程的联网护栏。导入即安装,联网失败即使被捕获,进程也必须失败。 完整隔离入口:python scripts/unit_test_guard.py scripts/test_batch3_units.py。 入口在空临时目录运行测试,只继承系统环境,不读取仓库 .env 或生产参数。 仅供测试使用,生产模块不得导入。 """ import atexit import os from pathlib import Path import runpy import sys import tempfile import traceback _violations = [] _ROOT = str(Path(__file__).resolve().parents[1]) + os.sep def _audit(event, args): if event in {"socket.connect", "socket.getaddrinfo", "socket.sendto", "socket.sendmsg"}: # 不输出连接地址或凭据,只保留调用位置。 _violations.append((event, traceback.extract_stack()[:-1])) raise RuntimeError("单测禁止联网: " + event) def _finish(): if _violations: print("\nTEST ISOLATION FAILED: 检测到未替换的外部调用", file=sys.stderr) try: for event, stack in _violations[:5]: print(event, file=sys.stderr) local = [frame for frame in stack if frame.filename.startswith(_ROOT)] traceback.print_list(local[-12:] or stack[-12:], file=sys.stderr) sys.stdout.flush() sys.stderr.flush() finally: os._exit(1) # atexit 里抛异常不会改变进程退出码。 def run_suite(path): path = Path(path).resolve() sys.path.insert(0, str(path.parent)) keep = {k: v for k, v in os.environ.items() if k in {"PATH", "SYSTEMROOT", "LANG", "LC_ALL"}} keep.update(TZ="Asia/Shanghai", PYTHONDONTWRITEBYTECODE="1") os.environ.clear() os.environ.update(keep) sys.dont_write_bytecode = True import time if hasattr(time, "tzset"): time.tzset() previous = Path.cwd() with tempfile.TemporaryDirectory(prefix="pms-unit-") as isolated: try: os.chdir(isolated) sys.argv = [str(path)] runpy.run_path(str(path), run_name="__main__") finally: os.chdir(previous) if __name__ == "__main__": if len(sys.argv) != 2: raise SystemExit("用法: python scripts/unit_test_guard.py 测试文件") # 独立模块保存护栏状态,不与 runpy 临时使用的 __main__ 共用全局字典。 from unit_test_guard import run_suite run_suite(sys.argv[1]) else: sys.addaudithook(_audit) atexit.register(_finish)