build_android_apk.sh no longer requires all four env vars to be set manually. It probes common SDK paths and uses the newest installed build-tools/NDK/platform when vars are absent. Also adds llvm-strip pass to strip debug symbols from .so files before packaging (controlled by STRIP_NATIVE_LIBS, default 1), moves the debug keystore to a stable target/android/debug.keystore path, and prints resolved paths at start. Also adds scripts/ANDROID_TESTING.md and scripts/android_smoke.sh for on-device smoke testing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.6 KiB
Android testing
This directory contains lightweight Android test helpers for Ferrous Solitaire.
They are intended to run against either a physical Android device or an emulator
connected through adb. When no device is connected the smoke script can
automatically launch an AVD for you.
Prerequisites
- Android SDK and NDK installed.
adbavailable onPATH.- One device/emulator visible in
adb devices, or at least one AVD created (the script will launch one automatically ifLAUNCH_AVD=1, which is the default). - If multiple devices are connected, set
ADB_SERIALto the target device serial. - Environment variables required by
scripts/build_android_apk.shwhen building:
export ANDROID_HOME=/path/to/android-sdk
export ANDROID_NDK_HOME=/path/to/android-ndk
export BUILD_TOOLS_VERSION=34.0.0
export PLATFORM=android-34
Smoke test
From the workspace root (Rusty_Solitaire/):
scripts/android_smoke.sh
The smoke test first checks whether adb can see a ready device. If no device
is connected and LAUNCH_AVD=1 (default), it:
- locates the
emulatorbinary underANDROID_HOMEorPATH, - picks the first available AVD (or uses
AVD_NAME), - launches the emulator in the foreground (or headless with
AVD_HEADLESS=1), - waits for
sys.boot_completed=1before proceeding, - dismisses the lock screen so the screenshot shows the app.
Once a device is ready (auto-launched or pre-existing) the script:
- builds the APK using
scripts/build_android_apk.sh, - installs it with
adb install -r -dso debug smoke builds can replace newer local builds, - force-stops the package by default for a clean launch,
- clears
logcat, - launches
com.ferrousapp.solitaire/android.app.NativeActivity, - waits for the app to settle,
- verifies the process is still running,
- captures a screenshot and
logcat, and - fails on fatal log patterns such as native crashes, JNI fatal errors, real ANRs, and Rust panics.
On exit the script kills any emulator it launched (SHUTDOWN_AVD_ON_EXIT=1 by
default). Set SHUTDOWN_AVD_ON_EXIT=0 to keep the emulator open for inspection.
Artifacts are written to target/android-smoke/<timestamp>/ by default. A successful run includes:
device.txt— selected device and display metadata,df-data-before.txt/df-data-after.txt— emulator/device storage snapshots,emulator.log— stdout/stderr from the emulator process (AVD runs only),emulator.pid— PID of the emulator process (AVD runs only),launch.png— screenshot after the wait period,logcat.txt— full captured log,log-summary.txt— grep summary for warnings, errors, JNI, safe-area, and crash terms, andpid.txt— running app process id.
Creating an AVD
If no AVDs exist, create one before running the smoke test:
# Install a system image
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" \
'system-images;android-34;google_apis;x86_64'
# Create the AVD
"$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager" create avd \
-n Pixel_7_API_34 \
-k 'system-images;android-34;google_apis;x86_64' \
--device 'pixel_7'
Then run the smoke test — it will pick Pixel_7_API_34 automatically:
scripts/android_smoke.sh
Faster iteration
If you already built the APK and only want to reinstall/relaunch:
BUILD_APK=0 scripts/android_smoke.sh
If the APK is already installed and you only want to relaunch/capture logs:
BUILD_APK=0 INSTALL_APK=0 scripts/android_smoke.sh
By default the script force-stops the package before launch so logcat and screenshots represent a clean app start. To test warm-launch behavior instead:
BUILD_APK=0 INSTALL_APK=0 FORCE_STOP=0 scripts/android_smoke.sh
This is also useful when an already-installed build is good enough for launch/log checks. On install failure, the script writes adb-install.txt, storage snapshots, and installed-package diagnostics to the output directory.
If install fails with INSTALL_FAILED_UPDATE_INCOMPATIBLE, the smoke script uninstalls the package and retries once by default (RESET_ON_SIGNATURE_MISMATCH=1). This resets app data on the device/emulator. Disable it with:
RESET_ON_SIGNATURE_MISMATCH=0 scripts/android_smoke.sh
To write artifacts to a stable path:
OUT_DIR=target/android-smoke/latest scripts/android_smoke.sh
When reusing an output directory, previous files are removed by default so stale artifacts do not contaminate the latest result. To keep existing files:
CLEAN_OUT_DIR=0 OUT_DIR=target/android-smoke/latest scripts/android_smoke.sh
To target a specific device when more than one is attached:
ADB_SERIAL=emulator-5554 scripts/android_smoke.sh
To wait longer for safe-area inset polling or slow devices:
WAIT_SECS=8 scripts/android_smoke.sh
AVD options
To pick a specific AVD by name instead of auto-selecting the first one:
AVD_NAME=Pixel_7_API_34 scripts/android_smoke.sh
To run headless (no emulator window) — useful in CI or on a display-less machine:
AVD_HEADLESS=1 scripts/android_smoke.sh
To give a slow machine more time to boot the emulator (default is 120 s):
AVD_BOOT_TIMEOUT=180 scripts/android_smoke.sh
To keep the emulator running after the test (useful for manual inspection):
SHUTDOWN_AVD_ON_EXIT=0 scripts/android_smoke.sh
To pass extra flags to the emulator (e.g. disable snapshot for a completely cold boot, or change GPU mode):
AVD_EXTRA_ARGS="-gpu swiftshader_indirect" scripts/android_smoke.sh
To disable AVD auto-launch entirely and fail immediately if no device is connected:
LAUNCH_AVD=0 scripts/android_smoke.sh
For build-only validation without requiring a connected device, use the lower-level APK builder directly:
scripts/build_android_apk.sh
For smoke testing, scripts/android_smoke.sh defaults to the connected device's primary ABI when BUILD_APK=1, which keeps emulator APKs much smaller than the full multi-ABI default. You can still override it explicitly:
ABIS=x86_64 scripts/android_smoke.sh
For build-only validation, scripts/build_android_apk.sh still defaults to all configured ABIs unless you set ABIS yourself:
ABIS=x86_64 scripts/build_android_apk.sh
The APK builder signs debug builds with a persistent keystore at target/android/debug.keystore by default. This avoids signature churn across smoke-test runs.
The APK builder also strips native debug symbols by default before packaging (STRIP_NATIVE_LIBS=1). This keeps debug APKs installable on emulators with limited /data storage. To preserve native debug symbols for low-level debugging:
STRIP_NATIVE_LIBS=0 ABIS=x86_64 scripts/build_android_apk.sh
Device checklist
The script is only a smoke test. Before shipping Android builds, also verify:
- safe-area insets arrive and shift the HUD after a few seconds,
- HUD does not overlap the top status bar,
- modal Done buttons are above the gesture/navigation bar,
- stock tap works,
- drag-and-drop works on tableau, waste, and foundation piles,
- Settings/Help/Profile modals open and close,
- login tokens persist after app restart, and
target/android-smoke/.../logcat.txtcontains no fatal JNI/native crash output.
Notes
adb shell input tap X Yuses physical pixels, not Bevy logical pixels.- The project’s common test device mapping is physical
1080×2400, Bevy logical900×2000, scale factor1.20; multiply logical coordinates by1.20for scriptedadb shell inputcommands on that device. - Keep generated screenshots/logs under
target/android-smoke/so they stay out of source control.