becfda0f6c
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>
229 lines
7.6 KiB
Markdown
229 lines
7.6 KiB
Markdown
# 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/<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, 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.
|