mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Test] (P8-A): register the monolith-only integration cases on the split, spawn and tcp arms (P8Coverage.cmake: 246 ambient cases, five server-knob lanes each on its own tcp supervisor), retire the ClientSideIndices and GuiBatch exclusions, and gate the rest with split_coverage.py and its exemption table
This commit is contained in:
@@ -1461,6 +1461,15 @@ jobs:
|
||||
- name: Spawn/split lane parity (9.2)
|
||||
run: python3 scripts/ci/spawn_lane_parity.py build-split
|
||||
|
||||
# P8-A (ID-P8-2): every monolith case, tails normalised, is on a gated split arm or is named
|
||||
# with a reason class in scripts/data/split_coverage_exemptions.txt. The same `ctest -N`
|
||||
# cost as the parity step above, and the question it cannot answer: parity compares the
|
||||
# split arms with EACH OTHER, so a case missing from all three passes it.
|
||||
- name: Split coverage against monolith (P8-A)
|
||||
run: |
|
||||
python3 scripts/ci/split_coverage.py --self-test
|
||||
python3 scripts/ci/split_coverage.py build-split
|
||||
|
||||
- name: Unit tests on the split runtime
|
||||
working-directory: build-split
|
||||
env:
|
||||
|
||||
@@ -2587,8 +2587,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# had no split coverage at all, so every claim about it was a reading of the code rather than
|
||||
# a run of it. Arming the lanes is the first half of the work and the census is the second:
|
||||
# what reds here IS the remaining worklist, and each red is either D1's to fix or excluded BY
|
||||
# NAME with the Fatal quoted, the way the d1 draw block excludes
|
||||
# MultiDrawScenario.ClientSideIndicesBatchMatchesUnrolledDraws.
|
||||
# NAME with the Fatal quoted, the way the d1 draw block excluded
|
||||
# MultiDrawScenario.ClientSideIndicesBatchMatchesUnrolledDraws until P8-A.
|
||||
mgl_itest_register_split_arms("PixelStoreSweepScenario.*" "")
|
||||
mgl_itest_register_split_arms("PackedWordReadbackScenario.*" "")
|
||||
mgl_itest_register_split_arms("LayeredTextureReadbackScenario.*" "")
|
||||
@@ -2627,7 +2627,12 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# left all of them passing. A split arm that inherited the ambient default would be measuring
|
||||
# the same non-path three more times. So the pin is MIRRORED here, which is what the plan's
|
||||
# row asks for.
|
||||
foreach(mglItestDsArm IN LISTS MGL_ITEST_MAGMA_ARMS)
|
||||
#
|
||||
# P8-A: SPLIT AND SPAWN ONLY HERE. The knob is read by the server, and the Tcp arm's server
|
||||
# is the lane-wide fixture, so a Tcp copy registered here ran the NATIVE readback under the
|
||||
# ForcedDs. name. The same-named Tcp entries are registered in P8Coverage.cmake, against a
|
||||
# supervisor started with the knob.
|
||||
foreach(mglItestDsArm Split Spawn)
|
||||
mgl_itest_magma_arm_shape(${mglItestDsArm})
|
||||
string(TOLOWER "${mglItestDsArm}" mglItestDsArmLower)
|
||||
mgl_itest_join_environment(mglItestDsEnvironment
|
||||
@@ -2755,19 +2760,20 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# the two multi-draws, an indirect draw), so a record that did not cross is a wrong picture
|
||||
# and not a green. MultiDrawScenario, DrawParametersScenario and PrimitiveRestartScenario are
|
||||
# the census's own DrawElements / MultiDrawElementsBaseVertex / DrawArraysInstancedBaseInstance
|
||||
# / MultiDraw*Indirect* first-blocker scenarios, now at their pictures. The ONE case excluded
|
||||
# by filter is MultiDrawScenario's ClientSideIndicesBatchMatchesUnrolledDraws: a multi-draw
|
||||
# with client-side indices is refused BY NAME under split (Fatal{UnmigratedVerb,
|
||||
# "MultiDrawElementsBaseVertex+CLIENT_INDICES"}, CONTRACT-P5B.md d1 - P8's HostResolve.cpp
|
||||
# flattens it), which is its next first blocker and not a lane failure. It is listed in
|
||||
# d1-v1.md, not deleted (G14). GuiBatchScenario (the census's other DrawElements scenario) is
|
||||
# NOT registered: past its draws it stops at Fatal{UnmigratedVerb, "MemoryBarrier"}, which is
|
||||
# i1's to flip; i1 registers it when that lands.
|
||||
# / MultiDraw*Indirect* first-blocker scenarios, now at their pictures. The whole of
|
||||
# MultiDrawScenario is here since P8-A: its ClientSideIndicesBatchMatchesUnrolledDraws was
|
||||
# excluded while a multi-draw with client-side indices was refused BY NAME under split
|
||||
# (Fatal{UnmigratedVerb, "MultiDrawElementsBaseVertex+CLIENT_INDICES"}), and the client now
|
||||
# flattens every sub-draw's client indices into one owned element buffer before the record is
|
||||
# emitted (EmitTables.cpp's EmitMultiIndexedDraw, OwnedDrawInputs.h), so the
|
||||
# server's CLIENT_INDICES latch is unreachable from our client and this case is the entry that
|
||||
# would see it come back. GuiBatchScenario (the census's other DrawElements scenario) waited
|
||||
# on Fatal{UnmigratedVerb, "MemoryBarrier"}; that verb has crossed since i1 and class C is
|
||||
# empty, so P8Coverage.cmake registers it with the other monolith-only cases.
|
||||
set(MGL_SPLIT_D1_SCENARIOS IndexedDrawFamilyScenario MultiDrawScenario DrawParametersScenario
|
||||
PrimitiveRestartScenario)
|
||||
set(MGL_SPLIT_D1_FILTER_IndexedDrawFamilyScenario "IndexedDrawFamilyScenario.*")
|
||||
set(MGL_SPLIT_D1_FILTER_MultiDrawScenario
|
||||
"MultiDrawScenario.*-MultiDrawScenario.ClientSideIndicesBatchMatchesUnrolledDraws")
|
||||
set(MGL_SPLIT_D1_FILTER_MultiDrawScenario "MultiDrawScenario.*")
|
||||
set(MGL_SPLIT_D1_FILTER_DrawParametersScenario "DrawParametersScenario.*")
|
||||
set(MGL_SPLIT_D1_FILTER_PrimitiveRestartScenario "PrimitiveRestartScenario.*")
|
||||
foreach(mglItestD1Scenario IN LISTS MGL_SPLIT_D1_SCENARIOS)
|
||||
@@ -2810,8 +2816,9 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
# THE FILTER IS THE TIER-SENSITIVE WORKLOAD and nothing else, which is this file's own rule
|
||||
# for an armed Split entry: MultiDrawScenario (its whole point is that a batch matches the
|
||||
# unrolled draws, which is precisely what a wrong index buffer breaks quietly) and
|
||||
# IndexedDrawFamilyScenario's three MultiDraw cases. The client-side-indices case keeps the
|
||||
# d1 block's exclusion - it is refused by name under split before any tier is chosen.
|
||||
# IndexedDrawFamilyScenario's three MultiDraw cases. The client-side-indices case joined with
|
||||
# the d1 block's (P8-A): its flattened indices arrive as one owned element buffer, which is
|
||||
# exactly the bound index buffer each tier asks its questions of.
|
||||
#
|
||||
# NOTE FOR THE STRICT-MARKER CENSUS (PREAMBLE-ADDENDUM-WAVE3 §4): these entries carry a THIRD
|
||||
# name segment, so `strict_census2.sh` re-derives their gtest filter from the last two
|
||||
@@ -2827,7 +2834,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_drawelements "DrawElements")
|
||||
set(MGL_SPLIT_MULTIDRAW_TIER_PREFIX_compute "Compute")
|
||||
set(MGL_SPLIT_MULTIDRAW_TIER_FILTER
|
||||
"MultiDrawScenario.*:IndexedDrawFamilyScenario.MultiDraw*-MultiDrawScenario.ClientSideIndicesBatchMatchesUnrolledDraws")
|
||||
"MultiDrawScenario.*:IndexedDrawFamilyScenario.MultiDraw*")
|
||||
foreach(mglItestMultiDrawTier IN LISTS MGL_SPLIT_MULTIDRAW_TIERS)
|
||||
mgl_itest_join_environment(MGL_ITEST_GLES_SPLIT_MULTIDRAW_ENVIRONMENT
|
||||
"MOBILEGL_BACKEND_TYPE=DirectGLES" "MOBILEGL_TRANSPORT=inproc" "MOBILEGL_ITEST_REQUIRE_GPU=1"
|
||||
@@ -4648,6 +4655,11 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# P8-A
|
||||
if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/P8Coverage.cmake)
|
||||
endif()
|
||||
|
||||
# =====================================================================================
|
||||
# P7 PACKAGE L: THE INFORMATIONAL MAGMA CENSUS - TIER 2 OF TWO
|
||||
# =====================================================================================
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
# P8-A (ID-P8-2): the monolith cases' split, spawn and tcp registrations.
|
||||
#
|
||||
# Included ONCE from CMakeLists.txt, inside `if (MOBILEGL_BUILD_DISAGGREGATED)`, and the place is
|
||||
# load-bearing twice over: it must come BEFORE the Magma tier-2 replay (every
|
||||
# mgl_itest_register_split_arms call here is replayed onto DirectVulkan's informational
|
||||
# integration-magma-all-{split,spawn,tcp} arms from MGL_SPLIT_ARM_CALLS) and BEFORE the
|
||||
# SplitLogPaths configure_file at the end of that file (every entry here gets its private
|
||||
# MOBILEGL_LOG_FILE_PATH from MGL_SPLIT_ARM_TEST_LISTS, or SplitLogPaths.PrivateAndDistinct reds).
|
||||
#
|
||||
# THE GATE IS scripts/ci/split_coverage.py: after the arm and tail segments come off, every
|
||||
# monolith DirectGLES. / DirectVulkan. case is on a gated split arm or is named, with a reason
|
||||
# class, in scripts/data/split_coverage_exemptions.txt. Deleting a line below reds it BY NAME.
|
||||
# The per-case census this list was cut from (inproc / spawn / tcp, junit per arm) is
|
||||
# docs/Disaggregated/notes/p8/A.md.
|
||||
|
||||
# ---- (a) THE AMBIENT CASES -----------------------------------------------------------------
|
||||
#
|
||||
# Every case here was registered on monolith only, and on all three arms under the armed split
|
||||
# environment it either PASSED (records crossed: ScenarioFixture's EmitSeq assertion is armed on
|
||||
# these arms) or SKIPPED for the same reason its monolith entry skips on this host (ClipDistance,
|
||||
# DualSourceBlend, IterationRP*, SsboDeclarationForm's two known gaps, and the knob-pinned cases
|
||||
# the (a') block below runs with their knob). Cases that belong to monolith by nature are in the
|
||||
# exemption table instead, never here: no-records (limits / shader-binary queries answered from
|
||||
# the caps mirror), single-backend (Magma-only lanes and declines), mechanism, pending-fix.
|
||||
#
|
||||
# ONE FILTER, not one call per scenario: each call is three discovery runs plus three more for
|
||||
# the Magma replay. Explicit names where a scenario is split between registered and exempt
|
||||
# cases; `Scenario.*` only where every case of the scenario is here.
|
||||
set(MGL_P8A_AMBIENT_CASES
|
||||
AdvertisedLimitsScenario.ImageUnitBindingsAreReportedFromTheFrontendState
|
||||
AdvertisedLimitsScenario.IndexedBufferBindingsAreReportedVerbatimOnBothWidths
|
||||
BufferTextureScenario.*
|
||||
ClearTexImageUndefinedLevelZeroScenario.*
|
||||
ClipDistanceScenario.*
|
||||
CopyImageLevelRangeScenario.*
|
||||
CopyImagePacked16Scenario.*
|
||||
CrossFrameBufferScenario.*
|
||||
DefaultFramebufferAcrossSwapScenario.*
|
||||
DepthStencilReadbackAttachmentShapeScenario.DepthOfALayeredCubeAttachmentReadsBack
|
||||
DepthStencilReadbackAttachmentShapeScenario.DepthOfAPlainTexture2DAttachmentReadsBack
|
||||
DepthStencilReadbackAttachmentShapeScenario.DepthOfAnArrayLayerAttachmentReadsBack
|
||||
DepthStencilReadbackAttachmentShapeScenario.PackedArrayLayerAttachmentReadsBackBothAspects
|
||||
DepthStencilReadbackScenario.*
|
||||
DoublePrecisionScenario.*
|
||||
DualSourceBlendScenario.*
|
||||
EmptyScissorScenario.*
|
||||
F1WireScenario.ColorBlitToDefaultFromANonSampleableSourceDegrades
|
||||
F1WireScenario.ColorBlitToDefaultHonorsScissorAndReversedRect
|
||||
F1WireScenario.ColorBlitToDefaultIgnoresViewportAndPreservesOrientation
|
||||
F1WireScenario.ComputeImageStoreFramebufferPixels
|
||||
F1WireScenario.ComputeImageStoreThroughViewPreservesOtherRootLayer
|
||||
F1WireScenario.ComputeWrittenVertexAndIndexBuffersDrawAndReadBack
|
||||
F1WireScenario.CopyImageInPlaceAcrossLevelsAndLayers
|
||||
F1WireScenario.EnabledVertexBufferDrawKeepsNamedP7Fatal
|
||||
F1WireScenario.GenerateMipmapHonorsMutableBaseAndMaxWithoutChangingOtherLevels
|
||||
F1WireScenario.GenerateMipmapThroughViewKeepsOwnerLevelsAndLayersOutsideItsWindow
|
||||
F1WireScenario.MultisampleColorBlitScalesAndFlipsThroughTheResolveScratch
|
||||
F1WireScenario.PartialTextureUploadPreservesGpuClearPixels
|
||||
F1WireScenario.SrgbDrawTracksFramebufferConversion
|
||||
F1WireScenario.StreamedBufferSubDataBeforeEachDrawIsOrdered
|
||||
F1WireScenario.Texture1DFramebufferClearPixels
|
||||
F1WireScenario.TextureReadbackLargerThanAReplySlotContainsGpuWrites
|
||||
F1WireScenario.TextureViewClearTargetsItsRootMipAndLayer
|
||||
F1WireScenario.UniformBufferRangeAndRebindPixels
|
||||
F1WireScenario.VertexIdSamplerAndScalarUniformPixels
|
||||
FormatlessImageBakeScenario.*
|
||||
FragCoordOriginScenario.*
|
||||
FragmentOutputArrayIndexScenario.*
|
||||
GeometryDrawModeScenario.*
|
||||
GuiBatchScenario.*
|
||||
ImageFormatQualifierScenario.*
|
||||
ImageLoadStoreSsoScenario.PerElementImageUnitsReachAPipelineDraw
|
||||
ImageLoadStoreSsoScenario.ReassigningAnImageUnitBetweenDrawsReachesTheNextDraw
|
||||
IterationRPFirstReductionScenario.*
|
||||
IterationRPProgram203Scenario.*
|
||||
IterationRPScratchFixScenario.*
|
||||
LayeredAttachmentBarrierScenario.*
|
||||
LayeredAttachmentShapeScenario.*
|
||||
NonCoreImageFormatScenario.*
|
||||
OrientationScenario.*
|
||||
P4aFinalFixScenario.*
|
||||
P4aSeamAuditScenario.*
|
||||
PipelineFailureScenario.*
|
||||
PointSizeDemotionScenario.*
|
||||
ProgramPipelineScenario.ASamplerUnitRewrittenBetweenDrawsKeepsPaintingTheRightTexture
|
||||
ProgramPipelineScenario.AStageProgramsUniformBlockBindingReachesThePipelineDraw
|
||||
ProgramPipelineScenario.ATwoStagePipelinePaintsWhatItsStagesDescribe
|
||||
ProgramPipelineScenario.AUniformDeclaredInTwoStagesKeepsTheValueTheWrittenStageHolds
|
||||
ProgramPipelineScenario.ComputeAndGraphicsStagesShareOnePipeline
|
||||
ProgramPipelineScenario.RebindingAUniformBlockBetweenDrawsReachesTheNextDraw
|
||||
ProgramPipelineScenario.SwitchingBetweenAPipelineAndAMonolithicProgramLeavesNoError
|
||||
ProgramPipelineScenario.UniformsGoToTheActiveShaderProgram
|
||||
RenderbufferBlendFormatScenario.*
|
||||
ResidentIndexScenario.*
|
||||
SampleMaskScopeScenario.*
|
||||
SampleVariablesScenario.*
|
||||
SnormAttachmentScenario.*
|
||||
SpirvShaderBinaryScenario.SpecializedModulesLinkAndRenderWithTheirConstantsApplied
|
||||
SsboArrayDynamicIndexScenario.*
|
||||
SsboArrayLengthScenario.*
|
||||
SsboDeclarationFormScenario.*
|
||||
StorageBufferRegrowScenario.AGrownStoreIsVisibleThroughItsExistingIndexedBinding
|
||||
StreamedArenaScenario.*
|
||||
TessellationDrawModeScenario.*
|
||||
ThreeChannelAttachmentScenario.*
|
||||
UnboundCounterBlockScenario.*
|
||||
UnlocatedIoBlockScenario.*
|
||||
UnwrittenPositionOutputScenario.ARedeclaredButUnwrittenPositionStillDraws
|
||||
UnwrittenPositionOutputScenario.AShaderWithNoPositionBlockStillDraws
|
||||
UnwrittenPositionOutputScenario.AWrittenRedeclaredPositionStillDraws
|
||||
UnwrittenPositionOutputScenario.CapturingAWrittenPositionStillDraws
|
||||
VertexArrayEnableDisableScenario.*
|
||||
VertexAttribBindingScenario.*
|
||||
ViewportArrayScenario.*
|
||||
)
|
||||
list(JOIN MGL_P8A_AMBIENT_CASES ":" mglP8aAmbientFilter)
|
||||
mgl_itest_register_split_arms("${mglP8aAmbientFilter}" "")
|
||||
|
||||
# ---- (a') THE SERVER-READ KNOB LANES, EACH WITH A TCP SUPERVISOR OF ITS OWN ------------------
|
||||
#
|
||||
# The monolith file pins five whole-process knobs in lanes of their own (PointSizeDemotion.,
|
||||
# UnlocatedIoBlocks., NoViewportArrayEmulation., WidenedPacked16., ForcedDepthStencilEmulation.),
|
||||
# because without them the cases are unfalsifiable on llvmpipe or skip. Every one of the five is
|
||||
# read through ConfigLoader IN THE PROCESS THAT RUNS THE BACKEND - the server. Under inproc that
|
||||
# is this process and under spawn the child inherits ::environ, so the entry's ENVIRONMENT
|
||||
# reaches it; under tcp the server is a supervisor started by a fixture, and the lane-wide
|
||||
# TcpServer.Start supervisor never sees a per-entry variable. MEASURED on this tree before this
|
||||
# block existed: with the knob in the client's environment only, the Tcp copies of
|
||||
# PointSizeDemotion's and UnlocatedIoBlocks' arming cases read no arming line and
|
||||
# NoViewportArrayEmulation's negative control saw the emulation still on - red, where the Split
|
||||
# and Spawn copies were green.
|
||||
#
|
||||
# THE KNOB CANNOT GO ON THE SHARED FIXTURE (the IC TcpServer.Start precedent, which works for
|
||||
# MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW only because every arm sets that one): it would retarget
|
||||
# every other tcp case - viewport emulation off breaks ViewportArrayScenario's positive cases,
|
||||
# the IO-block strip rewrites every interface block. So each lane gets a supervisor of its own,
|
||||
# on its own port, started by its own fixture with the knob in the fixture's ENVIRONMENT.
|
||||
#
|
||||
# THE PORTS: the lane endpoint's port + MOBILEGL_ITEST_TCP_KNOB_PORT_OFFSET + the lane's index.
|
||||
# The default offset (1000) keeps the five clear of every per-tree endpoint in use (40613 CI,
|
||||
# 407xx / 409xx split and verify builds, 484x0 package trees) and of each other's trees.
|
||||
#
|
||||
# THE LOCK IS STILL mobilegl-tcp: spawn_lane_parity.py / junit_tally.py require it of every
|
||||
# integration-tcp entry, and one tcp session at a time is what every other tcp entry assumes.
|
||||
#
|
||||
# NOT FOR DirectVulkan: all five knobs are Espryt's (Magma routes viewports natively, has no
|
||||
# IO-block strip, no packed-16 widening, no DS readback emulation; point-size demotion's Magma
|
||||
# lane is monolith-only and stays so here).
|
||||
set(MOBILEGL_ITEST_TCP_KNOB_PORT_OFFSET 1000 CACHE STRING
|
||||
"Port offset (from MOBILEGL_ITEST_TCP_ENDPOINT's) of the per-knob loopback supervisors")
|
||||
if(NOT MOBILEGL_ITEST_TCP_ENDPOINT MATCHES "^(tcp://.+):([0-9]+)$")
|
||||
message(FATAL_ERROR "P8-A: MOBILEGL_ITEST_TCP_ENDPOINT '${MOBILEGL_ITEST_TCP_ENDPOINT}' is not "
|
||||
"tcp://<host>:<port>; the per-knob tcp supervisors derive their ports from it")
|
||||
endif()
|
||||
set(mglP8aTcpHost "${CMAKE_MATCH_1}")
|
||||
set(mglP8aTcpPort "${CMAKE_MATCH_2}")
|
||||
|
||||
set(MGL_P8A_KNOB_LANES PointSizeDemotion UnlocatedIoBlocks NoViewportArrayEmulation WidenedPacked16
|
||||
ForcedDs)
|
||||
set(MGL_P8A_KNOB_FILTER_PointSizeDemotion "PointSizeDemotionScenario.*")
|
||||
set(MGL_P8A_KNOB_ENV_PointSizeDemotion "MOBILEGL_POINT_SIZE_DEMOTION=1")
|
||||
set(MGL_P8A_KNOB_FILTER_UnlocatedIoBlocks "UnlocatedIoBlockScenario.*")
|
||||
set(MGL_P8A_KNOB_ENV_UnlocatedIoBlocks "MOBILEGL_ESPRYT_UNLOCATED_IO_BLOCKS=1")
|
||||
# One case, as on monolith: the three positive cases describe behaviour the backend does not
|
||||
# have with the emulation off.
|
||||
set(MGL_P8A_KNOB_FILTER_NoViewportArrayEmulation
|
||||
"ViewportArrayScenario.WithoutTheEmulationEveryIndexCollapsesOntoViewportZero")
|
||||
set(MGL_P8A_KNOB_ENV_NoViewportArrayEmulation "MOBILEGL_ESPRYT_FORCE_VIEWPORT_ARRAY_EMULATION=0")
|
||||
set(MGL_P8A_KNOB_FILTER_WidenedPacked16 "CopyImagePacked16Scenario.*")
|
||||
set(MGL_P8A_KNOB_ENV_WidenedPacked16 "MOBILEGL_ESPRYT_WIDEN_PACKED16_STORAGE=1")
|
||||
# ForcedDs keeps the spelling the existing Split/Spawn DepthStencilReadbackMatrix block uses. The
|
||||
# default-framebuffer case is left out on every arm: red on spawn and tcp with or without the
|
||||
# knob (pending-fix row in the exemption table).
|
||||
set(MGL_P8A_KNOB_FILTER_ForcedDs
|
||||
"DepthStencilReadbackScenario.*:DepthStencilReadbackAttachmentShapeScenario.*-DepthStencilReadbackAttachmentShapeScenario.DefaultFramebufferDepthStencilFormatIsBlitCompatible")
|
||||
set(MGL_P8A_KNOB_ENV_ForcedDs "MOBILEGL_ESPRYT_FORCE_DS_READBACK_EMULATION=1")
|
||||
# The Matrix scenario's Tcp copy moves HERE from the ForcedDs block (same names): that block
|
||||
# put the knob in the client's environment only, so its Tcp arm ran the native readback path.
|
||||
set(MGL_P8A_KNOB_TCP_FILTER_ForcedDs
|
||||
"DepthStencilReadbackMatrixScenario.*:${MGL_P8A_KNOB_FILTER_ForcedDs}")
|
||||
|
||||
set(mglP8aKnobIndex 0)
|
||||
foreach(mglP8aLane IN LISTS MGL_P8A_KNOB_LANES)
|
||||
string(TOLOWER "${mglP8aLane}" mglP8aLaneLower)
|
||||
math(EXPR mglP8aKnobPort "${mglP8aTcpPort} + ${MOBILEGL_ITEST_TCP_KNOB_PORT_OFFSET} + ${mglP8aKnobIndex}")
|
||||
math(EXPR mglP8aKnobIndex "${mglP8aKnobIndex} + 1")
|
||||
set(mglP8aKnobEndpoint "${mglP8aTcpHost}:${mglP8aKnobPort}")
|
||||
set(mglP8aKnobFixture "mobilegl-tcp-p8a-${mglP8aLaneLower}")
|
||||
set(mglP8aKnobState "${CMAKE_CURRENT_BINARY_DIR}/tcp-server-p8a-${mglP8aLaneLower}.json")
|
||||
set(mglP8aKnob "${MGL_P8A_KNOB_ENV_${mglP8aLane}}")
|
||||
add_test(NAME TcpServer.Start.P8a${mglP8aLane} COMMAND "${Python3_EXECUTABLE}" "${MGL_TCP_FIXTURE}"
|
||||
start --server "${MGL_ITEST_SPLIT_SERVER_PATH}" --endpoint "${mglP8aKnobEndpoint}"
|
||||
--state "${mglP8aKnobState}")
|
||||
add_test(NAME TcpServer.Stop.P8a${mglP8aLane} COMMAND "${Python3_EXECUTABLE}" "${MGL_TCP_FIXTURE}"
|
||||
stop --state "${mglP8aKnobState}")
|
||||
# The texture-view opt-in as on the shared fixture, so this supervisor differs from it in
|
||||
# the knob alone.
|
||||
set_tests_properties(TcpServer.Start.P8a${mglP8aLane} PROPERTIES
|
||||
FIXTURES_SETUP ${mglP8aKnobFixture} TIMEOUT 20
|
||||
ENVIRONMENT "MOBILEGL_ESPRYT_ENABLE_TEXTURE_VIEW=1;${mglP8aKnob}")
|
||||
set_tests_properties(TcpServer.Stop.P8a${mglP8aLane} PROPERTIES
|
||||
FIXTURES_CLEANUP ${mglP8aKnobFixture} TIMEOUT 20)
|
||||
|
||||
foreach(mglP8aArm Split Spawn Tcp)
|
||||
string(TOLOWER "${mglP8aArm}" mglP8aArmLower)
|
||||
string(TOUPPER "${mglP8aArm}" mglP8aArmUpper)
|
||||
set(mglP8aEnvironment "${MGL_ITEST_GLES_${mglP8aArmUpper}_ENVIRONMENT}")
|
||||
set(mglP8aFilter "${MGL_P8A_KNOB_FILTER_${mglP8aLane}}")
|
||||
set(mglP8aProperties)
|
||||
if(mglP8aArm STREQUAL "Tcp")
|
||||
string(REPLACE "MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT}"
|
||||
"MOBILEGL_IPC_CONTROL=${mglP8aKnobEndpoint}"
|
||||
mglP8aEnvironment "${mglP8aEnvironment}")
|
||||
if(mglP8aEnvironment STREQUAL MGL_ITEST_GLES_TCP_ENVIRONMENT)
|
||||
message(FATAL_ERROR "P8-A: the Tcp arm environment names no "
|
||||
"MOBILEGL_IPC_CONTROL=${MOBILEGL_ITEST_TCP_ENDPOINT} to retarget")
|
||||
endif()
|
||||
set(mglP8aProperties FIXTURES_REQUIRED ${mglP8aKnobFixture} RESOURCE_LOCK mobilegl-tcp)
|
||||
if(DEFINED MGL_P8A_KNOB_TCP_FILTER_${mglP8aLane})
|
||||
set(mglP8aFilter "${MGL_P8A_KNOB_TCP_FILTER_${mglP8aLane}}")
|
||||
endif()
|
||||
endif()
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectGLES.${mglP8aArm}.${mglP8aLane}."
|
||||
TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}"
|
||||
TEST_FILTER "${mglP8aFilter}"
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES
|
||||
LABELS "integration-gpu\;integration-${mglP8aArmLower}"
|
||||
TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
ENVIRONMENT "${mglP8aEnvironment}\;${mglP8aKnob}"
|
||||
${mglP8aProperties}
|
||||
)
|
||||
list(APPEND MGL_SPLIT_ARM_TEST_LISTS "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}")
|
||||
math(EXPR MGL_SPLIT_ARM_INDEX "${MGL_SPLIT_ARM_INDEX} + 1")
|
||||
endforeach()
|
||||
endforeach()
|
||||
message(STATUS "Integration tests: P8-A registers ${mglP8aKnobIndex} server-knob lane(s) on "
|
||||
"split/spawn/tcp, each tcp arm on its own supervisor")
|
||||
Executable
+328
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env python3
|
||||
"""P8-A (ID-P8-2): every monolith integration case runs on a split arm or is exempt BY NAME.
|
||||
|
||||
THE QUESTION. The monolith registrations (`DirectGLES.<case>`, `DirectVulkan.<case>`, plus their
|
||||
knob-pinned lanes such as `DirectGLES.ForcedDepthStencilEmulation.<case>`) define what the suite
|
||||
tests. A case that exists there and on no split arm is a case the disaggregated runtime was never
|
||||
asked about - and nothing about a green split lane says so. This asserts, per backend:
|
||||
|
||||
monolith cases ⊆ split cases ∪ exempt cases
|
||||
|
||||
after the arm and tail segments come off both sides: an entry name is `<Backend>.[<Arm>.][<tail>.]
|
||||
<Suite>.<Case>`, and only the last two segments (the gtest name) are compared. `Split.F1.`,
|
||||
`Spawn.ForcedDs.`, `Tcp.MultiDrawTierBaseVertex.` and the monolith's
|
||||
`ForcedDepthStencilEmulation.` all normalise away.
|
||||
|
||||
WHAT COUNTS AS A SPLIT CASE differs by backend, on purpose:
|
||||
* DirectGLES - an entry with an arm segment (Split/Spawn/Tcp/TcpDevice) that carries a GATED
|
||||
label. The one informational spelling family is `integration-magma-{all,full}-<arm>`
|
||||
(MG_IntegrationTest/CMakeLists.txt, P7 package L tiers 2 and 3), and an entry carrying only
|
||||
those labels is not coverage.
|
||||
* DirectVulkan - any split-arm entry, the informational tiers included. Magma's split arms are
|
||||
informational by ruling (ID-P8-2): cases move from tier 2/3 into the gated Magma tier with the
|
||||
commit that retires their blocker. The report prints how many are gated and how many are
|
||||
informational-only, so the number is visible without being a gate.
|
||||
|
||||
WHAT COUNTS AS A MONOLITH CASE: an entry with no arm segment carrying `integration-gpu` - the label
|
||||
the monolith registrations and the monolith-control mechanism entries share. That puts
|
||||
`DirectGLES.PushMonolithArm.<case>` (labelled integration-split, but no arm) on the monolith side,
|
||||
where it belongs, and leaves build-verify's `DirectGLES.VerifySplit.<case>` (no integration-gpu)
|
||||
out of both sides.
|
||||
|
||||
THE EXEMPTION TABLE (scripts/data/split_coverage_exemptions.txt), one row per pattern:
|
||||
|
||||
[<Backend>:]<gtest pattern> <reason-class> <one-line reason>
|
||||
|
||||
with reason classes mechanism / single-backend / no-records / inproc-peek / pending-fix. The table
|
||||
keeps itself honest the way spawn_lane_parity.py's exception lists do - every one of these is an
|
||||
error, not a warning:
|
||||
* a pattern that matches no monolith case (an exemption about nothing);
|
||||
* a pattern every case of which is already on a split arm (stale: a pending-fix that landed must
|
||||
take its row with it, or the next regression of that case is silently exempt);
|
||||
* a `pending-fix` row that names neither a package (`P8-B`) nor a DEBTS.md row;
|
||||
* a `single-backend` row with no `<Backend>:` scope (it exempts the backend the case does NOT
|
||||
belong to, and unscoped it would also hide a gap on the one it does);
|
||||
* an unknown reason class, a missing reason, a duplicated pattern.
|
||||
|
||||
This script does not compare the arms with each other - spawn_lane_parity.py owns that.
|
||||
"""
|
||||
import argparse
|
||||
import fnmatch
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
BACKENDS = ("DirectGLES", "DirectVulkan")
|
||||
ARMS = ("Split", "Spawn", "Tcp", "TcpDevice")
|
||||
REASON_CLASSES = ("mechanism", "single-backend", "no-records", "inproc-peek", "pending-fix")
|
||||
MONOLITH_LABEL = "integration-gpu"
|
||||
INFORMATIONAL_LABEL = re.compile(r"^integration-magma-(all|full)-(split|spawn|tcp)$")
|
||||
GTEST_NAME = re.compile(r"^[A-Za-z0-9_]+\.[A-Za-z0-9_]+$")
|
||||
PATTERN = re.compile(r"^(?:(DirectGLES|DirectVulkan):)?([A-Za-z0-9_*?]+\.[A-Za-z0-9_*?]+)$")
|
||||
OWNER = re.compile(r"\bP\d+[a-z]?-[0-9A-Z]+\b|DEBTS\.md")
|
||||
DEFAULT_EXEMPTIONS = Path(__file__).resolve().parents[2] / "scripts/data/split_coverage_exemptions.txt"
|
||||
|
||||
|
||||
def labels_of(test):
|
||||
for prop in test.get("properties", []):
|
||||
if prop.get("name") == "LABELS":
|
||||
value = prop.get("value") or []
|
||||
return [value] if isinstance(value, str) else list(value)
|
||||
return []
|
||||
|
||||
|
||||
def classify(document, errors):
|
||||
"""{backend: {"mono": set, "gated": set, "info": set, "arms": {arm: set}}} from a
|
||||
`ctest --show-only=json-v1` document. Names under a backend that do not parse are errors."""
|
||||
out = {b: {"mono": set(), "gated": set(), "info": set(), "arms": {a: set() for a in ARMS}}
|
||||
for b in BACKENDS}
|
||||
for test in document.get("tests", []):
|
||||
name = test.get("name", "")
|
||||
parts = name.split(".")
|
||||
if parts[0] not in BACKENDS:
|
||||
continue
|
||||
backend = parts[0]
|
||||
labels = labels_of(test)
|
||||
arm = parts[1] if len(parts) > 1 and parts[1] in ARMS else None
|
||||
gtest = ".".join(parts[-2:])
|
||||
rest = parts[2:] if arm else parts[1:]
|
||||
if len(rest) < 2 or not GTEST_NAME.match(gtest):
|
||||
errors.append(f"{name}: does not parse as <Backend>.[<Arm>.][<tail>.]<Suite>.<Case>, so "
|
||||
f"this gate would silently drop it")
|
||||
continue
|
||||
side = out[backend]
|
||||
if arm is None:
|
||||
if MONOLITH_LABEL in labels:
|
||||
side["mono"].add(gtest)
|
||||
continue
|
||||
if labels and not all(INFORMATIONAL_LABEL.match(label) for label in labels):
|
||||
side["gated"].add(gtest)
|
||||
side["arms"][arm].add(gtest)
|
||||
else:
|
||||
side["info"].add(gtest)
|
||||
return out
|
||||
|
||||
|
||||
def parse_exemptions(text, errors):
|
||||
rows = []
|
||||
seen = set()
|
||||
for number, raw in enumerate(text.splitlines(), 1):
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
fields = line.split(None, 2)
|
||||
where = f"exemptions:{number}"
|
||||
if len(fields) < 3 or not fields[2].strip():
|
||||
errors.append(f"{where}: `{line}` needs <pattern> <reason-class> <reason>")
|
||||
continue
|
||||
pattern, reason_class, reason = fields
|
||||
match = PATTERN.match(pattern)
|
||||
if not match:
|
||||
errors.append(f"{where}: `{pattern}` is not [<Backend>:]<Suite>.<Case> (globs * and ? allowed)")
|
||||
continue
|
||||
if reason_class not in REASON_CLASSES:
|
||||
errors.append(f"{where}: reason class `{reason_class}` is not one of {', '.join(REASON_CLASSES)}")
|
||||
continue
|
||||
if reason_class == "pending-fix" and not OWNER.search(reason):
|
||||
errors.append(f"{where}: pending-fix `{pattern}` names no owner - a package (P8-B) or a "
|
||||
f"DEBTS.md row must be in the reason, or the row outlives everyone's memory of it")
|
||||
continue
|
||||
if reason_class == "single-backend" and match.group(1) is None:
|
||||
errors.append(f"{where}: single-backend `{pattern}` must be scoped `<Backend>:` to the backend "
|
||||
f"it exempts; unscoped it would also hide a gap on the backend the case belongs to")
|
||||
continue
|
||||
if pattern in seen:
|
||||
errors.append(f"{where}: `{pattern}` is listed twice")
|
||||
continue
|
||||
seen.add(pattern)
|
||||
backends = (match.group(1),) if match.group(1) else BACKENDS
|
||||
rows.append({"where": where, "pattern": pattern, "glob": match.group(2), "backends": backends,
|
||||
"class": reason_class, "reason": reason.strip()})
|
||||
return rows
|
||||
|
||||
|
||||
def check(document, exemption_text, out=print):
|
||||
"""Returns the exit code; every finding goes through `out`."""
|
||||
errors = []
|
||||
sides = classify(document, errors)
|
||||
rows = parse_exemptions(exemption_text, errors)
|
||||
covered = {"DirectGLES": sides["DirectGLES"]["gated"],
|
||||
"DirectVulkan": sides["DirectVulkan"]["gated"] | sides["DirectVulkan"]["info"]}
|
||||
exempt = {b: {} for b in BACKENDS}
|
||||
for row in rows:
|
||||
matched = needed = 0
|
||||
for backend in row["backends"]:
|
||||
for case in sorted(sides[backend]["mono"]):
|
||||
if not fnmatch.fnmatchcase(case, row["glob"]):
|
||||
continue
|
||||
matched += 1
|
||||
if case not in covered[backend]:
|
||||
needed += 1
|
||||
exempt[backend].setdefault(case, row)
|
||||
if matched == 0:
|
||||
errors.append(f"{row['where']}: `{row['pattern']}` matches no monolith case - an exemption "
|
||||
f"about nothing (renamed or deleted case?)")
|
||||
elif needed == 0:
|
||||
errors.append(f"{row['where']}: `{row['pattern']}` is STALE - every monolith case it names is "
|
||||
f"on a split arm now; delete the row so the next regression is not exempt")
|
||||
for backend in BACKENDS:
|
||||
side = sides[backend]
|
||||
mono = side["mono"]
|
||||
if not mono:
|
||||
errors.append(f"{backend}: no monolith cases in this build - an empty set is covered by "
|
||||
f"anything, which is the one way this gate could stop meaning something")
|
||||
continue
|
||||
missing = sorted(mono - covered[backend] - set(exempt[backend]))
|
||||
by_class = {}
|
||||
for row in exempt[backend].values():
|
||||
by_class[row["class"]] = by_class.get(row["class"], 0) + 1
|
||||
per_arm = ", ".join(f"{arm.lower()} {len(mono & cases)}" for arm, cases in side["arms"].items() if cases)
|
||||
classes = ", ".join(f"{name} {count}" for name, count in sorted(by_class.items())) or "none"
|
||||
if backend == "DirectGLES":
|
||||
out(f"split coverage {backend}: monolith {len(mono)}, on a gated split arm "
|
||||
f"{len(mono & side['gated'])} ({per_arm}), exempt {len(exempt[backend])} ({classes}), "
|
||||
f"missing {len(missing)}")
|
||||
else:
|
||||
gated = mono & side["gated"]
|
||||
out(f"split coverage {backend}: monolith {len(mono)}, on a gated split arm {len(gated)} "
|
||||
f"({per_arm}), informational tiers only {len(mono & side['info'] - side['gated'])}, "
|
||||
f"exempt {len(exempt[backend])} ({classes}), missing {len(missing)}")
|
||||
for case in missing:
|
||||
errors.append(f"{backend}.{case} is registered on monolith and on no "
|
||||
f"{'gated ' if backend == 'DirectGLES' else ''}split arm, and no exemption "
|
||||
f"names it: register it (MG_IntegrationTest/P8Coverage.cmake) or add a row "
|
||||
f"to scripts/data/split_coverage_exemptions.txt with its reason class")
|
||||
for message in errors:
|
||||
out(f"::error::{message}")
|
||||
return 1 if errors else 0
|
||||
|
||||
|
||||
def ctest_document(build_dir):
|
||||
text = subprocess.run(["ctest", "--show-only=json-v1"], cwd=build_dir, capture_output=True,
|
||||
text=True, check=True).stdout
|
||||
return json.loads(text)
|
||||
|
||||
|
||||
# ---- self-test -------------------------------------------------------------------------------
|
||||
|
||||
def _entry(name, *labels):
|
||||
return {"name": name, "properties": [{"name": "LABELS", "value": list(labels)}]}
|
||||
|
||||
|
||||
def _doc(*entries):
|
||||
return {"tests": list(entries)}
|
||||
|
||||
|
||||
BASE = [
|
||||
_entry("DirectGLES.AScenario.One", "integration-gpu"),
|
||||
_entry("DirectGLES.AScenario.Two", "integration-gpu"),
|
||||
_entry("DirectGLES.ForcedDepthStencilEmulation.BScenario.Three", "integration-gpu"),
|
||||
_entry("DirectGLES.Split.AScenario.One", "integration-gpu", "integration-split"),
|
||||
_entry("DirectGLES.Spawn.F1.AScenario.Two", "integration-gpu", "integration-spawn"),
|
||||
_entry("DirectGLES.Tcp.ForcedDs.BScenario.Three", "integration-gpu", "integration-tcp"),
|
||||
_entry("DirectVulkan.AScenario.One", "integration-gpu"),
|
||||
_entry("DirectVulkan.MagmaOnlyScenario.Four", "integration-gpu"),
|
||||
_entry("DirectVulkan.Split.AScenario.One", "integration-magma-all-split"),
|
||||
_entry("DirectVulkan.Tcp.Fm.MagmaOnlyScenario.Four", "integration-magma-tcp"),
|
||||
_entry("TcpServer.Start"),
|
||||
]
|
||||
|
||||
|
||||
def _run(document, exemptions):
|
||||
lines = []
|
||||
rc = check(document, exemptions, out=lines.append)
|
||||
return rc, "\n".join(lines)
|
||||
|
||||
|
||||
def self_test():
|
||||
failures = []
|
||||
ran = []
|
||||
|
||||
def expect(label, document, exemptions, rc, *needles, absent=()):
|
||||
ran.append(label)
|
||||
got, text = _run(document, exemptions)
|
||||
if got != rc or any(n not in text for n in needles) or any(n in text for n in absent):
|
||||
failures.append(f"{label}: rc {got} (want {rc}); output:\n{text}")
|
||||
|
||||
expect("tails normalise on both sides", _doc(*BASE), "", 0,
|
||||
"DirectGLES: monolith 3, on a gated split arm 3", "missing 0")
|
||||
expect("a monolith case on no arm reds BY NAME",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")), "", 1,
|
||||
"DirectGLES.CScenario.Five is registered on monolith")
|
||||
expect("an informational-only arm is not DirectGLES coverage",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu"),
|
||||
_entry("DirectGLES.Split.CScenario.Five", "integration-magma-full-split")), "", 1,
|
||||
"DirectGLES.CScenario.Five is registered on monolith")
|
||||
expect("an informational tier IS DirectVulkan coverage, and is reported as such", _doc(*BASE), "", 0,
|
||||
"DirectVulkan: monolith 2, on a gated split arm 1", "informational tiers only 1")
|
||||
expect("an arm-less entry without integration-gpu is on neither side",
|
||||
_doc(*BASE, _entry("DirectGLES.VerifySplit.CScenario.Five", "integration-verify-split")), "", 0)
|
||||
expect("PushMonolithArm (integration-split, no arm) is a MONOLITH case",
|
||||
_doc(*BASE, _entry("DirectGLES.PushMonolithArm.CScenario.Five", "integration-gpu",
|
||||
"integration-split")), "", 1,
|
||||
"DirectGLES.CScenario.Five is registered on monolith")
|
||||
expect("an exemption covers a missing case",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.* mechanism toggles a process-local mask", 0, "exempt 1 (mechanism 1)")
|
||||
expect("a stale exemption reds",
|
||||
_doc(*BASE), "AScenario.One no-records query only", 1, "is STALE")
|
||||
expect("an exemption about nothing reds",
|
||||
_doc(*BASE), "ZScenario.* mechanism gone", 1, "matches no monolith case")
|
||||
expect("pending-fix without an owner reds",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.Five pending-fix red on spawn", 1, "names no owner")
|
||||
expect("pending-fix owned by a package passes",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.Five pending-fix red on spawn until P8-B lands", 0)
|
||||
expect("pending-fix owned by a DEBTS.md row passes",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.Five pending-fix red on spawn (DEBTS.md row: default framebuffer)", 0)
|
||||
expect("an unknown reason class reds",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.Five flaky it is slow", 1, "is not one of")
|
||||
expect("an unscoped single-backend row reds",
|
||||
_doc(*BASE, _entry("DirectGLES.MagmaOnlyScenario.Four", "integration-gpu")),
|
||||
"MagmaOnlyScenario.* single-backend Magma's lane", 1, "must be scoped")
|
||||
expect("a scoped single-backend row exempts only its backend",
|
||||
_doc(*BASE, _entry("DirectGLES.MagmaOnlyScenario.Four", "integration-gpu")),
|
||||
"DirectGLES:MagmaOnlyScenario.* single-backend Magma's lane", 0)
|
||||
expect("a duplicated pattern reds",
|
||||
_doc(*BASE, _entry("DirectGLES.CScenario.Five", "integration-gpu")),
|
||||
"CScenario.* mechanism a\nCScenario.* mechanism b", 1, "listed twice")
|
||||
expect("an unparseable backend entry reds", _doc(*BASE, _entry("DirectGLES.Split.Oops", "integration-split")),
|
||||
"", 1, "does not parse")
|
||||
expect("an empty monolith side reds", _doc(*[e for e in BASE if e["name"].startswith("DirectVulkan")]),
|
||||
"", 1, "DirectGLES: no monolith cases")
|
||||
expect("a row with no reason reds", _doc(*BASE), "AScenario.Two mechanism", 1, "needs <pattern>")
|
||||
# The real table must at least parse (its coverage needs a build; the gate run checks that).
|
||||
if DEFAULT_EXEMPTIONS.exists():
|
||||
errors = []
|
||||
parse_exemptions(DEFAULT_EXEMPTIONS.read_text(), errors)
|
||||
if errors:
|
||||
failures.append("the checked-in exemption table does not parse:\n" + "\n".join(errors))
|
||||
for failure in failures:
|
||||
print(f"::error::self-test: {failure}", file=sys.stderr)
|
||||
print(f"split_coverage self-test: {len(ran)} cases + the checked-in table's parse, "
|
||||
f"{len(failures)} failure(s), {'OK' if not failures else 'FAILED'}")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__.split("\n", 1)[0])
|
||||
parser.add_argument("build_dir", nargs="?", help="a configured build (runs ctest --show-only=json-v1)")
|
||||
parser.add_argument("--json", help="a saved `ctest --show-only=json-v1` document instead of a build")
|
||||
parser.add_argument("--exemptions", default=str(DEFAULT_EXEMPTIONS))
|
||||
parser.add_argument("--self-test", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.self_test:
|
||||
return self_test()
|
||||
if bool(args.build_dir) == bool(args.json):
|
||||
parser.error("give exactly one of build_dir or --json")
|
||||
document = json.loads(Path(args.json).read_text()) if args.json else ctest_document(args.build_dir)
|
||||
return check(document, Path(args.exemptions).read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,64 @@
|
||||
# Monolith integration cases that are on NO split arm, and why (P8-A, ID-P8-2).
|
||||
# Read by scripts/ci/split_coverage.py; its docstring states every rule a row must keep.
|
||||
#
|
||||
# [<Backend>:]<gtest pattern> <reason-class> <one-line reason>
|
||||
#
|
||||
# Reason classes:
|
||||
# mechanism the case asserts a process-local mechanism or pins one arm by design
|
||||
# (MGL_ITEST_MONOLITH_MECHANISM_FILTER in MG_IntegrationTest/CMakeLists.txt)
|
||||
# single-backend the case belongs to the OTHER backend and skips on this one; scoped to this one
|
||||
# no-records the workload never reaches a verb (answered from the caps mirror or the
|
||||
# client's own error surface), so ScenarioFixture's armed split assertion
|
||||
# (EmitSeq must advance) is red by construction
|
||||
# inproc-peek reads server state through an in-process peek only an inproc arm can answer
|
||||
# pending-fix red on a split arm today; the reason names the package or DEBTS.md row that
|
||||
# fixes it, and the row goes when the case is registered (a stale row is an error)
|
||||
#
|
||||
# Evidence for every row: docs/Disaggregated/notes/p8/A.md (per-arm census, junit per case).
|
||||
|
||||
# ---- mechanism: MGL_ITEST_MONOLITH_MECHANISM_FILTER, pinned to monolith by construction -------
|
||||
HandleRecycleScenario.* mechanism pointer-key ABA / slot recycling inside one process (integration-monolith-control)
|
||||
CsoContentAddressingScenario.* mechanism toggles the process-local CSO content-addressing switch
|
||||
ResourceSubsystemControlScenario.* mechanism toggles the process-local P3a push mask
|
||||
ObjectSubsystemControlScenario.* mechanism toggles the process-local P4a push mask
|
||||
PipeVerifyArmingScenario.* mechanism same-address-space verify comparator; its split arm is build-verify's integration-verify-split
|
||||
PoisonOmissionScenario.* mechanism withheld-stamp oracle; its split arm is build-verify's VerifySplitPoisonOmitted entries
|
||||
StorageBufferRegrowScenario.NStorageDefinitionsCostNMapPersistentRoundtripsNotOnePerDraw mechanism counts synchronous stats-slot roundtrips of direct persistent adoption
|
||||
DirectGLES:MonolithAttachmentClearScenario.* mechanism pins the push-monolith arm (DirectGLES.PushMonolithArm., ID-107); the split clear-attachment path is F1WireScenario's
|
||||
DirectVulkan:DualBlockScenario.* mechanism needs MOBILEGL_IPC_ROLE_SPLIT_STATE=1; its lane is integration-dualblock-split (Espryt) and the Magma census filters it out
|
||||
|
||||
# ---- single-backend: Magma's cases, skipping on Espryt ----------------------------------------
|
||||
DirectGLES:MagmaRunAheadScenario.* single-backend Magma run-ahead lane only (integration-magma-runahead)
|
||||
DirectGLES:MagmaWireCacheScenario.* single-backend Magma wire-cache lane only (integration-magma-caches)
|
||||
DirectGLES:MagmaWireReclaimScenario.* single-backend Magma wire-arm store reclaim
|
||||
DirectGLES:MagmaVertexLayoutScenario.* single-backend Magma's vertex format map (Espryt hands GL_FIXED to the driver)
|
||||
DirectGLES:PrimitivesGeneratedNoXfbScenario.* single-backend the stream-query defect and its reroute are DirectVulkan's
|
||||
DirectGLES:ImageLoadStoreSsoScenario.AnImageArrayAlongsideAnotherDescriptorKeepsBothBindings single-backend DirectVulkan's descriptor binding remap
|
||||
DirectGLES:UnwrittenPositionOutputScenario.CapturingAnUnwrittenPositionStillDraws single-backend pins DirectVulkan's gl_Position injection (Espryt gap noted in the case)
|
||||
DirectGLES:F1WireScenario.CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone single-backend Magma wire-arm decline (CONTRACT-P7 3.2 copy-image-in-place)
|
||||
DirectGLES:F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession single-backend Magma wire-arm decline (depth-stencil-mipmap)
|
||||
DirectGLES:F1WireScenario.MultisampleBlitFromASingleSampleSourceDeclinesAndKeepsTheSession single-backend Magma wire-arm decline (multisample-blit-aspect)
|
||||
DirectGLES:F1WireScenario.MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeepsTheSession single-backend Magma wire-arm decline (multisample-blit-shape)
|
||||
DirectGLES:F1WireScenario.ShortUniformBufferRangePadsMissingBytes single-backend Magma short-UBO compatibility policy
|
||||
DirectGLES:F1WireScenario.SubWordUniformBufferRangePadsMissingBytes single-backend Magma short-UBO compatibility policy
|
||||
DirectGLES:F1WireScenario.UnalignedAtomicCounterRangeDeclinesAndTheSessionLives single-backend Magma descriptor-alignment decline
|
||||
DirectGLES:F1WireScenario.GenerateMipmapWithoutNativeBlitPixels single-backend Magma shader-mip arm (MGITEST_MAGMA_FORCE_SHADER_MIPMAP, Magma Fm lane)
|
||||
DirectGLES:F1WireScenario.GenerateMipmapDepthWithoutNativeBlitPixels single-backend Magma depth shader-mip arm (MGITEST_MAGMA_FORCE_SHADER_MIPMAP, Magma Fm lane)
|
||||
|
||||
# ---- no-records: EmitSeq does not move, so the armed split assertion is red by construction ----
|
||||
AdvertisedLimitsScenario.ComputeLocalSizeComesFromTheLinkedProgram no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.ComputeUniformBlocksIsAnAmountAnApplicationCouldActuallyAllocate no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.ComputeWorkGroupLimitsAreTheCapsBlocksAnswer no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.EveryBufferLimitIsWithinItsAdvertisedRange no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.EveryGL45CoreMinimumIsMet no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.IndexedTargetSizeSurvivesDivisionByItsBindingCount no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.PerStageBlockCountsFitInTheirBindingPoints no-records limits query answered from the client's CapsMirror
|
||||
AdvertisedLimitsScenario.ViewportArrayLimitsMeetTheirGL43Floors no-records limits query answered from the client's CapsMirror
|
||||
SpirvShaderBinaryScenario.AnUnsupportedBinaryFormatIsRejectedInsteadOfSilentlyAccepted no-records shader-binary error surface stays on the client
|
||||
SpirvShaderBinaryScenario.CompileShaderOnASpirvShaderIsInvalidOperationAndShaderSourceTakesItBack no-records shader-binary error surface stays on the client
|
||||
SpirvShaderBinaryScenario.ShaderBinaryFormatIsAdvertisedExactlyOnce no-records extension-string query answered from the client's CapsMirror
|
||||
SpirvShaderBinaryScenario.SpecializeShaderErrorSurfaceMatchesTheExtension no-records shader-binary error surface stays on the client
|
||||
ProgramPipelineScenario.AReservedNameTakesStateBeforeItIsAProgramPipeline no-records a reserved pipeline name's state is client-side and draws nothing
|
||||
|
||||
# ---- pending-fix -------------------------------------------------------------------------------
|
||||
DirectGLES:DepthStencilReadbackAttachmentShapeScenario.DefaultFramebufferDepthStencilFormatIsBlitCompatible pending-fix Espryt spawn+tcp blit the default framebuffer's depth out as 0 (inproc and all Magma arms green, knob or not); no P8 package owns it - DEBTS.md row requested in notes/p8/A.md
|
||||
Reference in New Issue
Block a user