feat: initialize XAS test console with native execution

This commit is contained in:
AdamWen95
2026-10-14 09:38:58 +08:00
commit 438844b01a
83 changed files with 9334 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
"""Fast deterministic example; no model service or GPU access."""
import argparse
import json
import os
from pathlib import Path
import sys
import time
parser = argparse.ArgumentParser()
parser.add_argument("--scenario", choices=["success", "failure", "wait"], default="success")
args = parser.parse_args()
root = Path(os.environ["XAS_RUN_DIR"])
print("开始快速流程验收(模拟数据),不调用模型。", flush=True)
for index in range(3):
print(f"步骤 {index + 1}/3:日志输出正常", flush=True)
time.sleep(0.3)
if args.scenario == "wait":
print("等待取消或超时。", flush=True)
time.sleep(120)
report = {"synthetic": True, "scenario": args.scenario, "success": args.scenario == "success", "checks": 3}
(root / "result.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print("结果已写入 result.json", flush=True)
sys.exit(0 if args.scenario == "success" else 2)
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""Run only the bundled synthetic acceptance task, with bounded lifetime."""
import argparse
import ctypes
from datetime import datetime, timezone
import hashlib
import json
import math
import os
from pathlib import Path
import re
import signal
import subprocess
import sys
import time
MODES = ("success", "failure", "invalid-report", "wait")
def now():
return datetime.now(timezone.utc).isoformat()
def write_json(path, value):
temporary = path.with_suffix(".tmp")
with temporary.open("x", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2)
stream.write("\n")
stream.flush()
os.fsync(stream.fileno())
temporary.replace(path)
def subreaper():
# Adopt the fixture's child so cancellation can reap it even in containers.
if sys.platform == "linux":
libc = ctypes.CDLL(None, use_errno=True)
if libc.prctl(36, 1, 0, 0, 0) != 0:
raise OSError(ctypes.get_errno(), "cannot enable child subreaper")
def cleanup(proc):
if os.name == "posix":
for sig in (signal.SIGTERM, signal.SIGKILL):
try:
os.killpg(proc.pid, sig)
except ProcessLookupError:
break
if sig == signal.SIGTERM:
time.sleep(.15)
elif proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=2)
if sys.platform == "linux":
deadline = time.monotonic() + 2
while True:
try:
pid, _ = os.waitpid(-1, os.WNOHANG)
except ChildProcessError:
break
if pid == 0:
if time.monotonic() >= deadline:
raise RuntimeError("child cleanup could not be confirmed")
time.sleep(.02)
def read_result(folder, run_id):
path = folder / "result.json"
if path.is_symlink() or path.stat().st_size > 65536:
raise ValueError("invalid result file")
result = json.loads(path.read_text(encoding="utf-8"))
if (not isinstance(result, dict) or result.get("run_id") != run_id
or result.get("synthetic") is not True
or type(result.get("checks_passed")) is not int
or result["checks_passed"] != 3):
raise ValueError("result does not satisfy the acceptance contract")
return result
def arguments():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--mode", choices=MODES, default=os.getenv("RD_OPTION_MODE", "success"))
parser.add_argument("--run-id", default=os.getenv("RD_JOB_EXECID"))
parser.add_argument("--timeout", type=float, default=os.getenv("RD_OPTION_TIMEOUT", "8"))
parser.add_argument("--results-root", type=Path,
default=Path(os.getenv("XAS_RESULTS_ROOT", "/home/rundeck/test-results")))
args = parser.parse_args()
if args.mode not in MODES:
parser.error("unregistered mode")
if not args.run_id or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]{0,63}", args.run_id):
parser.error("run-id must be 1-64 letters, digits, underscores or hyphens")
if not math.isfinite(args.timeout) or not 2 <= args.timeout <= 30:
parser.error("timeout must be between 2 and 30 seconds")
if os.name != "posix" and args.mode == "wait":
parser.error("wait/cancellation acceptance requires Linux")
return args
def main():
args = arguments()
os.umask(0o077)
args.results_root.mkdir(parents=True, exist_ok=True)
folder = args.results_root.resolve() / args.run_id
try:
folder.mkdir()
except FileExistsError:
print("Run ID already exists; history was preserved", file=sys.stderr)
return 2
source = Path(__file__).resolve().parent
summary = {"schema_version": 1, "run_id": args.run_id, "synthetic": True,
"mode": args.mode, "timeout_seconds": args.timeout,
"started_at": now(), "status": "running", "exit_code": None,
"source_sha256": {name: hashlib.sha256((source / name).read_bytes()).hexdigest()
for name in ("run_smoke.py", "smoke_task.py")}}
write_json(folder / "request.json", summary)
cancelled = False
def on_signal(signum, frame):
nonlocal cancelled
cancelled = True
signal.signal(signal.SIGTERM, on_signal)
signal.signal(signal.SIGINT, on_signal)
proc = None
rc = 1
try:
subreaper()
proc = subprocess.Popen([sys.executable, "-u", str(source / "smoke_task.py"),
args.mode, args.run_id, str(folder)],
start_new_session=(os.name == "posix"))
deadline = time.monotonic() + args.timeout
while proc.poll() is None and not cancelled and time.monotonic() < deadline:
time.sleep(.04)
summary["exit_code"] = proc.poll()
if cancelled:
summary["status"], rc = "cancelled", 130
elif proc.poll() is None:
summary["status"], rc = "timed_out", 124
elif proc.returncode != 0:
summary["status"] = "failed"
else:
try:
summary["result"] = read_result(folder, args.run_id)
summary["status"], rc = "succeeded", 0
except (OSError, ValueError, TypeError):
summary["status"] = "invalid_result"
except (OSError, RuntimeError) as exc:
summary["status"] = "runner_error"
summary["error_type"] = type(exc).__name__
finally:
if proc is not None:
try:
cleanup(proc)
summary["cleanup_confirmed"] = True
except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
summary["status"], rc = "cleanup_failed", 1
summary["cleanup_confirmed"] = False
summary["error_type"] = type(exc).__name__
summary["worker_pid"] = proc.pid
summary["final_exit_code"] = proc.returncode
child_file = folder / "child.pid"
if child_file.exists():
summary["child_pid"] = int(child_file.read_text(encoding="utf-8"))
summary["finished_at"] = now()
write_json(folder / "summary.json", summary)
print("XAS_ACCEPTANCE_RESULT " + json.dumps(summary, ensure_ascii=False), flush=True)
return rc
if __name__ == "__main__":
sys.exit(main())
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Synthetic fixture. No GPU, network, model calls or existing data access."""
import json
from pathlib import Path
import subprocess
import sys
import time
def main():
mode, run_id, destination = sys.argv[1:]
folder = Path(destination)
print("SYNTHETIC FLOW ACCEPTANCE: not a model benchmark", flush=True)
if mode == "wait":
child = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(60)"])
(folder / "child.pid").write_text(str(child.pid), encoding="utf-8")
print(f"waiting; controlled_child_pid={child.pid}", flush=True)
child.wait(timeout=65)
return 0
if mode == "failure":
print("Expected fixture failure", flush=True)
return 23
if mode == "invalid-report":
(folder / "result.json").write_text('{"incomplete":true}', encoding="utf-8")
return 0
if mode != "success":
return 2
for step in range(1, 4):
print(f"progress={step}/3", flush=True)
time.sleep(.2)
(folder / "result.json").write_text(json.dumps({
"run_id": run_id, "synthetic": True, "checks_passed": 3,
"description": "Synthetic flow acceptance only"}), encoding="utf-8")
return 0
if __name__ == "__main__":
sys.exit(main())