R19 raised two things about this comment. One resolved itself: it used to explain why the matrix had no API 37 row at all, and #56 added the gating row, so that half is gone. The other survived, and this is it. The comment read api-level must be "37.0". A bare 37 is not an SDK package and fails during setup The second sentence is true and was measured -- it cost a run to find. The first overstates it. What must be true is that the api-level is a POINT release; 37.0 is one of several. api37-debug.yml's own input descriptions already say so: API level, as the SDK spells it. 37.0, 37.1, 37.2-beta3, 36 ... System image target. android-37.1 and 37.2-beta* ship ONLY as google_apis_ps16k and docs/api-37-emulator-crash.md measures android-37.0 rev 6 and android-37.1 rev 8 side by side, both aborting. So the repo already knows 37.1 exists and behaves the same; only this comment implied otherwise. That matters for the reader it is written for. Someone debugging this row and wondering whether a newer image helps reads "must be 37.0" as a constraint and stops. The measured answer is that it does not help, which is a better thing to learn than a rule that is not one -- and the ps16k-only wrinkle above 37.0 is the detail that would actually bite them. Comment only. No job, matrix, filter or gating behaviour changes. actionlint clean at the pinned digest. Closes #28.
510 lines
26 KiB
YAML
510 lines
26 KiB
YAML
name: Status check
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
# A newer push to the same PR makes the in-flight run obsolete. An emulator matrix is
|
|
# expensive, so cancel rather than let runs pile up.
|
|
concurrency:
|
|
group: status-check-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# Third-party actions are pinned to a commit rather than a tag. A tag is mutable: the
|
|
# owner can repoint v4 at new code, so a tag reference is an open invitation to run
|
|
# whatever that repository contains tomorrow. The trailing comment records which
|
|
# release each hash corresponds to, since a bare hash is unreadable.
|
|
# Nothing here writes: these jobs read the code, build it and attach reports. Declared
|
|
# explicitly rather than inherited from the repository default, for the same reason the
|
|
# action SHAs above are pinned -- the token's reach should be readable here, and a default
|
|
# that widens later should not silently widen these jobs with it. build.yml's release job
|
|
# makes the opposite declaration for the same reason.
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
GRADLE_CACHE_PATHS: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
|
|
jobs:
|
|
# ---------------------------------------------------------------------------
|
|
# Validates the committed FFmpeg archive. It does not build anything: the whole
|
|
# point of checking the binary in is that a red run means broken code rather than
|
|
# a cross-compile that hiccuped.
|
|
#
|
|
# Its own job so a bad archive reports once, clearly, instead of surfacing as five
|
|
# confusing emulator failures. It takes seconds, so gating the matrix on it costs
|
|
# almost nothing.
|
|
# ---------------------------------------------------------------------------
|
|
ffmpeg:
|
|
name: FFmpeg binary
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Verify the committed archive
|
|
run: |
|
|
AAR=bin/ffmpeg-kit-next-8.1.1.aar
|
|
test -f "$AAR" || { echo "::error::$AAR is missing"; exit 1; }
|
|
|
|
# A truncated file, or a Git LFS pointer checked out without LFS, would pass
|
|
# a file-exists check and then surface much later as a confusing linker
|
|
# error. Assert the archive actually carries native libraries for both ABIs.
|
|
for abi in arm64-v8a x86_64; do
|
|
n=$(unzip -l "$AAR" | grep -c "jni/$abi/.*\.so$" || true)
|
|
echo " $abi: $n shared libraries"
|
|
test "$n" -gt 0 || { echo "::error::AAR has no $abi libraries"; exit 1; }
|
|
done
|
|
|
|
# 16 KB alignment is a Play requirement and is easy to lose in a rebuild,
|
|
# so it is checked here rather than discovered at submission.
|
|
unzip -q -o "$AAR" 'jni/*' -d /tmp/aarcheck
|
|
bad=0
|
|
for f in /tmp/aarcheck/jni/*/*.so; do
|
|
align=$(readelf -lW "$f" | awk '$1=="LOAD"{print $NF}' | sort -u)
|
|
if [ "$align" != "0x4000" ]; then
|
|
echo "::error::$(basename "$f") is $align, not 16 KB aligned"; bad=1
|
|
fi
|
|
done
|
|
test "$bad" -eq 0 || exit 1
|
|
echo " all libraries are 16 KB aligned"
|
|
|
|
# Record what shipped, so a failing run elsewhere can be tied to a version.
|
|
echo " sha256: $(sha256sum "$AAR" | cut -d' ' -f1)"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# JVM tests: the routing matrix, the FFmpeg argument builder, the concat planner
|
|
# and the retry rule. No device needed, so this is the fastest signal on a PR.
|
|
# ---------------------------------------------------------------------------
|
|
unit:
|
|
name: Unit tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25' # Matches the daemon JVM pinned in gradle/gradle-daemon-jvm.properties
|
|
|
|
# Gradle is invoked through the committed wrapper rather than a setup action.
|
|
# The wrapper verifies its own distribution against distributionSha256Sum, and
|
|
# caching is a handful of lines, so the action earned little here.
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
- name: Unit tests
|
|
run: ./gradlew :app:testDebugUnitTest
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: unit-test-report
|
|
path: app/build/reports/tests/
|
|
|
|
# Reported, not gated. A coverage floor is only meaningful against a measured
|
|
# baseline, and this is the thing that measures it -- currently 31% of lines. Once
|
|
# that number has settled, a jacocoTestCoverageVerification task can hold it.
|
|
- name: Coverage report
|
|
run: ./gradlew :app:jacocoTestReport
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: coverage-report
|
|
path: app/build/reports/jacoco/jacocoTestReport/
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Three tools, one job, because they answer three different questions and a
|
|
# developer wants all three answers at once rather than one per push.
|
|
#
|
|
# ktlint -- formatting. Owns it outright; detekt's formatting ruleset is off,
|
|
# so the two can never disagree about the same line.
|
|
# detekt -- static analysis. Its config lives in config/detekt/detekt.yml and
|
|
# overrides only the rules this codebase legitimately breaks.
|
|
# lint -- the Android-specific things neither of the others can see: opt-in
|
|
# markers, API-level misuse, manifest and resource problems.
|
|
#
|
|
# --continue is what makes it one round trip: a ktlint failure still lets detekt
|
|
# and lint report, so a red run hands over the whole list rather than the first
|
|
# item on it.
|
|
#
|
|
# No emulator and no FFmpeg archive needed, so this is the cheapest gate here and
|
|
# deliberately does not depend on the ffmpeg job.
|
|
# ---------------------------------------------------------------------------
|
|
static-analysis:
|
|
name: Static analysis
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
# Shell is the other language in this repo -- four scripts, one of them the CI
|
|
# entry point itself -- and nothing was checking it. `git ls-files` rather than a
|
|
# fixed list, so a script added later is covered without editing this workflow.
|
|
#
|
|
# Full severity, `info` included. The findings it raises today are answered with
|
|
# targeted `disable` directives carrying their reason, the same way
|
|
# config/detekt/detekt.yml carries only the rules this codebase legitimately
|
|
# breaks. A blanket --severity=warning would have hidden them and the next real
|
|
# one alike.
|
|
#
|
|
# PINNED BY DIGEST, for the reason CLAUDE.md already gives for pinning ktlint,
|
|
# detekt and JaCoCo: a new rule in a linter makes files nobody touched stop
|
|
# passing, so CI goes red on a PR whose diff cannot explain it. That is not
|
|
# hypothetical here. The first cut of this step used the runner's ambient
|
|
# shellcheck, which is 0.9.0, and 0.9.0 reports a trap handler as seven
|
|
# unreachable commands (SC2317) where 0.11.0 reports it once on the declaration
|
|
# (SC2329) -- same script, same directive, different answer, and a red build on
|
|
# the PR that introduced the step. The version is printed so a finding that
|
|
# appears out of nowhere can be tied to a bump of this line.
|
|
- name: shellcheck
|
|
env:
|
|
SHELLCHECK: koalaman/shellcheck@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d
|
|
run: |
|
|
docker run --rm "$SHELLCHECK" --version
|
|
git ls-files -z '*.sh' | xargs -0 -r docker run --rm -v "$PWD:/mnt" "$SHELLCHECK"
|
|
|
|
# actionlint closes the half shellcheck cannot see. The step above reads .sh files;
|
|
# a good deal of this repo's bash lives in inline `run:` blocks instead -- the release
|
|
# verification here, the emulator setup and teardown in this file and in
|
|
# api37-debug.yml. actionlint parses each workflow and runs shellcheck over every
|
|
# `run:`, on top of its own checks for expression syntax, `needs:` references, matrix
|
|
# keys and action input names.
|
|
#
|
|
# Pinned by digest for the same reason shellcheck is, and with a second reason of its
|
|
# own: actionlint's documented install is `bash <(curl -s .../download-actionlint.bash)`
|
|
# off a moving branch, which would sit badly in a repo that pins every action by SHA.
|
|
- name: actionlint
|
|
env:
|
|
ACTIONLINT: rhysd/actionlint@sha256:9d36088643581e728c969f35141f88139fec77280b2be23c1f66f8e40e1025e7
|
|
run: |
|
|
docker run --rm "$ACTIONLINT" -version
|
|
docker run --rm -v "$PWD:/repo" -w /repo "$ACTIONLINT" -color
|
|
|
|
# `!cancelled()` rather than a plain sequence: a shellcheck failure above must not
|
|
# cost the ktlint/detekt/lint lists. Same reason this step passes --continue -- one
|
|
# round trip should produce every list, not stop at the first.
|
|
- name: ktlint, detekt and Android lint
|
|
if: '!cancelled()'
|
|
run: ./gradlew :app:ktlintCheck :app:detekt :app:lintDebug --continue --stacktrace
|
|
|
|
# The XML matters as much as the HTML: it is the one that can be diffed between
|
|
# runs to see what a change actually moved.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: static-analysis-reports
|
|
path: |
|
|
app/build/reports/ktlint/
|
|
app/build/reports/detekt/
|
|
app/build/reports/lint-results-debug.html
|
|
app/build/reports/lint-results-debug.xml
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# One runner per API level, across the whole supported range.
|
|
#
|
|
# The range is the point: minSdk is 33, and the foreground service type differs
|
|
# across it -- none below 34, dataSync at 34, mediaProcessing from 35. Testing a
|
|
# single level would leave two thirds of that branch unexercised.
|
|
#
|
|
# It reaches targetSdk 37, but the API 37 row is not like the other four and the
|
|
# comment on it says how. Two tests are excluded there and run in their own
|
|
# advisory job below. See docs/api-37-emulator-crash.md.
|
|
#
|
|
# FFmpeg is not built here. The AAR is committed under bin/, so a red run means the
|
|
# code is broken rather than that a cross-compile hiccuped.
|
|
# ---------------------------------------------------------------------------
|
|
e2e:
|
|
name: E2E API ${{ matrix.label }}
|
|
runs-on: ubuntu-latest
|
|
needs: ffmpeg
|
|
timeout-minutes: 60
|
|
strategy:
|
|
# Report every API level rather than stopping at the first red one. Knowing
|
|
# whether a failure is universal or specific to one level is most of the
|
|
# diagnosis.
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- label: "33"
|
|
api-level: "33"
|
|
- label: "34"
|
|
api-level: "34"
|
|
- label: "35"
|
|
api-level: "35"
|
|
- label: "36"
|
|
api-level: "36"
|
|
# API 37, and it is NOT the same device as the four rows above it.
|
|
#
|
|
# CAVEAT, read this before trusting a green here: this leg runs with
|
|
# SystemUI disabled and the framework restarted under it. No other leg
|
|
# and no Pixel run uses that configuration. It is defensible only because
|
|
# nothing THIS LEG RUNS touches system UI -- Media3, FFmpeg and
|
|
# WorkManager tests -- and because the alternative is no CI coverage of
|
|
# the level this app targets. **Anything that ever does depend on system
|
|
# UI must not trust this row.** E2E_DISABLE_SYSTEM_UI is what does it;
|
|
# .github/scripts/e2e-run.sh explains the mechanism and why every step of
|
|
# it is verified rather than assumed.
|
|
#
|
|
# "this leg" and not "this suite", since 2026-08-24, and the difference is
|
|
# now load-bearing: SafPickerRoundTripTest DOES touch system UI. It drives
|
|
# DocumentsUI and rotates the display, and both reach the gralloc mapper
|
|
# this image aborts in -- disabling SystemUI removes the IDLE trigger, not
|
|
# those. Measured per method on android-37.0: the ROTATION test takes the
|
|
# framework down (INSTRUMENTATION_ABORTED) and carries
|
|
# @FailsOnEmulatorApi37, so notAnnotation below keeps it off this row; the
|
|
# PICKER test passes and runs here like anything else. A rotation rebuilds
|
|
# every surface at once, and starting another app's activity does not.
|
|
#
|
|
# So this row does now run one test that depends on system UI, and the
|
|
# caveat above still applies to it: a green here is not evidence the picker
|
|
# works on a device with SystemUI running -- the Pixel release check is.
|
|
# docs/api-37-emulator-crash.md has the per-method measurements, and the
|
|
# correction that produced them.
|
|
#
|
|
# api-level must be a POINT release. A bare 37 is not an SDK package and
|
|
# fails during setup, which cost a run to discover. `37.0` is the choice
|
|
# here rather than the only option: `37.1` and `37.2-beta*` exist and
|
|
# abort the same way, and api37-debug.yml's inputs document both, with
|
|
# the wrinkle that above 37.0 they ship only as google_apis_ps16k.
|
|
# docs/api-37-emulator-crash.md measures 37.0 rev 6 and 37.1 rev 8 side
|
|
# by side, so pinning 37.0 is a decision, not a constraint.
|
|
#
|
|
# notAnnotation removes the three tests that do not pass on this image; they
|
|
# run in the advisory job below, off the same marker so they cannot end up
|
|
# in both or neither. docs/api-37-emulator-crash.md has the measurements.
|
|
- label: "37"
|
|
api-level: "37.0"
|
|
disable-system-ui: "1"
|
|
gradle-args: "-Pandroid.testInstrumentationRunnerArguments.notAnnotation=org.libremediaconverter.FailsOnEmulatorApi37"
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
# Without this the emulator falls back to software rendering and takes minutes
|
|
# longer to boot, when it boots at all.
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Instrumented tests
|
|
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
|
|
# Both of these are empty on every row but 37, and both are read with a
|
|
# `:-` default in e2e-run.sh, so the four legs below 37 run the identical
|
|
# gradle command they always have.
|
|
env:
|
|
E2E_DISABLE_SYSTEM_UI: ${{ matrix.disable-system-ui }}
|
|
E2E_EXTRA_GRADLE_ARGS: ${{ matrix.gradle-args }}
|
|
with:
|
|
api-level: ${{ matrix.api-level }}
|
|
target: google_apis
|
|
arch: x86_64
|
|
profile: pixel_6
|
|
# swiftshader_indirect is correct here only because runners have no GPU to
|
|
# pass through. On a workstation the same setting routes through
|
|
# SwiftShader's JIT, which is a known crash source.
|
|
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
disable-animations: true
|
|
# The default userdata partition is not big enough for this APK once the
|
|
# FFmpeg libraries are in it. One level failed outright with "Requested
|
|
# internal only, but not enough space", and the margin was thin everywhere
|
|
# else, so give them all room.
|
|
disk-size: 8G
|
|
# Pinned because the emulator's own default is not uniform: it raises an
|
|
# undersized guest to a floor that varies by API level -- 2048M at 33, 2560M
|
|
# at 34 through 36 -- and skips levels it does not recognise entirely. 2560M
|
|
# is the highest of those floors, so no level gets less memory than it
|
|
# already had, and none of them depend on that heuristic any more.
|
|
ram-size: 2560M
|
|
# Build only the ABI the emulator can execute. FFmpeg's native libraries
|
|
# dominate the APK, so shipping arm64 to an x86_64 emulator doubles the
|
|
# install for code that can never run: 114 MB against 80 MB.
|
|
#
|
|
# Diagnostics live in .github/scripts/e2e-run.sh, not here. This action splits
|
|
# `script` on newlines and runs each line as its own `sh -c`, so a handler written
|
|
# inline has to fit on ONE line -- which is how the previous version ended up as an
|
|
# unreadable chain of semicolons. One line invokes the script; the script can use
|
|
# functions, and captures a hang as well as a failure. See its header.
|
|
script: bash .github/scripts/e2e-run.sh ${{ matrix.label }}
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-report-api${{ matrix.label }}
|
|
path: |
|
|
app/build/reports/androidTests/
|
|
app/build/outputs/androidTest-results/
|
|
if-no-files-found: warn
|
|
|
|
# The streamed logcat and the failure dump. Uploaded always, because a leg that goes red
|
|
# once and green on re-run is exactly the one worth reading afterwards, and by then the
|
|
# emulator is long gone.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-diagnostics-api${{ matrix.label }}
|
|
path: |
|
|
${{ runner.temp }}/logcat-api${{ matrix.label }}.txt
|
|
${{ runner.temp }}/diagnostics-api${{ matrix.label }}.txt
|
|
if-no-files-found: warn
|
|
|
|
# Only exists when the wrapper timeout tripped, so `ignore` keeps healthy runs quiet
|
|
# instead of warning on every green leg.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-wedge-api${{ matrix.label }}
|
|
path: ${{ runner.temp }}/wedge-diagnostics-api${{ matrix.label }}.txt
|
|
if-no-files-found: ignore
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The three API 37 tests the gating row above excludes, run on their own so they
|
|
# stay visible instead of disappearing behind a notAnnotation.
|
|
#
|
|
# continue-on-error: it reports, it never blocks. That is the whole reason it is
|
|
# a separate job rather than a sixth matrix row: a row would share the gating
|
|
# job's `E2E API <label>` name, and a check cannot be both required and advisory
|
|
# under one name.
|
|
#
|
|
# It was named for WHAT IT RUNS, and that is now APPROXIMATE rather than exact.
|
|
# When this job was created it held two tests, both driving a full H.264 -> H.265
|
|
# hardware transcode through Media3Engine -- which is exactly what separated them
|
|
# from the two Media3EngineTest cases that pass here, since those two never decode
|
|
# video. Since 2026-08-25 it also holds SafPickerRoundTripTest's rotation case,
|
|
# which drives no transcode at all: a real rotation rebuilds every surface at once
|
|
# and takes the framework down on this image (INSTRUMENTATION_ABORTED), which is a
|
|
# different failure from the decoder one below.
|
|
#
|
|
# The name is kept anyway, and that is a decision rather than an oversight. This is
|
|
# not a required context, it is red on every PR by design, and it is one people
|
|
# have learned to look for -- renaming a check costs more than the imprecision
|
|
# does. **The marker is the definition, not the name:** what this job holds is the
|
|
# tests that cannot pass on the API 37 emulator image, whatever their subject. The
|
|
# theory about the Media3 pair is in the next paragraph, where it can be corrected
|
|
# without touching the name.
|
|
#
|
|
# THEORY, NOT SETTLED: the exception surfaces at `dequeueOutputBuffer` on
|
|
# `c2.goldfish.h264.decoder`, the emulator's own codec, which gets its frames out
|
|
# of a host-side colour buffer -- the same readback machinery that aborts
|
|
# surfaceflinger on this image. It is about the MEDIA3 PAIR only; the rotation
|
|
# case above fails for its own reason. What is MEASURED is narrower: those two
|
|
# fail on
|
|
# the API 37 emulator image; pass at API 36 on this runner under the same renderer
|
|
# AND the same SystemUI-disable path; pass at API 33-36 without that path at all,
|
|
# since nothing below 37 needs it; and pass on a physical Pixel 10 Pro XL at 37. That the decoder is the culprit rather than something else
|
|
# the decode path touches is inference. docs/api-37-emulator-crash.md separates
|
|
# the two, and the images also differ on the encoder side, which is why "broken
|
|
# h264 decoder" is not written into this job's name.
|
|
#
|
|
# WHEN THIS GOES GREEN, delete the annotation rather than this job: the gating
|
|
# row picks the tests back up automatically, and this job goes empty and can go
|
|
# with it.
|
|
# ---------------------------------------------------------------------------
|
|
e2e-api37-advisory:
|
|
name: E2E API 37 Media3 hardware transcode (advisory)
|
|
runs-on: ubuntu-latest
|
|
needs: ffmpeg
|
|
timeout-minutes: 60
|
|
continue-on-error: true
|
|
env:
|
|
E2E_LABEL: "37-media3-transcode"
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Instrumented tests
|
|
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
|
|
env:
|
|
E2E_DISABLE_SYSTEM_UI: "1"
|
|
# The complement of the gating row's notAnnotation, off the same marker,
|
|
# so a test can never be excluded from both jobs or run in both.
|
|
E2E_EXTRA_GRADLE_ARGS: "-Pandroid.testInstrumentationRunnerArguments.annotation=org.libremediaconverter.FailsOnEmulatorApi37"
|
|
with:
|
|
# Every device pin below matches the gating row exactly, so a difference
|
|
# between the two jobs is the test selection and nothing else.
|
|
api-level: "37.0"
|
|
target: google_apis
|
|
arch: x86_64
|
|
profile: pixel_6
|
|
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
disable-animations: true
|
|
disk-size: 8G
|
|
ram-size: 2560M
|
|
script: bash .github/scripts/e2e-run.sh ${{ env.E2E_LABEL }}
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-report-api${{ env.E2E_LABEL }}
|
|
path: |
|
|
app/build/reports/androidTests/
|
|
app/build/outputs/androidTest-results/
|
|
if-no-files-found: warn
|
|
|
|
# Uploaded always, and here it matters more than anywhere else in this file:
|
|
# this job is EXPECTED to be red, so the logcat is the only thing that says
|
|
# whether it is red for the known reason or for a new one.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-diagnostics-api${{ env.E2E_LABEL }}
|
|
path: |
|
|
${{ runner.temp }}/logcat-api${{ env.E2E_LABEL }}.txt
|
|
${{ runner.temp }}/diagnostics-api${{ env.E2E_LABEL }}.txt
|
|
if-no-files-found: warn
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-wedge-api${{ env.E2E_LABEL }}
|
|
path: ${{ runner.temp }}/wedge-diagnostics-api${{ env.E2E_LABEL }}.txt
|
|
if-no-files-found: ignore
|