# 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.
