fix(android): auto-discover SDK/NDK in build script, strip native libs
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>
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user