mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
Exit gates 9.2 and 9.3. Until now nothing ran the integration suite across a
process boundary at all - the spawn shape was proven only by the retrace lane,
which exercises one trace per case and no scenario assertions.
integration-spawn: 102 cases, 102 green.
THE SET IDENTITY IS THE POINT, AND IT IS NOT MAINTAINED BY HAND. 9.2 asks that
the spawn lane's case-name set equal the split lane's. Registering the spawn
arm as a second copy of each block would satisfy that today and drift the first
time somebody adds a scenario to one of them - and a spawn lane covering LESS
goes green while proving less, with nothing about a smaller green lane looking
wrong. The eleven split-environment blocks become eleven calls to one macro
that emits BOTH arms from one filter, so a case added to either is added to
both or to neither.
scripts/ci/spawn_lane_parity.py asserts it anyway, because a macro is one edit
from a hand-written block. It compares SETS rather than counts - a count can
agree while the membership does not - and carries exactly one sanctioned
asymmetry, named in the file: integration-split also labels the PUSH-MONOLITH
arm, whose scenario is monolith-only by construction. R-16: introduce a
split-only block and it reds naming the case and the macro to use.
THREE CASES SKIP UNDER SPAWN, BY NAME. CtWireScenario reads PipeApplier's
in-process tallies through ServerVerbs(); under spawn those live in the SERVER's
process and this one's copy is 0 forever, so the assertions were failing about
somebody else's memory. That is a harness limit, not a product defect - the
records themselves cross, which the retrace lane proves. They are SKIPPED and
not deregistered: a case dropped from the registration would make the two sets
differ silently, which is the drift the macro exists to prevent. What retires
the skip is cross-process telemetry, which is also what S8's SessionFaultCount
needs.
TWO REAL REGRESSIONS THE EXISTING GATES CAUGHT, both from this refactor:
* the macro dropped the per-block TEST_LIST variables, which were what
SplitLogPaths fed to give every entry a PRIVATE MOBILEGL_LOG_FILE_PATH.
SplitLogPaths.PrivateAndDistinct went red naming the first orphan. The
macro accumulates list NAMES now, so a block added later is covered without
anyone remembering to extend a list - and the names have to be unique,
because gtest_discover_tests' TEST_LIST is populated when CTEST runs, not
at configure time, so one shared variable held only the last block's
entries. That matters more on the spawn arm: there TWO PROCESSES append to
the path, so a shared file interleaves two sessions rather than merely
being ambiguous.
* the configure_file that substitutes those names ran BEFORE the F1 and Ct
blocks registered, so it captured neither. Same gate, naming
F1WireScenario.ClearBufferfiPixels. It now runs last.
junit_tally gains --require-spawn-ran beside --require-split-ran: the prefix IS
the arm, and a shared flag would let one lane tally the other's entries and call
itself run. Verified to discriminate - the spawn junit reads 96/0/5 under its
own flag and 0/0/0 under the split flag, which correctly fails.
Two death tests follow the ADOPT_TIER message the census commit retagged with
Fatal{UnimplementedAdoptTier}; their regexes asserted the old wording.
Unit 2353 (one pre-existing environmental failure), integration-split 186/186,
integration-spawn 102/102, retrace 6/6, fatal census 0 unmarked, G1 0 symbols
and .text unchanged at 0xa52203.
66 lines
2.6 KiB
Python
Executable File
66 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Tally a ctest --output-junit file as "passed failed skipped".
|
|
|
|
Split out of .github/workflows/test.yml's negative-control step so that the workflow, the local
|
|
gate and the control smoke test all count a run the same way.
|
|
|
|
WHY THIS EXISTS AT ALL (ID-46 finding 8, second half). The counter this replaces lived inline in
|
|
the workflow and counted a case as having "run" when it was merely not <skipped/>:
|
|
|
|
if case.find('skipped') is None and case.get('status') not in ('notrun', 'disabled'):
|
|
ran += 1
|
|
|
|
so a case that RAN AND FAILED armed the negative controls below it. Combined with the `|| true`
|
|
that hid the baseline's exit code, a lane in which every split entry was already red reported
|
|
itself armed, and a control that turns an already-red entry red then "passed". Passed, failed and
|
|
skipped are three different answers and the caller needs all three.
|
|
"""
|
|
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
# P6 `t6`: ONE FLAG PER ARM, because the prefix IS the arm and a shared flag would let a lane
|
|
# tally the other lane's entries and call itself run. Exit gate 9.3 compares the two.
|
|
ARM_PREFIXES = {
|
|
'--require-split-ran': 'DirectGLES.Split.',
|
|
'--require-spawn-ran': 'DirectGLES.Spawn.',
|
|
}
|
|
|
|
|
|
def tally(path, prefix=None):
|
|
passed = failed = skipped = 0
|
|
for case in ET.parse(path).getroot().iter('testcase'):
|
|
if prefix is not None and not case.get('name', '').startswith(prefix):
|
|
continue
|
|
if case.find('failure') is not None or case.find('error') is not None:
|
|
failed += 1
|
|
elif case.find('skipped') is not None or case.get('status') in ('notrun', 'disabled'):
|
|
skipped += 1
|
|
else:
|
|
passed += 1
|
|
return passed, failed, skipped
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) not in (2, 3) or (len(sys.argv) == 3 and sys.argv[2] not in ARM_PREFIXES):
|
|
print("usage: junit_tally.py <junit.xml> [" + " | ".join(sorted(ARM_PREFIXES)) + "]",
|
|
file=sys.stderr)
|
|
return 2
|
|
try:
|
|
prefix = ARM_PREFIXES[sys.argv[2]] if len(sys.argv) == 3 else None
|
|
passed, failed, skipped = tally(sys.argv[1], prefix=prefix)
|
|
except Exception as exc: # a malformed file is not "zero of everything"
|
|
print(f"junit_tally: cannot parse {sys.argv[1]}: {exc}", file=sys.stderr)
|
|
return 1
|
|
print(f"{passed} {failed} {skipped}")
|
|
if len(sys.argv) == 3 and (passed == 0 or failed):
|
|
print(f'{prefix} baseline FAILED: no successful entries with that arm prefix, or an '
|
|
f'already-red selection', file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|