taler-monitoring/android-test/GUI-AUTOMATION-NOTES.md
2026-07-18 13:56:54 +02:00

362 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GUI automation notes (Android wallet)
Canonical notes for **graphical** Android wallet tests against GOA / stage.
---
## Minimal variant (implemented first) — status 2026-07
This is the **minimal vanilla** path we actually shipped; full dual-platform
GUI is planned via flags (below), not required to use these scripts today.
| Piece | Status | Notes |
|-------|--------|--------|
| F-Droid published APK install | **done** | `net.taler.wallet.fdroid` 1.6.1 / 854 |
| Source build + same smoke | **done** | `:wallet:assembleFdroidDebug``net.taler.wallet.fdroid.debug` |
| Default source branch | **done** | `dev/hernani-inference/fix-bank-withdraw-auto-exchange` (minimal GOA exchange auto-add) |
| Deep-link entry (≡ QR) | **done** | `adb` `VIEW` **withdraw then pay** (`taler://withdraw/…``taler://pay…`) |
| Explorer API mint (chain) | **done** | `run-goa-gui-chain.sh` (GOA + stage) |
| UI taps via uiautomator | **done** | best-effort Confirm/ToS/Pay; ANR → prefer **Wait** |
| Screenshots / logcat artifacts | **done** | under `out*`, `out-gui-chain/` (gitignored) |
| Host CLI e2e settlement | **separate** | `taler-monitoring.sh e2e` / `ladder` (not Android UI) |
| Reliable unattended Confirm on 4GiB Linux host | **limited** | System UI ANR; use phone or more RAM |
| iOS GUI | **not in this tree** | see taler-ios `dev/hernani-inference/gui-workflows` |
| macOS full dual AVD helpers | **upstream** | taler-android `gui-workflows` (Homebrew); ported ideas here for Linux |
**Ground rule:** bring it to run; app diffs only if needed and **minimal**; prefer
extending existing `dev/hernani-inference/*` branches over new trees.
### Minimalvariante: was geht / was nicht
Einschränkungen der **aktuell implementierten** Minimalvariante (deep-link +
uiautomator best-effort). Kein voller Dual-Platform-GUI-Stack, kein Ersatz für
Host-CLI-Settlement.
#### Geht (grün)
| Fähigkeit | Wie / Hinweis |
|-----------|----------------|
| APK installieren | F-Droid `net.taler.wallet.fdroid` **oder** Source `assembleFdroidDebug` (`.debug`) |
| GOA-Exchange ohne manuelles Add | Source-Build von `fix-bank-withdraw-auto-exchange` (Default in `run-android-build-and-smoke.sh`) |
| **Abheben + Bezahlen** | Beide Beine: erst Withdraw-URI + Taps, dann Pay-URI + Taps (`DO_WITHDRAW`/`DO_PAY`) |
| Light UI-Drive | Confirm / Accept / Pay-Taps über Label-Suche (`lib_ui.py`, gui-smoke, chain) |
| Multi-Runden-Taps | `run-android-gui-smoke.sh` / `run-goa-gui-chain.sh` (best-effort) |
| Explorer-Mint (Kette) | `run-goa-gui-chain.sh` + `EXP_PW_FILE` (GOA + stage) |
| Stack-Wahl | `STACK=goa` / `stage` (Bank/Merchant-URLs) |
| Artefakte | Screenshots, UI-XML, logcat unter `out*` / `out-gui-chain/` (gitignored) |
| Hybrid-Smoke | Install + Intent + leichte Taps: `run-android-pay-smoke.sh` |
| Settlement **nachweisen** | **Host-seitig**: `taler-monitoring.sh e2e` / `ladder` (nicht Android-UI) |
| Flags abschalten | `AUTO_ANDROID=0` skip; `AUTO_GUI=0` → nur deep-link/pay-smoke |
#### Geht **nicht** oder nur eingeschränkt (rot / gelb)
| Beschränkung | Folge / Workaround |
|--------------|--------------------|
| **Unattended Confirm → settled im Emulator (~4GiB Host)** | Häufig **System UI ANR**; Taps bleiben hängen. Prefer **Wait** auf ANR-Dialog, echtes Gerät, oder Host mit mehr RAM. Kein zuverlässiges grünes E2E nur über GUI auf dem schwachen Emulator. |
| **Vollständige Wallet-Settlement-Assertion in der GUI** | Minimalvariante prüft **nicht** „Balance final / transfer_done“ in der App-Oberfläche. Proof bleibt CLI/e2e. |
| **Kamera / physischer QR-Scan** | Absichtlich nicht automatisiert; deep-link ist der Shortcut. Landing-QR-Pfade sind **HTTP**-Thema, nicht Android-GUI. |
| **Browser-Shop-Checkout-UI** | Kein Chromium/WebView-Drive; Pay-URI kommt per Template-`POST` + Intent. |
| **Volles ToS-Scroll / alle Dialoge** | Best-effort Accept-Tap; lange ToS oder unerwartete Sheets können hängen bleiben. |
| **Published F-Droid allein auf GOA** | Kann Exchange-Add / Spinner-Probleme zeigen → **fix-branch-APK** bauen; bei Spinner-Hang optional `fix-withdraw-spinner-fallback`. |
| **iOS-GUI** | Nicht in diesem Tree; siehe taler-ios `gui-workflows`. |
| **macOS dual AVD / Homebrew-Helpers** | Upstream `gui-workflows`; hier nur Linux-taugliche Port-Ideen. |
| **`AUTO_*` in jedem Script erzwungen** | Design + teilweise verdrahtet; Wrapper-Checkliste noch offen. |
| **`run-until-done` / CLI-Wallet in GUI-Skripten** | Gehört zu CLI-Automation, nicht GUI; siehe `CLI-AUTOMATION-NOTES.md`. |
| **CI grün = „User hat bezahlt“** | Smoke = Intent + best-effort UI. Wirtschaftlicher Erfolg = Host-e2e / ladder / Explorer. |
#### Erwartetes Ergebnis pro Schicht
| Schicht | Erfolgskriterium Minimalvariante |
|---------|----------------------------------|
| A Install | APK installed, App startet |
| B Deep-link | Intent delivered, Wallet öffnet Withdraw/Pay-Flow |
| C GUI taps | Dump+Tap ohne Crash; ANR ggf. mit Wait; **kein** Garant für „Paid“ |
| D Evidence | Artefakte geschrieben |
| Settlement | **Außerhalb** GUI: Host e2e/ladder |
#### Wann Minimalvariante reicht
- Smoke nach APK-/Branch-Build (Intent kommt an, App crasht nicht).
- Manuelle Nacharbeit am Gerät nach vorbereiteten URIs.
- GOA/stage **Erreichbarkeit** von Landing/Template + Wallet-Einstieg.
#### Wann nicht reicht → nächster Schritt
- Unattended grünes Pay-E2E auf dem Emulator → mehr RAM / physisches Gerät, ggf. `fix-withdraw-spinner-fallback`.
- Beweis „Münzen da / Merchant paid“ → CLI e2e/ladder, nicht GUI-smoke.
- iOS / Dual-Platform → andere Repos/Flags, nicht diese Skripte.
```bash
cd scripts/taler-monitoring/android-test
# Minimal hybrid (install + deep-link + light taps)
STACK=goa ./run-android-pay-smoke.sh
# GUI multi-round taps
STACK=goa ./run-android-gui-smoke.sh
STACK=stage ./run-android-gui-smoke.sh
# Explorer mint + multi withdraw/pay chain (from gui-workflows)
EXP_PW_FILE=$HOME/src/koopa/koopa-admin-secrets/koopa/host-root/taler-bank/bank-explorer-password.txt \
STACK=goa PKG=net.taler.wallet.fdroid.debug \
./run-goa-gui-chain.sh
# Build fix-branch APK then smoke (GUI=1 for gui smoke)
./run-android-build-and-smoke.sh
GUI=1 ./run-android-build-and-smoke.sh
```
---
## Platform capability flags (planned + defaults)
Automation can run on hosts that support **Android only**, **GUI tooling only**
(conceptual), or **both**. Flags keep that explicit for CI and laptops.
### Proposed env flags
| Variable | Values | Meaning |
|----------|--------|---------|
| `AUTO_ANDROID` | `0` / `1` | Run Android wallet automation (adb, APK, emulator/device) |
| `AUTO_GUI` | `0` / `1` | Drive **graphical** UI (uiautomator taps, multi-round). If `0`, deep-link/CLI-only install smoke. |
| `AUTO_PLATFORM` | `auto` / `linux` / `macos` / `ios` | Host family (optional override) |
| `EMULATOR_HEADLESS` | **`1` (default)** / `0` | No host window (`-no-window`). Server/CI-safe. |
| `WINDOWED` | `0` / `1` | Shortcut: `WINDOWED=1` ⇒ show emulator window (`EMULATOR_HEADLESS=0`) |
| `EMULATOR_GPU` | default **`swiftshader_indirect`** | Guest GLES still runs headless (layout/taps/screenshots). Override `host` only if windowed + real GPU. |
| `AUTO_START_EMULATOR` | **`1` (default)** / `0` | If no adb device, start AVD via `start-android-emulator.sh --wait` |
---
## Headless mode (default)
**Default for all GUI / pay smokes:** no window on the host. Opening an emulator
GUI on servers (or headless CI) is wrong; these scripts therefore start the AVD
with **`-no-window`** and still keep **graphics logic** in the guest:
| Piece | Headless default |
|-------|------------------|
| Host window | **off** (`-no-window`, `QT_QPA_PLATFORM=offscreen`, no `DISPLAY`) |
| Guest GLES | **on** via **SwiftShader** (`-gpu swiftshader_indirect`) |
| UI automation | **on**`uiautomator` dump/tap + `screencap` (no X11 needed) |
| Audio / boot anim | off (`-no-audio -no-boot-anim`) |
```bash
# explicit (same as default)
./start-android-emulator.sh --wait
# or let smoke auto-start headless AVD when no phone is plugged in
STACK=goa ./run-android-gui-smoke.sh
# laptop: show the emulator window
WINDOWED=1 ./start-android-emulator.sh --wait
# or: EMULATOR_HEADLESS=0 EMULATOR_GPU=host ./start-android-emulator.sh --wait
```
Shared code: `lib_android_env.sh` + `start-android-emulator.sh`.
**Semantics:**
| `AUTO_ANDROID` | `AUTO_GUI` | Behaviour |
|----------------|------------|-----------|
| `1` | `0` | Android install + deep-link smoke only (no multi-round taps) |
| `1` | `1` | Android + graphical drive (vanilla GUI / chain) |
| `0` | `1` | Reserved (e.g. future desktop/web GUI); currently **no-op** with a clear message |
| `0` | `0` | Skip mobile automation |
### Defaults by host (when flags unset)
| Host (`uname -s`) | Default `AUTO_ANDROID` | Default `AUTO_GUI` | Rationale |
|-------------------|------------------------|--------------------|-----------|
| **Linux** | **`1`** | **`1`** if device/emulator present, else scripts exit 3 | This repos day-to-day path; **Android only** (no iOS here) |
| **Darwin (macOS)** | `1` | `1` | Can run Android emulators **and** (separately) taler-ios GUI helpers; both flags on for Android scripts; iOS is out of tree |
| **Other** | `0` | `0` | Fail closed |
**Linux default = Android** (no second mobile platform in this suite).
**macOS** may enable both ecosystems in the wider monorepo sense; for *these*
scripts only Android is implemented — set `AUTO_ANDROID=1` (default) and keep
iOS under `taler-ios`.
### Resolution helper (convention for future wrappers)
```bash
# Example for a future run-all-mobile.sh
os=$(uname -s)
: "${AUTO_PLATFORM:=auto}"
case "$AUTO_PLATFORM" in
auto) case "$os" in Linux) AUTO_PLATFORM=linux ;; Darwin) AUTO_PLATFORM=macos ;; *) AUTO_PLATFORM=other ;; esac ;;
esac
case "$AUTO_PLATFORM" in
linux)
: "${AUTO_ANDROID:=1}"
: "${AUTO_GUI:=1}"
# no iOS
;;
macos)
: "${AUTO_ANDROID:=1}"
: "${AUTO_GUI:=1}"
# optional later: AUTO_IOS=1 for taler-ios scripts
;;
*)
: "${AUTO_ANDROID:=0}"
: "${AUTO_GUI:=0}"
;;
esac
```
Scripts **today** implement Android only; they should honour:
- `AUTO_ANDROID=0` → exit 0 with “skipped (AUTO_ANDROID=0)”
- `AUTO_GUI=0` → call deep-link smoke without multi-round GUI (or set `GUI_ROUNDS=0`)
---
## Vanilla layers (detail)
| Layer | What | Tooling |
|-------|------|---------|
| **A — Install** | F-Droid or from-source APK | `adb install` |
| **B — Entry (shortcut)** | `taler://withdraw/…` / `taler://pay…` | `adb am start -a VIEW` |
| **C — Graphical UI** | Confirm / ToS / Pay taps; ANR Wait | `uiautomator` + `lib_ui.py` / chain |
| **D — Evidence** | Screenshots, XML, logcat, URIs | `out*/` |
### Legitimate shortcuts
| Shortcut | Replaces |
|----------|----------|
| Deep-link withdraw/pay | Camera QR / opening paywall |
| Host `POST` template → pay URI | Browser shop checkout UI |
| Label-list taps | Human reading button text |
| Skip full ToS scroll | Tap Accept if shown |
### Scripts
| Script | Role |
|--------|------|
| `run-android-pay-smoke.sh` | Hybrid: install + deep-link + light taps |
| `run-android-gui-smoke.sh` | Multi-round GUI drive |
| `run-goa-gui-chain.sh` | Multi mint/withdraw/pay (gui-workflows port) |
| `run-android-build-and-smoke.sh` | Build git ref (branch/tag) + smoke (`GUI=1` optional) |
| `run-android-variant-matrix.sh` | **published + stable-self + master** (compare builds) |
| `start-android-emulator.sh` | Start AVD (**headless default**, SwiftShader) |
| `lib_android_env.sh` | Flags, PATH, `android_ensure_device` |
| `lib_ui.py` | Dump/tap/ANR helpers |
### Build variants (anti“weird local build”)
| Variant | Source | Severity | Purpose |
|---------|--------|----------|---------|
| **stable-self** | **only** rebuild of current stable tag | WARN | Self-build vs published for **one** release |
| **published** | F-Droid current APK | **≤14d → BLOCKER**, else WARN | What users install |
| **older / milestones** | F-Droid APK ~**3 / 6 / 9 / 12 mo** | **always WARN** | No self-build of old tags |
| **master** | `origin/master` rebuild | WARN | Tip — never hard-blocks |
| **fix** (optional) | inference branch | soft | GOA automation fixes |
**Self-build:** current stable only (plus master/fix if requested) — not older releases.
**Blocker window:** releases ≤ 14 days.
**Milestones:** nearest tags to 90/180/270/365d with a still-hosted F-Droid APK.
---
## Stacks under test
| Stack | Withdraw | Pay |
|-------|----------|-----|
| **GOA** | explorer mint / `demo-withdraw.json` @ bank.hacktivism.ch | goa-shop templates / Paivana pay-template |
| **stage** | explorer / demo-withdraw @ stage.bank… | fermes / jardin public templates |
### Lageübersicht GOA + stage (as-is survey · 2026-07-17 ~22:0522:18)
**Device:** headless `emulator-5554` · host ~3.7GiB RAM (often <300MiB free) · timeout per leg ≤420s.
**Legs:** Abheben + Bezahlen (`DO_WITHDRAW=1 DO_PAY=1`). Artefakte: `out-survey-20260717-220527/` (gitignored).
#### Varianten
| Variante | Quelle | Bemerkung |
|----------|--------|-----------|
| **stable (published)** | F-Droid `net.taler.wallet.fdroid` **1.6.1 / 854** | as-is |
| **master as-is** | rebuild `origin/master` @ `f65a976c``.fdroid.debug` | as-is, no patches |
| **ältere Releases** (3/6/9/12 mon) | F-Droid APKs für `1.5.0`/`1.3.0`/`1.1.0`/`1.0.8` | **nicht auffindbar** (HTTP miss) → **SKIP** (kein Selbstbau älterer Tags) |
| **master + inference** (extra) | local branch `local/master-with-inference` @ `3ebe8b09` | merges `fix-bank-withdraw-auto-exchange` + `fix-withdraw-spinner-fallback` · **not pushed** |
#### Ergebnis-Matrix
| Variant | Stack | Install / deep-link | Withdraw URI | Pay URI | wallet-core Abheben | wallet-core Pay | ANR | Exit |
|---------|-------|---------------------|--------------|---------|---------------------|-----------------|-----|------|
| stable published | **goa** (hacktivism) | OK | OK bank.hacktivism.ch | OK paivana template | **NO** | soft/YES once* | **YES** | **10** |
| stable published | **stage** (*.lefrancpaysan) | OK | OK stage.bank… | OK fermes order | **NO** | **NO** | **YES** | **10** |
| master as-is | **goa** | OK | OK | OK paivana | **NO** | **NO** | **YES** | **10** |
| master as-is | **stage** | OK | OK | OK fermes | **NO** | **NO** | **YES** | **10** |
| master+inference | **goa** | OK | OK | OK | **NO** | **NO** | **YES** | **10** |
| master+inference | **stage** | OK | OK | OK | **NO** | **NO** | **YES** | **10** |
\*stable-goa console once reported pay wallet-core YES mid-run; final score still ANR-dominated / not reliable settlement.
#### Was pro Stack **steht** (Infra)
| Check | **goa / hacktivism** | **stage / \*.lefrancpaysan** |
|-------|----------------------|------------------------------|
| Bank `…/intro/demo-withdraw.json``taler://withdraw/…` | **OK** | **OK** |
| Pay entry | paivana `pay-template` **OK** | fermes `panier-legumes` POST → `taler://pay/…` **OK** |
| adb `VIEW` Intent delivery | **OK** | **OK** |
| Unattended Confirm/Abheben/Pay GUI | **FAIL (ANR)** | **FAIL (ANR)** |
| wallet-core prepare/accept withdraw in logcat | **not seen** | **not seen** |
**Fazit as-is:** Beide Stacks liefern die **Eingänge** (Withdraw- + Pay-URIs). Die **GUI/wallet-core-Strecke** ist auf diesem Emulator für **alle** getesteten App-Varianten (stable, master, inference) **gleich tot** — System UI / Android System ANR, keine brauchbaren Taps. Das ist **kein Stack-Unterschied** GOA vs stage und **kein** belastbarer App-Versions-Vergleich.
#### Inference-Patch-Versuch (lokal, **kein Push**)
Weil **nichts** der as-is-Varianten wallet-core-grün war, wurde zusätzlich gebaut:
```text
taler-android branch: local/master-with-inference # NOT pushed
base: origin/master
merged: origin/dev/hernani-inference/fix-bank-withdraw-auto-exchange
origin/dev/hernani-inference/fix-withdraw-spinner-fallback
commit: 3ebe8b091379
APK: apks/wallet-master-inference-3ebe8b091379.apk (local)
```
| Stack | Inference smoke | Besser als master/stable? |
|-------|-----------------|---------------------------|
| goa | exit 10, ANR, no withdraw core | **Nein** |
| stage | exit 10, ANR, no withdraw core | **Nein** |
**Kein grünes Inference-Signal** auf diesem Host → wir können **nicht** belegen, dass die Patches Withdraw/Pay „retten“.
Ebenso können wir **nicht** belegen, dass master ohne Patches „schuld“ ist: die Umgebung erreicht wallet-core gar nicht.
> **Wenn später auf Gerät/mehr RAM die Inference-APK plötzlich grün und master/stable rot ist:**
> das ist der **klare Fix-Hinweis** — Patches aus `dev/hernani-inference/*` upstreamen / in master holen.
> **Heute:** noch **nicht** der Fall; primärer Blocker = **Emulator/RAM/ANR**.
#### Nächste sinnvolle Schritte (später)
1. Physisches Gerät oder Host ≥68GiB free RAM
2. Nur **eine** Wallet-Package-ID installiert (kein Open-with)
3. Survey wiederholen: stable · master · `local/master-with-inference` auf **goa + stage**
4. Ältere F-Droid-APKs nur wenn wieder gehostet; kein Selbstbau alter Tags
---
## Upstream branches (`taler-android` `dev/hernani-inference/*`)
| Branch | Role |
|--------|------|
| `gui-workflows` | macOS emulator helpers + original `goa-chain-emu.sh` |
| `fix/bank-withdraw-auto-exchange` | **Minimal** GOA exchange auto-add (default build branch) |
| `fix/withdraw-spinner-fallback` | Spinner + OIM; use if spinner still hangs |
| `experimental-oim*` | Optional cash UI |
---
## Checklist
- [x] Minimal variant documented
- [x] Minimalvariante: was geht / was nicht (Beschränkungen)
- [x] Two stacks (goa / stage)
- [x] Dual-stack run documented (2026-07-17: both FAIL ANR, landings OK)
- [x] Flags design (`AUTO_ANDROID` / `AUTO_GUI` / platform defaults)
- [x] **Headless emulator default** (`EMULATOR_HEADLESS=1`, SwiftShader)
- [x] Linux default = Android suite only
- [x] Inference-branch policy
- [ ] Wrapper enforces flags in every entry script (incremental)
- [ ] Reliable unattended Confirm on low-RAM Linux emulator