101 lines
4.0 KiB
Python
101 lines
4.0 KiB
Python
# -*- 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())
|