72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
|
|
# -*- 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)
|