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 4 GiB 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 (~4 GiB 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.
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) |
# 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 repo’s 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)
# 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:05–22:18)
Device: headless emulator-5554 · host ~3.7 GiB RAM (often <300 MiB free) · timeout per leg ≤420 s.
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:
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)
- Physisches Gerät oder Host ≥6–8 GiB free RAM
- Nur eine Wallet-Package-ID installiert (kein Open-with)
- Survey wiederholen: stable · master ·
local/master-with-inference auf goa + stage
- Ä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