#!/bin/sh # Run scrcpy-android tests inside the project-local Docker image. # # ./test unit tests (JVM only, no KVM) # ./test unit same # ./test e2e single-emulator self-mirror E2E (needs /dev/kvm) # ./test all unit + e2e # ./test record boot emulator, run a Mirror session, inject events, # capture screen recording to ./output.mp4 (needs /dev/kvm) # ./test screenshots # boot emulator, capture the fastlane store screenshots # into fastlane/metadata/.../phoneScreenshots (needs /dev/kvm) # ./test apk build a signed release APK with a throwaway keystore # (exercises scripts/build-apk end-to-end, no KVM) # # The image is built on first run and cached as $IMG. # /dev/kvm must be readable by the invoking user for `./test e2e` and `record`. set -eu ROOT="$(cd "$(dirname "$0")" && pwd)" cmd="${1:-unit}" target=unit case "$cmd" in e2e|all|record|screenshots) target=e2e ;; esac # The image tag is a hash of the rig inputs, so editing the Dockerfile # or the SDK package list automatically invalidates the cached image. IMG="scrcpy-android-test-$target:$(cat "$ROOT/test-rig/Dockerfile" \ "$ROOT/test-rig/sdk-packages.txt" "$ROOT/test-rig/sdk-packages-e2e.txt" \ | sha256sum | cut -c1-12)" case "$cmd" in unit|e2e|all|record|screenshots|apk) ;; -h|--help) # Usage is the header comment block: print from line 2 down to # the first non-comment line. awk 'NR > 1 && /^#/ { sub(/^# ?/, ""); print; next } NR > 1 { exit }' "$0" >&2 exit 0 ;; *) echo "usage: $0 [unit|e2e|all|record|screenshots|apk]" >&2 exit 2 ;; esac # Cheap supply-chain check: the gradle wrapper jar is a small binary # we check into the tree; verify its sha before letting it run. "$ROOT/scripts/check-wrapper" # Build the image if needed. --network=host so RUN apt/curl can reach # the outside on hosts where the default docker bridge has no DNS. if ! docker image inspect "$IMG" >/dev/null 2>&1; then echo "test: building $IMG (first run, ~10 min)" >&2 # Cheap pre-flight: warn the user up-front if the host can't resolve # the docker registry, so they don't watch an apt step time out. if ! getent hosts registry-1.docker.io >/dev/null 2>&1; then echo "test: WARNING - registry-1.docker.io did not resolve via /etc/resolv.conf." >&2 echo " Build will likely fail. Fix the host DNS (e.g." >&2 echo " append 'nameserver 1.1.1.1' to /etc/resolv.conf) and retry." >&2 fi docker build --network=host --target "$target" -t "$IMG" "$ROOT/test-rig" fi # KVM check for tiers that boot the emulator. needs_kvm= case "$cmd" in e2e|all|record|screenshots) needs_kvm=1 ;; esac kvm_args= if [ -n "$needs_kvm" ]; then if [ ! -e /dev/kvm ]; then echo "test: /dev/kvm not present on this host - e2e cannot run." >&2 exit 3 fi if [ ! -r /dev/kvm ] || [ ! -w /dev/kvm ]; then echo "test: /dev/kvm exists but is not rw for uid $(id -u)." >&2 echo " one-time fix:" >&2 echo " sudo chmod 666 /dev/kvm" >&2 echo " or add yourself to the kvm group and re-login:" >&2 echo " sudo usermod -aG kvm $(id -un)" >&2 exit 3 fi # Pass through /dev/kvm's owning gid (numeric - group names from the # host don't exist inside the container) so the container user # (mapped to host uid:gid) can actually open it. kvm_args="--device /dev/kvm --group-add $(stat -c %g /dev/kvm)" fi # Forward Ctrl-C cleanly, run as the host user so files in .tools/ stay # owned by the invoker (avoids root-owned gradle cache on the host). # --network=host so the gradle wrapper can fetch the distribution and # maven deps in environments where the docker bridge DNS misbehaves. exec docker run --rm -i \ --network=host \ $kvm_args \ -u "$(id -u):$(id -g)" \ -v "$ROOT":/work \ -w /work \ -e HOME=/work/.tools/home \ "$IMG" \ /work/test-rig/run.sh "$cmd"