Files
dpb/run_regression.py
T

101 lines
4.0 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""统计引擎全量回归驱动:顺序串跑全量测试(Temp/claude 的 e2e_* 探针 + backend/scripts 的 tc 套件)。
用法:
python run_regression.py # 全量(e2e 探针 + 正式 tc 套件)
python run_regression.py gwas ocs # 只跑名字含 gwas / ocs 的测试
python run_regression.py --keep-logs # 保留上次日志(默认每次清空)
覆盖:
- Temp/claude/e2e_*.py(排除 e2e_probe* 探查工具)——61 个统计引擎/业务链探针
- backend/scripts/test_bre_analysis_*_tc.py——正式 tc 套件(TestClient 走真实 API
ssGBLUP/OCS/MABC/孟德尔 golden 数值断言,新模块必测)
说明:
- 每个测试完整输出写 Temp/claude/regression_logs/<name>.log,控制台只回显尾部 + EXIT。
- 测试均为独立进程(不需后端 uvicorn 服务),自带 fixture 清理,顺序串跑最稳
(曾有过并发机器争用 RC=124 超时的历史,故不并发)。
- 最终按 EXIT 汇总:任一失败整体 exit != 0(供交付流程判断)。
- 解释器默认取 sys.executable(须用 dpb env python 运行本脚本);可用环境变量
DPB_PYTHON 覆盖(如 DPB_PYTHON=C:/ai/miniconda3/envs/dpb/python.exe)。
"""
import glob
import os
import subprocess
import sys
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
ROOT = os.path.dirname(os.path.abspath(__file__))
PROBE_DIR = os.path.join(ROOT, "Temp", "claude")
TC_DIR = os.path.join(ROOT, "backend", "scripts")
LOG_DIR = os.path.join(PROBE_DIR, "regression_logs")
PY = os.environ.get("DPB_PYTHON", sys.executable)
KEEP_LOGS = "--keep-logs" in sys.argv
def main() -> int:
filters = [a for a in sys.argv[1:] if not a.startswith("--")]
probes = sorted(glob.glob(os.path.join(PROBE_DIR, "e2e_*.py")))
probes = [p for p in probes if "e2e_probe" not in os.path.basename(p)]
probes += sorted(glob.glob(os.path.join(TC_DIR, "test_bre_analysis_*_tc.py")))
if filters:
probes = [p for p in probes if any(f in os.path.basename(p) for f in filters)]
if not probes:
print("未找到匹配探针")
return 1
if not KEEP_LOGS and os.path.isdir(LOG_DIR):
for old in glob.glob(os.path.join(LOG_DIR, "*.log")):
os.remove(old)
os.makedirs(LOG_DIR, exist_ok=True)
env = dict(os.environ)
env.setdefault("ENVIRONMENT", "dev")
env["PYTHONPATH"] = os.pathsep.join([os.path.join(ROOT, "backend"), PROBE_DIR])
env.setdefault("PYTHONIOENCODING", "utf-8")
passed, failed, timed_out = [], [], []
for i, p in enumerate(probes, 1):
name = os.path.basename(p)
log_path = os.path.join(LOG_DIR, name + ".log")
print(f"[{i}/{len(probes)}] {name}")
try:
with open(log_path, "w", encoding="utf-8") as log:
rc = subprocess.call(
[PY, "-X", "utf8", p], cwd=os.path.dirname(p), env=env,
stdout=log, stderr=subprocess.STDOUT, timeout=1800)
except subprocess.TimeoutExpired:
rc = 124
with open(log_path, "a", encoding="utf-8") as log:
log.write("\n[回归驱动] 探针超时(>30min)被杀\n")
with open(log_path, "r", encoding="utf-8") as log:
tail = log.readlines()[-12:]
for line in tail:
print(" " + line.rstrip())
print(f" -> EXIT={rc}")
if rc == 0:
passed.append(name)
elif rc == 124:
timed_out.append(name)
failed.append(name)
else:
failed.append(name)
print("\n" + "=" * 40)
print(f"全量回归:pass={len(passed)} fail={len(failed)}"
+ (f"(其中超时 {len(timed_out)}" if timed_out else ""))
if failed:
print("FAILED:")
for f in failed:
print(f" {f}")
print(f"完整输出见 {LOG_DIR}/")
return 1
print("全部通过")
return 0
if __name__ == "__main__":
sys.exit(main())