Files
dpb/run_regression.py
T
34047007@qq.com b95053c52c init: 初始化 dpb 桃育种系统代码库
前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
2026-08-06 00:17:49 +08:00

101 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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())