mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 05:08:31 +09:00
[Test] (Retrace): make a retrace under MOBILEGL_PIPE_VERIFY prove it armed, and give the lane a label
- A retrace that exports MOBILEGL_PIPE_VERIFY=1 at a library configured without
-DMOBILEGL_PIPE_VERIFY=ON is a no-op: the frames still match their goldens and the
case reports green having compared nothing. run_trace_case.cmake now demands the
evidence whenever the variable is set to anything but 0/false - mobilegl.log must
exist, must carry "MGPipe: verify armed", and must carry neither
Fatal{PipeVerifyDiffer nor Fatal{UnmigratedPipeInput. With the variable unset the
script is byte-for-byte the old one.
- The Fatal scan is not redundant with the replay's exit status:
MOBILEGL_PIPE_VERIFY_FATAL=0 is the supported triage configuration, and there a
divergence is logged and counted rather than aborted, so the run would finish 0
with its own report sitting unread in the log.
- LABELS retrace on both registrations: the lane was selectable only by regex, so
`ctest -L retrace --no-tests=error` - the spelling that reds a lane which
registered nothing - could not be written at all.
- "verify": true on eight cases (the five 180s cases plus minecraft-1.21.4-in-world,
minecraft-1.21.4-fabric-sodium-in-world and improved-transparency-minecraft-26.3),
and --format github-verify-matrix over that subset: 16 entries, against the full
lane's 77. The verify build compares at every verb boundary and again at every
accessor read, which the design budgets at 5-10x, so the per-push job runs the
subset and the full sweep is a phase-exit / workflow_dispatch run.
- The flag is validated in the manifest loader, not at the matrix, so a non-boolean
or a verify case excluded from CI is a loud error in every consumer instead of a
subset that is quietly one case short.
This commit is contained in:
@@ -372,10 +372,12 @@ function(add_trace_replay_test CASE_NAME BACKEND)
|
|||||||
-P ${MOBILEGL_TRACE_ROOT}/run_trace_case.cmake)
|
-P ${MOBILEGL_TRACE_ROOT}/run_trace_case.cmake)
|
||||||
if(BACKEND STREQUAL "DirectGLES")
|
if(BACKEND STREQUAL "DirectGLES")
|
||||||
set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES
|
set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES
|
||||||
ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330")
|
ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330"
|
||||||
|
LABELS retrace)
|
||||||
else()
|
else()
|
||||||
set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES
|
set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES
|
||||||
ENVIRONMENT "LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330")
|
ENVIRONMENT "LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330"
|
||||||
|
LABELS retrace)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|||||||
@@ -127,3 +127,60 @@ endif()
|
|||||||
if(NOT replay_result EQUAL 0)
|
if(NOT replay_result EQUAL 0)
|
||||||
message(FATAL_ERROR "${TRACE_CASE_NAME} ${TRACE_BACKEND} trace replay failed with status ${replay_result}")
|
message(FATAL_ERROR "${TRACE_CASE_NAME} ${TRACE_BACKEND} trace replay failed with status ${replay_result}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# --- MOBILEGL_PIPE_VERIFY: the third CI mode's own assertions (gates G3 and G8) --------------
|
||||||
|
#
|
||||||
|
# A retrace that exported MOBILEGL_PIPE_VERIFY=1 at a library which was never configured with
|
||||||
|
# -DMOBILEGL_PIPE_VERIFY=ON is a no-op that looks exactly like a clean pass: the variable steers
|
||||||
|
# nothing, the frames still match their goldens, and the case reports green having verified
|
||||||
|
# nothing at all. The mode therefore has to prove it ran, and the only channel a `cmake -P` script
|
||||||
|
# has for that is the library's own log.
|
||||||
|
#
|
||||||
|
# Three demands, all of them silent when MOBILEGL_PIPE_VERIFY is unset or "0", so an ordinary
|
||||||
|
# retrace is untouched:
|
||||||
|
# * mobilegl.log exists - the replay wrote one, so the library was loaded and logging;
|
||||||
|
# * it carries "MGPipe: verify armed" - the comparator armed in THIS process;
|
||||||
|
# * it carries neither Fatal{PipeVerifyDiffer (a push/pull divergence, the thing the mode
|
||||||
|
# exists to find) nor Fatal{UnmigratedPipeInput (a backend read of a field the verb's fill
|
||||||
|
# table does not list - fixed by adding the row to MG_Pipe/FillPoints.def, never by marking
|
||||||
|
# the field sticky).
|
||||||
|
# The Fatal check is not redundant with the replay's exit status: MOBILEGL_PIPE_VERIFY_FATAL=0 is
|
||||||
|
# the supported triage configuration, and there the divergence is logged and counted rather than
|
||||||
|
# aborted, so the run would otherwise finish 0 with its own report in the log.
|
||||||
|
if(DEFINED ENV{MOBILEGL_PIPE_VERIFY} AND NOT "$ENV{MOBILEGL_PIPE_VERIFY}" STREQUAL "")
|
||||||
|
set(pipe_verify_case "${TRACE_CASE_NAME} ${TRACE_BACKEND}")
|
||||||
|
if("$ENV{MOBILEGL_PIPE_VERIFY}" STREQUAL "0" OR "$ENV{MOBILEGL_PIPE_VERIFY}" STREQUAL "false")
|
||||||
|
message(STATUS "MGPipe verify: MOBILEGL_PIPE_VERIFY=$ENV{MOBILEGL_PIPE_VERIFY}, no verify assertions for ${pipe_verify_case}")
|
||||||
|
elseif(NOT EXISTS "${mobilegl_log}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"MOBILEGL_PIPE_VERIFY is set for ${pipe_verify_case} but the run wrote no ${mobilegl_log}, "
|
||||||
|
"so there is no evidence the comparator ever armed. A verify retrace with no library log "
|
||||||
|
"cannot be counted as a verify retrace.")
|
||||||
|
else()
|
||||||
|
file(READ "${mobilegl_log}" pipe_verify_log)
|
||||||
|
string(FIND "${pipe_verify_log}" "MGPipe: verify armed" pipe_verify_armed_at)
|
||||||
|
if(pipe_verify_armed_at EQUAL -1)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"MOBILEGL_PIPE_VERIFY is set for ${pipe_verify_case} and the library never reported "
|
||||||
|
"\"MGPipe: verify armed\". Either this libMobileGL.so was not built with "
|
||||||
|
"-DMOBILEGL_PIPE_VERIFY=ON - in which case the whole verify retrace lane is comparing "
|
||||||
|
"nothing - or the runtime knob never reached the process. Check that the VERIFY "
|
||||||
|
"runtime artifact is the one unpacked at ${MOBILEGL_LIBRARY}.")
|
||||||
|
endif()
|
||||||
|
file(STRINGS "${mobilegl_log}" pipe_verify_fatals
|
||||||
|
REGEX "Fatal\\{(PipeVerifyDiffer|UnmigratedPipeInput)")
|
||||||
|
if(pipe_verify_fatals)
|
||||||
|
foreach(line IN LISTS pipe_verify_fatals)
|
||||||
|
message(STATUS "${line}")
|
||||||
|
endforeach()
|
||||||
|
list(LENGTH pipe_verify_fatals pipe_verify_fatal_count)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"${pipe_verify_case}: ${pipe_verify_fatal_count} MGPipe Fatal(s) under "
|
||||||
|
"MOBILEGL_PIPE_VERIFY. Fatal{PipeVerifyDiffer, \"<Field>@<Verb>\"} is a real push/pull "
|
||||||
|
"divergence and is recorded, not silenced; Fatal{UnmigratedPipeInput, \"<Field>@<Verb>\"} "
|
||||||
|
"is a missing row in MG_Pipe/FillPoints.def's class table - add it, regenerate, rerun "
|
||||||
|
"(never mark the field sticky).")
|
||||||
|
endif()
|
||||||
|
message(STATUS "MGPipe verify: ${pipe_verify_case} armed, zero divergences, zero unmigrated reads")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"cases": [
|
"cases": [
|
||||||
{
|
{
|
||||||
"name": "OpenRA",
|
"name": "OpenRA",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "openra.tgz",
|
"trace_archive": "openra.tgz",
|
||||||
"trace_file": "openra.trace",
|
"trace_file": "openra.trace",
|
||||||
"golden": "openra.0000031249.png",
|
"golden": "openra.0000031249.png",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.21.4-startup",
|
"name": "minecraft-1.21.4-startup",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.21.4-startup.tgz",
|
"trace_archive": "minecraft-1.21.4-startup.tgz",
|
||||||
"golden": "minecraft-1.21.4-startup.0000092195.png",
|
"golden": "minecraft-1.21.4-startup.0000092195.png",
|
||||||
"target_call": 92195,
|
"target_call": 92195,
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.21.4-main-menu",
|
"name": "minecraft-1.21.4-main-menu",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.21.4-main-menu.tgz",
|
"trace_archive": "minecraft-1.21.4-main-menu.tgz",
|
||||||
"golden": "minecraft-1.21.4-main-menu.0000481787.png",
|
"golden": "minecraft-1.21.4-main-menu.0000481787.png",
|
||||||
"alternate_golden": "minecraft-1.21.4-main-menu.0000481787-mali.png",
|
"alternate_golden": "minecraft-1.21.4-main-menu.0000481787-mali.png",
|
||||||
@@ -42,6 +45,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.21.11-main-menu",
|
"name": "minecraft-1.21.11-main-menu",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.21.11-main-menu.tgz",
|
"trace_archive": "minecraft-1.21.11-main-menu.tgz",
|
||||||
"golden": "minecraft-1.21.11-main-menu.0000205347.png",
|
"golden": "minecraft-1.21.11-main-menu.0000205347.png",
|
||||||
"target_call": 205347,
|
"target_call": 205347,
|
||||||
@@ -49,6 +53,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.17-main-menu-854",
|
"name": "minecraft-1.17-main-menu-854",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.17-main-menu-854.tgz",
|
"trace_archive": "minecraft-1.17-main-menu-854.tgz",
|
||||||
"golden": "minecraft-1.17-main-menu-854.0000117757.png",
|
"golden": "minecraft-1.17-main-menu-854.0000117757.png",
|
||||||
"target_call": 117757,
|
"target_call": 117757,
|
||||||
@@ -56,6 +61,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.21.4-in-world",
|
"name": "minecraft-1.21.4-in-world",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.21.4-in-world.tgz",
|
"trace_archive": "minecraft-1.21.4-in-world.tgz",
|
||||||
"golden": "minecraft-1.21.4-in-world.0000280000.png",
|
"golden": "minecraft-1.21.4-in-world.0000280000.png",
|
||||||
"target_call": 280000
|
"target_call": 280000
|
||||||
@@ -70,6 +76,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "minecraft-1.21.4-fabric-sodium-in-world",
|
"name": "minecraft-1.21.4-fabric-sodium-in-world",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "minecraft-1.21.4-fabric-sodium-in-world.tgz",
|
"trace_archive": "minecraft-1.21.4-fabric-sodium-in-world.tgz",
|
||||||
"golden": "minecraft-1.21.4-fabric-sodium-in-world.0000923340.png",
|
"golden": "minecraft-1.21.4-fabric-sodium-in-world.0000923340.png",
|
||||||
"target_call": 923340,
|
"target_call": 923340,
|
||||||
@@ -277,6 +284,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "improved-transparency-minecraft-26.3",
|
"name": "improved-transparency-minecraft-26.3",
|
||||||
|
"verify": true,
|
||||||
"trace_archive": "improved-transparency-minecraft-26.3.tgz",
|
"trace_archive": "improved-transparency-minecraft-26.3.tgz",
|
||||||
"golden": "improved-transparency-minecraft-26.3.0002667619.png",
|
"golden": "improved-transparency-minecraft-26.3.0002667619.png",
|
||||||
"target_call": 2667619,
|
"target_call": 2667619,
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ def load_trace_case_manifest(path=TRACE_CASES_JSON):
|
|||||||
for key in ("trace_archive", "trace_file", "golden", "target_call", "width", "height"):
|
for key in ("trace_archive", "trace_file", "golden", "target_call", "width", "height"):
|
||||||
if key not in merged:
|
if key not in merged:
|
||||||
raise ValueError(f"{key} is required for {name}")
|
raise ValueError(f"{key} is required for {name}")
|
||||||
|
# "verify" opts a case into the MOBILEGL_PIPE_VERIFY retrace subset. It is checked here
|
||||||
|
# rather than where the matrix is built so that a typo is a loud manifest error in every
|
||||||
|
# consumer (the cmake emitter included) instead of a subset that is quietly one case short.
|
||||||
|
verify = merged.get("verify", False)
|
||||||
|
if not isinstance(verify, bool):
|
||||||
|
raise ValueError(f"verify must be true or false for {name}")
|
||||||
|
if verify and not merged.get("ci", True):
|
||||||
|
raise ValueError(
|
||||||
|
f"{name} is marked verify but excluded from CI, so the verify matrix would drop it"
|
||||||
|
)
|
||||||
cases.append(merged)
|
cases.append(merged)
|
||||||
return {"defaults": defaults, "cases": cases}
|
return {"defaults": defaults, "cases": cases}
|
||||||
|
|
||||||
@@ -72,6 +82,16 @@ def ci_trace_cases(cases):
|
|||||||
return [case for case in cases if case.get("ci", True)]
|
return [case for case in cases if case.get("ci", True)]
|
||||||
|
|
||||||
|
|
||||||
|
def verify_trace_cases(cases):
|
||||||
|
"""The subset the third CI mode retraces.
|
||||||
|
|
||||||
|
The verify build compares two state models at every verb boundary and again at every accessor
|
||||||
|
read, which the design budgets at 5-10x, so the per-push lane runs a named subset and the full
|
||||||
|
79-case sweep happens at the phase exit and on workflow_dispatch.
|
||||||
|
"""
|
||||||
|
return [case for case in cases if case.get("verify", False)]
|
||||||
|
|
||||||
|
|
||||||
def ci_backends(case):
|
def ci_backends(case):
|
||||||
backends = case.get("ci_backends")
|
backends = case.get("ci_backends")
|
||||||
if backends is None:
|
if backends is None:
|
||||||
@@ -98,6 +118,10 @@ def github_test_matrix(cases):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def github_verify_matrix(cases):
|
||||||
|
return github_test_matrix(verify_trace_cases(cases))
|
||||||
|
|
||||||
|
|
||||||
def github_apk_matrix(cases):
|
def github_apk_matrix(cases):
|
||||||
backends = {
|
backends = {
|
||||||
"DirectGLES": {"name": "DirectGLES", "gpu": "software"},
|
"DirectGLES": {"name": "DirectGLES", "gpu": "software"},
|
||||||
@@ -158,6 +182,7 @@ def parse_args():
|
|||||||
choices=(
|
choices=(
|
||||||
"names",
|
"names",
|
||||||
"github-test-matrix",
|
"github-test-matrix",
|
||||||
|
"github-verify-matrix",
|
||||||
"github-apk",
|
"github-apk",
|
||||||
"github-apk-matrix",
|
"github-apk-matrix",
|
||||||
"fixture-files",
|
"fixture-files",
|
||||||
@@ -177,6 +202,8 @@ def main():
|
|||||||
print(json.dumps([case["name"] for case in cases], separators=(",", ":")))
|
print(json.dumps([case["name"] for case in cases], separators=(",", ":")))
|
||||||
elif args.format == "github-test-matrix":
|
elif args.format == "github-test-matrix":
|
||||||
print(json.dumps(github_test_matrix(cases), separators=(",", ":")))
|
print(json.dumps(github_test_matrix(cases), separators=(",", ":")))
|
||||||
|
elif args.format == "github-verify-matrix":
|
||||||
|
print(json.dumps(github_verify_matrix(cases), separators=(",", ":")))
|
||||||
elif args.format == "github-apk":
|
elif args.format == "github-apk":
|
||||||
print(json.dumps([github_apk_case(case) for case in cases], separators=(",", ":")))
|
print(json.dumps([github_apk_case(case) for case in cases], separators=(",", ":")))
|
||||||
elif args.format == "github-apk-matrix":
|
elif args.format == "github-apk-matrix":
|
||||||
|
|||||||
Reference in New Issue
Block a user