#!/usr/bin/env bash # On-device: first closed loop advances Circle quest 0 → 1. # Hold/action steps are covered by JVM CircleQuestTest. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" ADB="${ADB:-adb}" COMP='com.example.crkl/com.example.crkl.accessibility.CrklAccessibilityService' PKG=com.example.crkl die() { echo "FAIL: $*" >&2; exit 1; } pass() { echo "OK: $*"; } "$ADB" devices | awk '/device( |$)/ && $1 !~ /List/{found=1} END{exit !found}' \ || die "no adb device" echo "== smoke quest on $($ADB get-serialno) ==" # Fresh quest prefs (debug builds only — run-as). "$ADB" shell settings put secure accessibility_enabled 1 "$ADB" shell settings put secure enabled_accessibility_services "$COMP" "$ADB" shell am force-stop "$PKG" >/dev/null 2>&1 || true sleep 0.5 "$ADB" shell run-as "$PKG" rm -f shared_prefs/crkl_integrations.xml \ || die "run-as failed — need debug APK" "$ADB" shell settings put secure enabled_accessibility_services "$COMP" "$ADB" shell settings put secure accessibility_enabled 1 sleep 1.5 "$ADB" shell am start -n "$PKG/.fixtures.TestFixturesActivity" >/dev/null sleep 1.5 enter_circle() { "$ADB" logcat -c "$ADB" shell input tap 964 2200 sleep 0.4 if "$ADB" logcat -d -s OverlayView:D | grep -q 'floating button clicked'; then return 0 fi for y in $(seq 2140 20 2280); do for x in $(seq 900 20 1030); do "$ADB" shell input tap "$x" "$y" sleep 0.05 if "$ADB" logcat -d -s OverlayView:D | grep -q 'floating button clicked'; then return 0 fi done done return 1 } draw_loop() { local cx="$1" cy="$2" r="$3" python3 - "$cx" "$cy" "$r" <<'PY' import math, subprocess, sys, time cx, cy, r = map(int, sys.argv[1:4]) adb = lambda *a: subprocess.check_call(["adb", *a]) n = 28 pts = [ ( int(cx + r * math.cos(2 * math.pi * i / n - math.pi / 2)), int(cy + r * math.sin(2 * math.pi * i / n - math.pi / 2)), ) for i in range(n + 1) ] adb("shell", "input", "motionevent", "DOWN", str(pts[0][0]), str(pts[0][1])) for x, y in pts[1:]: adb("shell", "input", "motionevent", "MOVE", str(x), str(y)) adb("shell", "input", "motionevent", "UP", str(pts[-1][0]), str(pts[-1][1])) for _ in range(25): time.sleep(0.3) out = subprocess.check_output(["adb", "logcat", "-d"], text=True) if "extract:" in out or "Selection ready" in out or "quest: step=" in out: break PY } "$ADB" shell uiautomator dump /sdcard/crkl-smoke.xml >/dev/null "$ADB" pull /sdcard/crkl-smoke.xml /tmp/crkl-smoke-quest.xml >/dev/null eval "$(python3 <<'PY' import re, sys xml = open("/tmp/crkl-smoke-quest.xml").read() m = re.search(r'text="How to circle"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"', xml) if not m: sys.exit("How to circle not on screen") x1, y1, x2, y2 = map(int, m.groups()) cx, cy = (x1 + x2) // 2, (y1 + y2) // 2 r = max(60, min(100, (x2 - x1) // 2 + 30, (y2 - y1) // 2 + 40)) print(f"HEAD_CX={cx}; HEAD_CY={cy}; HEAD_R={r}") PY )" enter_circle || die "FAB not clickable — is Circle Overlay ON?" draw_loop "$HEAD_CX" "$HEAD_CY" "$HEAD_R" sleep 1 logs="$("$ADB" logcat -d)" echo "$logs" | grep -q 'quest: step=1' || die "quest did not advance to step 1 (check logcat)" pass "log: quest step=1 after first circle" prefs="$("$ADB" shell run-as "$PKG" cat shared_prefs/crkl_integrations.xml 2>/dev/null || true)" echo "$prefs" | grep -q 'name="quest_step" value="1"' \ || die "prefs quest_step != 1 (got: $(echo "$prefs" | tr '\n' ' ' | head -c 200))" pass "prefs: quest_step=1" # Skip path via clearing + writing would need app code; JVM covers skip(). echo "== smoke quest PASSED =="