mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
83 lines
4.7 KiB
Python
83 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
# MobileGL - tools/device_bench/p5f/verify_arm.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
|
|
|
|
"""Read-only verdict for the prepared public-pixel arm; never invokes adb."""
|
|
import pathlib
|
|
import re
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
|
|
d = pathlib.Path(sys.argv[1])
|
|
def hashes(path):
|
|
return {name.removeprefix('./'): digest for digest, name in
|
|
(line.split(maxsplit=1) for line in path.read_text().splitlines() if line.strip())}
|
|
assert hashes(d / 'sha256.txt') == hashes(d / 'device-sha256.txt'), 'host/device artifact hash mismatch'
|
|
arm = dict(re.findall(r'(backend|transport|role)=(\S+)', (d / 'arm.txt').read_text()))
|
|
assert (d / 'process-exit.txt').read_text().strip() == '0', 'process failed'
|
|
cases = ET.parse(d / 'xml').getroot().findall('.//testcase')
|
|
expected_cases = {
|
|
'ClipDistanceScenario.AnEnabledClipDistanceRemovesTheNegativeHalf',
|
|
'ClipDistanceScenario.ADisabledClipDistanceRemovesNothing',
|
|
'ClipDistanceScenario.TheEnablesAreIndependentPerDistance',
|
|
'CopyImageLayeredScenario.ArrayToArrayCopiesEverySlice',
|
|
'CopyImageLayeredScenario.ArrayToArrayHonoursDifferentLayerOffsets',
|
|
'CopyImageLayeredScenario.VolumeToVolumeHonoursNonZeroZ',
|
|
'CopyImageLayeredScenario.VolumeToVolumeAtNonZeroMipLevel',
|
|
'CopyImageLayeredScenario.ArrayToVolumeCopiesEverySlice',
|
|
'CopyImageLayeredScenario.VolumeToArrayCopiesEverySlice',
|
|
'P5fPublicMipmapScenario.UploadedRgba8BaseGeneratesRedMipChain',
|
|
'P5fPublicMipmapScenario.GpuWrittenBaseGeneratesGreenMipChainWithoutStaleCpuUpload',
|
|
}
|
|
case_names = [c.get('classname', '') + '.' + c.get('name', '') for c in cases]
|
|
assert len(cases) == len(expected_cases), f'expected 11 selected cases, got {len(cases)}'
|
|
assert len(case_names) == len(set(case_names)), ('duplicate test cases', case_names)
|
|
assert set(case_names) == expected_cases, (
|
|
'selected case set mismatch',
|
|
{'missing': sorted(expected_cases - set(case_names)),
|
|
'extra': sorted(set(case_names) - expected_cases)})
|
|
# The two pre-existing Vulkan per-distance-enable limitations are explicit,
|
|
# matched by exact case and reason, and never counted as executed passes.
|
|
allowed_skips = {
|
|
'ClipDistanceScenario.ADisabledClipDistanceRemovesNothing': 'clips by a DISABLED gl_ClipDistance',
|
|
'ClipDistanceScenario.TheEnablesAreIndependentPerDistance': 'clips by every declared gl_ClipDistance regardless of the enables',
|
|
} if arm['backend'] == 'DirectVulkan' else {}
|
|
skipped = []
|
|
for c in cases:
|
|
name = c.get('classname', '') + '.' + c.get('name', '')
|
|
assert c.get('status') == 'run', ('case did not execute', name, c.attrib)
|
|
assert c.find('failure') is None and c.find('error') is None, c.attrib
|
|
skip = c.find('skipped')
|
|
if skip is not None:
|
|
assert c.get('result') == 'skipped', ('invalid executed-skip result', name, c.attrib)
|
|
text = ' '.join(skip.itertext()) + ' ' + skip.get('message', '')
|
|
assert name in allowed_skips and allowed_skips[name] in text, ('unexpected skip', c.attrib, text)
|
|
skipped.append(name)
|
|
else:
|
|
assert c.get('result') == 'completed', ('case did not complete', name, c.attrib)
|
|
stdout = (d / 'stdout.txt').read_text(errors='replace')
|
|
log = (d / 'library.log').read_text(errors='replace')
|
|
assert f'backend={arm["backend"]}' in stdout
|
|
assert 'AImageReader window' in stdout, 'not the Android window harness'
|
|
assert not re.search(r'Fatal\{|SIGSEGV|SIGABRT|AddressSanitizer', stdout + log)
|
|
stats = [line for line in log.splitlines() if 'MGPipe stats:' in line and re.search(r'\bframes=\d+', line)]
|
|
assert stats, 'no actual stats windows'
|
|
assert all(re.search(r'\brsp=0(?:\s|$)', line) for line in stats), 'nonzero/missing rsp'
|
|
assert any(int(re.search(r'\bframes=(\d+)', line)[1]) > 0 for line in stats), 'no present frames'
|
|
vbs = [int(re.search(r'\bvbs=(\d+)', line)[1]) for line in stats]
|
|
if arm['transport'] == 'inproc':
|
|
assert any(v > 0 for v in vbs), 'no applied verb boundaries; transport not proven'
|
|
assert re.search(r'Config: IPC .*strict=1 .*role-split-state=' + arm['role'] + r'\b', log)
|
|
# Current Magma publishes the capability after its queued-state migration.
|
|
# Historical P5f bundles should be checked with their matching source verifier.
|
|
assert 'run-ahead ARMED' in log, 'missing runtime capability evidence'
|
|
else:
|
|
assert all(v == 0 for v in vbs), 'monolith control has applied verb boundaries'
|
|
print(f'PASS {arm}: {len(cases)-len(skipped)} passed, {len(skipped)} named legacy feature skips, {len(stats)} stats windows, rsp=0, vbs={sum(vbs)}')
|
|
for name in skipped: print(' preserved limitation:', name)
|