mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Test] (.github/workflows, scripts/ci, tools/trace_replay): assert each split leg's transport in-test
- run_trace_case.cmake writes output/split-evidence.txt only after its transport assertions pass - MGTRACE_WIRE_EVIDENCE (a harness name) turns on the library's counters and requires DrawVbo records on the wire and draws on the server role - retrace-split's OpenRA legs ask for wire evidence; every split leg, and every inproc retrace row, checks its evidence file with retrace_split_evidence.py - test_run_trace_case.py runs the real script on canned logs: each assertion goes red on its bad log, and pipe-gates runs it
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
# MobileGL - scripts/ci/retrace_split_evidence.py
|
||||
# Copyright (c) 2025-2026 MobileGL-Dev
|
||||
# Licensed under the GNU Lesser General Public License v3.0:
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
# https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
# End of Source File Header
|
||||
|
||||
"""Check the evidence file a split retrace leg leaves behind.
|
||||
|
||||
tools/trace_replay/run_trace_case.cmake writes output/split-evidence.txt only after every
|
||||
assertion of its MOBILEGL_TRANSPORT block passed: the client log carried the transport marker
|
||||
(and, under spawn, the separate server pid), a server log existed, no Fatal{ and no MGWIRE-FLOOR
|
||||
line, and, on the legs that asked for it, draw records on the wire and draws on the server. The
|
||||
leg's ctest exit status alone cannot tell "the assertions passed" from "the assertions never ran",
|
||||
so the workflow runs this after ctest: a missing file means the transport block was skipped.
|
||||
|
||||
Usage: retrace_split_evidence.py FILE --transport T [--case C] [--backend B] [--require-wire]
|
||||
"""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def parse(text):
|
||||
values = {}
|
||||
for line in text.splitlines():
|
||||
if '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
values[key.strip()] = value.strip()
|
||||
return values
|
||||
|
||||
|
||||
def count(values, key):
|
||||
try:
|
||||
return int(values.get(key, ''))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def problems_in(values, transport, case=None, backend=None, require_wire=False):
|
||||
problems = []
|
||||
if values.get('transport') != transport:
|
||||
problems.append(f"transport is '{values.get('transport')}', expected '{transport}'")
|
||||
if case is not None and values.get('case') != case:
|
||||
problems.append(f"case is '{values.get('case')}', expected '{case}'")
|
||||
if backend is not None and values.get('backend') != backend:
|
||||
problems.append(f"backend is '{values.get('backend')}', expected '{backend}'")
|
||||
if not values.get('marker'):
|
||||
problems.append('no transport marker recorded')
|
||||
if values.get('server_log') != '1':
|
||||
problems.append('no server log recorded')
|
||||
for key in ('fatal_lines', 'floor_lines'):
|
||||
if count(values, key) != 0:
|
||||
problems.append(f"{key} is '{values.get(key)}', expected 0")
|
||||
tcp = values.get('control', '-').startswith('tcp://')
|
||||
if transport == 'spawn' and values.get('second', '-') == '-':
|
||||
problems.append('spawn leg recorded no server-process sentence')
|
||||
wire = values.get('wire') == 'ON'
|
||||
if require_wire and not wire:
|
||||
problems.append('wire evidence was required and the leg did not collect it')
|
||||
if wire:
|
||||
records = count(values, 'wire_draw_records')
|
||||
if records is None or records < 1:
|
||||
problems.append(f"wire_draw_records is '{values.get('wire_draw_records')}', expected at least 1")
|
||||
draws = values.get('server_draws')
|
||||
if not (tcp and draws == 'n/a'):
|
||||
if count(values, 'server_draws') is None or count(values, 'server_draws') < 1:
|
||||
problems.append(f"server_draws is '{draws}', expected at least 1")
|
||||
return problems
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('evidence', type=Path)
|
||||
parser.add_argument('--transport', required=True)
|
||||
parser.add_argument('--case')
|
||||
parser.add_argument('--backend')
|
||||
parser.add_argument('--require-wire', action='store_true')
|
||||
args = parser.parse_args(argv)
|
||||
if not args.evidence.is_file():
|
||||
print(f'::error::{args.evidence} does not exist: run_trace_case.cmake writes it only after its '
|
||||
f'MOBILEGL_TRANSPORT assertions passed, so on this leg they did not run or did not finish')
|
||||
return 1
|
||||
values = parse(args.evidence.read_text(errors='replace'))
|
||||
problems = problems_in(values, args.transport, args.case, args.backend, args.require_wire)
|
||||
if problems:
|
||||
print(f'::error::{args.evidence}: ' + '; '.join(problems))
|
||||
return 1
|
||||
print(f"split evidence: {values.get('case')} {values.get('backend')} transport={values.get('transport')} "
|
||||
f"second='{values.get('second')}' wire={values.get('wire')} "
|
||||
f"wire_draw_records={values.get('wire_draw_records')} server_draws={values.get('server_draws')}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user