P28-01-COLLECTOR: 現HEADの性能基準:windowed x1/x3・Operations ON collector
This commit is contained in:
+89
-2
@@ -8,6 +8,24 @@ from pathlib import Path
|
||||
import re
|
||||
|
||||
PREFIX = "P28_01_PERF_REPORT="
|
||||
RAW_SOURCE = "clock.performance_budget (product diagnostic, raw, not a verdict)"
|
||||
CAP_SOURCE = "SPEC §5 phase cap"
|
||||
DEADLINE_SOURCE = "H6 probe deadline (cap−60s)"
|
||||
VERDICT_SCOPE = ("Budget verdicts come only from windowed phases, using kernel samples, for "
|
||||
"simulation_x1, simulation_x3 and main_thread_stall. Save, load and path_query "
|
||||
"verdicts are null in this collector, reason: not_measured_with_kernel_definition.")
|
||||
RAW_FIELDS = {"budget_at_warmup_end", "budget_at_phase_end", "kernel_at_warmup_end", "kernel_at_phase_end"}
|
||||
METRICS = {"simulation_x1", "simulation_x3", "main_thread_stall"}
|
||||
R02_FIELDS = {
|
||||
**{"x1_outer_advance_one_minute_wall_" + stat:
|
||||
("advance_one_minute_wall_ms_x1." + stat, stat,
|
||||
"headless outer advance_one_minute wall; not kernel-defined") for stat in ("mean", "p95", "max")},
|
||||
"path_cold_p95": ("path_after_tick_ms.found_cold.p95", "p95", "headless probe path timing; not kernel-defined"),
|
||||
"snapshot_mean": ("save_load_ms.snapshot.mean", "mean", "headless probe snapshot timing; not kernel-defined"),
|
||||
**{kind + "_" + stat: ("save_load_ms." + kind + "." + stat, stat,
|
||||
"headless probe " + kind + " timing; not kernel-defined")
|
||||
for kind in ("manual", "autosave", "load") for stat in ("mean", "max")},
|
||||
}
|
||||
PHASES = {"R0": ("R", 1, 0, 10), "P1": ("R", 1, 30, 120),
|
||||
"P2": ("R", 3, 30, 120), "O0": ("O", 1, 0, 10),
|
||||
"P3": ("O", 1, 30, 120), "P4": ("O", 3, 30, 120)}
|
||||
@@ -28,6 +46,64 @@ INTEGER_FIELDS = {
|
||||
OPAQUE_FIELDS = {"rng_streams", "startup", "kernel_at_warmup_end", "kernel_at_phase_end",
|
||||
"operations_at_end", "restore_result", "save_policy_after"}
|
||||
|
||||
def check_budget_scope(value, report_paths=(), path=(), raw=False):
|
||||
"""Walk the entire receipt; privileges are tied to exact report locations."""
|
||||
if isinstance(value, list):
|
||||
for i, child in enumerate(value):
|
||||
check_budget_scope(child, report_paths, path + (i,), raw)
|
||||
return
|
||||
if not isinstance(value, dict):
|
||||
return
|
||||
raw_root = any(path == root + (name,) for root in report_paths for name in RAW_FIELDS)
|
||||
verdict = any(len(path) == len(root) + 2 and path[:len(root)] == root
|
||||
and path[-2] == "budget_verdict" and path[-1] in METRICS for root in report_paths)
|
||||
has_pass = bool({"passed", "within_budget"} & value.keys())
|
||||
in_report = any(path[:len(root)] == root for root in report_paths)
|
||||
if has_pass and in_report and not raw:
|
||||
need(verdict, "pass flag copied outside kernel verdict")
|
||||
if "budget_verdict" in value:
|
||||
need(path in report_paths and isinstance(value["budget_verdict"], dict)
|
||||
and set(value["budget_verdict"]) <= METRICS, "budget verdict location/metrics")
|
||||
if has_pass and {"limit", "limit_us", "limit_ms"} & value.keys():
|
||||
need(verdict, "budget verdict outside report.budget_verdict")
|
||||
if raw_root:
|
||||
need(value.get("source") == RAW_SOURCE, "raw diagnostic source missing")
|
||||
keys(value, ("source", "data"))
|
||||
if not raw:
|
||||
need("budget_exceeded" not in value, "copied product budget_exceeded")
|
||||
need("limit_us" not in value or verdict, "copied product limit_us")
|
||||
if "limit_seconds" in value:
|
||||
need(path and path[-1] in ("projection", "phase_stop", "probe_deadline"), "limit location")
|
||||
deadline = path[-1] == "probe_deadline"
|
||||
need(not has_pass and value["limit_seconds"] == (1740 if deadline else 1800)
|
||||
and value.get("source") == (DEADLINE_SOURCE if deadline else CAP_SOURCE), "execution limit")
|
||||
for key, child in value.items():
|
||||
check_budget_scope(child, report_paths, path + (key,), raw or (raw_root and key == "data"))
|
||||
|
||||
|
||||
def check_admission(admission):
|
||||
check_budget_scope(admission)
|
||||
need(admission.get("budget_verdict_scope") == VERDICT_SCOPE, "admission verdict scope")
|
||||
reused = admission["reused_r02"]
|
||||
def no_verdict_fields(value):
|
||||
if isinstance(value, dict):
|
||||
need(all(not k.startswith(("limit", "budget")) and k not in ("passed", "within_budget")
|
||||
for k in value), "verdict fields in reused_r02")
|
||||
for child in value.values():
|
||||
no_verdict_fields(child)
|
||||
elif isinstance(value, list):
|
||||
for child in value:
|
||||
no_verdict_fields(child)
|
||||
no_verdict_fields(reused)
|
||||
observations = reused["observations"]
|
||||
need(set(observations) == set(R02_FIELDS), "r02 observations")
|
||||
for name, (field, statistic, scope) in R02_FIELDS.items():
|
||||
item = observations[name]
|
||||
keys(item, ("value", "unit", "statistic", "scope", "r02_field"))
|
||||
need(number(item["value"]) and item["value"] >= 0, "r02 value")
|
||||
need((item["unit"], item["statistic"], item["scope"], item["r02_field"]) ==
|
||||
("ms", statistic, scope, field), "r02 observation provenance")
|
||||
|
||||
|
||||
def normalize_report(value, path=()):
|
||||
"""Single ingestion boundary, also used when checking raw stdout binding."""
|
||||
@@ -187,6 +263,12 @@ def interval(report, name):
|
||||
|
||||
|
||||
def check_report(report):
|
||||
check_budget_scope(report, ((),))
|
||||
for name in RAW_FIELDS:
|
||||
keys(report[name], ("source", "data"))
|
||||
equal(report["probe_deadline"], {"limit_seconds": 1740, "source": DEADLINE_SOURCE}, "probe deadline")
|
||||
equal(report["budget_verdict"], budget_verdict(report["budget_at_warmup_end"]["data"],
|
||||
report["budget_at_phase_end"]["data"], report["speed"]), "kernel interval budget")
|
||||
keys(report, ("schema", "pid", "phase", "input", "speed", "metadata", "input_sha256",
|
||||
"frames", "stop_reason", "warmup", "measurement", "actual_minutes",
|
||||
"elapsed_wall_seconds", "kernel_at_phase_end", "operations_at_end", "end_wall",
|
||||
@@ -329,8 +411,8 @@ def check_report(report):
|
||||
interval(report, name)
|
||||
need(report["budget_scope"] == "kernel process_real_delta only; excludes observer_us", "budget scope")
|
||||
need(report["render_submit_note"] == "CPU側の描画提出wall、GPU時間ではない", "render submit scope")
|
||||
equal(report["budget_verdict"], budget_verdict(report["budget_at_warmup_end"],
|
||||
report["budget_at_phase_end"], speed), "kernel interval budget")
|
||||
equal(report["budget_verdict"], budget_verdict(report["budget_at_warmup_end"]["data"],
|
||||
report["budget_at_phase_end"]["data"], speed), "kernel interval budget")
|
||||
need(number(report["startup_wall_seconds"]) and report["startup_wall_seconds"] >= 0, "startup timing")
|
||||
for save in report["save_events"]:
|
||||
keys(save, ("frame", "game_minute", "ok", "operation", "path", "sample_counts"))
|
||||
@@ -347,6 +429,10 @@ def check_report(report):
|
||||
|
||||
def check_receipt(receipt, directory):
|
||||
from collect_p28_01 import OWNED_FILES, projection, extrapolation, guard_gate
|
||||
check_budget_scope(receipt, tuple(("phases", i, "report")
|
||||
for i, row in enumerate(receipt["phases"]) if "report" in row))
|
||||
check_admission(receipt["admission"])
|
||||
equal(receipt["admission"], json.loads((directory / "admission.json").read_text()), "admission binding")
|
||||
need(receipt["source_before"] == receipt["source_after"], "source map changed")
|
||||
need(receipt["default_player_before"] == receipt["default_player_after"], "player changed")
|
||||
need(not receipt["cleanup_errors"] and all(r["engine_ended"] is True for r in receipt["cleanup"]), "cleanup")
|
||||
@@ -359,6 +445,7 @@ def check_receipt(receipt, directory):
|
||||
rows = receipt["phases"]
|
||||
need([r["phase"] for r in rows] == ["import", *PHASES], "phase order")
|
||||
for row in rows:
|
||||
equal(row["phase_stop"], {"limit_seconds": 1800, "source": CAP_SOURCE}, "phase cap origin")
|
||||
forecast = None
|
||||
if row["phase"] in ("P1", "P2", "P3", "P4"):
|
||||
source = PHASES[row["phase"]][0]
|
||||
|
||||
+28
-18
@@ -13,7 +13,7 @@ import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
from check_p28_01_report import check_report, check_receipt, check_probe, normalize_report
|
||||
from check_p28_01_report import check_report, check_receipt, check_probe, normalize_report, check_admission, R02_FIELDS, VERDICT_SCOPE, CAP_SOURCE
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = HERE.parents[5]
|
||||
FIXTURE_SHA = "eb4f770071eb07cd7e136fe54c3f78768cc8de7a4264e62a9af1f06a8f90f9bd"
|
||||
@@ -110,7 +110,7 @@ def projection(stage0):
|
||||
if evidence["seconds_per_phase"] is not None else None)
|
||||
return {"seconds_per_phase": evidence["seconds_per_phase"],
|
||||
"startup_wall_seconds": stage0["report"]["startup_wall_seconds"],
|
||||
"projected_total_seconds": seconds, "limit_seconds": LIMIT,
|
||||
"projected_total_seconds": seconds, "limit_seconds": LIMIT, "source": CAP_SOURCE,
|
||||
"stage0_phase": stage0["phase"],
|
||||
"stop_reason": "projected_over_limit" if seconds is not None and seconds > LIMIT else None}
|
||||
|
||||
@@ -165,7 +165,8 @@ def phase_schedule(rows):
|
||||
forecast = projection(stage0)
|
||||
if forecast["stop_reason"]:
|
||||
rows.append({"phase": label, "passed": False, "engines_started": 0,
|
||||
"stop_reason": "projected_over_limit", "projection": forecast})
|
||||
"stop_reason": "projected_over_limit", "projection": forecast,
|
||||
"phase_stop": {"limit_seconds": LIMIT, "source": CAP_SOURCE}})
|
||||
continue
|
||||
yield index, phase, forecast
|
||||
|
||||
@@ -195,7 +196,8 @@ def run_engine(outer, command, folder, environment):
|
||||
capture.get("passed") is True, capture.get("all_captured_ended") is True,
|
||||
len(capture.get("actual_captured_pids", [])) == 1, all(raw.values()), not bad))
|
||||
row = {"passed": clean, "process": result, "capture": capture, "raw": raw,
|
||||
"diagnostics": bad, "timeout_seconds": LIMIT, "argv": command}
|
||||
"diagnostics": bad, "timeout_seconds": LIMIT, "argv": command,
|
||||
"phase_stop": {"limit_seconds": LIMIT, "source": CAP_SOURCE}}
|
||||
row["owned_files"] = {name: {"sha256": sha(folder / name),
|
||||
"bytes": (folder / name).stat().st_size}
|
||||
for name in OWNED_FILES if (folder / name).is_file()}
|
||||
@@ -218,6 +220,24 @@ def inputs(readonly):
|
||||
return {"manifest_sha256": sha(manifest_path), "files": pins}
|
||||
|
||||
|
||||
def reused_observations(readonly, pins):
|
||||
relative = "run-r02/after/evidence/report.json"
|
||||
path = readonly / relative
|
||||
require(sha(path) == pins["files"][relative], "r02 report SHA changed")
|
||||
raw = json.loads(path.read_text())
|
||||
observations = {}
|
||||
for name, (field, statistic, scope) in R02_FIELDS.items():
|
||||
value = raw
|
||||
for part in field.split("."):
|
||||
value = value[part]
|
||||
observations[name] = {"value": value, "unit": "ms", "statistic": statistic,
|
||||
"scope": scope, "r02_field": field}
|
||||
return {"execution_sha256": pins["files"]["run-r02/execution.json"],
|
||||
"report_sha256": pins["files"][relative],
|
||||
"scope": "x1 headless Operations OFF; reused without remeasurement",
|
||||
"observations": observations}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--readonly", type=Path, required=True)
|
||||
@@ -262,19 +282,9 @@ def main():
|
||||
admission = {"commit": commit, "source_map": before, "source_map_sha256": digest(before),
|
||||
"readonly": pins, "engine_sha256": ENGINE_SHA, "engine_version": VERSION,
|
||||
"fixture_sha256": FIXTURE_SHA, "planned_argv": planned, "phases": PHASES,
|
||||
"reused_r02": {"execution_sha256": pins["files"]["run-r02/execution.json"],
|
||||
"report_sha256": pins["files"]["run-r02/after/evidence/report.json"],
|
||||
"scope": "x1 headless Operations OFF; reused without remeasurement",
|
||||
"tick_ms": {"mean": 826.6, "p95": 1702.7, "max": 2918.1},
|
||||
"path_cold_p95_ms": 0.61, "snapshot_mean_ms": 77.6,
|
||||
"manual_save_ms": 297, "autosave_ms": 327,
|
||||
"load_mean_ms": 3193, "load_max_ms": 3368,
|
||||
"budget_comparisons_ms": {
|
||||
"x1_tick_mean": {"value": 826.6, "limit": 4, "passed": False},
|
||||
"path_cold_p95": {"value": 0.61, "limit": 2, "passed": True},
|
||||
"manual_save": {"value": 297, "limit": 5000, "passed": True},
|
||||
"autosave": {"value": 327, "limit": 5000, "passed": True},
|
||||
"load_max": {"value": 3368, "limit": 8000, "passed": True}}}}
|
||||
"budget_verdict_scope": VERDICT_SCOPE,
|
||||
"reused_r02": reused_observations(args.readonly, pins)}
|
||||
check_admission(admission)
|
||||
if args.check:
|
||||
print(json.dumps(dict(admission, stages_created=0, engines_started=0), ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
@@ -374,7 +384,7 @@ def main():
|
||||
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
|
||||
after = source_map(standard, ROOT)
|
||||
player_after = standard.default_player(ROOT)
|
||||
receipt = {"phases": rows, "error": error, "source_before": before, "source_after": after,
|
||||
receipt = {"admission": admission, "phases": rows, "error": error, "source_before": before, "source_after": after,
|
||||
"probe_check": probe_check,
|
||||
"default_player_before": player, "default_player_after": player_after,
|
||||
"cleanup": cleanup, "cleanup_errors": cleanup_errors,
|
||||
|
||||
+10
-8
@@ -266,7 +266,9 @@ func begin() -> void:
|
||||
"render_submit_note": "CPU側の描画提出wall、GPU時間ではない",
|
||||
"observer_note": "observer_us includes reads, serialization and frame emission; excludes its own timing line and final report; never subtracted",
|
||||
"sample_definition": "is_minute_sample marks a changed game minute; other frames carry counts_from_minute",
|
||||
"budget_at_warmup_end": {}, "save_events": save_events, "key_form": key_forms,
|
||||
"budget_at_warmup_end": {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": {}},
|
||||
"kernel_at_warmup_end": {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": null, "reason": "warmup_not_reached"},
|
||||
"probe_deadline": {"limit_seconds": 1740, "source": "H6 probe deadline (cap−60s)"}, "save_events": save_events, "key_form": key_forms,
|
||||
"count_definitions": {"registered": "service collections; walkable cells",
|
||||
"active": "guests exclude SPAWNING/LEAVING/DESPAWNING; staff current attended unended shift; facilities OPERATING",
|
||||
"drawn": "world_preview submitted model serial and viewport geometry"},
|
||||
@@ -355,8 +357,8 @@ func begin() -> void:
|
||||
last_stall_count = initial_budget.main_thread_stall.lifetime_count
|
||||
if warmup == 0:
|
||||
warmup_ended = true
|
||||
report["kernel_at_warmup_end"] = clock.get_performance_reports()
|
||||
report["budget_at_warmup_end"] = initial_budget
|
||||
report["kernel_at_warmup_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": clock.get_performance_reports()}
|
||||
report["budget_at_warmup_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": initial_budget}
|
||||
report["startup_wall_seconds"] = float(start_us) / 1000000.0
|
||||
report["start_minute"] = start_minute
|
||||
report["start_wall"] = Time.get_unix_time_from_system()
|
||||
@@ -430,8 +432,8 @@ func sample(observer_started_us: int) -> void:
|
||||
previous_minute = minute
|
||||
if not warmup_ended and minute - start_minute >= warmup:
|
||||
warmup_ended = true
|
||||
report["kernel_at_warmup_end"] = clock.get_performance_reports()
|
||||
report["budget_at_warmup_end"] = budget_snapshot()
|
||||
report["kernel_at_warmup_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": clock.get_performance_reports()}
|
||||
report["budget_at_warmup_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": budget_snapshot()}
|
||||
if warmup > 0:
|
||||
measurement_start = minute
|
||||
var operations: Dictionary = runtime.get_operations_report()
|
||||
@@ -540,9 +542,9 @@ func finish(reason: String) -> void:
|
||||
report["actual_minutes"] = int(clock.absolute_minute) - start_minute if start_us > 0 else 0
|
||||
report["elapsed_wall_seconds"] = float(Time.get_ticks_usec() - start_us) / 1000000.0 if start_us > 0 else 0.0
|
||||
report["end_wall"] = Time.get_unix_time_from_system()
|
||||
report["kernel_at_phase_end"] = clock.get_performance_reports()
|
||||
report["budget_at_phase_end"] = budget_snapshot()
|
||||
report["budget_verdict"] = kernel_verdict(report.budget_at_warmup_end, report.budget_at_phase_end)
|
||||
report["kernel_at_phase_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": clock.get_performance_reports()}
|
||||
report["budget_at_phase_end"] = {"source": "clock.performance_budget (product diagnostic, raw, not a verdict)", "data": budget_snapshot()}
|
||||
report["budget_verdict"] = kernel_verdict(report.budget_at_warmup_end.data, report.budget_at_phase_end.data)
|
||||
report["operations_at_end"] = runtime.get_operations_report()
|
||||
report["warmup"] = summary("warmup")
|
||||
report["measurement"] = summary("measurement")
|
||||
|
||||
+95
-6
@@ -118,10 +118,97 @@ def synthetic_report():
|
||||
"statistics": {"count": 10, "average_us": 3000, "p95_us": 3000, "maximum_us": 3000}}
|
||||
for k, aggregate, limit in (("simulation_x1", "average", 4000),
|
||||
("main_thread_stall", "maximum", 50000))}
|
||||
for name in checker.RAW_FIELDS:
|
||||
report[name] = {"source": checker.RAW_SOURCE, "data": report[name]}
|
||||
report["probe_deadline"] = {"limit_seconds": 1740, "source": checker.DEADLINE_SOURCE}
|
||||
return report
|
||||
|
||||
|
||||
def synthetic_admission():
|
||||
return {"budget_verdict_scope": checker.VERDICT_SCOPE, "reused_r02": {"observations": {
|
||||
name: {"value": 1.25, "unit": "ms", "statistic": stat, "scope": scope, "r02_field": field}
|
||||
for name, (field, stat, scope) in checker.R02_FIELDS.items()}}}
|
||||
|
||||
|
||||
class ReportTests(unittest.TestCase):
|
||||
def test_r1_recursive_verdict_scope(self):
|
||||
report = synthetic_report()
|
||||
receipt = {"admission": synthetic_admission(), "phases": [{"report": report, "passed": True}],
|
||||
"process": {"passed": True}, "capture": {"passed": True},
|
||||
"probe_check": {"passed": True}, "passed": True}
|
||||
checker.check_budget_scope(receipt, (("phases", 0, "report"),))
|
||||
for flag in ("passed", "within_budget"):
|
||||
for limit in ("limit", "limit_us", "limit_ms"):
|
||||
for location in ("admission", "extra"):
|
||||
broken = copy.deepcopy(receipt)
|
||||
broken.setdefault(location, {})["nested"] = [{flag: True, limit: 4}]
|
||||
with self.subTest(flag=flag, limit=limit, location=location), self.assertRaises(ValueError):
|
||||
checker.check_budget_scope(broken, (("phases", 0, "report"),))
|
||||
self.reject(lambda r: r.update(extra={"passed": True, "limit": 4}))
|
||||
self.reject(lambda r: r["budget_verdict"].update(path_query=copy.deepcopy(r["budget_verdict"]["simulation_x1"])))
|
||||
|
||||
def test_r2_raw_diagnostics_preserved(self):
|
||||
r = synthetic_report()
|
||||
original = [{"limit_us": 4000, "budget_exceeded": True, "measured_us": 5000}]
|
||||
r["kernel_at_phase_end"]["data"] = copy.deepcopy(original)
|
||||
checker.check_report(r)
|
||||
self.assertEqual(r["kernel_at_phase_end"]["data"], original)
|
||||
for name in checker.RAW_FIELDS:
|
||||
self.reject(lambda r, name=name: r[name].pop("source"))
|
||||
self.reject(lambda r, name=name: r[name].update(source="invented"))
|
||||
for field, value in (("budget_exceeded", False), ("limit_us", 4000), ("passed", True)):
|
||||
self.reject(lambda r, field=field, value=value: r.update(extra={field: value}))
|
||||
self.reject(lambda r: r["kernel_at_phase_end"].update(data={"passed": True, "limit_us": 4000}))
|
||||
|
||||
def test_r3_execution_limit_origin(self):
|
||||
projection = collector.projection({"phase": "R0", "report": {"startup_wall_seconds": 1},
|
||||
"extrapolation": {"seconds_per_phase": 20}})
|
||||
valid = {"projection": projection,
|
||||
"phase_stop": {"limit_seconds": 1800, "source": checker.CAP_SOURCE},
|
||||
"probe_deadline": {"limit_seconds": 1740, "source": checker.DEADLINE_SOURCE}}
|
||||
checker.check_budget_scope(valid)
|
||||
for location in valid:
|
||||
for change in ({"passed": True}, {"limit_seconds": 1799}, {"source": "wrong"}):
|
||||
broken = copy.deepcopy(valid)
|
||||
broken[location].update(change)
|
||||
with self.subTest(location=location, change=change), self.assertRaises(ValueError):
|
||||
checker.check_budget_scope(broken)
|
||||
with self.assertRaises(ValueError):
|
||||
checker.check_budget_scope({"elsewhere": {"limit_seconds": 1800, "source": checker.CAP_SOURCE}})
|
||||
self.reject(lambda r: r["probe_deadline"].update(limit_seconds=1800))
|
||||
|
||||
def test_r4_observations_and_pinned_input(self):
|
||||
admission = synthetic_admission()
|
||||
checker.check_admission(admission)
|
||||
for key in ("limit", "limit_ms", "passed", "budget_comparisons_ms", "budget_exceeded"):
|
||||
broken = copy.deepcopy(admission)
|
||||
broken["reused_r02"]["nested"] = [{key: 4}]
|
||||
with self.subTest(key=key), self.assertRaises(ValueError):
|
||||
checker.check_admission(broken)
|
||||
for key in ("unit", "statistic", "scope", "r02_field"):
|
||||
broken = copy.deepcopy(admission)
|
||||
broken["reused_r02"]["observations"]["manual_max"].pop(key)
|
||||
with self.subTest(key=key), self.assertRaises(ValueError):
|
||||
checker.check_admission(broken)
|
||||
# Synthetic source with distinct exact values tests extraction, not rounded constants.
|
||||
raw = {}
|
||||
for i, (field, _, _) in enumerate(checker.R02_FIELDS.values()):
|
||||
target = raw
|
||||
parts = field.split(".")
|
||||
for part in parts[:-1]:
|
||||
target = target.setdefault(part, {})
|
||||
target[parts[-1]] = i + .123456789
|
||||
pins = {"files": {"run-r02/after/evidence/report.json": "a" * 64,
|
||||
"run-r02/execution.json": "b" * 64}}
|
||||
with patch.object(collector, "sha", return_value="a" * 64), \
|
||||
patch.object(Path, "read_text", return_value=json.dumps(raw)):
|
||||
reused = collector.reused_observations(Path("readonly"), pins)
|
||||
for i, name in enumerate(checker.R02_FIELDS):
|
||||
self.assertEqual(reused["observations"][name]["value"], i + .123456789)
|
||||
checker.check_admission({"budget_verdict_scope": checker.VERDICT_SCOPE, "reused_r02": reused})
|
||||
with patch.object(collector, "sha", return_value="c" * 64), self.assertRaises(ValueError):
|
||||
collector.reused_observations(Path("readonly"), pins)
|
||||
|
||||
def reject(self, mutate):
|
||||
report = synthetic_report()
|
||||
mutate(report)
|
||||
@@ -225,20 +312,22 @@ class ReportTests(unittest.TestCase):
|
||||
"report": r, "capture": {"actual_captured_pids": [123]},
|
||||
"extrapolation": collector.extrapolation(r),
|
||||
"guard": synthetic_guard(), "runtime_profile": "private", "runtime_name": "runtime"}
|
||||
receipt = {"source_before": {"x": "a"}, "source_after": {"x": "a"},
|
||||
receipt = {"admission": synthetic_admission(), "source_before": {"x": "a"}, "source_after": {"x": "a"},
|
||||
"default_player_before": {}, "default_player_after": {}, "cleanup_errors": [],
|
||||
"cleanup": [{"engine_ended": True}], "error": None, "passed": True,
|
||||
"phases": [{"phase": "import", "passed": True, "timeout_seconds": 1800, "raw": {}}, row]}
|
||||
probe_text = (HERE / "measure_p28_01.gd").read_text()
|
||||
receipt["probe_check"] = {"sha256": hashlib.sha256(probe_text.encode()).hexdigest(), "passed": True}
|
||||
for phase in receipt["phases"]:
|
||||
phase["phase_stop"] = {"limit_seconds": 1800, "source": checker.CAP_SOURCE}
|
||||
phase["probe_check"] = receipt["probe_check"].copy()
|
||||
phase["owned_files"] = {name: {"sha256": hashlib.sha256(raw).hexdigest(), "bytes": len(raw)}
|
||||
for name in collector.OWNED_FILES}
|
||||
with patch.object(checker, "PHASES", {"O0": ("O", 1, 0, 10)}), \
|
||||
patch.object(Path, "read_bytes", return_value=raw), \
|
||||
patch.object(Path, "read_text", autospec=True,
|
||||
side_effect=lambda p: probe_text if p.name == "measure_p28_01.gd" else raw.decode()):
|
||||
side_effect=lambda p: (probe_text if p.name == "measure_p28_01.gd" else
|
||||
json.dumps(receipt["admission"]) if p.name == "admission.json" else raw.decode())):
|
||||
checker.check_receipt(receipt, Path("synthetic"))
|
||||
for label, mutate in {
|
||||
"source": lambda x: x.update(source_after={"x": "b"}),
|
||||
@@ -279,9 +368,9 @@ class ReportTests(unittest.TestCase):
|
||||
for reason, count in (("interval_exceeds_window", 600),
|
||||
("no_interval_samples", 30), ("counts_unavailable", None)):
|
||||
r = synthetic_report()
|
||||
r["budget_at_phase_end"]["simulation_x1"]["lifetime_count"] = count
|
||||
r["budget_verdict"] = checker.budget_verdict(r["budget_at_warmup_end"],
|
||||
r["budget_at_phase_end"], 1)
|
||||
r["budget_at_phase_end"]["data"]["simulation_x1"]["lifetime_count"] = count
|
||||
r["budget_verdict"] = checker.budget_verdict(r["budget_at_warmup_end"]["data"],
|
||||
r["budget_at_phase_end"]["data"], 1)
|
||||
self.assertEqual(r["budget_verdict"]["simulation_x1"]["reason"], reason)
|
||||
self.assertIsNone(r["budget_verdict"]["simulation_x1"]["value"])
|
||||
checker.check_report(r)
|
||||
@@ -322,7 +411,7 @@ class ReportTests(unittest.TestCase):
|
||||
r["metadata"]["machine"]["memory_bytes"] = 24000000000.0
|
||||
r["pid"] = 123.0
|
||||
r["frames"][0]["registered"]["guests"] = 3.0
|
||||
r["budget_at_phase_end"]["simulation_x1"]["samples_us"] = [3000.0] * 40
|
||||
r["budget_at_phase_end"]["data"]["simulation_x1"]["samples_us"] = [3000.0] * 40
|
||||
normalized = collector.normalize_report(json.loads(json.dumps(r)))
|
||||
checker.check_report(normalized)
|
||||
self.assertIs(type(normalized["pid"]), int)
|
||||
|
||||
Reference in New Issue
Block a user