mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[CI] (.github/workflows, scripts/ci, tools/trace_replay): check the verify control's reason in one script
- scripts/ci/retrace_verify_reason.py holds the retrace-verify control's reason check, a plain search for Fatal{PipeVerifyDiffer, "GetRenderStateParameters@ (run_trace_case.cmake prints the library log through message(STATUS), which never re-wraps, so the old prefix stripping and whitespace collapse are gone); the workflow step and test_run_trace_case.py both run it, so the test can no longer pass on a copy
- canned reds carrying a different Fatal (Fatal{UnmigratedPipeInput, "GetPixelStoreParameters@ReadPixels"}) or the comparator's divergence on another field (Fatal{PipeVerifyDiffer, "GetPixelStoreParameters@ReadPixels"}) must fail the check
This commit is contained in:
@@ -3287,12 +3287,9 @@ jobs:
|
|||||||
echo "::error::MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters left the OpenRA retrace GREEN, so the comparator is not comparing and the whole verify retrace lane proves nothing."
|
echo "::error::MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters left the OpenRA retrace GREEN, so the comparator is not comparing and the whole verify retrace lane proves nothing."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Its own red: ctest -V prefixes every line with the test number, and CMake re-wraps text.
|
# Its red must carry the comparator's divergence on the corrupted field
|
||||||
if ! sed -E 's/^[0-9]+: //' "${RUNNER_TEMP}/verify-control.txt" | tr -s '[:space:]' ' ' \
|
# (scripts/ci/retrace_verify_reason.py, which test_run_trace_case.py runs on canned output).
|
||||||
| grep -qF 'Fatal{PipeVerifyDiffer, "GetRenderStateParameters@'; then
|
python3 "${GITHUB_WORKSPACE}/scripts/ci/retrace_verify_reason.py" "${RUNNER_TEMP}/verify-control.txt"
|
||||||
echo "::error::the corrupted-snapshot retrace went red without Fatal{PipeVerifyDiffer, \"GetRenderStateParameters@...\"}: something other than the comparator failed it"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "the corrupted field turned the retrace red, as it must (ctest exit ${control_rc})"
|
echo "the corrupted field turned the retrace red, as it must (ctest exit ${control_rc})"
|
||||||
|
|
||||||
- name: Upload core dumps
|
- name: Upload core dumps
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# MobileGL - scripts/ci/retrace_verify_reason.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
|
||||||
|
|
||||||
|
"""Whether retrace-verify's corrupted-snapshot control went red for its own reason.
|
||||||
|
|
||||||
|
The control replays OpenRA with MOBILEGL_PIPE_VERIFY_CORRUPT=GetRenderStateParameters and must go
|
||||||
|
red with the comparator's divergence on that field: a red without it (another Fatal, a divergence
|
||||||
|
on another field) proves nothing about the comparator. The check requires that divergence in the
|
||||||
|
transcript; it does not rule out other failures beside it. run_trace_case.cmake prints the library
|
||||||
|
log through message(STATUS), which never re-wraps, so each log line stays one transcript line.
|
||||||
|
|
||||||
|
Usage: retrace_verify_reason.py TRANSCRIPT (the control run's ctest -V output)
|
||||||
|
Exit 0 when the divergence is there, 1 when it is not, 2 when the transcript cannot be read.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
REASON = 'Fatal{PipeVerifyDiffer, "GetRenderStateParameters@'
|
||||||
|
|
||||||
|
|
||||||
|
def carries_reason(text):
|
||||||
|
return REASON in text
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
argv = sys.argv[1:] if argv is None else argv
|
||||||
|
if len(argv) != 1:
|
||||||
|
print(__doc__)
|
||||||
|
return 2
|
||||||
|
try:
|
||||||
|
text = open(argv[0], encoding='utf-8', errors='replace').read()
|
||||||
|
except OSError as error:
|
||||||
|
print(f'::error::retrace_verify_reason: cannot read {argv[0]}: {error}')
|
||||||
|
return 2
|
||||||
|
if carries_reason(text):
|
||||||
|
print(f'retrace_verify_reason: the red carries {REASON}...')
|
||||||
|
return 0
|
||||||
|
print(f'::error::the corrupted-snapshot retrace went red without {REASON}...: '
|
||||||
|
'something other than the comparator failed it')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -29,6 +29,7 @@ import unittest
|
|||||||
HERE = Path(__file__).resolve().parent
|
HERE = Path(__file__).resolve().parent
|
||||||
SCRIPT = HERE / 'run_trace_case.cmake'
|
SCRIPT = HERE / 'run_trace_case.cmake'
|
||||||
CHECKER = HERE.parents[1] / 'scripts' / 'ci' / 'retrace_split_evidence.py'
|
CHECKER = HERE.parents[1] / 'scripts' / 'ci' / 'retrace_split_evidence.py'
|
||||||
|
VERIFY_REASON = HERE.parents[1] / 'scripts' / 'ci' / 'retrace_verify_reason.py'
|
||||||
sys.path.insert(0, str(CHECKER.parent))
|
sys.path.insert(0, str(CHECKER.parent))
|
||||||
import retrace_split_evidence as checker # noqa: E402
|
import retrace_split_evidence as checker # noqa: E402
|
||||||
|
|
||||||
@@ -60,10 +61,6 @@ with open(os.path.join(out, 'result.json'), 'w') as result:
|
|||||||
json.dump({'passed': True, 'ssim': 1.0}, result)
|
json.dump({'passed': True, 'ssim': 1.0}, result)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# The retrace-verify control's reason check, as the workflow runs it on the tee'd ctest -V output.
|
|
||||||
VERIFY_CONTROL_REASON = ("sed -E 's/^[0-9]+: //' \"$1\" | tr -s '[:space:]' ' ' "
|
|
||||||
"| grep -qF 'Fatal{PipeVerifyDiffer, \"GetRenderStateParameters@'")
|
|
||||||
|
|
||||||
PREFIX = '[12:00:00] [Linux mobilegl_trace_/INFO]: '
|
PREFIX = '[12:00:00] [Linux mobilegl_trace_/INFO]: '
|
||||||
MARKER = {
|
MARKER = {
|
||||||
'inproc': 'Config: MOBILEGL_TRANSPORT=inproc - the MGPipe record stream crosses a real ring to an apply thread',
|
'inproc': 'Config: MOBILEGL_TRANSPORT=inproc - the MGPipe record stream crosses a real ring to an apply thread',
|
||||||
@@ -307,17 +304,24 @@ class RunTraceCaseTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_v4_an_aborted_verify_run_names_its_divergence(self):
|
def test_v4_an_aborted_verify_run_names_its_divergence(self):
|
||||||
# The comparator aborts the replay before result.json, so the script dumps the library log;
|
# The comparator aborts the replay before result.json, so the script dumps the library log;
|
||||||
# the retrace-verify control requires that dump to carry the divergence it caused.
|
# the retrace-verify control requires that dump to carry the divergence it caused, through
|
||||||
|
# the same scripts/ci/retrace_verify_reason.py the workflow runs. Another Fatal is not it,
|
||||||
|
# and neither is the comparator's divergence on a field the control did not corrupt.
|
||||||
fatal = 'MGPipe: Fatal{PipeVerifyDiffer, "GetRenderStateParameters@Clear"} field=3 where=entry'
|
fatal = 'MGPipe: Fatal{PipeVerifyDiffer, "GetRenderStateParameters@Clear"} field=3 where=entry'
|
||||||
|
other = 'MGPipe: Fatal{UnmigratedPipeInput, "GetPixelStoreParameters@ReadPixels"}'
|
||||||
|
other_field = 'MGPipe: Fatal{PipeVerifyDiffer, "GetPixelStoreParameters@ReadPixels"} field=1 where=entry'
|
||||||
env = {'MOBILEGL_PIPE_VERIFY': '1', 'STUB_ABORT': '1'}
|
env = {'MOBILEGL_PIPE_VERIFY': '1', 'STUB_ABORT': '1'}
|
||||||
rc, output = self.run_case(env, {'mobilegl.log': lines('MGPipe: verify armed', fatal)})
|
rc, output = self.run_case(env, {'mobilegl.log': lines('MGPipe: verify armed', fatal)})
|
||||||
self.assertNotEqual(rc, 0, output)
|
self.assertNotEqual(rc, 0, output)
|
||||||
transcript = self.root / 'control.txt'
|
transcript = self.root / 'control.txt'
|
||||||
for log, expected in ((lines('MGPipe: verify armed', fatal), 0), (lines('MGPipe: verify armed'), 1)):
|
for log, expected in ((lines('MGPipe: verify armed', fatal), 0), (lines('MGPipe: verify armed'), 1),
|
||||||
|
(lines('MGPipe: verify armed', other), 1),
|
||||||
|
(lines('MGPipe: verify armed', other_field), 1)):
|
||||||
_, raw = self.run_case(env, {'mobilegl.log': log}, squashed=False)
|
_, raw = self.run_case(env, {'mobilegl.log': log}, squashed=False)
|
||||||
transcript.write_text(''.join('1: ' + row + '\n' for row in raw.splitlines()))
|
transcript.write_text(''.join('1: ' + row + '\n' for row in raw.splitlines()))
|
||||||
check = subprocess.run(['bash', '-c', VERIFY_CONTROL_REASON, 'reason', str(transcript)])
|
check = subprocess.run([sys.executable, str(VERIFY_REASON), str(transcript)],
|
||||||
self.assertEqual(check.returncode, expected, raw)
|
capture_output=True, text=True)
|
||||||
|
self.assertEqual(check.returncode, expected, raw + check.stdout)
|
||||||
|
|
||||||
def test_v3_verify_green(self):
|
def test_v3_verify_green(self):
|
||||||
rc, output = self.run_case({'MOBILEGL_PIPE_VERIFY': '1'}, {'mobilegl.log': lines('MGPipe: verify armed')})
|
rc, output = self.run_case({'MOBILEGL_PIPE_VERIFY': '1'}, {'mobilegl.log': lines('MGPipe: verify armed')})
|
||||||
|
|||||||
Reference in New Issue
Block a user