# -*- 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 ", 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 ", 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")