[CI] replace E1 pixel races with observed protocol wait controls

This commit is contained in:
2026-09-20 10:27:41 -04:00
parent 08c63f77e2
commit 10bfd29fc5
8 changed files with 324 additions and 49 deletions
@@ -0,0 +1,109 @@
// E1: real transport wait boundaries, with a peer that cannot complete until
// either the client parks or its emission has already returned. No GPU or
// scheduling-delay assumption decides whether the wait was observed.
#if MGTEST_HAVE_FORK
namespace {
struct E1BoundaryPeer : Codec::WireVerbSink {
BudgetBell& bell;
std::atomic<Bool> clientReturned{false};
std::atomic<Uint32> seen{0};
MGPWireOp expected;
explicit E1BoundaryPeer(BudgetBell& value, MGPWireOp op) : bell(value), expected(op) {}
Bool Apply(MGPWireOp op) {
if (op != expected) ::_exit(84);
++seen;
while (!bell.entered.load() && !clientReturned.load()) std::this_thread::yield();
return true;
}
Bool OnGenerateMipmap(const MGPMipPlan&) override { return Apply(MGPWireOp::GenerateMipmap); }
Bool OnClear(const MGPClear&) override { return Apply(MGPWireOp::Clear); }
void Install() {
if (Srv::ServerLoopInstance().RunProbeOnApplyThreadForTesting([](void* self) {
auto& decoder = Srv::ServerSessionInstance().Applier().*PeerMember(DecoderTag{});
decoder.SetVerbSink(static_cast<E1BoundaryPeer*>(self));
return MOBILEGL_OK;
}, this) != MOBILEGL_OK) ::_exit(82);
}
};
void CheckE1Boundary(Bool serverCap, MGPWireOp op, Bool expectWait, const char* diagnostic) {
const auto child = RunInChild([=] {
::alarm(15);
const char* barrier = std::getenv("MOBILEGL_IPC_VERB_BARRIER");
if (barrier && std::strcmp(barrier, "0") && std::strcmp(barrier, "1")) ::_exit(80);
MG_Config::Ipc.VerbBarrier = barrier ? static_cast<Uint32>(barrier[0] - '0') : 1;
MG_Config::Ipc.BatchWaits = 1;
MG_Config::Ipc.RunAhead = 1;
MG_Config::Ipc.SpinUs = 0;
MG_Config::Transport = MG_Config::TransportMode::InProcess;
const Uint64 caps = serverCap ? static_cast<Uint64>(kCapRunAheadApply) : 0;
Srv::ServerSessionInstance().SetCapabilityBits(caps);
Srv::ServerSessionInstance().SetConsumedSubsystems(kMGPipeSubsystemsMigratedAtP4a);
AdoptSnapshot(MakeSnapshot(kMGPipeSubsystemsMigratedAtP4a, caps));
auto& session = ClientSessionInstance();
if (session.Start(MG_Config::TransportMode::InProcess, {}) != MOBILEGL_OK) ::_exit(81);
auto& producer = session.Producer();
BudgetBell bell(producer.SelfDoorbell());
E1BoundaryPeer peer(bell, op);
peer.Install();
const Uint64 before = session.Control()->submittedSeq.load();
producer.*PeerMember(ProducerBellTag{}) = &bell;
const MGPMipPlan mip{};
const MGPClear clear{};
const Uint64 seq = op == MGPWireOp::GenerateMipmap
? session.EmitAndWait(op, &mip, sizeof(mip), nullptr, 0, nullptr, 0, nullptr)
: session.EmitAndWait(op, &clear, sizeof(clear), nullptr, 0, nullptr, 0, nullptr);
// Observe BEFORE allowing the peer to complete a non-waiting emission.
const Bool parked = bell.entered.load();
const Bool appliedBeforeReturn = session.Control()->appliedSeq.load() >= seq;
peer.clientReturned.store(true);
producer.*PeerMember(ProducerBellTag{}) = bell.original;
if (session.WaitForApplied(seq, 2000) != Transport::SessionWait::Reached) ::_exit(85);
if (seq <= before || peer.seen.load() != 1 || session.Control()->appliedSeq.load() < seq) ::_exit(86);
const Bool correct = expectWait ? parked && appliedBeforeReturn : !parked && !appliedBeforeReturn;
std::printf("E1 observation: op=%s cap=%u barrier=%u parked=%u applied_before_return=%u emitted=1 peer_seen=1\n",
op == MGPWireOp::GenerateMipmap ? "GenerateMipmap" : "Clear",
static_cast<unsigned>(serverCap), MG_Config::Ipc.VerbBarrier,
static_cast<unsigned>(parked), static_cast<unsigned>(appliedBeforeReturn));
std::fflush(stdout);
session.Stop();
::_exit(correct ? 0 : 101);
});
ASSERT_TRUE(WIFEXITED(child.Status)) << DescribeStatus(child) << '\n' << child.Log;
const int code = WEXITSTATUS(child.Status);
ASSERT_TRUE(code == 0 || code == 101) << "E1 probe/setup failed: " << DescribeStatus(child) << '\n' << child.Log;
EXPECT_EQ(code, 0) << diagnostic;
}
} // namespace
#endif
TEST(RemoteWaitBoundaryControl, AppliedClassWaitsWithRunAheadCap) {
#if MGTEST_HAVE_FORK
ASSERT_EQ(MGPipeWaitClassFor(MGPWireOp::GenerateMipmap), kWaitApplied);
CheckE1Boundary(true, MGPWireOp::GenerateMipmap, true,
"E1 wait boundary: GenerateMipmap returned before apply");
#else
GTEST_SKIP() << "E1 requires POSIX child isolation";
#endif
}
TEST(RemoteWaitBoundaryControl, MissingServerCapKeepsClearLockstep) {
#if MGTEST_HAVE_FORK
ASSERT_EQ(MGPipeWaitClassFor(MGPWireOp::Clear), kWaitNone);
CheckE1Boundary(false, MGPWireOp::Clear, true,
"E1 wait boundary: Clear without server cap returned before apply");
#else
GTEST_SKIP() << "E1 requires POSIX child isolation";
#endif
}
TEST(RemoteWaitBoundaryControl, ServerCapAllowsClearToRunAhead) {
#if MGTEST_HAVE_FORK
ASSERT_EQ(MGPipeWaitClassFor(MGPWireOp::Clear), kWaitNone);
CheckE1Boundary(true, MGPWireOp::Clear, false,
"E1 positive boundary: Clear with server cap waited for apply");
#else
GTEST_SKIP() << "E1 requires POSIX child isolation";
#endif
}
@@ -2710,6 +2710,8 @@ TEST(RemoteRunAhead, OnlyTheTextureHalfOfResourceSubDataWantsItsReply) {
}
#include "RemoteClientE1Controls.inc"
int main(int argc, char** argv) {
namespace fs = std::filesystem;
const fs::path path =
+6
View File
@@ -87,6 +87,12 @@ expect FAILED "the knob leaves the selection green" -- run_split green
expect FAILED "the baseline is already red" -- run_split red-baseline
# P5 is complete: losing the runtime implementation must no longer disarm the gate.
expect FAILED "every split entry skipped (implementation lost)" -- run_split all-skipped
expect FAILED "E1 missing boundary probes" -- run_split e1-empty
expect FAILED "E1 own baseline is already red" -- run_split e1-red-baseline
expect FAILED "E1 restored boundary remains red" -- run_split e1-restore-red
expect FAILED "E1 record never reaches the peer" -- run_split e1-no-peer
expect FAILED "E1 observed no real emission" -- run_split e1-no-emit
expect FAILED "E1 incorrectly waits every record" -- run_split e1-waitall
echo
echo "=== the retrace lane's pull-library control (scripts/ci/retrace_pull_library_control.sh)"
+18 -8
View File
@@ -13,12 +13,14 @@ set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)" || exit 1
HELPER="${HERE}/../../MobileGL/MG_IntegrationTest/Harness/split_log_paths.py"
trap 'cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"; cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"; cp "${WORK}/dropdraw.orig" "${HERE}/retrace_drop_draw_control.sh"; cp "${WORK}/helper.orig" "${HELPER}"; rm -rf "${WORK}"' EXIT
WAIT_HELPER="${HERE}/wait_boundary_negative_control.py"
trap 'cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"; cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"; cp "${WORK}/dropdraw.orig" "${HERE}/retrace_drop_draw_control.sh"; cp "${WORK}/helper.orig" "${HELPER}"; cp "${WORK}/wait.orig" "${WAIT_HELPER}"; rm -rf "${WORK}"' EXIT
cp "${HERE}/split_negative_controls.sh" "${WORK}/split.orig"
cp "${HERE}/retrace_pull_library_control.sh" "${WORK}/retrace.orig"
cp "${HERE}/retrace_drop_draw_control.sh" "${WORK}/dropdraw.orig"
cp "${HELPER}" "${WORK}/helper.orig"
cp "${WAIT_HELPER}" "${WORK}/wait.orig"
echo "=== baseline: the smoke test must be GREEN before anything is perturbed"
if ! bash "${HERE}/control_smoke_test.sh" > "${WORK}/before.log" 2>&1; then
@@ -31,9 +33,9 @@ tail -1 "${WORK}/before.log"
echo
echo "=== perturbation: remove the evidence check from both controls"
python3 - "${HERE}/split_negative_controls.sh" "${HERE}/retrace_pull_library_control.sh" \
"${HERE}/retrace_drop_draw_control.sh" <<'PY' || exit 1
"${HERE}/retrace_drop_draw_control.sh" "${WAIT_HELPER}" <<'PY' || exit 1
import sys
split, retrace, dropdraw = sys.argv[1], sys.argv[2], sys.argv[3]
split, retrace, dropdraw, wait = sys.argv[1:]
# (file, [(needle, the prefix the line must start with)]). The draw-drop control has THREE
# evidence checks rather than one, because "the picture went red" has three different ways of
# being somebody else's red: the SSIM never fell, the library never said the knob armed, and the
@@ -49,16 +51,17 @@ split, retrace, dropdraw = sys.argv[1], sys.argv[2], sys.argv[3]
# nothing was ever silently proved - but the one control it was most about was never perturbed.
# Reproduced on p5/joint@e61d0012 before this line changed.
#
# AND THE SPLIT CONTROL HAS THREE EVIDENCE CHECKS, NOT ONE, so all three are reverted. E1's is a
# read of the entry's own private library log, E3(a)'s ctest-output regex is the `elif` above,
# and E3(a)'s private-log half is ID-65's addition. Perturbing only one of the three leaves the
# AND THE SPLIT CONTROL HAS THREE EVIDENCE CHECKS, NOT ONE. E1 checks each boundary
# probe's own observation and assertion; E3(a) checks its assertion and private log.
# All three are reverted: perturbing only one leaves the
# other two catching the smoke test's "unrelated failure" case, and the case never flips - which
# is what this script measured the first time the perturbation actually applied.
rules = [
(split, [('LINE', '"${log_helper}" assertion', ('if ! ', 'elif ! ')),
('SUBST', '"[A-Za-z_][A-Za-z_0-9]*"\\}\' || exit 1', '"[A-Za-z_][A-Za-z_0-9]*"\\}\' || true'),
('SUBST', '"${private_evidence}" "${name}" || exit 1',
'"${private_evidence}" "${name}" || true')]),
(wait, [('SUBST', 'if not own_negative(text, observation, diagnostic):',
'if False: # E1 evidence check removed by red-check')]),
(retrace, [('LINE', 'grep -qF "${EVIDENCE}"', ('if ! ',))]),
(dropdraw, [('LINE', 'awk -v a="${ssim}"', ('if ! ',)),
('LINE', '[ -z "${armed_line}" ]', ('if ',)),
@@ -134,8 +137,9 @@ echo
cp "${WORK}/split.orig" "${HERE}/split_negative_controls.sh"
cp "${WORK}/retrace.orig" "${HERE}/retrace_pull_library_control.sh"
cp "${WORK}/dropdraw.orig" "${HERE}/retrace_drop_draw_control.sh"
cp "${WORK}/wait.orig" "${WAIT_HELPER}"
echo "=== ID-62 perturbation: remove only the skip check"
python3 - "${HELPER}" <<'PY' || exit 1
python3 - "${HELPER}" "${WAIT_HELPER}" <<'PY' || exit 1
from pathlib import Path
import sys
path = Path(sys.argv[1])
@@ -144,6 +148,12 @@ needle = ' if skipped:\n'
if text.count(needle) != 1:
raise SystemExit('expected exactly one skip check')
path.write_text(text.replace(needle, ' if False: # skip check removed by red-check\n'))
path = Path(sys.argv[2])
text = path.read_text()
needle = ' if case.find("skipped") is not None or case.get("status") in {"notrun", "disabled"}:\n'
if text.count(needle) != 1:
raise SystemExit('expected exactly one E1 skip check')
path.write_text(text.replace(needle, ' if False: # E1 skip check removed by red-check\n'))
PY
bash "${HERE}/control_smoke_test.sh" > "${WORK}/skips.log" 2>&1
rc=$?
+15 -30
View File
@@ -33,8 +33,10 @@
# CONTROL", "is the E3(a) NEGATIVE CONTROL"), and it is tempting to grep for that. It is not
# evidence: it is written at config load, by every process in the run, whatever happens next. A
# setup abort would carry it too. It proves the knob was READ, never that the knob caused the red.
# Only the failing case's own diagnostic does that. ID-53 gives each Split entry a private
# library log: E1 reads its Fatal there; E3(a) reads the scenario assertion in ctest output.
# Only the failing case's own diagnostic does that. E1 now observes deterministic transport
# waits in dedicated CPU tests; E3(a) retains its scenario assertion and private library log.
# P5e ID-122: ordinary run-ahead draws need not fail with VERB_BARRIER=0, and the old
# overlap Fatal cannot fire with the default BATCH_WAITS=1. Pixel failures are not E1 evidence.
#
# Usage: split_negative_controls.sh [--self-test]
# CTEST ctest binary (default: ctest)
@@ -139,20 +141,14 @@ run_control() {
cat "${out}"
# Inspect JUnit before exit status or private Fatal: a skipped pre-flight can carry both.
label='E3(a)'
[ "${evidence}" != private-barrier-fatal ] || label=E1
python3 "${log_helper}" results "${manifest}" "${filter}" "${result}" "${label}" || exit 1
python3 "${log_helper}" results "${manifest}" "${filter}" "${result}" 'E3(a)' || exit 1
if [ "${control_rc}" -eq 0 ]; then
echo "::error::${name} left ${matched} split entries GREEN, so the knob it turns is not load-bearing and the gate it controls proves nothing."
exit 1
fi
# E1's MGLOG_F sink is the private file, never ctest's status or transcript.
if [ "${evidence}" = "private-barrier-fatal" ]; then
python3 "${log_helper}" evidence "${manifest}" "${filter}" \
'Fatal\{BarrierViolation, "[A-Za-z_][A-Za-z_0-9]*"\}' || exit 1
elif ! python3 "${log_helper}" assertion "${manifest}" "${filter}" "${result}" "${evidence}"; then
if ! python3 "${log_helper}" assertion "${manifest}" "${filter}" "${result}" "${evidence}"; then
echo "::error::${name} FAILED: red lacks its persistent-map push diagnostic. Required: ${evidence}"
exit 1
fi
@@ -168,26 +164,15 @@ run_control() {
echo "${name} turned ${matched} selected entries red, and the red carries the scenario's own diagnostic, as it must"
}
# E1: c1 ClientSession::EmitAndWait emits MGLOG_F Fatal{BarrierViolation, "<slot>"}.
# This asserts an observed overlap with the applier, which is timing-dependent.
# Without that Fatal in a fresh selected file E1 fails; pixel/status fallbacks do not count.
# Keep the ID-53-approved SmallRing selection as well as the default lane.
# ...MINUS TheServerStampedAVerbBoundaryOnThisDrawingFrame, and that exclusion is load-bearing
# rather than tidying (P5e, ID-122). That case is package gl's ID-115 POSITIVE CONTROL: it reads
# a PipeStats window out of the lane's own private log, so it runs ONLY in the strict-arming
# lane and skips everywhere else BY DESIGN, whatever this knob is set to. E1's selection began
# matching it the moment gl added it, and ID-62 makes a SKIPPED SELECTED entry a hard failure -
# correctly, because a control that silently loses its subjects proves nothing. E1 then reported
# "the knob killed the pre-flight, not the entry" while the knob was in fact working: four other
# selected entries went red with the abort in the same run. So the bug was in WHO E1 selects,
# not in what it asserts, and the fix excludes a case that can never run here rather than
# teaching E1 to tolerate skips - which would have thrown ID-62 away.
run_control "negative control E1 (MOBILEGL_IPC_VERB_BARRIER=0)" \
'DirectGLES\.Split\.(SmallRing\.)?(Triangle|ClearThenReadPixels)' \
'private-barrier-fatal' \
'' \
'TheServerStampedAVerbBoundaryOnThisDrawingFrame' \
MOBILEGL_IPC_VERB_BARRIER=0
# E1: the default arm must actually park for a kWaitApplied record and for a
# kWaitNone record whose server has no run-ahead cap. Removing the verb barrier
# must fail those exact return-before-apply assertions. A third positive case
# proves that a cap-authorized kWaitNone record does not wait. The peer releases
# on an observed Park or on the client's return, not on a sleep or pixel race.
# The helper demands green baseline, two named reds, and green restoration;
# skipped/missing cases, timeouts and unrelated failures never count as evidence.
python3 "$(dirname "$0")/wait_boundary_negative_control.py" \
--ctest "${CTEST}" --out "${CONTROL_TMPDIR}/e1" || exit 1
# E3(a): PersistentMapTracker::PushBlocksFor stops at blockBytes == 0 - deliberately, because 0
# is the negative control and not "unlimited". Two independent halves are now required:
+7 -11
View File
@@ -1,5 +1,5 @@
#!/bin/bash
# Exercise the production control, including the private-file read (ID-53 / R-16).
# Exercise E1's per-test boundary evidence and E3(a)'s private-file read (R-16).
set -euo pipefail
HERE="$(cd "$(dirname "$0")/.." && pwd)"
WORK=$(mktemp -d "${TMPDIR:-/tmp}/split-private-log.XXXXXX")
@@ -14,25 +14,21 @@ for mode in missing-fatal stdout-fatal stale-fatal evidence e3-unrelated e3-no-p
bash "${HERE}/split_negative_controls.sh" > "${WORK}/${mode}.out" 2>&1 || rc=$?
if [ "${mode}" = evidence ]; then
[ "${rc}" = 0 ] && grep -q "negative control E3(a).*scenario's own diagnostic" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK private-file Fatal must PASS"; exit 1;
cat "${WORK}/${mode}.out"; echo "NOT OK own boundary/private-file evidence must PASS"; exit 1;
}
grep -qFx "private-log evidence: DirectGLES.Split.ClearThenReadPixelsScenario.ClearWithNoDrawIsVisibleToDefaultFramebufferReadPixels: ${WORK}/${mode}/entry.log" "${WORK}/${mode}.out" || {
grep -qFx "private-log evidence: DirectGLES.Split.PersistentCoherentMapScenario.TwoWritesThroughTheCoherentPointerEachReachTheirOwnDraw: ${WORK}/${mode}/pmap.log" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK private-file evidence line must name the selected entry and path"; exit 1;
}
else
message='E1 FAILED: selected private logs lack expected Fatal'
message='red lacks its own observed wait-boundary failure'
[ "${mode}" != e3-unrelated ] || message='FAILED: red lacks its persistent-map push diagnostic'
case "${mode}" in
e3-no-private) message='no selected private log carries /MGPipe: persistent-map push disabled' ;;
skipped-selection) message='SplitLogPaths FAILED: E1 control: the knob killed the pre-flight, not the entry - 1 selected entries skipped' ;;
skipped-selection|notrun-selection) message='selected testcase skipped or did not run' ;;
e3-skipped-selection) message='SplitLogPaths FAILED: E3(a) control: the knob killed the pre-flight, not the entry - 1 selected entries skipped' ;;
notrun-selection|missing-selection) message='SplitLogPaths FAILED: E1 control: 1 selected entries did not run' ;;
missing-selection) message='selected testcase names are missing, duplicated or unexpected' ;;
esac
if [[ "${mode}" = *-selection ]]; then
match=(-qFx)
else
match=(-qF)
fi
match=(-qF)
[ "${rc}" != 0 ] && grep "${match[@]}" "${message}" "${WORK}/${mode}.out" || {
cat "${WORK}/${mode}.out"; echo "NOT OK ${mode}: control must report FAILED for its own reason"; exit 1;
}
+70
View File
@@ -52,10 +52,80 @@ for a in "$@"; do
[ "$a" = "--show-only=json-v1" ] && json_requested=1
[ "$a" = "-N" ] && listing_requested=1
if [ "$prev" = "--output-junit" ]; then junit="$a"; fi
if [ "$prev" = "-R" ]; then selector="$a"; fi
prev="$a"
done
listing_requested="${listing_requested:-0}"
# E1's CPU-only boundary probes use their own testcase results. Keep observations
# inside each testcase so stdout-only and stale evidence remain rejected.
if [[ "${selector:-}" == *RemoteWaitBoundaryControl* ]]; then
python3 - "${mode}" "${junit}" "${selector}" <<'PY'
import os, re, sys
import xml.etree.ElementTree as ET
mode, path, selector = sys.argv[1:]
barrier = int(os.environ['MOBILEGL_IPC_VERB_BARRIER'])
negative = barrier == 0
prefix = 'RemoteWaitBoundaryControl.'
specs = [
('AppliedClassWaitsWithRunAheadCap', 'GenerateMipmap', 1, True,
'E1 wait boundary: GenerateMipmap returned before apply'),
('MissingServerCapKeepsClearLockstep', 'Clear', 0, True,
'E1 wait boundary: Clear without server cap returned before apply'),
('ServerCapAllowsClearToRunAhead', 'Clear', 1, False, ''),
]
root = ET.Element('testsuite')
failures = 0
for index, (short, op, cap, waits, diagnostic) in enumerate(specs):
name = prefix + short
if not re.search(selector, name):
continue
if mode == 'e1-empty' or (negative and mode == 'missing-selection' and index == 1):
continue
case = ET.SubElement(root, 'testcase', name=name, status='run')
waited = int(waits and barrier == 1)
observation = (f'E1 observation: op={op} cap={cap} barrier={barrier} parked={waited} '
f'applied_before_return={waited} emitted=1 peer_seen=1')
failed = negative and mode != 'green'
if mode == 'e1-red-baseline' and 'baseline' in path:
failed = True
if mode == 'e1-restore-red' and 'restored' in path:
failed = True
if mode == 'e1-waitall' and not waits:
failed = True
observation = observation.replace('parked=0', 'parked=1').replace('applied_before_return=0', 'applied_before_return=1')
if negative and mode == 'skipped-selection':
ET.SubElement(case, 'skipped')
failed = False
if negative and mode == 'notrun-selection':
case.set('status', 'notrun')
failed = False
if failed:
ET.SubElement(case, 'failure', message='control red')
failures += 1
text = observation + '\n' + (diagnostic if negative else '')
if negative:
if mode == 'unrelated' or mode == 'wrong-fatal':
text = observation + '\nUNRELATED_CONTROL_FAILURE'
elif mode == 'missing-fatal' or (mode == 'partial-fatal' and index == 1):
text = diagnostic
elif mode == 'stdout-fatal':
print(text) # Must not qualify as this testcase's own evidence.
text = 'UNRELATED_CONTROL_FAILURE'
elif mode == 'stale-fatal':
text = text.replace('barrier=0', 'barrier=1')
elif mode == 'e1-no-peer':
text = text.replace('peer_seen=1', 'peer_seen=0')
elif mode == 'e1-no-emit':
text = text.replace('emitted=1', 'emitted=0')
ET.SubElement(case, 'system-out').text = text
ET.ElementTree(root).write(path, encoding='utf-8', xml_declaration=True)
print(f'E1 stub: {len(root)} selected, {failures} failed')
sys.exit(8 if failures else 0)
PY
exit $?
fi
emit_listing() {
echo "Test project /stub"
if [ "${mode}" = "retrace-noselect" ] || [ "${mode}" = "dropdraw-noselect" ]; then
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""E1: green/red/restored-green proof of real ClientSession wait boundaries.
The peer holds apply until the actual transport parks or the emit call returns.
VERB_BARRIER=0 must fail only the two owed waits; ordinary run-ahead draws need
not fail. Each arm is checked from its own CTest JUnit testcase, never a global
grep, stale library log, arbitrary nonzero exit, or timing-dependent pixel.
"""
import argparse
import os
from pathlib import Path
import re
import subprocess
import sys
import xml.etree.ElementTree as ET
PREFIX = "RemoteWaitBoundaryControl."
CASES = {
"AppliedClassWaitsWithRunAheadCap": ("GenerateMipmap", 1, True,
"E1 wait boundary: GenerateMipmap returned before apply"),
"MissingServerCapKeepsClearLockstep": ("Clear", 0, True,
"E1 wait boundary: Clear without server cap returned before apply"),
"ServerCapAllowsClearToRunAhead": ("Clear", 1, False, ""),
}
def own_negative(text, expected_observation, diagnostic):
return expected_observation in text and diagnostic in text
def inspect(path, selected, barrier, negative, rc):
root = ET.parse(path).getroot()
cases = root.findall(".//testcase")
names = [case.get("name") for case in cases]
if len(names) != len(set(names)) or set(names) != set(selected):
raise ValueError("selected testcase names are missing, duplicated or unexpected")
for case in cases:
name = case.get("name")
if case.find("skipped") is not None or case.get("status") in {"notrun", "disabled"}:
raise ValueError(f"{name}: selected testcase skipped or did not run")
failed = case.find("failure") is not None or case.find("error") is not None
op, cap, waits, diagnostic = CASES[name.removeprefix(PREFIX)]
waited = int(waits and barrier == 1)
observation = (f"E1 observation: op={op} cap={cap} barrier={barrier} parked={waited} "
f"applied_before_return={waited} emitted=1 peer_seen=1")
text = "\n".join(case.itertext())
if negative:
if not failed or rc == 0:
raise ValueError(f"{name}: disabled barrier did not fail the owed wait")
if not own_negative(text, observation, diagnostic):
raise ValueError(f"{name}: red lacks its own observed wait-boundary failure")
elif failed or rc != 0 or observation not in text:
raise ValueError(f"{name}: baseline/restored arm did not execute its green boundary proof")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--ctest", default=os.environ.get("CTEST", "ctest"))
parser.add_argument("--out", type=Path, required=True)
args = parser.parse_args()
args.out.mkdir(parents=True, exist_ok=True)
all_cases = [PREFIX + name for name in CASES]
negative_cases = [PREFIX + name for name, (_, _, waits, _) in CASES.items() if waits]
ok = True
for arm, barrier, negative, selected in (
("baseline", 1, False, all_cases),
("control", 0, True, negative_cases),
("restored", 1, False, all_cases),
):
# Never run a negative arm against a failed baseline. A failed negative
# still proceeds through restoration and must not consume stale XML.
if arm == "control" and not ok:
continue
result = args.out / f"e1-{arm}.xml"
result.unlink(missing_ok=True)
env = os.environ.copy()
env["MOBILEGL_IPC_VERB_BARRIER"] = str(barrier)
selector = "^(" + "|".join(re.escape(name) for name in selected) + ")$"
command = [args.ctest, "-L", "unit", "-R", selector, "--no-tests=error",
"--output-on-failure", "--output-junit", str(result)]
try:
run = subprocess.run(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, timeout=90)
(args.out / f"e1-{arm}.out").write_text(run.stdout, encoding="utf-8")
print(run.stdout, end="", flush=True)
inspect(result, selected, barrier, negative, run.returncode)
print(f"E1 {arm}: {'EXPECTED_RED' if negative else 'GREEN'} ({len(selected)} cases)", flush=True)
except (OSError, ValueError, ET.ParseError, subprocess.TimeoutExpired) as exc:
print(f"E1 FAILED: {arm}: {exc}", file=sys.stderr, flush=True)
ok = False
if ok:
print("negative control E1: owed waits failed by their own observed boundary assertions; baseline and restoration green")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())