101 lines
5.0 KiB
Bash
Executable File
101 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# qa/entrypoint_smoke.sh — Cold-start / entry-point launch-integrity smoke.
|
||
#
|
||
# WHY THIS EXISTS: a whole bug class makes the app "open and immediately close" on a REAL notification
|
||
# cold-start (e.g. the splash-exit `provider.iconView` NPE — the OS hands over a splash with no icon on
|
||
# the notification/PendingIntent launch path). It breaks EVERY notification at once (shared cold-start
|
||
# path) yet is invisible to `adb am start` (different launch path) and to the normal launcher icon.
|
||
# The only reliable catch is a real push, delivered to a genuinely killed app (`am kill`, NOT
|
||
# `am force-stop` — force-stopped apps are excluded from FCM), tapped from the shade.
|
||
#
|
||
# For each entry the app must: OPEN, STAY UP (process alive, 0 FATAL), and land OFF the launcher.
|
||
# Emulator FCM-to-killed-app is flaky (FcmRetry); undeliverable cases are reported BLOCKED, not FAIL —
|
||
# only an actual open-and-close / crash is a FAIL.
|
||
#
|
||
# Usage: qa/entrypoint_smoke.sh <serial> [recipient_uid]
|
||
# qa/entrypoint_smoke.sh emulator-5556 imDjjOTTQvXGGjyUhUc5JSeHWkU2
|
||
# Env overrides: PKG, SA_JSON, COUPLE_ID.
|
||
# Runtime: ~3–7 min (flaky emulator FCM adds retries). Run in the background and read the matrix.
|
||
# Exit 0 only if zero FAIL (BLOCK = undeliverable push, environmental — rerun, not an app bug).
|
||
set -u
|
||
SERIAL="${1:-emulator-5554}"
|
||
RUID="${2:-}"
|
||
PKG="${PKG:-closer.app}"
|
||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||
export NODE_PATH="${NODE_PATH:-$HERE/../functions/node_modules}"
|
||
PASS=0; FAIL=0; BLOCKED=0
|
||
adbs() { adb -s "$SERIAL" "$@"; }
|
||
alive() { adbs shell pidof "$PKG" 2>/dev/null | tr -d '\r'; }
|
||
crashed() { adbs logcat -d -t 1500 2>/dev/null | grep -E "FATAL EXCEPTION|getIconView|Force finishing.*$PKG" | grep -v AppsFilter | head -3; }
|
||
on_launcher() { adbs shell dumpsys activity activities 2>/dev/null | grep -m1 "ResumedActivity" | grep -qi "launcher"; }
|
||
ok() { echo " PASS $1"; PASS=$((PASS+1)); }
|
||
bad() { echo " FAIL $1 [$2]"; FAIL=$((FAIL+1)); }
|
||
blkd() { echo " BLOCK $1 [$2]"; BLOCKED=$((BLOCKED+1)); }
|
||
|
||
verify() { # <label> — the real check: app opened and STAYED (no crash/close)
|
||
local c a; c="$(crashed)"; a="$(alive)"
|
||
if [ -n "$a" ] && [ -z "$c" ] && ! on_launcher; then ok "$1"
|
||
else bad "$1" "alive='${a:-none}' crash='${c:-none}' onLauncher=$(on_launcher && echo yes || echo no)"; fi
|
||
}
|
||
|
||
settle_fcm() { adbs shell monkey -p "$PKG" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1; sleep 15; adbs shell input keyevent KEYCODE_HOME >/dev/null 2>&1; sleep 1; }
|
||
|
||
# find the QA-SMOKE:<type> notification in the shade; echoes "x y" if present
|
||
find_notif() { # <type>
|
||
adbs shell cmd statusbar expand-notifications >/dev/null 2>&1; sleep 2
|
||
adbs shell uiautomator dump /sdcard/sm.xml >/dev/null 2>&1
|
||
adbs pull /sdcard/sm.xml "/tmp/sm_${SERIAL}.xml" >/dev/null 2>&1
|
||
python3 - "/tmp/sm_${SERIAL}.xml" "$1" <<'PY'
|
||
import re,sys
|
||
xml=open(sys.argv[1],encoding="utf-8",errors="ignore").read(); want="QA-SMOKE:"+sys.argv[2]
|
||
for n in re.findall(r'<node[^>]*>',xml):
|
||
t=re.search(r'text="([^"]*)"',n)
|
||
if t and want in t.group(1):
|
||
b=re.search(r'bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',n)
|
||
if b: print((int(b.group(1))+int(b.group(3)))//2,(int(b.group(2))+int(b.group(4)))//2); break
|
||
PY
|
||
}
|
||
|
||
echo "== entrypoint smoke :: $SERIAL =="
|
||
settle_fcm # clear 'stopped' flag, register token, give FCM time to connect
|
||
|
||
# 1) Launcher icon cold-start.
|
||
adbs shell am kill "$PKG" >/dev/null 2>&1; sleep 2
|
||
adbs logcat -c 2>/dev/null
|
||
adbs shell monkey -p "$PKG" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1
|
||
sleep 6
|
||
verify "launcher cold-start"
|
||
|
||
# 2) Notification cold-starts (real push -> killed app -> tap).
|
||
if [ -n "$RUID" ]; then
|
||
for CASE in \
|
||
"partner_started_game|game_type=this_or_that" \
|
||
"partner_completed_part|game_type=this_or_that" \
|
||
"partner_finished_game|game_type=wheel" \
|
||
"chat_message|conversation_id=main" \
|
||
"partner_answered|" ; do
|
||
TYPE="${CASE%%|*}"; EXTRAS="${CASE#*|}"
|
||
XY=""; ATTEMPT=0
|
||
while [ -z "$XY" ] && [ "$ATTEMPT" -lt 2 ]; do
|
||
ATTEMPT=$((ATTEMPT+1))
|
||
[ "$ATTEMPT" -eq 2 ] && settle_fcm # 2nd try: relaunch to kick the FCM transport
|
||
adbs shell am kill "$PKG" >/dev/null 2>&1; sleep 2
|
||
adbs logcat -c 2>/dev/null
|
||
OUT="$(node "$HERE/qa_push.js" "$RUID" "$TYPE" $EXTRAS 2>&1)"
|
||
if ! echo "$OUT" | grep -q "^SENT"; then bad "notif:$TYPE (send)" "$OUT"; break; fi
|
||
# poll for delivery up to ~24s
|
||
for _ in 1 2 3 4 5 6; do sleep 4; XY="$(find_notif "$TYPE")"; [ -n "$XY" ] && break; adbs shell cmd statusbar collapse >/dev/null 2>&1; done
|
||
done
|
||
if echo "${OUT:-}" | grep -q "^SENT" && [ -z "$XY" ]; then blkd "notif:$TYPE" "not delivered (flaky emulator FCM); rerun"; continue; fi
|
||
[ -z "$XY" ] && continue
|
||
adbs shell input tap $XY >/dev/null 2>&1
|
||
sleep 8
|
||
verify "notif:$TYPE tap -> opens & stays"
|
||
done
|
||
else
|
||
echo " (notification cases skipped — pass a recipient_uid to run them)"
|
||
fi
|
||
|
||
echo "== result: ${PASS} passed, ${FAIL} failed, ${BLOCKED} blocked =="
|
||
[ "$FAIL" -eq 0 ]
|