linux-arm64 builds libwai and the JNI layer for the runner and runs the unit tests on the host JVM, requiring every test class to have run and none to fail or skip; no Android SDK is used there. macos, where the platform SDK, its NDK and its emulator are installed, builds every ABI and the AAR, checks the AAR holds both libraries for each ABI, and runs the instrumented tests in a headless emulator, failing unless at least one ran and all passed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
164 lines
7.1 KiB
YAML
164 lines
7.1 KiB
YAML
name: wai-sink-android native sink library
|
|
|
|
# The native sink's Kotlin library (wai/wai-sink-android, extensions/native-sink.md).
|
|
#
|
|
# On the linux-arm64 runner, the platform-free half's unit tests on the host JVM: libwai and the
|
|
# JNI layer built for the runner, then the corpus live-conformance/native-sink through the JNI
|
|
# layer, every case and every refused configuration; the page-parity bundle byte for byte; the
|
|
# C interface's statuses as the JNI layer carries them; the loopback relay over the reference
|
|
# package's floor (presented byte for byte, a changed fragment answered 502 on every request,
|
|
# ranges, content codings, an unsigned playlist refused, nothing answered outside the session's
|
|
# token); and the observation loop's hidden and idle endings with its clock advanced by the test.
|
|
# No Android SDK is used there.
|
|
#
|
|
# On the macOS runner, the host where the platform SDK, its NDK and its emulator are installed:
|
|
# libwai and the JNI layer for each ABI with the pinned NDK, the AAR, and the instrumented tests in
|
|
# a headless emulator (the host's ABI, arm64-v8a): the corpus and the relay on the device, the
|
|
# platform's media player library playing the floor through the sink to its end, a changed
|
|
# fragment refused on every retry, a session nobody can see ended as hidden, and the attention
|
|
# signals read from a real activity. Nothing listens beyond loopback.
|
|
#
|
|
# Push filters match the push's delta, so every path the jobs read is listed.
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'wai/wai-sink-android/**'
|
|
- 'wai/wai-rs/**'
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/wai-ops/**'
|
|
- 'wai/live-conformance/native-sink/**'
|
|
- 'wai/extensions/native-sink.md'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-sink-android.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'wai/wai-sink-android/**'
|
|
- 'wai/wai-rs/**'
|
|
- 'wai/wai-quantum/**'
|
|
- 'wai/wai-ops/**'
|
|
- 'wai/live-conformance/native-sink/**'
|
|
- 'wai/extensions/native-sink.md'
|
|
- 'rust-toolchain.toml'
|
|
- '.github/workflows/wai-sink-android.yml'
|
|
|
|
jobs:
|
|
relevant:
|
|
name: does this change touch it
|
|
runs-on: linux-arm64
|
|
outputs:
|
|
run: ${{ steps.gate.outputs.run }}
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
- id: gate
|
|
run: |
|
|
set -euo pipefail
|
|
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-sink-android.yml \
|
|
"${{ github.event.pull_request.base.sha }}" \
|
|
"${{ github.event.pull_request.head.sha }}")
|
|
echo "this change touches wai-sink-android: $r"
|
|
echo "run=$r" >> "$GITHUB_OUTPUT"
|
|
|
|
jvm-linux-arm64:
|
|
name: the JNI layer and the platform-free half on the host JVM · linux-arm64
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: linux-arm64
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Install Rust 1.98.0
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.cargo/bin:$PATH"
|
|
command -v rustup >/dev/null 2>&1 || \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0
|
|
rustup toolchain install 1.98.0 --profile minimal
|
|
rustup default 1.98.0
|
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
# A JDK 17, from the image's packages when the image has none.
|
|
- name: JDK 17
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
j="/usr/lib/jvm/java-17-openjdk-$(dpkg --print-architecture)"
|
|
if [ ! -x "$j/bin/javac" ]; then
|
|
sudo=$(command -v sudo || true)
|
|
$sudo apt-get update -qq
|
|
$sudo apt-get install -y -qq --no-install-recommends openjdk-17-jdk-headless >/dev/null
|
|
fi
|
|
test -f "$j/include/jni.h" || { echo "::error::no JDK 17 with jni.h at $j"; exit 1; }
|
|
echo "JAVA_HOME=$j" >> "$GITHUB_ENV"
|
|
- name: libwai and the JNI layer for this runner
|
|
shell: bash
|
|
working-directory: wai/wai-sink-android
|
|
run: bash scripts/build-native.sh host
|
|
# The pinned Gradle and the build's dependencies are kept on the runner's cache volume when
|
|
# it has one.
|
|
- name: The unit tests
|
|
shell: bash
|
|
working-directory: wai/wai-sink-android
|
|
run: |
|
|
set -euo pipefail
|
|
cache=/ci-cache/wai-sink-android
|
|
mkdir -p "$cache" 2>/dev/null || cache="$RUNNER_TEMP/wai-sink-android"
|
|
export WAI_SINK_GRADLE_DISTS="$cache/dists" GRADLE_USER_HOME="$cache/gradle-home"
|
|
bash scripts/gradle.sh -Pwai.sink.jvmOnly=true :core:test
|
|
python3 -I - core/build/test-results/test <<'PY'
|
|
import sys, pathlib, xml.etree.ElementTree as ET
|
|
runs = {}
|
|
tests = fails = skips = 0
|
|
for f in pathlib.Path(sys.argv[1]).glob("*.xml"):
|
|
s = ET.parse(f).getroot()
|
|
tests += int(s.get("tests", 0)); fails += int(s.get("failures", 0)) + int(s.get("errors", 0)); skips += int(s.get("skipped", 0))
|
|
runs[s.get("name").split(".")[-1]] = int(s.get("tests", 0))
|
|
print(f"unit tests: {tests} run, {fails} failed, {skips} skipped: {runs}")
|
|
need = {"CorpusTest", "RelayTest", "SessionDriverTest", "JsonTest"}
|
|
missing = sorted(c for c in need if runs.get(c, 0) == 0)
|
|
if missing: print(f"::error::these classes ran no test: {missing}")
|
|
sys.exit(0 if tests > 0 and fails == 0 and skips == 0 and not missing else 1)
|
|
PY
|
|
|
|
android-macos:
|
|
name: every ABI, the AAR, and the instrumented tests in a headless emulator · macos
|
|
needs: relevant
|
|
if: needs.relevant.outputs.run == 'true'
|
|
runs-on: macos
|
|
timeout-minutes: 90
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
# launchd's PATH omits cargo on the macOS host runner.
|
|
- name: Put cargo on PATH
|
|
shell: bash
|
|
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: The targets the ABIs need
|
|
shell: bash
|
|
run: rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
|
|
- name: libwai and the JNI layer for each ABI
|
|
shell: bash
|
|
working-directory: wai/wai-sink-android
|
|
run: nice -n 10 bash scripts/build-native.sh android
|
|
- name: The AAR
|
|
shell: bash
|
|
working-directory: wai/wai-sink-android
|
|
run: |
|
|
set -euo pipefail
|
|
nice -n 10 bash scripts/gradle.sh :sink:assembleRelease
|
|
aar=sink/build/outputs/aar/sink-release.aar
|
|
for abi in arm64-v8a armeabi-v7a x86_64; do
|
|
for so in libwai.so libwai_sink_jni.so; do
|
|
unzip -l "$aar" | grep -q " jni/$abi/$so\$" || { echo "::error::the AAR has no jni/$abi/$so"; exit 1; }
|
|
done
|
|
done
|
|
unzip -l "$aar" | grep -q ' classes.jar$' || { echo "::error::the AAR has no classes"; exit 1; }
|
|
- name: The instrumented tests in a headless emulator
|
|
shell: bash
|
|
working-directory: wai/wai-sink-android
|
|
run: nice -n 10 bash scripts/test-emulator.sh
|