[CI, Android] (Disaggregated): run full inproc trace acceptance beside monolith controls

This commit is contained in:
2026-09-20 10:43:06 -04:00
parent 7fd83133c0
commit bb3a51995c
3 changed files with 140 additions and 16 deletions
+64 -16
View File
@@ -44,6 +44,9 @@ jobs:
distribution: zulu
java-version: '17'
- name: Android replay lifecycle regression
run: python3 tools/trace_replay/test_android_lifecycle.py
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
@@ -275,6 +278,8 @@ jobs:
name: trace case matrix
runs-on: ubuntu-latest
needs: build
env:
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci') && 'ON' || 'OFF' }}
outputs:
android: ${{ steps.trace-cases.outputs.android }}
names: ${{ steps.trace-cases.outputs.names }}
@@ -285,7 +290,12 @@ jobs:
- name: Load trace cases
id: trace-cases
run: |
echo "android=$(python3 tools/trace_replay/trace_cases.py --ci --format github-apk-matrix)" >> "$GITHUB_OUTPUT"
python3 tools/trace_replay/trace_cases.py --self-test-transport-matrices
matrix_format=github-apk-matrix
if [ "${MOBILEGL_CI_DISAGGREGATED}" = "ON" ]; then
matrix_format=github-apk-transport-matrix
fi
echo "android=$(python3 tools/trace_replay/trace_cases.py --ci --format "${matrix_format}")" >> "$GITHUB_OUTPUT"
echo "names=$(python3 tools/trace_replay/trace_cases.py --ci --format names)" >> "$GITHUB_OUTPUT"
trace-fixtures:
@@ -398,7 +408,7 @@ jobs:
--avd-name "${AVD_NAME}"
retrace:
name: retrace (${{ matrix.backend.name }}, ${{ matrix.case.name }})
name: retrace (${{ matrix.backend.name }} / ${{ matrix.lane || 'monolith-control' }} / ${{ matrix.transport || 'monolith' }}, ${{ matrix.case.name }})
runs-on: ubuntu-latest
needs:
- build
@@ -408,7 +418,8 @@ jobs:
if: ${{ always() && needs.build.result == 'success' && needs.android-avd.result == 'success' && needs.trace-cases.result == 'success' }}
timeout-minutes: 75
env:
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci') && 'ON' || 'OFF' }}
MOBILEGL_CI_TRANSPORT: ${{ matrix.transport || 'monolith' }}
MOBILEGL_CI_LANE: ${{ matrix.lane || 'monolith-control' }}
AVD_NAME: mobilegl-ci
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
@@ -508,11 +519,13 @@ jobs:
apk_file="android-retrace-apks/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk"
test -f "${apk_file}"
extra_retrace_args=()
if [ "${MOBILEGL_CI_DISAGGREGATED}" = "ON" ]; then
if [ "${MOBILEGL_CI_TRANSPORT}" = "inproc" ]; then
extra_retrace_args+=(
--env "MOBILEGL_TRANSPORT=inproc;MOBILEGL_IPC_ROLE_SPLIT_STATE=1;MOBILEGL_IPC_STRICT_ERRORS=1;MOBILEGL_IPC_RUN_AHEAD=1"
--require-inproc
)
else
extra_retrace_args+=(--env "MOBILEGL_TRANSPORT=monolith")
fi
# Bliss needs the newer signed ANGLE variant plus sampler mipmap
# min-filter downgrading on ANGLE llvmpipe.
@@ -595,6 +608,12 @@ jobs:
safe_case="$(printf '%s' '${{ matrix.case.name }}' | sed 's/[^A-Za-z0-9._-]/_/g')"
result_dir="android-retrace-result/${safe_case}-${{ matrix.backend.name }}"
mkdir -p "${result_dir}"
python3 - "${result_dir}/lane.json" <<'PY'
import json, os, sys
from pathlib import Path
Path(sys.argv[1]).write_text(json.dumps({"lane": os.environ["MOBILEGL_CI_LANE"],
"transport": os.environ["MOBILEGL_CI_TRANSPORT"]}, indent=2))
PY
if [ -s "${{ matrix.case.golden }}" ]; then
cp "${{ matrix.case.golden }}" "${result_dir}/${safe_case}-${{ matrix.backend.name }}-golden.png"
fi
@@ -631,7 +650,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v7
with:
name: MobileGL-android-retrace-result-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-${{ matrix.case.name }}
name: MobileGL-android-retrace-result-${{ matrix.lane || 'monolith-control' }}-${{ matrix.transport || 'monolith' }}-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-${{ matrix.case.name }}
path: android-retrace-result/**
if-no-files-found: warn
@@ -640,6 +659,8 @@ jobs:
runs-on: ubuntu-latest
needs: retrace
if: always()
env:
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci') && 'ON' || 'OFF' }}
steps:
- name: Checkout repo
@@ -654,25 +675,52 @@ jobs:
with:
node-version: '22'
- name: Download Android retrace results
- name: Download Android monolith control results
uses: actions/download-artifact@v8
with:
pattern: MobileGL-android-retrace-result-*
path: retrace-artifacts
pattern: MobileGL-android-retrace-result-monolith-control-monolith-*
path: retrace-artifacts/monolith-control
- name: Render retrace summary
- name: Download Android inproc acceptance results
if: env.MOBILEGL_CI_DISAGGREGATED == 'ON'
uses: actions/download-artifact@v8
with:
pattern: MobileGL-android-retrace-result-inproc-acceptance-inproc-*
path: retrace-artifacts/inproc-acceptance
- name: Render monolith control summary
run: |
node tools/trace_replay/render_retrace_summary.mjs \
--input retrace-artifacts \
--output-dir android-retrace-summary \
--title "MobileGL Android retrace overview" \
--group-label "Android Emulator" \
--html mobilegl-android-retrace-overview.html
--input retrace-artifacts/monolith-control \
--output-dir android-retrace-summary/monolith-control \
--title "MobileGL Android monolith control" \
--group-label "Android Emulator / monolith control" \
--html mobilegl-android-monolith-control.html
- name: Upload Android retrace summary
- name: Render inproc acceptance summary
if: env.MOBILEGL_CI_DISAGGREGATED == 'ON'
run: |
node tools/trace_replay/render_retrace_summary.mjs \
--input retrace-artifacts/inproc-acceptance \
--output-dir android-retrace-summary/inproc-acceptance \
--title "MobileGL Android inproc acceptance" \
--group-label "Android Emulator / inproc acceptance" \
--html mobilegl-android-inproc-acceptance.html
- name: Upload Android monolith control summary
uses: actions/upload-artifact@v7
with:
path: android-retrace-summary/mobilegl-android-retrace-overview.html
name: MobileGL-android-monolith-control-summary-${{ github.sha }}
path: android-retrace-summary/monolith-control/mobilegl-android-monolith-control.html
archive: false
if-no-files-found: error
- name: Upload Android inproc acceptance summary
if: env.MOBILEGL_CI_DISAGGREGATED == 'ON'
uses: actions/upload-artifact@v7
with:
name: MobileGL-android-inproc-acceptance-summary-${{ github.sha }}
path: android-retrace-summary/inproc-acceptance/mobilegl-android-inproc-acceptance.html
archive: false
if-no-files-found: error
+14
View File
@@ -5,6 +5,14 @@ def mobileGlLogActiveLevel = {
}
def standalonePluginBuild = rootProject.name == 'MobileGLPlugin'
// Match the standalone app's declared ABI set. The library module otherwise
// configures unrelated NDK ABIs even when the app only packages these two.
// Embedded FCL builds retain their own ABI policy.
def mobileGlAbiFilters = {
def value = (rootProject.findProperty('mobilegl.abis') ?: System.getenv('MOBILEGL_ABIS') ?: 'arm64-v8a') as String
value.equalsIgnoreCase('all') ? ['arm64-v8a', 'x86_64'] : value.split(',').collect { it.trim() }.findAll { it }
}
// P0 spike A opt-in. OFF by default: the spike binary is dead weight in every trace APK
// that is not running the spike, and a flavour that silently carries an extra executable
// is exactly the kind of thing nobody notices until it ships. Turn it on for the spike
@@ -34,6 +42,12 @@ android {
defaultConfig {
minSdk 26
if (standalonePluginBuild) {
ndk {
abiFilters.addAll(mobileGlAbiFilters())
}
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
+62
View File
@@ -217,6 +217,58 @@ def github_apk_matrix(cases):
}
def github_transport_matrix(cases, *, apk=False):
"""Run the entire original matrix as both control and inproc acceptance.
The two transports use the same D/P artifact and each case's existing backend
list. The legacy split:true subset does not limit this main acceptance matrix.
Nested APK case/backend metadata is deliberately left untouched.
"""
build = github_apk_matrix if apk else github_test_matrix
if not cases:
raise ValueError("transport matrix needs at least one trace case")
return {"include": [
{**row, "lane": lane, "transport": transport}
for selected, lane, transport in (
(cases, "monolith-control", "monolith"),
(cases, "inproc-acceptance", "inproc"),
)
for row in build(selected)["include"]
]}
def self_test_transport_matrices():
cases = ci_trace_cases(load_trace_cases())
for apk in (False, True):
build = github_apk_matrix if apk else github_test_matrix
original = build(cases)["include"]
mixed = github_transport_matrix(cases, apk=apk)["include"]
controls = [row for row in mixed if row["transport"] == "monolith"]
acceptance = [row for row in mixed if row["transport"] == "inproc"]
strip_lane = lambda rows: [{key: value for key, value in row.items()
if key not in ("lane", "transport")} for row in rows]
assert strip_lane(controls) == original, "control lost original case/backend/parameters"
assert strip_lane(acceptance) == original, "acceptance lost original case/backend/parameters"
assert all(row["lane"] == "monolith-control" for row in controls)
assert all(row["lane"] == "inproc-acceptance" for row in acceptance)
identities = [(row["case"]["name"] if apk else row["case"],
row["backend"]["name"] if apk else row["backend"], row["transport"])
for row in mixed]
assert len(identities) == len(set(identities)), "duplicate lane identity"
# A restricted case keeps exactly its original backend and golden data.
restricted = [{**cases[0], "split": False, "ci_backends": ["DirectGLES"]}]
rows = github_transport_matrix(restricted, apk=apk)["include"]
assert len(rows) == 2 and strip_lane(rows[:1]) == strip_lane(rows[1:])
try:
github_transport_matrix([], apk=apk)
except ValueError:
pass
else:
raise AssertionError("an empty acceptance lane silently passed")
print(f"transport matrix {'APK' if apk else 'Linux'}: {len(controls)} unchanged controls, "
f"{len(acceptance)} full acceptance entries; exact metadata and negative controls OK")
def cmake_quote(value):
return '"' + str(value).replace("\\", "/").replace('"', '\\"') + '"'
@@ -265,16 +317,19 @@ def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--case", dest="case_name", help="Trace case name.")
parser.add_argument("--ci", action="store_true", help="Only include cases enabled for CI.")
parser.add_argument("--self-test-transport-matrices", action="store_true")
parser.add_argument("--fixture-root", default="tools/trace_replay/fixtures")
parser.add_argument(
"--format",
choices=(
"names",
"github-test-matrix",
"github-test-transport-matrix",
"github-verify-matrix",
"github-split-matrix",
"github-apk",
"github-apk-matrix",
"github-apk-transport-matrix",
"fixture-files",
"cmake",
),
@@ -285,6 +340,9 @@ def parse_args():
def main():
args = parse_args()
if args.self_test_transport_matrices:
self_test_transport_matrices()
return 0
cases = load_trace_cases()
if args.ci:
cases = ci_trace_cases(cases)
@@ -292,6 +350,8 @@ def main():
print(json.dumps([case["name"] for case in cases], separators=(",", ":")))
elif args.format == "github-test-matrix":
print(json.dumps(github_test_matrix(cases), separators=(",", ":")))
elif args.format == "github-test-transport-matrix":
print(json.dumps(github_transport_matrix(cases), separators=(",", ":")))
elif args.format == "github-verify-matrix":
print(json.dumps(github_verify_matrix(cases), separators=(",", ":")))
elif args.format == "github-split-matrix":
@@ -300,6 +360,8 @@ def main():
print(json.dumps([github_apk_case(case) for case in cases], separators=(",", ":")))
elif args.format == "github-apk-matrix":
print(json.dumps(github_apk_matrix(cases), separators=(",", ":")))
elif args.format == "github-apk-transport-matrix":
print(json.dumps(github_transport_matrix(cases, apk=True), separators=(",", ":")))
elif args.format == "fixture-files":
if not args.case_name:
print("--case is required for --format fixture-files", file=sys.stderr)