#!/usr/bin/env bash # Run dr-face's test suite on a connected Android device. # # ./tools/face-tests-on-device.sh [extra cargo test args…] # # ## Why this exists # # `dr-face`'s similarity scan picks its dot product per machine (docs/dev/faces.md # §9): AVX2 where the CPU has it, **NEON on aarch64**, and a portable loop # otherwise. The NEON kernel is the one that runs on the phone and the tablet, # and it is the one a desktop `cargo test` never executes — a wrong lane index # or a mishandled tail there would be a silent wrong answer on exactly the # devices nobody runs the test suite on. # # `the_fastest_kernel_agrees_with_the_portable_one` is written to catch that, # and this is how it gets to run on the hardware it is about. Everything else # in the suite comes along for free: `dr-face` carries no weights and touches # no display, so its tests are a plain ARM64 binary that runs under `adb # shell` with nothing installed. # # Not part of CI, which has no device attached. Run it when the kernels change. set -euo pipefail TARGET=aarch64-linux-android API=26 DEST=/data/local/tmp/dr_face_tests ndk="${ANDROID_NDK_HOME:-}" if [[ -z "$ndk" ]]; then # The newest NDK the SDK has, which is what the app is built with. ndk=$(find "${ANDROID_HOME:-$HOME/Android/Sdk}/ndk" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort -V | tail -1) fi [[ -n "$ndk" && -d "$ndk" ]] || { echo "no NDK found — set ANDROID_NDK_HOME" >&2 exit 1 } clang="$ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/$TARGET$API-clang" [[ -x "$clang" ]] || { echo "no $TARGET$API-clang in $ndk" >&2 exit 1 } rustup target list --installed | grep -qx "$TARGET" || rustup target add "$TARGET" # Told to the device before the build, so a missing tablet costs seconds rather # than the two minutes it takes to compile for it. adb devices | grep -qw device || { echo "no device attached — plug the tablet in and enable USB debugging" >&2 exit 1 } echo "building dr-face tests for $TARGET…" binary=$( CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$clang" \ CC_aarch64_linux_android="$clang" \ cargo test -p dr-face --lib --release --target "$TARGET" --no-run --message-format=json "$@" | python3 -c ' import json, sys for line in sys.stdin: m = json.loads(line) if m.get("profile", {}).get("test") and m.get("executable"): print(m["executable"]) ' | tail -1 ) [[ -n "$binary" ]] || { echo "cargo produced no test binary" >&2 exit 1 } echo "pushing $(basename "$binary")…" adb push "$binary" "$DEST" >/dev/null adb shell chmod 755 "$DEST" # `--test-threads` left alone: the scan spawns its own workers and the point is # to exercise them the way the app will. adb shell "$DEST" --color never adb shell rm -f "$DEST"