zcbot/scripts/test_bgproc_manual.py

96 lines
3.4 KiB
Python

# -*- coding: utf-8 -*-
"""bg proc host 模式手工验证(ASCII-only 输出,GBK console 安全)。
场景:
1. shell background 启动一个 ~6s 的命令 -> 立即返回
2. 期间 status = running
3. 结束后 status = finished, exit_code=0, 日志内容对
4. inline python background + kill
5. count_running / list_procs / sweep 冒烟
"""
import shutil
import sys
import tempfile
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from core import procs # noqa: E402
anchor = Path(tempfile.mkdtemp(prefix="zcbot_bgproc_test_"))
task_id = "11111111-1111-1111-1111-111111111111"
fails = []
def check(name, cond):
print(("[ok] " if cond else "[FAIL] ") + name)
if not cond:
fails.append(name)
# 1. python inline bg: 3s 后打印再退出
code = "import time\nprint('phase1', flush=True)\ntime.sleep(3)\nprint('phase2 done', flush=True)\n"
t0 = time.time()
pid1, d1 = procs.launch_host(
anchor, task_id, kind="python", command_display="python <inline>",
cwd=anchor, timeout_s=120, env=None, inline_code=code,
argv=None, shell_cmd=None,
)
launch_elapsed = time.time() - t0
check("launch returns fast (<2s), got %.2fs" % launch_elapsed, launch_elapsed < 2)
time.sleep(1.5)
meta1 = procs.read_meta(d1)
st, ec = procs.status_of(meta1, d1)
check("running mid-flight (got %s)" % st, st == "running")
check("count_running == 1 (got %d)" % procs.count_running(anchor), procs.count_running(anchor) == 1)
for _ in range(20):
time.sleep(0.5)
st, ec = procs.status_of(procs.read_meta(d1), d1)
if st == "finished":
break
check("finished with exit 0 (got %s/%s)" % (st, ec), st == "finished" and ec == 0)
log = procs.tail_log(d1)
check("log has phase2 output", "phase2 done" in log)
# 2. timeout kill: sleep 60 but timeout_s=61 clamp floor -> use argv sleep via python
code2 = "import time\ntime.sleep(600)\n"
pid2, d2 = procs.launch_host(
anchor, task_id, kind="python", command_display="python <sleeper>",
cwd=anchor, timeout_s=120, env=None, inline_code=code2,
argv=None, shell_cmd=None,
)
time.sleep(1.5)
meta2 = procs.read_meta(d2)
st2, _ = procs.status_of(meta2, d2)
check("sleeper running", st2 == "running")
msg = procs.kill_proc(meta2, d2)
time.sleep(1.0)
st2b, ec2 = procs.status_of(procs.read_meta(d2), d2)
check("killed -> finished 137 (got %s/%s, msg=%s)" % (st2b, ec2, msg),
st2b == "finished" and ec2 == 137)
# 3. list + format
items = procs.list_procs(anchor, task_id)
check("list_procs sees 2 (got %d)" % len(items), len(items) == 2)
for m in items:
print(" " + procs.format_proc_line(m))
# 4. sweep: nothing removed (fresh), then with ttl=0 removes finished dirs
stats = procs.sweep(anchor.parent / "_nonexistent_users")
stats2 = procs.sweep(anchor.parent, ttl_s=10 ** 9) # anchor.parent as users base: anchor is one 'user'
# note: sweep expects user_root_base -> iterates children; anchor.parent contains anchor
check("sweep noop keeps dirs", (anchor / procs.PROCS_SUBDIR / task_id).is_dir())
stats3 = procs.sweep(anchor.parent, ttl_s=0)
remaining = list((anchor / procs.PROCS_SUBDIR / task_id).glob("*")) if (anchor / procs.PROCS_SUBDIR / task_id).is_dir() else []
check("sweep ttl=0 removed finished dirs (left %d)" % len(remaining), len(remaining) == 0)
shutil.rmtree(anchor, ignore_errors=True)
print()
if fails:
print("RESULT: %d FAILURE(S): %s" % (len(fails), "; ".join(fails)))
sys.exit(1)
print("RESULT: ALL PASS")