mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
151 lines
8.0 KiB
Python
151 lines
8.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Run the four real arming scenarios with server log forwarding enabled and disabled."""
|
|
import argparse
|
|
import errno
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import signal
|
|
import socket
|
|
import subprocess
|
|
import time
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
CASES = {
|
|
'unlocated': ('DirectGLES', 'UnlocatedIoBlockScenario.TheEmulationIsActuallyArmedWhenTheEnvironmentPinsItOn',
|
|
{'MOBILEGL_ESPRYT_UNLOCATED_IO_BLOCKS': '1'}, 'WITHOUT their layout(location) qualifier'),
|
|
'primitives': ('DirectVulkan', 'PrimitivesGeneratedNoXfbScenario.TheRerouteIsActuallyArmedWhenTheEnvironmentPinsItOn',
|
|
{'MOBILEGL_MAGMA_PRIMGEN_QUERY_REROUTE': '1'}, 'PRIMITIVES_GENERATED reroute engaged'),
|
|
'point-size': ('DirectGLES', 'PointSizeDemotionScenario.TheDemotionIsActuallyArmedWhenTheEnvironmentPinsItOn',
|
|
{'MOBILEGL_POINT_SIZE_DEMOTION': '1'}, 'demoted to an ordinary varying'),
|
|
'verify': ('DirectGLES', 'PipeVerifyArmingScenario.Armed',
|
|
{'MOBILEGL_PIPE_VERIFY': '1', 'MGITEST_PIPE_ARMING_LANE': '1'}, 'MGPipe: verify armed'),
|
|
}
|
|
|
|
|
|
def read(path):
|
|
return path.read_text(errors='replace') if path.exists() else ''
|
|
|
|
|
|
def run_arm(build, out, case, forward):
|
|
backend, test, knobs, marker = CASES[case]
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
with socket.socket() as reserve:
|
|
reserve.bind(('127.0.0.1', 0))
|
|
port = reserve.getsockname()[1]
|
|
endpoint = f'tcp://127.0.0.1:{port}'
|
|
env = dict(os.environ, MOBILEGL_TRANSPORT='spawn', MOBILEGL_BACKEND_TYPE=backend,
|
|
MOBILEGL_IPC_REQUIRE_SAME_BUILD='1', MOBILEGL_IPC_TOKEN='', EGL_PLATFORM='surfaceless',
|
|
LIBGL_ALWAYS_SOFTWARE='1', MOBILEGL_ITEST_REQUIRE_GPU='1', **knobs)
|
|
for name in ('MOBILEGL_PIPE_VERIFY_CORRUPT', 'MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_DIAL'):
|
|
env.pop(name, None)
|
|
if Path('/usr/share/glvnd/egl_vendor.d/50_mesa.json').exists():
|
|
env['__EGL_VENDOR_LIBRARY_FILENAMES'] = '/usr/share/glvnd/egl_vendor.d/50_mesa.json'
|
|
if Path('/usr/share/vulkan/icd.d/lvp_icd.json').exists():
|
|
env.setdefault('VK_ICD_FILENAMES', '/usr/share/vulkan/icd.d/lvp_icd.json')
|
|
peer_env = dict(env, MOBILEGL_IPC_ROLE='server', MOBILEGL_IPC_DIAL='no',
|
|
MOBILEGL_IPC_LOG_FORWARD=str(int(forward)), MOBILEGL_LOG_FILE_PATH=str(out / 'peer.log'))
|
|
for name in ('MOBILEGL_TRANSPORT', 'MOBILEGL_IPC_SERVER_PATH', 'MOBILEGL_IPC_RING_MB', 'MOBILEGL_IPC_STAGE_MB'):
|
|
peer_env.pop(name, None)
|
|
with (out / 'supervisor.log').open('wb') as output:
|
|
server = subprocess.Popen([str(build / 'libMobileGLServer.so'), endpoint, '--serve'],
|
|
env=peer_env, stdout=output, stderr=output, start_new_session=True)
|
|
try:
|
|
for _ in range(100):
|
|
if server.poll() is not None:
|
|
raise RuntimeError('supervisor exited before listening: ' + read(out / 'supervisor.log'))
|
|
with socket.socket() as probe:
|
|
probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
try:
|
|
probe.bind(('127.0.0.1', port))
|
|
except OSError as error:
|
|
if error.errno == errno.EADDRINUSE:
|
|
break
|
|
raise
|
|
time.sleep(.05)
|
|
else:
|
|
raise RuntimeError('supervisor did not listen')
|
|
client_env = dict(env, MOBILEGL_IPC_CONTROL=endpoint, MOBILEGL_IPC_DATA='stream',
|
|
MOBILEGL_IPC_LOG_FORWARD='1', MOBILEGL_LOG_FILE_PATH=str(out / 'client.log'),
|
|
MGITEST_SPLIT_LANE='1', MGITEST_TCP_LANE='1')
|
|
result = subprocess.run([str(build / 'MobileGL/MG_IntegrationTest/MobileGLIntegrationTest'),
|
|
'--gtest_filter=' + test, '--gtest_output=xml:' + str(out / 'result.xml')],
|
|
env=client_env, capture_output=True, text=True, timeout=120)
|
|
(out / 'stdout.log').write_text(result.stdout + result.stderr)
|
|
finally:
|
|
try:
|
|
os.killpg(server.pid, signal.SIGTERM)
|
|
except ProcessLookupError:
|
|
pass
|
|
server.wait(timeout=10)
|
|
passed = failed = skipped = 0
|
|
marker_assertion_failed = False
|
|
if (out / 'result.xml').exists():
|
|
for entry in ET.parse(out / 'result.xml').getroot().iter('testcase'):
|
|
if entry.find('skipped') is not None or entry.get('status') == 'notrun':
|
|
skipped += 1
|
|
elif entry.find('failure') is not None:
|
|
failed += 1
|
|
marker_assertion_failed = any(marker in (failure.get('message', '') + (failure.text or ''))
|
|
for failure in entry.findall('failure'))
|
|
else:
|
|
passed += 1
|
|
client_log = read(out / 'client.client.log')
|
|
return {'case': test, 'returncode': result.returncode, 'passed': passed, 'failed': failed, 'skipped': skipped,
|
|
'tcp_armed': bool(re.search(r'control=tcp data=stream server=\S+ pid=[1-9][0-9]*', client_log)),
|
|
'marker_in_client': marker in client_log,
|
|
'marker_in_server': marker in read(out / 'peer.server.log'),
|
|
'marker_forwarded': marker in read(out / 'client.server.log'),
|
|
'marker_assertion_failed': marker_assertion_failed,
|
|
'output': str(out)}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--build-dir', type=Path, required=True)
|
|
parser.add_argument('--verify-build-dir', type=Path)
|
|
parser.add_argument('--out', type=Path, required=True)
|
|
parser.add_argument('--case', action='append', choices=CASES,
|
|
help='Explicit subset; the default runs all four and requires a verify build')
|
|
args = parser.parse_args()
|
|
selected = args.case or list(CASES)
|
|
if 'verify' in selected and not args.verify_build_dir:
|
|
parser.error('the verify scenario requires --verify-build-dir configured with MOBILEGL_PIPE_VERIFY=ON')
|
|
args.out.mkdir(parents=True, exist_ok=True)
|
|
results = {}
|
|
for case in selected:
|
|
build = args.verify_build_dir if case == 'verify' else args.build_dir
|
|
arms = {}
|
|
for forward in (True, False):
|
|
name = 'forward-on' if forward else 'forward-off'
|
|
try:
|
|
arms[name] = run_arm(build, args.out / case / name, case, forward)
|
|
except Exception as error:
|
|
arms[name] = {'error': str(error)}
|
|
on, off = arms['forward-on'], arms['forward-off']
|
|
if case == 'verify':
|
|
# PipeFill owns this marker in the client. Disabling peer forwarding
|
|
# must leave it visible; requiring red here would test a false design.
|
|
arms['expectation'] = 'client-owned marker remains green with peer forwarding disabled'
|
|
arms['proved'] = all(arm.get('passed') == 1 and arm.get('returncode') == 0
|
|
and arm.get('tcp_armed') and arm.get('marker_in_client')
|
|
for arm in (on, off))
|
|
else:
|
|
arms['expectation'] = 'server-owned marker passes with forwarding and fails without it'
|
|
arms['proved'] = bool(on.get('passed') == 1 and on.get('returncode') == 0
|
|
and on.get('tcp_armed') and on.get('marker_in_server') and on.get('marker_forwarded')
|
|
and off.get('failed') == 1 and not off.get('skipped') and off.get('tcp_armed')
|
|
and off.get('marker_in_server') and not off.get('marker_forwarded')
|
|
and off.get('marker_assertion_failed')
|
|
and not off.get('marker_in_client'))
|
|
results[case] = arms
|
|
print(case + ': ' + ('PASS' if arms['proved'] else 'NOT PROVEN'), flush=True)
|
|
(args.out / 'summary.json').write_text(json.dumps(results, indent=2))
|
|
return 0 if all(result['proved'] for result in results.values()) else 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|