aa60b3e34e
v2-ci / unit-tests (push) Has been cancelled
Acceptance runs for #799 (owner scope 2026-11-12: Windows x64 + Linux) on current master. Fixture: since #808 an UNS folder the authority snapshot does not place is denied under enforcement (fail closed). The canonical soak creates its own "SoakEquipment" folder but never placed it, so on master the very first cycle's UNS monitored item came back bad (retained failed Linux launch onb61f3e05, cycle 1). The rig gains AdditionalFolderScopes (merged into FolderScopes by Enforce) and the soak places its folder there, as a deployed generation does - the same fixture gap #888 fixed for the SDK matrix. No server authorization code, cap, baseline or canonical operation changed; the soak test bodies are otherwise byte-identical tob18f9723. Windows port (test/harness only, opt-in): - DescriptorCountsAsync gains a Windows branch: total = every handle in the process handle table, PKI = disk file/directory handles whose GetFinalPathNameByHandle path lies under a canonicalised PKI root; dedicated thread under the same 5 s bound as the macOS lsof probe. - Descriptor controls enabled on Windows (symlink alias; timeout control via cmd.exe + ping). - LateSlopeRun.ValidateRequest accepts Linux ARM64 or Windows x64; duration/runtime/leak tracking unchanged. - late-slope/analyze.py: expected architecture is a module constant (Linux default Arm64 unchanged). - windows/: prepare.py (win-x64 self-contained 10.0.14 bundle + manifest), launch.ps1 (container.sh equivalent: bundle re-verification, 2/2 controls first, PID/apphost/coreclr verification, external sampler, 7440 s outer deadline), analyze.py (Linux row/window logic, X64), README with every adaptation. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
73 lines
3.9 KiB
Python
73 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate a recorded Windows x64 late-slope run (lmxopcua#799) with the Linux analyzer's own row/window logic.
|
|
|
|
The row ledger, cold cycle-3 caps, first-rejection check, fixed windows and least-squares slopes come unchanged from
|
|
../late-slope/analyze.py (ARCHITECTURE switched to X64). Only the run-envelope checks differ: there is no container, so
|
|
the launcher's PowerShell receipts (run-receipt.json, target-runtime.txt) take the place of the Docker receipts.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
sys.dont_write_bytecode = True
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "late-slope"))
|
|
import analyze as core # noqa: E402
|
|
|
|
core.ARCHITECTURE = "X64"
|
|
APP = core.APP
|
|
|
|
|
|
def read_text(path):
|
|
return path.read_text(encoding="utf-8-sig")
|
|
|
|
|
|
def analyze(run, bundle_receipt, source_revision):
|
|
bundle = json.loads(read_text(bundle_receipt))
|
|
core.require(bundle["sourceRevision"] == source_revision and bundle["rid"] == "win-x64"
|
|
and bundle["runtimeVersion"] == core.RUNTIME and bundle["limits"] == core.BUDGETS, "Wrong Windows bundle")
|
|
rows = [json.loads(line) for line in read_text(run / "resources.jsonl").splitlines() if line.strip()]
|
|
start, samples, completions, rejection, result = core.validate_rows(iter(rows), source_revision)
|
|
mapping = dict(line.split("=", 1) for line in read_text(run / "target-runtime.txt").splitlines() if "=" in line)
|
|
core.require(mapping.get("targetPid") == str(start["processId"])
|
|
and mapping.get("actualExecutable", "").lower().endswith(f"\\app\\{APP}.exe".lower())
|
|
and mapping.get("mappedCoreClr", "").lower().endswith("\\app\\coreclr.dll")
|
|
and mapping.get("coreClrSha256") == bundle["coreClrSha256"],
|
|
"Published Windows apphost or mapped CLR was not verified for the recorded PID")
|
|
test_exit = int(read_text(run / "test-exit-status.txt").strip())
|
|
core.require((test_exit != 0) == bool(rejection), "Original test exit was not preserved")
|
|
controls = core.check_trx(run / "results/descriptor-controls.trx", core.DESCRIPTOR_TESTS)
|
|
workload = core.check_trx(run / "results/late-slope.trx", {core.TEST_NAME}, rejection and rejection["assertionMessage"])
|
|
receipt = json.loads(read_text(run / "run-receipt.json"))
|
|
core.require(receipt["targetVerified"] is True and receipt["testExitStatus"] == test_exit
|
|
and receipt["leftoverTaskProcesses"] == 0, "Launcher did not verify the target or left processes behind")
|
|
wall = receipt["wallSeconds"]
|
|
core.require(start["startupSeconds"] + result["cycleSeconds"] + result["shutdownSeconds"] <= wall,
|
|
"Startup, cycle and shutdown phases exceed measured wall time")
|
|
return {"protocol": core.PROTOCOL, "platform": "windows-x64", "sourceRevision": source_revision,
|
|
"bundleCoreClrSha256": bundle["coreClrSha256"], "diagnosticOutcome": result, "firstColdRejection": rejection,
|
|
"descriptorTests": controls, "experimentTests": workload, "testExitStatus": test_exit, "wallSeconds": wall,
|
|
**core.describe(samples, completions)}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--run", type=Path, required=True)
|
|
parser.add_argument("--bundle-receipt", type=Path, required=True)
|
|
parser.add_argument("--source-revision", required=True)
|
|
options = parser.parse_args()
|
|
core.require(re.fullmatch(r"[0-9a-f]{40}", options.source_revision), "Invalid revision")
|
|
try:
|
|
print(json.dumps(analyze(options.run.resolve(strict=True), options.bundle_receipt, options.source_revision),
|
|
indent=2, allow_nan=False))
|
|
except (OSError, ValueError, KeyError, TypeError) as error:
|
|
print(f"Incomplete Windows late-slope evidence: {error}", file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|