diff --git a/scripts/taler-monitoring/android-test/GUI-AUTOMATION-NOTES.md b/scripts/taler-monitoring/android-test/GUI-AUTOMATION-NOTES.md index 8326ac9..3248573 100644 --- a/scripts/taler-monitoring/android-test/GUI-AUTOMATION-NOTES.md +++ b/scripts/taler-monitoring/android-test/GUI-AUTOMATION-NOTES.md @@ -14,7 +14,7 @@ GUI is planned via flags (below), not required to use these scripts today. | 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` `taler://withdraw/…` / `taler://pay…` | +| 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) | @@ -38,7 +38,7 @@ Host-CLI-Settlement. |-----------|----------------| | 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`) | -| Withdraw/Pay **starten** | `adb` deep-link `taler://withdraw/…` / `taler://pay…` (Ersatz für QR-Scan) | +| **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) | diff --git a/scripts/taler-monitoring/android-test/README.md b/scripts/taler-monitoring/android-test/README.md index 7b2b0e3..d91d82c 100644 --- a/scripts/taler-monitoring/android-test/README.md +++ b/scripts/taler-monitoring/android-test/README.md @@ -1,12 +1,18 @@ -# Android pay smoke +# Android wallet smoke (Abheben + Bezahlen) -## 1) Published F-Droid wallet +Smokes always cover **withdraw first**, then **pay** (not pay-only). + +| Leg | Default | What | +|-----|---------|------| +| **1 WITHDRAW** | on | `taler://withdraw/…` + Confirm/Abheben taps | +| **2 PAY** | on | `taler://pay…` / pay-template + Pay taps | ```bash # needs adb device/emulator ./run-android-pay-smoke.sh STACK=goa ./run-android-pay-smoke.sh -STACK=stage ./run-android-pay-smoke.sh +DO_PAY=0 ./run-android-pay-smoke.sh # withdraw only +DO_WITHDRAW=0 DO_PAY=1 … # pay only (unusual) ``` APK: F-Droid **1.6.1** (`net.taler.wallet.fdroid`). diff --git a/scripts/taler-monitoring/android-test/run-android-gui-smoke.sh b/scripts/taler-monitoring/android-test/run-android-gui-smoke.sh index f52c783..a22f1b8 100755 --- a/scripts/taler-monitoring/android-test/run-android-gui-smoke.sh +++ b/scripts/taler-monitoring/android-test/run-android-gui-smoke.sh @@ -1,12 +1,9 @@ #!/usr/bin/env bash -# GUI-oriented Android smoke (vanilla level): deep-link entry + UI taps. +# GUI-oriented Android smoke: **withdraw (Abheben)** then **pay**, each with +# deep-link entry + multi-round uiautomator taps. Not pay-only. # -# Entry shortcut (legitimate — same as scanning a QR on the landing): -# adb VIEW taler://withdraw/… and taler://pay… -# Then drives the **graphical UI** via uiautomator dump + taps (Confirm / ToS / Pay). -# -# Systems: STACK=goa | stage | auto -# APK: published F-Droid or APK_PATH=… (from-source) +# 1) taler://withdraw/… + Confirm/Abheben/ToS taps +# 2) taler://pay… + Pay/Confirm taps # # See GUI-AUTOMATION-NOTES.md # @@ -85,20 +82,28 @@ echo "apk: $APK_PATH pkg: $PKG" "${ADB[@]}" shell settings put global transition_animation_scale 0 || true "${ADB[@]}" shell settings put global animator_duration_scale 0 || true -WURI=$(curl -sfS -m 25 "$WITHDRAW_JSON" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("taler_withdraw_uri") or d.get("qr_payload") or "")') -[ -n "$WURI" ] || { echo "no withdraw uri" >&2; exit 4; } -echo "withdraw: $WURI" -echo "$WURI" >"$OUT_DIR/last-withdraw.uri" +: "${DO_WITHDRAW:=1}" +: "${DO_PAY:=1}" +: "${SMOKE_STRICT:=0}" + +WURI="" +PAY_URI="" +WITHDRAW_OK=0 +PAY_OK=0 "${ADB[@]}" shell am force-stop "$PKG" 2>/dev/null || true "${ADB[@]}" logcat -c 2>/dev/null || true -# --- Shortcut A: deep-link withdraw (≡ QR scan on landing) --- -"${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$WURI" | tee "$OUT_DIR/start-withdraw.txt" -sleep 5 - -# --- Graphical: drive UI --- -python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$GUI_ROUNDS" "$GUI_SLEEP" <<'PY' +if [ "$DO_WITHDRAW" = "1" ]; then + echo + echo "======== 1 WITHDRAW (Abheben) ========" + WURI=$(curl -sfS -m 25 "$WITHDRAW_JSON" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("taler_withdraw_uri") or d.get("qr_payload") or "")') + [ -n "$WURI" ] || { echo "no withdraw uri" >&2; exit 4; } + echo "withdraw: $WURI" + echo "$WURI" >"$OUT_DIR/last-withdraw.uri" + "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$WURI" | tee "$OUT_DIR/start-withdraw.txt" + sleep 5 + python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$GUI_ROUNDS" "$GUI_SLEEP" <<'PY' import json, sys, importlib.util from pathlib import Path lib_path, serial, out, rounds, sleep_s = ( @@ -112,26 +117,28 @@ st = lib.gui_drive(serial, out / "gui-withdraw", rounds=rounds, sleep_s=sleep_s, print("gui-withdraw taps:", st.get("taps")) print("gui-withdraw anr:", st.get("anr_dismissals")) PY - -# Pay URI -PAY_URI="" -if [ "$STACK" = "stage" ]; then - if RESP=$(curl -sfS -m 20 -X POST -H 'Content-Type: application/json' -d '{}' \ - "$MERCHANT/instances/fermes-des-collines/templates/panier-legumes" 2>/dev/null); then - PAY_URI=$(printf '%s' "$RESP" | python3 -c 'import json,sys;d=json.load(sys.stdin);oid=d.get("order_id")or"";tok=d.get("token")or""; -print("taler://pay/stage.monnaie.lefrancpaysan.ch/instances/fermes-des-collines/%s/?c=%s"%(oid,tok) if oid and tok else "")') - fi else - PAY_URI="taler://pay-template/taler.hacktivism.ch/instances/goa-shop/paivana" + echo "======== 1 WITHDRAW skipped (DO_WITHDRAW=0) ========" fi -if [ -n "$PAY_URI" ]; then - echo "pay: $PAY_URI" - echo "$PAY_URI" >"$OUT_DIR/last-pay.uri" - # Shortcut B: deep-link pay (≡ scan pay QR / open paywall link) - "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$PAY_URI" | tee "$OUT_DIR/start-pay.txt" - sleep 5 - python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$GUI_ROUNDS" "$GUI_SLEEP" <<'PY' +if [ "$DO_PAY" = "1" ]; then + echo + echo "======== 2 PAY (Bezahlen) ========" + if [ "$STACK" = "stage" ]; then + if RESP=$(curl -sfS -m 20 -X POST -H 'Content-Type: application/json' -d '{}' \ + "$MERCHANT/instances/fermes-des-collines/templates/panier-legumes" 2>/dev/null); then + PAY_URI=$(printf '%s' "$RESP" | python3 -c 'import json,sys;d=json.load(sys.stdin);oid=d.get("order_id")or"";tok=d.get("token")or""; +print("taler://pay/stage.monnaie.lefrancpaysan.ch/instances/fermes-des-collines/%s/?c=%s"%(oid,tok) if oid and tok else "")') + fi + else + PAY_URI="taler://pay-template/taler.hacktivism.ch/instances/goa-shop/paivana" + fi + if [ -n "$PAY_URI" ]; then + echo "pay: $PAY_URI" + echo "$PAY_URI" >"$OUT_DIR/last-pay.uri" + "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$PAY_URI" | tee "$OUT_DIR/start-pay.txt" + sleep 5 + python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$GUI_ROUNDS" "$GUI_SLEEP" <<'PY' import json, sys, importlib.util from pathlib import Path lib_path, serial, out, rounds, sleep_s = ( @@ -145,29 +152,50 @@ st = lib.gui_drive(serial, out / "gui-pay", rounds=rounds, sleep_s=sleep_s, phas print("gui-pay taps:", st.get("taps")) print("gui-pay anr:", st.get("anr_dismissals")) PY + else + echo "pay: (no URI)" + fi +else + echo "======== 2 PAY skipped (DO_PAY=0) ========" fi -"${ADB[@]}" logcat -d -t 400 >"$OUT_DIR/logcat.txt" || true -grep -iE 'taler-wallet|prepareBank|preparePay|confirm|Error|success' "$OUT_DIR/logcat.txt" \ - | tail -100 >"$OUT_DIR/logcat-taler.txt" || true +"${ADB[@]}" logcat -d -t 500 >"$OUT_DIR/logcat.txt" || true +grep -iE 'taler-wallet|prepareBank|acceptWithdrawal|confirmWithdrawal|preparePay|confirmPay|Error|success|withdraw|pay' \ + "$OUT_DIR/logcat.txt" | tail -100 >"$OUT_DIR/logcat-taler.txt" || true +if [ "$DO_WITHDRAW" = "1" ] && grep -qE 'prepareBankIntegratedWithdrawal|acceptWithdrawal|confirmWithdrawal' \ + "$OUT_DIR/logcat.txt" 2>/dev/null; then + WITHDRAW_OK=1 +fi +if [ "$DO_PAY" = "1" ] && [ -n "$PAY_URI" ] && \ + grep -qiE 'preparePay|confirmPay|preparePurchase|checkPay|pay-template|PayForTemplate' \ + "$OUT_DIR/logcat.txt" 2>/dev/null; then + PAY_OK=1 +fi + +rc=0 echo -echo "=== GUI smoke summary ($STACK) ===" -echo "package: $PKG" -echo "shortcut withdraw URI: delivered" -echo "GUI withdraw: see $OUT_DIR/gui-withdraw-status.json + screenshots" -[ -n "$PAY_URI" ] && echo "shortcut pay URI: delivered" -[ -n "$PAY_URI" ] && echo "GUI pay: see $OUT_DIR/gui-pay-status.json + screenshots" -if grep -q prepareBankIntegratedWithdrawal "$OUT_DIR/logcat.txt" 2>/dev/null; then - echo "wallet-core: prepareBankIntegratedWithdrawal YES" -else - echo "wallet-core: prepareBankIntegratedWithdrawal no (ANR/slow UI?)" +echo "=== GUI smoke summary ($STACK) — withdraw + pay ===" +echo "package: $PKG DO_WITHDRAW=$DO_WITHDRAW DO_PAY=$DO_PAY STRICT=$SMOKE_STRICT" +if [ "$DO_WITHDRAW" = "1" ]; then + echo "withdraw URI: delivered → $OUT_DIR/gui-withdraw-status.json" + echo "withdraw wallet-core: $([ "$WITHDRAW_OK" = 1 ] && echo YES || echo NO)" + if [ "$SMOKE_STRICT" = "1" ] && [ "$WITHDRAW_OK" != "1" ]; then + echo "FAIL: strict withdraw missing"; rc=11 + fi fi -if grep -qiE 'preparePay|confirmPay|pay-template|preparePurchase' "$OUT_DIR/logcat.txt" 2>/dev/null; then - echo "wallet-core: pay path YES" -else - echo "wallet-core: pay path not confirmed in logcat" +if [ "$DO_PAY" = "1" ]; then + echo "pay URI: ${PAY_URI:-none} → $OUT_DIR/gui-pay-status.json" + echo "pay wallet-core: $([ "$PAY_OK" = 1 ] && echo YES || echo NO)" + if [ "$SMOKE_STRICT" = "1" ] && [ "$PAY_OK" != "1" ]; then + echo "FAIL: strict pay missing"; [ "$rc" -eq 0 ] && rc=13 + fi +fi +if grep -qiE "isn't responding|not responding" "$OUT_DIR/logcat.txt" 2>/dev/null \ + || grep -rqiE "isn't responding" "$OUT_DIR/gui-withdraw" "$OUT_DIR/gui-pay" 2>/dev/null; then + echo "FAIL: ANR"; rc=10 fi echo "artifacts: $OUT_DIR/" ls -la "$OUT_DIR" | sed 's/^/ /' echo "Documented shortcuts → GUI-AUTOMATION-NOTES.md" +exit "$rc" diff --git a/scripts/taler-monitoring/android-test/run-android-pay-smoke.sh b/scripts/taler-monitoring/android-test/run-android-pay-smoke.sh index a5a85b5..48ec49f 100755 --- a/scripts/taler-monitoring/android-test/run-android-pay-smoke.sh +++ b/scripts/taler-monitoring/android-test/run-android-pay-smoke.sh @@ -1,22 +1,20 @@ #!/usr/bin/env bash -# Android wallet smoke: install APK (published F-Droid **or** from-source), open -# withdraw/pay URIs via adb. +# Android wallet smoke: install APK, then **withdraw (Abheben)** and **pay** via +# deep-link + best-effort UI taps. Both legs are first-class (not pay-only). # -# No root required if: -# - adb is available (package android-tools-adb / platform-tools) -# - a device or emulator is connected (`adb devices` shows "device") -# - optional user-local SDK under $HOME/Android/Sdk (emulator) +# Flow (default both on): +# 1) WITHDRAW taler://withdraw/… from bank demo-withdraw.json + confirm taps +# 2) PAY taler://pay… / pay-template + optional taps +# +# Env: +# DO_WITHDRAW=1 (default) DO_PAY=1 (default) +# SMOKE_STRICT=1 → require wallet-core withdraw (+ pay if DO_PAY) + no ANR +# WITHDRAW_GUI_ROUNDS multi-round Abheben taps (default 6) # # Usage: -# ./android-test/run-android-pay-smoke.sh # published F-Droid APK +# ./android-test/run-android-pay-smoke.sh # STACK=goa ./android-test/run-android-pay-smoke.sh -# STACK=stage ./android-test/run-android-pay-smoke.sh -# # from-source (built first): -# ./android-test/run-android-build-and-smoke.sh -# APK_PATH=/path/to.apk PKG=net.taler.wallet.fdroid.debug ./run-android-pay-smoke.sh -# -# Published APK (default): F-Droid Taler Wallet 1.6.1 (854) -# https://f-droid.org/packages/net.taler.wallet.fdroid/ +# DO_PAY=0 ./android-test/run-android-pay-smoke.sh # withdraw-only # set -euo pipefail ROOT=$(cd "$(dirname "$0")" && pwd) @@ -98,32 +96,63 @@ fi echo "apk: $APK_PATH ($(wc -c <"$APK_PATH") bytes)" echo "pkg: $PKG" +: "${DO_WITHDRAW:=1}" +: "${DO_PAY:=1}" +: "${WITHDRAW_GUI_ROUNDS:=6}" +: "${PAY_GUI_ROUNDS:=4}" +: "${SMOKE_STRICT:=0}" + "${ADB[@]}" wait-for-device "${ADB[@]}" install -r "$APK_PATH" "${ADB[@]}" shell settings put global window_animation_scale 0 || true "${ADB[@]}" shell settings put global transition_animation_scale 0 || true "${ADB[@]}" shell settings put global animator_duration_scale 0 || true -# Withdraw URI from live mint -WURI=$(curl -sfS -m 25 "$WITHDRAW_JSON" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("taler_withdraw_uri") or d.get("qr_payload") or "")') -if [ -z "$WURI" ]; then - echo "FAIL: no taler_withdraw_uri from $WITHDRAW_JSON" >&2 - exit 4 -fi -echo "withdraw: $WURI" -echo "$WURI" >"$OUT_DIR/last-withdraw.uri" - "${ADB[@]}" shell am force-stop "$PKG" 2>/dev/null || true "${ADB[@]}" logcat -c 2>/dev/null || true -"${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$WURI" 2>&1 | tee "$OUT_DIR/start-withdraw.txt" -sleep 12 -"${ADB[@]}" exec-out screencap -p >"$OUT_DIR/01-after-withdraw-uri.png" || true -"${ADB[@]}" shell uiautomator dump /sdcard/ui.xml 2>/dev/null || true -"${ADB[@]}" pull /sdcard/ui.xml "$OUT_DIR/01-ui.xml" 2>/dev/null || true +WURI="" +PAY_URI="" +WITHDRAW_OK=0 +PAY_OK=0 -# Best-effort: tap common confirm labels -python3 - "$SERIAL" "$OUT_DIR/01-ui.xml" <<'PY' || true +# ═══════════════════════════════════════════════════════════════ +# 1) WITHDRAW / Abheben (required by default — not pay-only) +# ═══════════════════════════════════════════════════════════════ +if [ "$DO_WITHDRAW" = "1" ]; then + echo + echo "======== 1 WITHDRAW (Abheben) ========" + WURI=$(curl -sfS -m 25 "$WITHDRAW_JSON" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("taler_withdraw_uri") or d.get("qr_payload") or "")') + if [ -z "$WURI" ]; then + echo "FAIL: no taler_withdraw_uri from $WITHDRAW_JSON" >&2 + exit 4 + fi + echo "withdraw URI: $WURI" + echo "$WURI" >"$OUT_DIR/last-withdraw.uri" + + "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$WURI" 2>&1 | tee "$OUT_DIR/start-withdraw.txt" + sleep 6 + "${ADB[@]}" exec-out screencap -p >"$OUT_DIR/01-after-withdraw-uri.png" || true + "${ADB[@]}" shell uiautomator dump /sdcard/ui.xml 2>/dev/null || true + "${ADB[@]}" pull /sdcard/ui.xml "$OUT_DIR/01-ui.xml" 2>/dev/null || true + + # Multi-round confirm / Abheben / ToS (same idea as GUI smoke) + if [ -f "$ROOT/lib_ui.py" ]; then + python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$WITHDRAW_GUI_ROUNDS" 2 <<'PY' || true +import json, sys, importlib.util +from pathlib import Path +lib_path, serial, out, rounds, sleep_s = ( + Path(sys.argv[1]), sys.argv[2], Path(sys.argv[3]), int(sys.argv[4]), float(sys.argv[5]) +) +spec = importlib.util.spec_from_file_location("lib_ui", lib_path) +lib = importlib.util.module_from_spec(spec) +spec.loader.exec_module(lib) +st = lib.gui_drive(serial, out / "gui-withdraw", rounds=rounds, sleep_s=sleep_s, phase="wd") +(out / "gui-withdraw-status.json").write_text(json.dumps(st, indent=2)) +print("withdraw GUI taps:", st.get("taps"), "anr:", st.get("anr_dismissals")) +PY + else + python3 - "$SERIAL" "$OUT_DIR/01-ui.xml" <<'PY' || true import re, subprocess, sys serial, path = sys.argv[1], sys.argv[2] try: @@ -131,31 +160,46 @@ try: except FileNotFoundError: raise SystemExit(0) labels = [ - "Confirm", "Withdraw", "Accept", "Continue", "OK", - "I accept", "Agree", "Bestätigen", "Abheben", "Akzeptieren", + "Confirm", "Confirm withdrawal", "Withdraw", "Accept", "Continue", "OK", + "I accept", "Agree", "Bestätigen", "Abheben", "Akzeptieren", "Retirer", ] for label in labels: for pat in ( r'text="%s"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"' % re.escape(label), r'bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"[^>]*text="%s"' % re.escape(label), ): - for m in re.finditer(pat, xml): + for m in re.finditer(pat, xml, flags=re.I): x1, y1, x2, y2 = map(int, m.groups()) x, y = (x1 + x2) // 2, (y1 + y2) // 2 print(f"tap {label} @ {x},{y}") subprocess.run(["adb", "-s", serial, "shell", "input", "tap", str(x), str(y)], check=False) PY -sleep 8 -"${ADB[@]}" exec-out screencap -p >"$OUT_DIR/02-after-confirm-tap.png" || true + sleep 8 + fi + "${ADB[@]}" exec-out screencap -p >"$OUT_DIR/02-after-withdraw-taps.png" || true + # Mid-flow logcat: withdraw must register before pay + "${ADB[@]}" logcat -d -t 400 >"$OUT_DIR/logcat-after-withdraw.txt" || true + if grep -qE 'prepareBankIntegratedWithdrawal|acceptWithdrawal|confirmWithdrawal' \ + "$OUT_DIR/logcat-after-withdraw.txt" 2>/dev/null; then + WITHDRAW_OK=1 + echo "withdraw: wallet-core activity YES" + else + echo "withdraw: wallet-core activity NOT seen yet (onboarding/ANR/slow?)" + fi +else + echo "======== 1 WITHDRAW skipped (DO_WITHDRAW=0) ========" +fi -# Optional public pay template (stage fixed product / GOA shop if amount known) -# Stage: open monnaies shop is browser-side; deep link pay after template POST if available. -PAY_URI="" -if [ "$STACK" = "stage" ]; then - # fixed template panier-legumes → taler://pay/… - if RESP=$(curl -sfS -m 20 -X POST -H 'Content-Type: application/json' -d '{}' \ - "$MERCHANT/instances/fermes-des-collines/templates/panier-legumes" 2>/dev/null); then - PAY_URI=$(printf '%s' "$RESP" | python3 -c ' +# ═══════════════════════════════════════════════════════════════ +# 2) PAY (after withdraw; still first-class, but second leg) +# ═══════════════════════════════════════════════════════════════ +if [ "$DO_PAY" = "1" ]; then + echo + echo "======== 2 PAY (Bezahlen) ========" + if [ "$STACK" = "stage" ]; then + if RESP=$(curl -sfS -m 20 -X POST -H 'Content-Type: application/json' -d '{}' \ + "$MERCHANT/instances/fermes-des-collines/templates/panier-legumes" 2>/dev/null); then + PAY_URI=$(printf '%s' "$RESP" | python3 -c ' import json,sys d=json.load(sys.stdin) oid=d.get("order_id") or "" @@ -163,58 +207,102 @@ tok=d.get("token") or "" if oid and tok: print("taler://pay/stage.monnaie.lefrancpaysan.ch/instances/fermes-des-collines/%s/?c=%s"%(oid,tok)) ' 2>/dev/null || true) + fi + elif [ "$STACK" = "goa" ]; then + PAY_URI="taler://pay-template/taler.hacktivism.ch/instances/goa-shop/paivana" fi -elif [ "$STACK" = "goa" ]; then - # Paivana pay-template deep link (wallet prepares order) - PAY_URI="taler://pay-template/taler.hacktivism.ch/instances/goa-shop/paivana" -fi -if [ -n "$PAY_URI" ]; then - echo "pay: $PAY_URI ($PAY_HINT)" - echo "$PAY_URI" >"$OUT_DIR/last-pay.uri" - "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$PAY_URI" 2>&1 | tee "$OUT_DIR/start-pay.txt" - sleep 12 - "${ADB[@]}" exec-out screencap -p >"$OUT_DIR/03-after-pay-uri.png" || true + if [ -n "$PAY_URI" ]; then + echo "pay URI: $PAY_URI ($PAY_HINT)" + echo "$PAY_URI" >"$OUT_DIR/last-pay.uri" + "${ADB[@]}" shell am start -a android.intent.action.VIEW -d "$PAY_URI" 2>&1 | tee "$OUT_DIR/start-pay.txt" + sleep 6 + if [ -f "$ROOT/lib_ui.py" ]; then + python3 - "$ROOT/lib_ui.py" "$SERIAL" "$OUT_DIR" "$PAY_GUI_ROUNDS" 2 <<'PY' || true +import json, sys, importlib.util +from pathlib import Path +lib_path, serial, out, rounds, sleep_s = ( + Path(sys.argv[1]), sys.argv[2], Path(sys.argv[3]), int(sys.argv[4]), float(sys.argv[5]) +) +spec = importlib.util.spec_from_file_location("lib_ui", lib_path) +lib = importlib.util.module_from_spec(spec) +spec.loader.exec_module(lib) +st = lib.gui_drive(serial, out / "gui-pay", rounds=rounds, sleep_s=sleep_s, phase="pay") +(out / "gui-pay-status.json").write_text(json.dumps(st, indent=2)) +print("pay GUI taps:", st.get("taps"), "anr:", st.get("anr_dismissals")) +PY + fi + "${ADB[@]}" exec-out screencap -p >"$OUT_DIR/03-after-pay-uri.png" || true + else + echo "pay: (no public template URI for stack=$STACK)" + fi else - echo "pay: (skipped — no public template order)" + echo "======== 2 PAY skipped (DO_PAY=0) ========" fi -# Evidence from logcat -"${ADB[@]}" logcat -d -t 300 >"$OUT_DIR/logcat.txt" || true -grep -iE 'taler-wallet|prepareBank|preparePay|confirm|error|Error|success' "$OUT_DIR/logcat.txt" \ - | tail -80 >"$OUT_DIR/logcat-taler.txt" || true +# Final evidence +"${ADB[@]}" logcat -d -t 500 >"$OUT_DIR/logcat.txt" || true +grep -iE 'taler-wallet|prepareBank|acceptWithdrawal|confirmWithdrawal|preparePay|confirmPay|error|Error|success|withdraw|pay' \ + "$OUT_DIR/logcat.txt" | tail -100 >"$OUT_DIR/logcat-taler.txt" || true + +# Re-evaluate withdraw/pay from full logcat +if [ "$DO_WITHDRAW" = "1" ]; then + if grep -qE 'prepareBankIntegratedWithdrawal|acceptWithdrawal|confirmWithdrawal' \ + "$OUT_DIR/logcat.txt" 2>/dev/null; then + WITHDRAW_OK=1 + fi +fi +if [ "$DO_PAY" = "1" ] && [ -n "$PAY_URI" ]; then + if grep -qiE 'preparePay|confirmPay|preparePurchase|checkPay|pay-template|PayForTemplate' \ + "$OUT_DIR/logcat.txt" 2>/dev/null; then + PAY_OK=1 + fi +fi -# SMOKE_STRICT=1 (matrix gate default): ANR or missing wallet-core withdraw = hard fail -: "${SMOKE_STRICT:=0}" rc=0 echo -echo "=== summary ===" -echo "package installed: $PKG" -echo "withdraw intent delivered: $WURI" -if grep -qiE "isn't responding|isn.t responding|System UI isn" "$OUT_DIR/01-ui.xml" 2>/dev/null \ - || grep -qiE "isn't responding|not responding" "$OUT_DIR/logcat.txt" 2>/dev/null; then - echo "FAIL: System UI / app ANR (host too small or emulator hung)" - rc=10 -fi -if grep -q 'prepareBankIntegratedWithdrawal' "$OUT_DIR/logcat.txt" 2>/dev/null; then - echo "wallet-core: prepareBankIntegratedWithdrawal seen (wallet accepted withdraw URI)" -else - echo "wallet-core: prepareBankIntegratedWithdrawal NOT seen (UI may still show onboarding/ANR on small RAM)" - if [ "$SMOKE_STRICT" = "1" ]; then - echo "FAIL: SMOKE_STRICT=1 requires prepareBankIntegratedWithdrawal in logcat" - rc=11 - fi -fi -if [ -n "$PAY_URI" ]; then - if grep -qiE 'preparePay|confirmPay|pay-template|preparePurchase' "$OUT_DIR/logcat.txt" 2>/dev/null; then - echo "wallet-core: pay path activity seen" +echo "=== summary (withdraw + pay) ===" +echo "package: $PKG stack: $STACK" +echo "DO_WITHDRAW=$DO_WITHDRAW DO_PAY=$DO_PAY SMOKE_STRICT=$SMOKE_STRICT" +if [ "$DO_WITHDRAW" = "1" ]; then + echo "withdraw URI: ${WURI:-(none)}" + if [ "$WITHDRAW_OK" = "1" ]; then + echo "withdraw wallet-core: YES" else - echo "wallet-core: pay path not confirmed in logcat (check $OUT_DIR/03-after-pay-uri.png)" + echo "withdraw wallet-core: NO" + if [ "$SMOKE_STRICT" = "1" ]; then + echo "FAIL: SMOKE_STRICT requires withdraw (prepareBank/accept/confirm) in logcat" + rc=11 + fi fi fi +if [ "$DO_PAY" = "1" ]; then + echo "pay URI: ${PAY_URI:-(none)}" + if [ -z "$PAY_URI" ]; then + echo "pay: no URI (template/API)" + if [ "$SMOKE_STRICT" = "1" ]; then + echo "FAIL: SMOKE_STRICT requires a pay URI when DO_PAY=1" + rc=12 + fi + elif [ "$PAY_OK" = "1" ]; then + echo "pay wallet-core: YES" + else + echo "pay wallet-core: NO (intent may still have been delivered)" + if [ "$SMOKE_STRICT" = "1" ]; then + echo "FAIL: SMOKE_STRICT requires pay path activity in logcat" + [ "$rc" -eq 0 ] && rc=13 + fi + fi +fi +if grep -qiE "isn't responding|isn.t responding|System UI isn" "$OUT_DIR/01-ui.xml" 2>/dev/null \ + || grep -qiE "isn't responding|not responding" "$OUT_DIR/logcat.txt" 2>/dev/null \ + || grep -rqiE "isn't responding" "$OUT_DIR/gui-withdraw" 2>/dev/null; then + echo "FAIL: System UI / app ANR" + rc=10 +fi echo "artifacts: $OUT_DIR/" ls -la "$OUT_DIR" | sed 's/^/ /' echo -echo "NOTE: Full unattended UI payment needs a responsive device/emulator (≥6–8 GiB host RAM recommended)." -echo "This smoke proves: APK install + deep-link delivery on stack=$STACK (strict=$SMOKE_STRICT rc=$rc)." +echo "NOTE: Full unattended Abheben+Bezahlen needs responsive device (≥6–8 GiB host RAM)." +echo "Smoke legs: withdraw=$( [ "$DO_WITHDRAW" = 1 ] && echo on || echo off) pay=$( [ "$DO_PAY" = 1 ] && echo on || echo off) rc=$rc" exit "$rc"