From 9d6b4f819cac853d51bcc9219e05ee396065d991 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 22 Aug 2026 22:23:20 +0200 Subject: [PATCH] chore(player): a script for the conformance suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The desktop runner needed a hand-generated fixture and the Android one needed `-x :app:rustBuildUniversalDebug`, which nobody was going to remember. Both are now `bun run test:player` and `bun run test:player:android`. The fixture is generated on first use rather than committed: no media in the repo, and an exact duration, which the seek assertions depend on. The gradle exclusion carries its reason inline — raw gradle drives the Rust build through Tauri's android-studio-script, which expects a dev-server address file that only exists under `tauri android dev`, and the library already in jniLibs is what the test process loads. --- package.json | 4 +- scripts/test-player-conformance.sh | 60 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100755 scripts/test-player-conformance.sh diff --git a/package.json b/package.json index 076b6226..1df4d91d 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,9 @@ "traces:markdown": "bun run scripts/extract-traces.ts --format markdown > docs/traceability.md", "traces:coverage": "bun run scripts/extract-traces.ts --format coverage", "traces:validate": "bun run scripts/extract-traces.ts --format validate", - "release:notes": "bun run scripts/release-notes.ts" + "release:notes": "bun run scripts/release-notes.ts", + "test:player": "./scripts/test-player-conformance.sh", + "test:player:android": "./scripts/test-player-conformance.sh android" }, "dependencies": { "@tauri-apps/api": "^2.11.1", diff --git a/scripts/test-player-conformance.sh b/scripts/test-player-conformance.sh new file mode 100755 index 00000000..75e6b01a --- /dev/null +++ b/scripts/test-player-conformance.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Run the MediaPlayer conformance suite. +# +# See docs/specs/media-player-controller.md. One set of behaviours, run against +# every engine — so a wrapper is verified without building or launching the app. +# +# ./scripts/test-player-conformance.sh desktop engines (mpv, legacy) +# ./scripts/test-player-conformance.sh android ExoPlayer, on a connected device +# +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TARGET="${1:-desktop}" + +run_desktop() { + local fixture="${TMPDIR:-/tmp}/jellytau-conformance-1200s.mp4" + if [ ! -f "$fixture" ]; then + # Generated, not committed: the repo carries no media, and the duration + # is exact — the seek assertions depend on it. + echo "Generating a 20-minute fixture at $fixture" + ffmpeg -y -loglevel error \ + -f lavfi -i "testsrc2=size=640x360:rate=25" \ + -f lavfi -i "sine=frequency=440" \ + -t 1200 -c:v libx264 -preset ultrafast -pix_fmt yuv420p -g 50 \ + -c:a aac -shortest "$fixture" + fi + + cd "$PROJECT_ROOT/src-tauri" + local status=0 + for engine in mpv legacy; do + echo + cargo run --quiet --features conformance --bin player-conformance -- \ + "$fixture" "$engine" || status=1 + done + return $status +} + +run_android() { + if ! adb get-state >/dev/null 2>&1; then + echo "No device. Connect one and enable USB debugging." >&2 + exit 1 + fi + + "$PROJECT_ROOT/scripts/sync-android-sources.sh" >/dev/null + cd "$PROJECT_ROOT/src-tauri/gen/android" + + # `-x rustBuild...` because raw gradle drives the Rust build through Tauri's + # android-studio-script, which expects a dev-server address file that only + # exists under `tauri android dev`. The native library already in + # app/src/main/jniLibs is what the test process loads. + ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}" \ + ./gradlew :app:connectedUniversalDebugAndroidTest \ + -x :app:rustBuildUniversalDebug --console=plain +} + +case "$TARGET" in + desktop) run_desktop ;; + android) run_android ;; + *) echo "usage: $0 [desktop|android]" >&2; exit 2 ;; +esac