# 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. - `adb` available on `PATH`. - One device/emulator visible in `adb devices`, **or** at least one AVD created (the script will launch one automatically if `LAUNCH_AVD=1`, which is the default). - If multiple devices are connected, set `ADB_SERIAL` to the target device serial. - Environment variables required by `scripts/build_android_apk.sh` when building: ```sh 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/`): ```sh 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: 1. locates the `emulator` binary under `ANDROID_HOME` or `PATH`, 2. picks the first available AVD (or uses `AVD_NAME`), 3. launches the emulator in the foreground (or headless with `AVD_HEADLESS=1`), 4. waits for `sys.boot_completed=1` before proceeding, 5. dismisses the lock screen so the screenshot shows the app. Once a device is ready (auto-launched or pre-existing) the script: 1. builds the APK using `scripts/build_android_apk.sh`, 2. installs it with `adb install -r -d` so debug smoke builds can replace newer local builds, 3. force-stops the package by default for a clean launch, 4. clears `logcat`, 5. launches `com.ferrousapp.solitaire/android.app.NativeActivity`, 6. waits for the app to settle, 7. verifies the process is still running, 8. captures a screenshot and `logcat`, and 9. 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//` 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, and - `pid.txt` — running app process id. ## Creating an AVD If no AVDs exist, create one before running the smoke test: ```sh # 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: ```sh scripts/android_smoke.sh ``` ## Faster iteration If you already built the APK and only want to reinstall/relaunch: ```sh BUILD_APK=0 scripts/android_smoke.sh ``` If the APK is already installed and you only want to relaunch/capture logs: ```sh 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: ```sh 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: ```sh RESET_ON_SIGNATURE_MISMATCH=0 scripts/android_smoke.sh ``` To write artifacts to a stable path: ```sh 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: ```sh 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: ```sh ADB_SERIAL=emulator-5554 scripts/android_smoke.sh ``` To wait longer for safe-area inset polling or slow devices: ```sh WAIT_SECS=8 scripts/android_smoke.sh ``` ## AVD options To pick a specific AVD by name instead of auto-selecting the first one: ```sh 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: ```sh AVD_HEADLESS=1 scripts/android_smoke.sh ``` To give a slow machine more time to boot the emulator (default is 120 s): ```sh AVD_BOOT_TIMEOUT=180 scripts/android_smoke.sh ``` To keep the emulator running after the test (useful for manual inspection): ```sh 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): ```sh AVD_EXTRA_ARGS="-gpu swiftshader_indirect" scripts/android_smoke.sh ``` To disable AVD auto-launch entirely and fail immediately if no device is connected: ```sh LAUNCH_AVD=0 scripts/android_smoke.sh ``` For build-only validation without requiring a connected device, use the lower-level APK builder directly: ```sh 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: ```sh 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: ```sh 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: ```sh 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.txt` contains no fatal JNI/native crash output. ## Notes - `adb shell input tap X Y` uses physical pixels, not Bevy logical pixels. - The project’s common test device mapping is physical `1080×2400`, Bevy logical `900×2000`, scale factor `1.20`; multiply logical coordinates by `1.20` for scripted `adb shell input` commands on that device. - Keep generated screenshots/logs under `target/android-smoke/` so they stay out of source control.