Boot recovery runs twice on Android (both LOCKED_BOOT_COMPLETED and BOOT_COMPLETED fire). The first run marks missed alarms (missed=1), while the second run only reschedules future alarms (missed=0). TEST 2 was incorrectly extracting the last entry (missed=0) instead of the first entry (missed=1), causing false test failures. Changes: - Extract first missed count using head -n1 instead of tail -n1 - Add comment explaining why first entry is needed - Test now correctly validates missed alarm detection Fixes test validation for past alarm detection during boot recovery.
438 lines
15 KiB
Bash
Executable File
438 lines
15 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
set -euo pipefail
|
||
|
||
# ========================================
|
||
# Phase 3 Testing Script – Boot Recovery
|
||
# ========================================
|
||
|
||
# Source shared library
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||
|
||
# Phase 3 specific configuration
|
||
# Log tags / patterns (matched to actual ReactivationManager logs)
|
||
BOOT_SCENARIO_VALUE="BOOT"
|
||
NONE_SCENARIO_VALUE="NONE"
|
||
|
||
# Allow selecting specific tests on the command line (e.g. ./test-phase3.sh 1 3)
|
||
SELECTED_TESTS=()
|
||
|
||
# Phase 3 specific: override extract_scenario_from_logs to handle boot recovery
|
||
extract_scenario_from_logs() {
|
||
local logs="$1"
|
||
local scen
|
||
# Looks for "Detected scenario: BOOT" or "Starting boot recovery" format
|
||
if echo "$logs" | grep -qi "Starting boot recovery\|boot recovery"; then
|
||
echo "$BOOT_SCENARIO_VALUE"
|
||
else
|
||
# Use shared library function as fallback
|
||
scen="$(grep -oE "${SCENARIO_KEY}[A-Z_]+" <<<"$logs" | tail -n1 | sed "s/${SCENARIO_KEY}//" || true)"
|
||
echo "$scen"
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# TEST 1 – Boot with Future Alarms
|
||
# ------------------------------------------------------------------------------
|
||
|
||
test1_boot_future_alarms() {
|
||
section "TEST 1: Boot with Future Alarms"
|
||
|
||
echo "Purpose: Verify alarms are recreated on boot when schedules have future run times."
|
||
|
||
pause
|
||
|
||
substep "Step 1: Launch app & check plugin status"
|
||
launch_app
|
||
|
||
ui_prompt "In the app UI, verify plugin status:\n\n ⚙️ Plugin Settings: ✅ Configured\n 🔌 Native Fetcher: ✅ Configured\n\nIf either shows ❌ or 'Not configured', click 'Configure Plugin', wait until both are ✅, then press Enter."
|
||
|
||
ui_prompt "Now schedule at least one future notification (e.g., click 'Test Notification' to schedule for a few minutes in the future)."
|
||
|
||
substep "Step 2: Verify alarms are scheduled"
|
||
show_alarms
|
||
local before_count system_before
|
||
before_count="$(get_plugin_alarm_count)"
|
||
system_before="$(get_system_alarm_count)"
|
||
info "Plugin alarms before reboot: $before_count (expected: 1)"
|
||
info "System/other alarms: $system_before (for context)"
|
||
|
||
if [[ "$before_count" -eq 0 ]]; then
|
||
warn "No plugin alarms found before reboot; TEST 1 may not be meaningful."
|
||
elif [[ "$before_count" -eq 1 ]]; then
|
||
ok "Single plugin alarm confirmed (one per day)"
|
||
else
|
||
warn "Found $before_count plugin alarms (expected: 1)"
|
||
fi
|
||
|
||
pause
|
||
|
||
substep "Step 3: Reboot emulator"
|
||
warn "The emulator will reboot now. This will take 30-60 seconds."
|
||
pause
|
||
reboot_emulator
|
||
|
||
substep "Step 4: Collect boot recovery logs"
|
||
info "Collecting recovery logs from boot..."
|
||
sleep 2 # Give recovery a moment to complete
|
||
local logs
|
||
logs="$(get_recovery_logs)"
|
||
echo "$logs"
|
||
|
||
local missed rescheduled verified errors scenario
|
||
missed="$(extract_field_from_logs "$logs" "missed")"
|
||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||
verified="$(extract_field_from_logs "$logs" "verified")"
|
||
errors="$(extract_field_from_logs "$logs" "errors")"
|
||
scenario="$(extract_scenario_from_logs "$logs")"
|
||
|
||
echo
|
||
info "Parsed recovery summary:"
|
||
echo " scenario = ${scenario:-<none>}"
|
||
echo " missed = ${missed}"
|
||
echo " rescheduled= ${rescheduled}"
|
||
echo " verified = ${verified}"
|
||
echo " errors = ${errors}"
|
||
echo
|
||
|
||
if [[ "$errors" -gt 0 ]]; then
|
||
error "Recovery reported errors>0 (errors=$errors)"
|
||
fi
|
||
|
||
substep "Step 5: Verify alarms were recreated"
|
||
show_alarms
|
||
local after_count system_after
|
||
after_count="$(get_plugin_alarm_count)"
|
||
system_after="$(get_system_alarm_count)"
|
||
info "Plugin alarms after boot: $after_count (expected: 1)"
|
||
info "System/other alarms: $system_after (for context)"
|
||
|
||
if [[ "$scenario" == "$BOOT_SCENARIO_VALUE" && "$rescheduled" -gt 0 ]]; then
|
||
ok "TEST 1 PASSED: Boot recovery detected and alarms rescheduled (scenario=$scenario, rescheduled=$rescheduled)."
|
||
elif echo "$logs" | grep -qi "Starting boot recovery\|boot recovery"; then
|
||
if [[ "$rescheduled" -gt 0 ]]; then
|
||
ok "TEST 1 PASSED: Boot recovery ran and alarms rescheduled (rescheduled=$rescheduled)."
|
||
else
|
||
warn "TEST 1: Boot recovery ran but rescheduled=0. Check implementation or logs."
|
||
fi
|
||
else
|
||
warn "TEST 1: Boot recovery not clearly detected. Review logs and boot receiver implementation."
|
||
info "Scenario detected: ${scenario:-<none>}, rescheduled=$rescheduled"
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# TEST 2 – Boot with Past Alarms
|
||
# ------------------------------------------------------------------------------
|
||
|
||
test2_boot_past_alarms() {
|
||
section "TEST 2: Boot with Past Alarms"
|
||
|
||
echo "Purpose: Verify missed alarms are detected and next occurrence is scheduled on boot."
|
||
|
||
pause
|
||
|
||
substep "Step 1: Launch app & ensure plugin configured"
|
||
launch_app
|
||
|
||
ui_prompt "In the app UI, verify plugin status:\n\n ⚙️ Plugin Settings: ✅ Configured\n 🔌 Native Fetcher: ✅ Configured\n\nIf needed, click 'Configure Plugin', then press Enter."
|
||
|
||
ui_prompt "Click 'Test Notification' to schedule a notification for 2 minutes in the future.\n\nAfter scheduling, we'll wait for the alarm time to pass, then reboot."
|
||
|
||
substep "Step 2: Wait for alarm time to pass"
|
||
info "Waiting 3 minutes for scheduled alarm time to pass..."
|
||
warn "You can manually advance system time if needed (requires root/emulator)"
|
||
sleep 180 # Wait 3 minutes
|
||
|
||
substep "Step 3: Verify alarm time has passed"
|
||
info "Alarm time should now be in the past"
|
||
show_alarms
|
||
|
||
pause
|
||
|
||
substep "Step 4: Reboot emulator"
|
||
warn "The emulator will reboot now. This will take 30-60 seconds."
|
||
pause
|
||
reboot_emulator
|
||
|
||
substep "Step 5: Collect boot recovery logs"
|
||
info "Collecting recovery logs from boot..."
|
||
sleep 2
|
||
local logs
|
||
logs="$(get_recovery_logs)"
|
||
echo "$logs"
|
||
|
||
local missed rescheduled verified errors scenario
|
||
# For TEST 2, we need the FIRST entry (which has missed count) not the last
|
||
# Boot recovery runs twice (LOCKED_BOOT_COMPLETED and BOOT_COMPLETED)
|
||
# First run marks missed alarms, second run only reschedules (missed=0)
|
||
missed="$(echo "$logs" | grep -E "missed=" | sed -E "s/.*missed=([0-9]+).*/\1/" | head -n1 || echo "0")"
|
||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||
verified="$(extract_field_from_logs "$logs" "verified")"
|
||
errors="$(extract_field_from_logs "$logs" "errors")"
|
||
scenario="$(extract_scenario_from_logs "$logs")"
|
||
|
||
echo
|
||
info "Parsed recovery summary:"
|
||
echo " scenario = ${scenario:-<none>}"
|
||
echo " missed = ${missed}"
|
||
echo " rescheduled= ${rescheduled}"
|
||
echo " verified = ${verified}"
|
||
echo " errors = ${errors}"
|
||
echo
|
||
|
||
if [[ "$errors" -gt 0 ]]; then
|
||
error "Recovery reported errors>0 (errors=$errors)"
|
||
fi
|
||
|
||
if [[ "$missed" -ge 1 && "$rescheduled" -ge 1 ]]; then
|
||
ok "TEST 2 PASSED: Past alarms detected and next occurrence scheduled (missed=$missed, rescheduled=$rescheduled)."
|
||
elif [[ "$missed" -ge 1 ]]; then
|
||
warn "TEST 2: Past alarms detected (missed=$missed) but rescheduled=$rescheduled. Check reschedule logic."
|
||
else
|
||
warn "TEST 2: No missed alarms detected. Verify alarm time actually passed before reboot."
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# TEST 3 – Boot with No Schedules
|
||
# ------------------------------------------------------------------------------
|
||
|
||
test3_boot_no_schedules() {
|
||
section "TEST 3: Boot with No Schedules"
|
||
|
||
echo "Purpose: Verify boot recovery handles empty database gracefully."
|
||
|
||
pause
|
||
|
||
substep "Step 1: Uninstall app to clear DB/state"
|
||
set +e
|
||
$ADB_BIN uninstall "$APP_ID" >/dev/null 2>&1
|
||
set -e
|
||
ok "App uninstalled (state cleared)"
|
||
|
||
substep "Step 2: Reinstall app"
|
||
if $ADB_BIN install -r "$APK_PATH"; then
|
||
ok "App installed"
|
||
else
|
||
error "Reinstall failed"
|
||
exit 1
|
||
fi
|
||
|
||
info "Clearing logcat..."
|
||
$ADB_BIN logcat -c
|
||
ok "Logs cleared"
|
||
|
||
pause
|
||
|
||
substep "Step 3: Reboot emulator WITHOUT scheduling anything"
|
||
warn "Do NOT schedule any notifications. The app should have no schedules in the database."
|
||
warn "The emulator will reboot now. This will take 30-60 seconds."
|
||
pause
|
||
reboot_emulator
|
||
|
||
substep "Step 4: Collect boot recovery logs"
|
||
info "Collecting recovery logs from boot..."
|
||
sleep 2
|
||
local logs
|
||
logs="$(get_recovery_logs)"
|
||
echo "$logs"
|
||
|
||
local scenario rescheduled missed
|
||
scenario="$(extract_scenario_from_logs "$logs")"
|
||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||
missed="$(extract_field_from_logs "$logs" "missed")"
|
||
|
||
echo
|
||
info "Parsed recovery summary:"
|
||
echo " scenario = ${scenario:-<none>}"
|
||
echo " rescheduled= ${rescheduled}"
|
||
echo " missed = ${missed}"
|
||
echo
|
||
|
||
if [[ -z "$logs" ]]; then
|
||
ok "TEST 3 PASSED: No recovery logs when there are no schedules (safe behavior)."
|
||
return
|
||
fi
|
||
|
||
if echo "$logs" | grep -qiE "No schedules found|No schedules present"; then
|
||
ok "TEST 3 PASSED: Explicit 'No schedules found' message logged with no rescheduling."
|
||
elif [[ "$scenario" == "$NONE_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
||
ok "TEST 3 PASSED: NONE scenario detected with no rescheduling."
|
||
elif [[ "$rescheduled" -gt 0 ]]; then
|
||
warn "TEST 3: rescheduled>0 on first launch / empty DB. Check that boot recovery isn't misfiring."
|
||
else
|
||
info "TEST 3: Logs present but no rescheduling; review scenario handling to ensure it's explicit about NONE / NO_SCHEDULES."
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# TEST 4 – Silent Boot Recovery (App Never Opened)
|
||
# ------------------------------------------------------------------------------
|
||
|
||
test4_silent_boot_recovery() {
|
||
section "TEST 4: Silent Boot Recovery (App Never Opened)"
|
||
|
||
echo "Purpose: Verify boot recovery occurs even when the app is never opened after reboot."
|
||
|
||
pause
|
||
|
||
substep "Step 1: Launch app & ensure plugin configured"
|
||
launch_app
|
||
|
||
ui_prompt "In the app UI, verify plugin status:\n\n ⚙️ Plugin Settings: ✅ Configured\n 🔌 Native Fetcher: ✅ Configured\n\nIf needed, click 'Configure Plugin', then press Enter."
|
||
|
||
ui_prompt "Click 'Test Notification' to schedule a notification for a few minutes in the future."
|
||
|
||
substep "Step 2: Verify alarms are scheduled"
|
||
show_alarms
|
||
local before_count system_before
|
||
before_count="$(get_plugin_alarm_count)"
|
||
system_before="$(get_system_alarm_count)"
|
||
info "Plugin alarms before reboot: $before_count (expected: 1)"
|
||
info "System/other alarms: $system_before (for context)"
|
||
|
||
if [[ "$before_count" -eq 0 ]]; then
|
||
warn "No plugin alarms found; TEST 4 may not be meaningful."
|
||
elif [[ "$before_count" -eq 1 ]]; then
|
||
ok "Single plugin alarm confirmed (one per day)"
|
||
else
|
||
warn "Found $before_count plugin alarms (expected: 1)"
|
||
fi
|
||
|
||
pause
|
||
|
||
substep "Step 3: Reboot emulator (DO NOT open app after reboot)"
|
||
warn "IMPORTANT: After reboot, DO NOT open the app. Boot recovery should run silently."
|
||
warn "The emulator will reboot now. This will take 30-60 seconds."
|
||
pause
|
||
reboot_emulator
|
||
|
||
substep "Step 4: Collect boot recovery logs (without opening app)"
|
||
info "Collecting recovery logs from boot (app was NOT opened)..."
|
||
sleep 2
|
||
local logs
|
||
logs="$(get_recovery_logs)"
|
||
echo "$logs"
|
||
|
||
local missed rescheduled verified errors scenario
|
||
missed="$(extract_field_from_logs "$logs" "missed")"
|
||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||
verified="$(extract_field_from_logs "$logs" "verified")"
|
||
errors="$(extract_field_from_logs "$logs" "errors")"
|
||
scenario="$(extract_scenario_from_logs "$logs")"
|
||
|
||
echo
|
||
info "Parsed recovery summary:"
|
||
echo " scenario = ${scenario:-<none>}"
|
||
echo " missed = ${missed}"
|
||
echo " rescheduled= ${rescheduled}"
|
||
echo " verified = ${verified}"
|
||
echo " errors = ${errors}"
|
||
echo
|
||
|
||
substep "Step 5: Verify alarms were recreated (without opening app)"
|
||
show_alarms
|
||
local after_count system_after
|
||
after_count="$(get_plugin_alarm_count)"
|
||
system_after="$(get_system_alarm_count)"
|
||
info "Plugin alarms after boot (app never opened): $after_count (expected: 1)"
|
||
info "System/other alarms: $system_after (for context)"
|
||
|
||
if [[ "$after_count" -gt 0 && "$rescheduled" -gt 0 ]]; then
|
||
ok "TEST 4 PASSED: Boot recovery occurred silently and alarms were recreated (rescheduled=$rescheduled) without app launch."
|
||
elif [[ "$rescheduled" -gt 0 ]]; then
|
||
ok "TEST 4 PASSED: Boot recovery occurred silently (rescheduled=$rescheduled), but alarm count check unclear."
|
||
elif echo "$logs" | grep -qi "Starting boot recovery\|boot recovery"; then
|
||
warn "TEST 4: Boot recovery ran but alarms may not have been recreated. Check logs and implementation."
|
||
else
|
||
warn "TEST 4: Boot recovery not detected. Verify boot receiver is registered and has BOOT_COMPLETED permission."
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# Main
|
||
# ------------------------------------------------------------------------------
|
||
|
||
main() {
|
||
# Allow selecting specific tests: e.g. `./test-phase3.sh 1 3`
|
||
if [[ "$#" -gt 0 && ( "$1" == "-h" || "$1" == "--help" ) ]]; then
|
||
echo "Usage: $0 [TEST_IDS...]"
|
||
echo
|
||
echo "If no TEST_IDS are given, all tests (1, 2, 3, 4) will run."
|
||
echo "Examples:"
|
||
echo " $0 # run all tests"
|
||
echo " $0 1 # run only TEST 1"
|
||
echo " $0 2 3 # run only TEST 2 and TEST 3"
|
||
echo " $0 4 # run only TEST 4 (silent boot recovery)"
|
||
return 0
|
||
fi
|
||
|
||
SELECTED_TESTS=("$@")
|
||
|
||
echo
|
||
echo "========================================"
|
||
echo "Phase 3 Testing Script – Boot Recovery"
|
||
echo "========================================"
|
||
echo
|
||
echo "This script will guide you through Phase 3 tests."
|
||
echo "You'll be prompted when UI interaction is needed."
|
||
echo
|
||
echo "⚠️ WARNING: This script will reboot the emulator multiple times."
|
||
echo " Each reboot takes 30-60 seconds."
|
||
echo
|
||
|
||
pause
|
||
|
||
require_adb_device
|
||
build_app
|
||
install_app
|
||
|
||
if should_run_test "1" SELECTED_TESTS; then
|
||
test1_boot_future_alarms
|
||
pause
|
||
fi
|
||
|
||
if should_run_test "2" SELECTED_TESTS; then
|
||
test2_boot_past_alarms
|
||
pause
|
||
fi
|
||
|
||
if should_run_test "3" SELECTED_TESTS; then
|
||
test3_boot_no_schedules
|
||
pause
|
||
fi
|
||
|
||
if should_run_test "4" SELECTED_TESTS; then
|
||
test4_silent_boot_recovery
|
||
fi
|
||
|
||
section "Testing Complete"
|
||
|
||
echo "Test Results Summary (see logs above for details):"
|
||
echo
|
||
echo "TEST 1: Boot with Future Alarms"
|
||
echo " - Check logs for scenario=$BOOT_SCENARIO_VALUE and rescheduled>0"
|
||
echo
|
||
echo "TEST 2: Boot with Past Alarms"
|
||
echo " - Check that missed>=1 and rescheduled>=1"
|
||
echo
|
||
echo "TEST 3: Boot with No Schedules"
|
||
echo " - Check that no recovery runs, or NONE scenario is logged with rescheduled=0"
|
||
echo
|
||
echo "TEST 4: Silent Boot Recovery"
|
||
echo " - Check that boot recovery occurred and alarms were recreated without app launch"
|
||
echo
|
||
|
||
ok "Phase 3 testing script complete!"
|
||
echo
|
||
echo "Next steps:"
|
||
echo " - Review logs above"
|
||
echo " - Capture snippets into PHASE3-EMULATOR-TESTING.md"
|
||
echo " - Update PHASE3-VERIFICATION.md and unified directive status matrix"
|
||
echo
|
||
}
|
||
|
||
main "$@"
|