P28-01-COLLECTOR: 現HEADの性能基準:windowed x1/x3・Operations ON collector
This commit is contained in:
+91
-1
@@ -87,6 +87,19 @@ def check_budget_scope(value, report_paths=(), path=(), raw=False):
|
|||||||
|
|
||||||
def check_admission(admission):
|
def check_admission(admission):
|
||||||
check_budget_scope(admission)
|
check_budget_scope(admission)
|
||||||
|
if "planned_argv" in admission:
|
||||||
|
from collect_p28_01 import argv_for, parse_argv
|
||||||
|
planned = admission["planned_argv"]
|
||||||
|
need(len(planned) == 3 + len(admission["phases"]), "internal phase plan length")
|
||||||
|
engine = planned[0][0]
|
||||||
|
need("--path" in planned[0], "import stage missing")
|
||||||
|
stage = Path(planned[0][planned[0].index("--path") + 1])
|
||||||
|
wrapper = Path(planned[2][-1])
|
||||||
|
need(wrapper.name == "guarded.gd", "wrapper plan target")
|
||||||
|
equal(planned, [argv_for(engine, stage, wrapper, True),
|
||||||
|
parse_argv(engine, stage, wrapper.parent / "measure_p28_01.gd"),
|
||||||
|
parse_argv(engine, stage, wrapper)] +
|
||||||
|
[argv_for(engine, stage, wrapper) for _ in admission["phases"]], "execution argv plan")
|
||||||
need(admission.get("budget_verdict_scope") == VERDICT_SCOPE, "admission verdict scope")
|
need(admission.get("budget_verdict_scope") == VERDICT_SCOPE, "admission verdict scope")
|
||||||
reused = admission["reused_r02"]
|
reused = admission["reused_r02"]
|
||||||
def no_verdict_fields(value):
|
def no_verdict_fields(value):
|
||||||
@@ -442,6 +455,75 @@ def check_report(report):
|
|||||||
need(frames and all(f["registered"]["guests"] == 200 for f in frames), "200 guest claim")
|
need(frames and all(f["registered"]["guests"] == 200 for f in frames), "200 guest claim")
|
||||||
|
|
||||||
|
|
||||||
|
def check_parse_process(row, directory):
|
||||||
|
from collect_p28_01 import PARSE_NA, diagnostics, sha
|
||||||
|
equal(row["parse_na"], PARSE_NA, "parse-only N/A reasons")
|
||||||
|
need(not any(k in row for k in ("report", "guard", "audio_close", "runtime_profile")),
|
||||||
|
"parse claims runtime evidence")
|
||||||
|
need(row["passed"] is True, "parse failed")
|
||||||
|
need(row["source_preserved"] is True and row["player_preserved"] is True, "parse preservation")
|
||||||
|
need(set(row["raw"]) == {"stdout.log", "stderr.log", "engine-native.log"}, "parse raw missing")
|
||||||
|
text = ""
|
||||||
|
for name, pin in row["raw"].items():
|
||||||
|
path = directory / row["phase"] / name
|
||||||
|
need(pin == {"sha256": sha(path), "bytes": path.stat().st_size}, "parse raw pin")
|
||||||
|
text += path.read_text(errors="replace") + "\n"
|
||||||
|
need(not diagnostics(text) and row["diagnostics"] == [], "parse diagnostics")
|
||||||
|
need("P28_01_PERF_REPORT=" not in text and "P28_01_AUDIO_CLOSE=" not in text,
|
||||||
|
"runtime markers during parse")
|
||||||
|
process, capture = row["process"], row["capture"]
|
||||||
|
need(process["exit_code"] == 0 and process["helper_exit_code"] == 0 and
|
||||||
|
process["timed_out"] is False and process["error_lines"] == [] and
|
||||||
|
process["owned_popen_ended"] is True and process["old_helper_engine_ended"] is True and
|
||||||
|
process["start_receipt_written"] is True and process["end_receipt_written"] is True and
|
||||||
|
process["lifecycle_error"] is None and process["external_process_or_group_signals"] == 0,
|
||||||
|
"parse process failure")
|
||||||
|
need(capture["passed"] is True and capture["all_captured_ended"] is True and
|
||||||
|
capture["actual_captured_pids"] == [process["pid"]] and
|
||||||
|
not capture["outer_cleanup_errors"] and capture["foreign_process_or_group_signals"] == 0 and
|
||||||
|
all(c["engine_ended"] is True and not c["outer_cleanup_errors"] and
|
||||||
|
not c["cleanup"].get("errors") for c in capture["cleanup"]), "parse ownership")
|
||||||
|
equal(json.loads((directory / row["phase"] / "process.json").read_text()), process,
|
||||||
|
"parse process binding")
|
||||||
|
for name, pin in row["owned_files"].items():
|
||||||
|
path = directory / row["phase"] / name
|
||||||
|
need(pin == {"sha256": sha(path), "bytes": path.stat().st_size}, "parse owned pin")
|
||||||
|
from collect_p28_01 import OWNED_FILES
|
||||||
|
need(set(row["owned_files"]) == set(OWNED_FILES), "parse ownership evidence missing")
|
||||||
|
saved_capture = json.loads((directory / row["phase"] / "outer-owned-finally.json").read_text())
|
||||||
|
equal(saved_capture, {k: v for k, v in capture.items()
|
||||||
|
if k not in ("passed", "outer_receipt_written", "outer_receipt_error")},
|
||||||
|
"parse capture binding")
|
||||||
|
|
||||||
|
|
||||||
|
def check_parse(row, directory, source):
|
||||||
|
from collect_p28_01 import parse_argv, sha, digest
|
||||||
|
check_parse_process(row, directory)
|
||||||
|
target = directory / ("measure_p28_01.gd" if row["phase"] == "parse-base" else "guarded.gd")
|
||||||
|
command = parse_argv(row["argv"][0], directory / "stage", target)
|
||||||
|
command += ["--log-file", str(directory / row["phase"] / "engine-native.log"), "--verbose"]
|
||||||
|
equal(row["argv"], command, "parse argv")
|
||||||
|
equal(row["process"]["argv"], command, "parse owned argv")
|
||||||
|
equal(row["script_pins"], {str(directory / n): sha(directory / n)
|
||||||
|
for n in ("measure_p28_01.gd", "guarded.gd", "profile_guard.gd")},
|
||||||
|
"parse script pins")
|
||||||
|
startup = row["startup_project"]
|
||||||
|
need(not (directory / (row["phase"] + "-guard.json")).exists(), "Guard ran during parse")
|
||||||
|
path = directory / (row["phase"] + "-startup-project.godot")
|
||||||
|
need(startup["path"] == str(path) and startup["sha256"] == sha(path) and
|
||||||
|
startup["fresh_before"] is True, "parse startup evidence")
|
||||||
|
from collect_p28_01 import load_tool, EXTERNAL_IMPORTS
|
||||||
|
standard = load_tool(EXTERNAL_IMPORTS[0])
|
||||||
|
public = (directory / "stage/project.godot").read_bytes()
|
||||||
|
need(path.read_bytes() == standard.private_project(public, startup["name"]) and
|
||||||
|
startup["profile"] == str(standard.profile_path(startup["name"])) and
|
||||||
|
startup["restored_sha256"] == hashlib.sha256(public).hexdigest(), "parse private project")
|
||||||
|
stage_inventory = standard.inventory(directory / "stage", stage=True)
|
||||||
|
need(row["stage_before_sha256"] == row["stage_after_sha256"] ==
|
||||||
|
digest(stage_inventory), "parse stage preservation")
|
||||||
|
need(all(stage_inventory.get(k) == v for k, v in source.items()), "parse product preservation")
|
||||||
|
|
||||||
|
|
||||||
def check_receipt(receipt, directory):
|
def check_receipt(receipt, directory):
|
||||||
from collect_p28_01 import OWNED_FILES, projection, extrapolation, guard_gate
|
from collect_p28_01 import OWNED_FILES, projection, extrapolation, guard_gate
|
||||||
check_budget_scope(receipt, tuple(("phases", i, "report")
|
check_budget_scope(receipt, tuple(("phases", i, "report")
|
||||||
@@ -458,7 +540,10 @@ def check_receipt(receipt, directory):
|
|||||||
"execution probe pin/check")
|
"execution probe pin/check")
|
||||||
check_probe(text)
|
check_probe(text)
|
||||||
rows = receipt["phases"]
|
rows = receipt["phases"]
|
||||||
need([r["phase"] for r in rows] == ["import", *PHASES], "phase order")
|
from collect_p28_01 import PARSE_PHASES
|
||||||
|
need([r["phase"] for r in rows] == ["import", *PARSE_PHASES, *PHASES], "phase order")
|
||||||
|
parse_profiles = [r["startup_project"]["profile"] for r in rows if r["phase"] in PARSE_PHASES]
|
||||||
|
need(len(set(parse_profiles)) == len(parse_profiles), "parse profiles reused")
|
||||||
for row in rows:
|
for row in rows:
|
||||||
equal(row["phase_stop"], {"limit_seconds": 1800, "source": CAP_SOURCE}, "phase cap origin")
|
equal(row["phase_stop"], {"limit_seconds": 1800, "source": CAP_SOURCE}, "phase cap origin")
|
||||||
forecast = None
|
forecast = None
|
||||||
@@ -486,6 +571,11 @@ def check_receipt(receipt, directory):
|
|||||||
need(hashlib.sha256(data).hexdigest() == pin["sha256"] and len(data) == pin["bytes"], "raw SHA")
|
need(hashlib.sha256(data).hexdigest() == pin["sha256"] and len(data) == pin["bytes"], "raw SHA")
|
||||||
if row["phase"] == "import":
|
if row["phase"] == "import":
|
||||||
continue
|
continue
|
||||||
|
if row["phase"] in PARSE_PHASES:
|
||||||
|
check_parse(row, directory, receipt["source_before"])
|
||||||
|
index = 1 + PARSE_PHASES.index(row["phase"])
|
||||||
|
equal(row["argv"][:-3], receipt["admission"]["planned_argv"][index], "parse plan binding")
|
||||||
|
continue
|
||||||
guard_gate(row["guard"], Path(row["runtime_profile"]), row["runtime_name"])
|
guard_gate(row["guard"], Path(row["runtime_profile"]), row["runtime_name"])
|
||||||
stdout = (directory / row["phase"] / "stdout.log").read_text()
|
stdout = (directory / row["phase"] / "stdout.log").read_text()
|
||||||
printed = [normalize_report(json.loads(x[len(PREFIX):])) for x in stdout.splitlines() if x.startswith(PREFIX)]
|
printed = [normalize_report(json.loads(x[len(PREFIX):])) for x in stdout.splitlines() if x.startswith(PREFIX)]
|
||||||
|
|||||||
+54
-8
@@ -24,6 +24,9 @@ PHASES = (("R0", "R", 1, 0, 10), ("P1", "R", 1, 30, 120),
|
|||||||
("P2", "R", 3, 30, 120), ("O0", "O", 1, 0, 10),
|
("P2", "R", 3, 30, 120), ("O0", "O", 1, 0, 10),
|
||||||
("P3", "O", 1, 30, 120), ("P4", "O", 3, 30, 120))
|
("P3", "O", 1, 30, 120), ("P4", "O", 3, 30, 120))
|
||||||
EXTERNAL_IMPORTS = ("tools/run_all_tests.py", "tools/regression/owned_outer.py")
|
EXTERNAL_IMPORTS = ("tools/run_all_tests.py", "tools/regression/owned_outer.py")
|
||||||
|
PARSE_PHASES = ("parse-base", "parse-wrapper")
|
||||||
|
PARSE_NA = {key: {"value": None, "reason": "--check-only does not run Runtime or Guard"}
|
||||||
|
for key in ("PERF_REPORT", "AUDIO_CLOSE", "Guard.activate", "runtime_profile")}
|
||||||
|
|
||||||
|
|
||||||
def require(value, message):
|
def require(value, message):
|
||||||
@@ -98,6 +101,38 @@ def argv_for(engine, stage, wrapper, importing=False):
|
|||||||
return [str(engine), "--path", str(stage), "--script", str(wrapper)]
|
return [str(engine), "--path", str(stage), "--script", str(wrapper)]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_argv(engine, stage, target):
|
||||||
|
return [str(engine), "--headless", "--path", str(stage), "--check-only", "--script", str(target)]
|
||||||
|
|
||||||
|
|
||||||
|
def execution_plan(engine, stage, wrapper):
|
||||||
|
return [argv_for(engine, stage, wrapper, True),
|
||||||
|
parse_argv(engine, stage, wrapper.parent / "measure_p28_01.gd"),
|
||||||
|
parse_argv(engine, stage, wrapper)] + [argv_for(engine, stage, wrapper) for _ in PHASES]
|
||||||
|
|
||||||
|
|
||||||
|
def run_private_engine(standard, outer, command, folder, environment, stage, public, name):
|
||||||
|
"""Keep launch settings outside the owned folder and restore even on failure."""
|
||||||
|
project = stage / "project.godot"
|
||||||
|
private = standard.private_project(public, name)
|
||||||
|
startup = standard.profile_path(name)
|
||||||
|
require(not startup.exists(), "startup profile not fresh")
|
||||||
|
evidence = folder.parent / (folder.name + "-startup-project.godot")
|
||||||
|
evidence.write_bytes(private)
|
||||||
|
try:
|
||||||
|
project.write_bytes(private)
|
||||||
|
require(project.read_bytes() == private, "private startup project differs")
|
||||||
|
row = run_engine(outer, command, folder, environment)
|
||||||
|
require(project.read_bytes() == private, "startup project changed during execution")
|
||||||
|
finally:
|
||||||
|
project.write_bytes(public)
|
||||||
|
row["startup_project"] = {"path": str(evidence), "sha256": sha(evidence),
|
||||||
|
"name": name, "profile": str(startup), "fresh_before": True,
|
||||||
|
"restored_sha256": sha(project)}
|
||||||
|
require(project.read_bytes() == public, "project restoration failed")
|
||||||
|
return row
|
||||||
|
|
||||||
|
|
||||||
def checked_probe(path):
|
def checked_probe(path):
|
||||||
text = path.read_text()
|
text = path.read_text()
|
||||||
check_probe(text)
|
check_probe(text)
|
||||||
@@ -162,7 +197,8 @@ def bound_report(folder, row):
|
|||||||
|
|
||||||
def phase_schedule(rows):
|
def phase_schedule(rows):
|
||||||
"""Caller appends each executed result; exceptions abort the consuming loop."""
|
"""Caller appends each executed result; exceptions abort the consuming loop."""
|
||||||
for index, phase in enumerate((("import", "", 0, 0, 0),) + PHASES):
|
for index, phase in enumerate((("import", "", 0, 0, 0),) +
|
||||||
|
tuple((label, "", 0, 0, 0) for label in PARSE_PHASES) + PHASES):
|
||||||
label, source, _, _, _ = phase
|
label, source, _, _, _ = phase
|
||||||
forecast = None
|
forecast = None
|
||||||
if label in ("P1", "P2", "P3", "P4"):
|
if label in ("P1", "P2", "P3", "P4"):
|
||||||
@@ -282,8 +318,7 @@ def main():
|
|||||||
require(actual == blob, "dirty source: " + name)
|
require(actual == blob, "dirty source: " + name)
|
||||||
stage = args.output.resolve() / "stage"
|
stage = args.output.resolve() / "stage"
|
||||||
wrapper = args.output.resolve() / "guarded.gd"
|
wrapper = args.output.resolve() / "guarded.gd"
|
||||||
planned = [argv_for(args.engine, stage, wrapper, True)] + [
|
planned = execution_plan(args.engine, stage, wrapper)
|
||||||
argv_for(args.engine, stage, wrapper) for _ in PHASES]
|
|
||||||
admission = {"commit": commit, "source_map": before, "source_map_sha256": digest(before),
|
admission = {"commit": commit, "source_map": before, "source_map_sha256": digest(before),
|
||||||
"readonly": pins, "engine_sha256": ENGINE_SHA, "engine_version": VERSION,
|
"readonly": pins, "engine_sha256": ENGINE_SHA, "engine_version": VERSION,
|
||||||
"fixture_sha256": FIXTURE_SHA, "planned_argv": planned, "phases": PHASES,
|
"fixture_sha256": FIXTURE_SHA, "planned_argv": planned, "phases": PHASES,
|
||||||
@@ -351,20 +386,31 @@ def main():
|
|||||||
"THE_TREE_P28_WARMUP": str(warmup), "THE_TREE_P28_MINUTES": str(minutes),
|
"THE_TREE_P28_WARMUP": str(warmup), "THE_TREE_P28_MINUTES": str(minutes),
|
||||||
"THE_TREE_P28_FIXTURE": str(args.fixture.resolve()), "THE_TREE_P28_FIXTURE_SHA": FIXTURE_SHA,
|
"THE_TREE_P28_FIXTURE": str(args.fixture.resolve()), "THE_TREE_P28_FIXTURE_SHA": FIXTURE_SHA,
|
||||||
"THE_TREE_P28_META": json.dumps(meta)}
|
"THE_TREE_P28_META": json.dumps(meta)}
|
||||||
(stage / "project.godot").write_bytes(standard.private_project(public, name))
|
row = run_private_engine(standard, outer, planned[index], out / label, env,
|
||||||
try:
|
stage, public, name)
|
||||||
row = run_engine(outer, planned[index], out / label, env)
|
|
||||||
finally:
|
|
||||||
(stage / "project.godot").write_bytes(public)
|
|
||||||
row["phase"] = label
|
row["phase"] = label
|
||||||
row["projection"] = forecast
|
row["projection"] = forecast
|
||||||
row["probe_check"] = probe_check
|
row["probe_check"] = probe_check
|
||||||
|
if label in PARSE_PHASES:
|
||||||
|
row.update(parse_na=PARSE_NA, script_pins=script_pins,
|
||||||
|
stage_before_sha256=digest(expected),
|
||||||
|
stage_after_sha256=digest(standard.inventory(stage, stage=True)),
|
||||||
|
source_preserved=source_map(standard, ROOT) == before,
|
||||||
|
player_preserved=standard.default_player(ROOT) == player)
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
|
if label in PARSE_PHASES:
|
||||||
|
require(row["stage_before_sha256"] == row["stage_after_sha256"] and
|
||||||
|
row["source_preserved"] and row["player_preserved"] and
|
||||||
|
all(sha(Path(p)) == pin for p, pin in script_pins.items()),
|
||||||
|
"parse preservation failed")
|
||||||
require(row["passed"], "native phase failed; raw retained: " + label)
|
require(row["passed"], "native phase failed; raw retained: " + label)
|
||||||
after = standard.inventory(stage, stage=True)
|
after = standard.inventory(stage, stage=True)
|
||||||
if label == "import":
|
if label == "import":
|
||||||
stage_gate(expected, after, importing=True)
|
stage_gate(expected, after, importing=True)
|
||||||
expected = after
|
expected = after
|
||||||
|
elif label in PARSE_PHASES:
|
||||||
|
require(after == expected, "stage changed during parse")
|
||||||
|
row["stage_after_sha256"] = digest(after)
|
||||||
else:
|
else:
|
||||||
require(after == expected, "stage changed during measurement")
|
require(after == expected, "stage changed during measurement")
|
||||||
observation = json.loads(guard_path.read_text())
|
observation = json.loads(guard_path.read_text())
|
||||||
|
|||||||
+3
-3
@@ -478,9 +478,9 @@ func sample(observer_started_us: int, delta: float) -> void:
|
|||||||
var stall_count: Variant = field(clock.performance_budget.get("_sample_counts"), "main_thread_stall")
|
var stall_count: Variant = field(clock.performance_budget.get("_sample_counts"), "main_thread_stall")
|
||||||
var kernel_stalls: Array = []
|
var kernel_stalls: Array = []
|
||||||
if stall_count != null and last_stall_count != null and stall_raw is Array:
|
if stall_count != null and last_stall_count != null and stall_raw is Array:
|
||||||
var delta: int = stall_count - last_stall_count
|
var stall_delta: int = stall_count - last_stall_count
|
||||||
if delta > 0 and delta <= stall_raw.size():
|
if stall_delta > 0 and stall_delta <= stall_raw.size():
|
||||||
kernel_stalls = stall_raw.slice(stall_raw.size() - delta)
|
kernel_stalls = stall_raw.slice(stall_raw.size() - stall_delta)
|
||||||
last_stall_count = stall_count
|
last_stall_count = stall_count
|
||||||
var causes: Array = []
|
var causes: Array = []
|
||||||
for event: Dictionary in save_events:
|
for event: Dictionary in save_events:
|
||||||
|
|||||||
+170
-9
@@ -462,7 +462,10 @@ class ReportTests(unittest.TestCase):
|
|||||||
phase["probe_check"] = receipt["probe_check"].copy()
|
phase["probe_check"] = receipt["probe_check"].copy()
|
||||||
phase["owned_files"] = {name: {"sha256": hashlib.sha256(raw).hexdigest(), "bytes": len(raw)}
|
phase["owned_files"] = {name: {"sha256": hashlib.sha256(raw).hexdigest(), "bytes": len(raw)}
|
||||||
for name in collector.OWNED_FILES}
|
for name in collector.OWNED_FILES}
|
||||||
with patch.object(checker, "PHASES", {"O0": ("O", 1, 0, 10)}), \
|
# This fixture isolates the original runtime receipt checks. The actual
|
||||||
|
# parse prefix and its evidence are covered by test_l2_owned_parse_gate.
|
||||||
|
with patch.object(collector, "PARSE_PHASES", ()), \
|
||||||
|
patch.object(checker, "PHASES", {"O0": ("O", 1, 0, 10)}), \
|
||||||
patch.object(Path, "read_bytes", return_value=raw), \
|
patch.object(Path, "read_bytes", return_value=raw), \
|
||||||
patch.object(Path, "read_text", autospec=True,
|
patch.object(Path, "read_text", autospec=True,
|
||||||
side_effect=lambda p: (probe_text if p.name == "measure_p28_01.gd" else
|
side_effect=lambda p: (probe_text if p.name == "measure_p28_01.gd" else
|
||||||
@@ -686,6 +689,164 @@ class ReportTests(unittest.TestCase):
|
|||||||
self.assertFalse(errors)
|
self.assertFalse(errors)
|
||||||
self.assertTrue(all(r["engine_ended"] for r in cleanup))
|
self.assertTrue(all(r["engine_ended"] for r in cleanup))
|
||||||
|
|
||||||
|
def test_l2_owned_parse_gate(self):
|
||||||
|
"""Real owned Python children; no engine and no fabricated Runtime markers."""
|
||||||
|
standard = collector.load_tool(collector.EXTERNAL_IMPORTS[0])
|
||||||
|
outer = collector.load_tool(collector.EXTERNAL_IMPORTS[1])
|
||||||
|
child = (
|
||||||
|
"import os,sys,json,time,pathlib\n"
|
||||||
|
"mode=sys.argv[1]\n"
|
||||||
|
"identity={'pid':os.getpid(),'ppid':os.getppid(),'pgid':os.getpgrp(),"
|
||||||
|
"'args':' '.join([sys.executable]+sys.argv)}\n"
|
||||||
|
"base=pathlib.Path(sys.argv[0]).parent\n"
|
||||||
|
"tmp=base/'identity.tmp'; tmp.write_text(json.dumps(identity)); tmp.replace(base/'identity')\n"
|
||||||
|
"stage=pathlib.Path(sys.argv[sys.argv.index('--path')+1])\n"
|
||||||
|
"assert 'config/use_custom_user_dir=true' in (stage/'project.godot').read_text()\n"
|
||||||
|
"native=pathlib.Path(sys.argv[sys.argv.index('--log-file')+1])\n"
|
||||||
|
"native.write_text('ERROR: native parse failure\\n' if mode=='native' else 'native raw\\n')\n"
|
||||||
|
"print('Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org',flush=True)\n"
|
||||||
|
"print('SCRIPT ERROR: Parse Error: synthetic' if mode=='stdout' else 'parse stdout',flush=True)\n"
|
||||||
|
"print('Parse Error: synthetic' if mode=='stderr' else 'parse stderr',file=sys.stderr,flush=True)\n"
|
||||||
|
"time.sleep(.15)\n"
|
||||||
|
"sys.exit(3 if mode=='exit' else 0)\n")
|
||||||
|
for failure_phase, mode in ((None, "ok"), ("parse-base", "stdout"),
|
||||||
|
("parse-base", "stderr"), ("parse-base", "native"),
|
||||||
|
("parse-base", "exit"), ("parse-wrapper", "stdout")):
|
||||||
|
with self.subTest(failure_phase=failure_phase, mode=mode), tempfile.TemporaryDirectory() as temp:
|
||||||
|
base = Path(temp)
|
||||||
|
stage = base / "stage"
|
||||||
|
stage.mkdir()
|
||||||
|
public = b'[application]\n\nconfig/name="The Tree"\n'
|
||||||
|
(stage / "project.godot").write_bytes(public)
|
||||||
|
script = base / "child.py"
|
||||||
|
script.write_text(child)
|
||||||
|
pins = {}
|
||||||
|
for name in ("measure_p28_01.gd", "profile_guard.gd", "guarded.gd"):
|
||||||
|
(base / name).write_text("# Synthetic parse input; never executed by Godot.\n")
|
||||||
|
pins[str(base / name)] = collector.sha(base / name)
|
||||||
|
source = standard.inventory(stage, stage=True)
|
||||||
|
def diagnostic(command, folder, timeout, env, **kwargs):
|
||||||
|
def probe(pid):
|
||||||
|
deadline = time.monotonic() + 2
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
if (base / "identity").exists():
|
||||||
|
identity = json.loads((base / "identity").read_text())
|
||||||
|
if identity["pid"] == pid:
|
||||||
|
return identity
|
||||||
|
time.sleep(.005)
|
||||||
|
raise RuntimeError("child identity unavailable")
|
||||||
|
return outer.native.run_owned_diagnostic(command, folder, timeout, env,
|
||||||
|
probe_fn=probe, **kwargs)
|
||||||
|
def owned(command, folder, limit, env, **kwargs):
|
||||||
|
self.assertEqual(limit, 1800)
|
||||||
|
return outer.run_phase(command, folder, 5, env, diagnostic_fn=diagnostic, **kwargs)
|
||||||
|
rows, launched = [], []
|
||||||
|
try:
|
||||||
|
with patch.object(collector, "live_engines", return_value=[]):
|
||||||
|
for index, phase, _ in collector.phase_schedule(rows):
|
||||||
|
label = phase[0]
|
||||||
|
if label == "import":
|
||||||
|
rows.append({"phase": label})
|
||||||
|
continue # This engine-free fixture begins after successful import.
|
||||||
|
launched.append(label)
|
||||||
|
if label not in collector.PARSE_PHASES:
|
||||||
|
break # Record admission of R0 without starting any Runtime.
|
||||||
|
command = [sys.executable, str(script), mode if label == failure_phase else "ok",
|
||||||
|
"--headless", "--path", str(stage), "--check-only", "--script", "probe"]
|
||||||
|
row = collector.run_private_engine(
|
||||||
|
standard, SimpleNamespace(run_phase=owned), command, base / label, {},
|
||||||
|
stage, public, "TheTree-PCK-tests-" + base.name + "-" + label)
|
||||||
|
row.update(phase=label, parse_na=copy.deepcopy(collector.PARSE_NA),
|
||||||
|
source_preserved=True, player_preserved=True, script_pins=pins,
|
||||||
|
stage_before_sha256=collector.digest(source),
|
||||||
|
stage_after_sha256=collector.digest(standard.inventory(stage, stage=True)))
|
||||||
|
rows.append(row)
|
||||||
|
self.assertEqual((stage / "project.godot").read_bytes(), public)
|
||||||
|
self.assertEqual(row["startup_project"]["sha256"],
|
||||||
|
collector.sha(Path(row["startup_project"]["path"])))
|
||||||
|
self.assertTrue(row["capture"]["all_captured_ended"])
|
||||||
|
self.assertFalse(row["capture"]["outer_cleanup_errors"])
|
||||||
|
self.assertEqual(row["capture"]["actual_captured_pids"], [row["process"]["pid"]])
|
||||||
|
self.assertTrue((base / (label + "-collector-receipt.json")).is_file())
|
||||||
|
if label == failure_phase:
|
||||||
|
self.assertFalse(row["passed"])
|
||||||
|
# Claimed success cannot override actual diagnostics/nonzero exit.
|
||||||
|
broken = copy.deepcopy(row)
|
||||||
|
broken["passed"] = True
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
checker.check_parse_process(broken, base)
|
||||||
|
else:
|
||||||
|
checker.check_parse_process(row, base)
|
||||||
|
# Only argv generation is substituted for the Python fake child;
|
||||||
|
# exact Godot argv is asserted separately in the static plan test.
|
||||||
|
with patch.object(collector, "parse_argv", return_value=row["argv"][:-3]):
|
||||||
|
checker.check_parse(row, base, source)
|
||||||
|
for mutation in (
|
||||||
|
lambda r: r["startup_project"].update(name="wrong"),
|
||||||
|
lambda r: r.update(stage_after_sha256="f" * 64),
|
||||||
|
lambda r: r["script_pins"].update({str(base / "guarded.gd"): "f" * 64}),
|
||||||
|
):
|
||||||
|
broken = copy.deepcopy(row)
|
||||||
|
mutation(broken)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
checker.check_parse(broken, base, source)
|
||||||
|
for mutation in (
|
||||||
|
lambda r: r["parse_na"]["AUDIO_CLOSE"].update(reason=""),
|
||||||
|
lambda r: r.update(report={"pid": r["process"]["pid"]}),
|
||||||
|
lambda r: r["capture"].update(actual_captured_pids=[-1]),
|
||||||
|
lambda r: r.update(player_preserved=False),
|
||||||
|
lambda r: r["owned_files"].pop("ended.json"),
|
||||||
|
):
|
||||||
|
broken = copy.deepcopy(row)
|
||||||
|
mutation(broken)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
checker.check_parse_process(broken, base)
|
||||||
|
collector.require(row["passed"], "parse failed")
|
||||||
|
except ValueError:
|
||||||
|
self.assertIsNotNone(failure_phase)
|
||||||
|
finally:
|
||||||
|
cleanup, errors = outer.finish_captured(outer.OWNED)
|
||||||
|
self.assertFalse(errors)
|
||||||
|
self.assertTrue(all(r["engine_ended"] for r in cleanup))
|
||||||
|
self.assertEqual(launched, ["parse-base"] if failure_phase == "parse-base" else
|
||||||
|
["parse-base", "parse-wrapper"] if failure_phase else
|
||||||
|
["parse-base", "parse-wrapper", "R0"])
|
||||||
|
self.assertFalse(any((base / p).exists() for p in ("R0", "P1", "P2", "O0", "P3", "P4")))
|
||||||
|
|
||||||
|
def test_l2_plan_and_restoration(self):
|
||||||
|
plan = collector.execution_plan("engine", Path("/stage"), Path("/out/guarded.gd"))
|
||||||
|
self.assertEqual(len(plan), 9)
|
||||||
|
self.assertEqual(plan[1], ["engine", "--headless", "--path", "/stage", "--check-only",
|
||||||
|
"--script", "/out/measure_p28_01.gd"])
|
||||||
|
self.assertEqual(plan[2][-1], "/out/guarded.gd")
|
||||||
|
self.assertEqual(collector.PHASES[0], ("R0", "R", 1, 0, 10))
|
||||||
|
admission = synthetic_admission()
|
||||||
|
admission.update(planned_argv=plan, phases=collector.PHASES)
|
||||||
|
checker.check_admission(admission)
|
||||||
|
for mutate in (
|
||||||
|
lambda a: a["planned_argv"].pop(1),
|
||||||
|
lambda a: a["planned_argv"][1].remove("--check-only"),
|
||||||
|
lambda a: a["planned_argv"][1].__setitem__(-1, "/out/guarded.gd"),
|
||||||
|
lambda a: a["planned_argv"][3].append("--headless"),
|
||||||
|
):
|
||||||
|
broken = copy.deepcopy(admission)
|
||||||
|
mutate(broken)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
checker.check_admission(broken)
|
||||||
|
for command in plan[3:]:
|
||||||
|
self.assertNotIn("--headless", command)
|
||||||
|
self.assertNotIn("--check-only", command)
|
||||||
|
standard = collector.load_tool(collector.EXTERNAL_IMPORTS[0])
|
||||||
|
with tempfile.TemporaryDirectory() as temp:
|
||||||
|
base = Path(temp)
|
||||||
|
public = b'[application]\n\nconfig/name="The Tree"\n'
|
||||||
|
(base / "project.godot").write_bytes(public)
|
||||||
|
with patch.object(collector, "run_engine", side_effect=RuntimeError("owned failure")), \
|
||||||
|
self.assertRaises(RuntimeError):
|
||||||
|
collector.run_private_engine(standard, None, [], base / "parse-base", {}, base,
|
||||||
|
public, "TheTree-PCK-tests-" + base.name)
|
||||||
|
self.assertEqual((base / "project.godot").read_bytes(), public)
|
||||||
|
|
||||||
def test_j3_guard_and_whole_inventory(self):
|
def test_j3_guard_and_whole_inventory(self):
|
||||||
guard = synthetic_guard()
|
guard = synthetic_guard()
|
||||||
collector.guard_gate(guard, Path("private"), "runtime")
|
collector.guard_gate(guard, Path("private"), "runtime")
|
||||||
@@ -735,18 +896,18 @@ class ReportTests(unittest.TestCase):
|
|||||||
pass
|
pass
|
||||||
return rows, launched
|
return rows, launched
|
||||||
rows, launched = execute()
|
rows, launched = execute()
|
||||||
self.assertEqual(launched, ["import", "R0", "P1", "P2", "O0", "P3", "P4"])
|
self.assertEqual(launched, ["import", "parse-base", "parse-wrapper", "R0", "P1", "P2", "O0", "P3", "P4"])
|
||||||
self.assertEqual(rows[1]["report"]["stop_reason"], "simulation_overloaded")
|
self.assertEqual(rows[3]["report"]["stop_reason"], "simulation_overloaded")
|
||||||
self.assertEqual(execute(failure=True)[1], ["import", "R0"])
|
self.assertEqual(execute(failure=True)[1], ["import", "parse-base", "parse-wrapper", "R0"])
|
||||||
rows, launched = execute(duration=20)
|
rows, launched = execute(duration=20)
|
||||||
self.assertEqual(launched, ["import", "R0", "O0", "P3", "P4"])
|
self.assertEqual(launched, ["import", "parse-base", "parse-wrapper", "R0", "O0", "P3", "P4"])
|
||||||
self.assertEqual([r["phase"] for r in rows if r.get("engines_started") == 0],
|
self.assertEqual([r["phase"] for r in rows if r.get("engines_started") == 0],
|
||||||
["P1", "P2"])
|
["P1", "P2"])
|
||||||
rows, launched = execute(minutes=0)
|
rows, launched = execute(minutes=0)
|
||||||
self.assertEqual(len(launched), 7)
|
self.assertEqual(len(launched), 9)
|
||||||
self.assertIsNone(rows[1]["extrapolation"]["seconds_per_phase"])
|
self.assertIsNone(rows[3]["extrapolation"]["seconds_per_phase"])
|
||||||
self.assertEqual(rows[1]["extrapolation"]["reason"], "no_minutes_observed")
|
self.assertEqual(rows[3]["extrapolation"]["reason"], "no_minutes_observed")
|
||||||
self.assertIsNone(rows[2]["projection"]["projected_total_seconds"])
|
self.assertIsNone(rows[4]["projection"]["projected_total_seconds"])
|
||||||
shortfall = synthetic_report()
|
shortfall = synthetic_report()
|
||||||
shortfall.update(stop_reason="input_shortfall", frames=[], actual_minutes=0)
|
shortfall.update(stop_reason="input_shortfall", frames=[], actual_minutes=0)
|
||||||
checker.check_report(shortfall)
|
checker.check_report(shortfall)
|
||||||
|
|||||||
Reference in New Issue
Block a user