Add automatic app state reset for TEST 1 to ensure clean starting state when lingering alarms from TEST 0 are detected. Create PHASE1_TEST1_GOLDEN.md with actual values from successful run. TEST 1 Auto-Reset: - Detect lingering plugin alarms before TEST 1 starts - Automatically uninstall/reinstall app to clear alarms - Verify clean state (0 alarms) before proceeding - Gracefully skip TEST 1 if clean state cannot be achieved - Take failure screenshots when reset fails - Wrap all TEST 1 steps in conditional to skip on reset failure Documentation: - Create PHASE1_TEST1_GOLDEN.md with actual values from passing run - Document auto-reset behavior in golden run steps - Add cross-references between TEST 0 and TEST 1 golden docs - Include actual timestamps, scheduleIds, and recovery metrics This ensures TEST 1 always starts from a known clean state, making test results reliable and reproducible. The golden doc serves as a baseline for comparing future TEST 1 runs.
968 lines
43 KiB
Bash
Executable File
968 lines
43 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# Phase 1 Testing Script - Interactive Test Runner
|
||
# Guides through all Phase 1 tests with clear prompts for UI interaction
|
||
|
||
set -e # Exit on error
|
||
|
||
# Source shared library
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||
|
||
# Phase 1 specific configuration
|
||
VERIFY_FIRE=${VERIFY_FIRE:-false} # Set VERIFY_FIRE=true to enable alarm fire verification
|
||
APP_DIR="${SCRIPT_DIR}"
|
||
PROJECT_ROOT="$(cd "${APP_DIR}/../.." && pwd)"
|
||
|
||
# Allow selecting specific tests on the command line (e.g. ./test-phase1.sh 1 2)
|
||
SELECTED_TESTS=()
|
||
|
||
check_plugin_configured() {
|
||
print_info "Checking if plugin is already configured..."
|
||
|
||
# Wait a moment for app to fully load
|
||
sleep 3
|
||
|
||
# Check if database exists (indicates plugin has been used)
|
||
DB_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls databases/ 2>/dev/null | grep -c "daily_notification" || echo "0")
|
||
|
||
# Check if SharedPreferences has configuration (more reliable)
|
||
# The plugin stores config in SharedPreferences
|
||
PREFS_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls shared_prefs/ 2>/dev/null | grep -c "DailyNotification" || echo "0")
|
||
|
||
# Check recent logs for configuration activity
|
||
RECENT_CONFIG=$($ADB_BIN logcat -d -t 50 | grep -E "Plugin configured|configurePlugin|Configuration" | tail -3)
|
||
|
||
if [ "${DB_EXISTS}" -gt "0" ] || [ "${PREFS_EXISTS}" -gt "0" ]; then
|
||
print_success "Plugin appears to be configured (database or preferences exist)"
|
||
|
||
# Show user what to check in UI
|
||
print_info "Please verify in the app UI that you see:"
|
||
echo " ⚙️ Plugin Settings: ✅ Configured"
|
||
echo " 🔌 Native Fetcher: ✅ Configured"
|
||
echo ""
|
||
echo "If both show ✅, the plugin is configured and you can skip configuration."
|
||
echo "If either shows ❌ or 'Not configured', you'll need to click 'Configure Plugin'."
|
||
echo ""
|
||
|
||
return 0
|
||
else
|
||
print_info "Plugin not configured (no database or preferences found)"
|
||
print_info "You will need to click 'Configure Plugin' in the app UI"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
check_permissions() {
|
||
print_info "Checking notification permissions..."
|
||
|
||
# Check if POST_NOTIFICATIONS permission is granted (Android 13+)
|
||
PERM_CHECK=$($ADB_BIN shell dumpsys package "${APP_ID}" | grep -A 5 "granted=true" | grep -c "android.permission.POST_NOTIFICATIONS" || echo "0")
|
||
|
||
# Also check via app's permission status if available
|
||
PERM_GRANTED=false
|
||
if [ "${PERM_CHECK}" -gt "0" ]; then
|
||
PERM_GRANTED=true
|
||
else
|
||
# Check if we're on Android 12 or below (permission not required)
|
||
SDK_VERSION=$($ADB_BIN shell getprop ro.build.version.sdk)
|
||
if [ "${SDK_VERSION}" -lt "33" ]; then
|
||
PERM_GRANTED=true # Pre-Android 13 doesn't need runtime permission
|
||
fi
|
||
fi
|
||
|
||
if [ "${PERM_GRANTED}" = true ]; then
|
||
print_success "Notification permissions granted"
|
||
return 0
|
||
else
|
||
print_warn "Notification permissions may not be granted"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
ensure_permissions() {
|
||
if check_permissions; then
|
||
print_success "Permissions already granted"
|
||
return 0
|
||
else
|
||
print_info "Notification permissions needed"
|
||
wait_for_ui_action "In the app UI, click the 'Request Permissions' button.
|
||
|
||
This will show a system permission dialog.
|
||
|
||
Steps:
|
||
1. Click 'Request Permissions' button
|
||
2. In the system dialog, tap 'Allow' to grant notification permission
|
||
3. Return to the app and verify the status shows:
|
||
- 🔔 Notifications: ✅ Granted (or similar)
|
||
|
||
Once permission is granted, press Enter to continue."
|
||
|
||
# Re-check permissions
|
||
sleep 2
|
||
if check_permissions; then
|
||
print_success "Permissions granted"
|
||
return 0
|
||
else
|
||
print_warn "Permissions may still not be granted - continuing anyway"
|
||
print_info "If notifications fail, you may need to grant permissions manually"
|
||
return 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
ensure_plugin_configured() {
|
||
if check_plugin_configured; then
|
||
# Plugin might be configured, but let user verify
|
||
wait_for_ui_action "Please check the Plugin Status section at the top of the app.
|
||
|
||
If you see:
|
||
- ⚙️ Plugin Settings: ✅ Configured
|
||
- 🔌 Native Fetcher: ✅ Configured
|
||
- 🔔 Notifications: ✅ Granted (or similar)
|
||
|
||
Then the plugin is already configured - just press Enter to continue.
|
||
|
||
If any show ❌ or 'Not configured':
|
||
- Click 'Request Permissions' if notifications are not granted
|
||
- Click 'Configure Plugin' if settings/fetcher are not configured
|
||
- Wait for all to show ✅, then press Enter."
|
||
|
||
# Give a moment for any configuration that just happened
|
||
sleep 2
|
||
print_success "Continuing with tests (plugin configuration verified or skipped)"
|
||
return 0
|
||
else
|
||
# Plugin definitely needs configuration
|
||
print_info "Plugin needs configuration"
|
||
|
||
# First ensure permissions
|
||
ensure_permissions
|
||
|
||
wait_for_ui_action "Click the 'Configure Plugin' button in the app UI.
|
||
|
||
Wait for the status to update:
|
||
- ⚙️ Plugin Settings: Should change to ✅ Configured
|
||
- 🔌 Native Fetcher: Should change to ✅ Configured
|
||
|
||
Once both show ✅, press Enter to continue."
|
||
|
||
# Verify configuration completed
|
||
sleep 2
|
||
print_success "Plugin configuration completed (or verified)"
|
||
fi
|
||
}
|
||
|
||
# kill_app is now provided by the library, but Phase 1 uses it with PACKAGE variable
|
||
# The library version uses APP_ID, which is set to PACKAGE, so it should work
|
||
# But we keep this as a compatibility wrapper if needed
|
||
|
||
# Phase 1 specific helper: get_current_time (used for fire verification)
|
||
get_current_time() {
|
||
$ADB_BIN shell date +%s
|
||
}
|
||
|
||
# Main test execution
|
||
main() {
|
||
# Allow selecting specific tests: e.g. `./test-phase1.sh 1 2`
|
||
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"
|
||
return 0
|
||
fi
|
||
|
||
SELECTED_TESTS=("$@")
|
||
|
||
print_header "Phase 1 Testing Script"
|
||
echo "This script will guide you through Phase 1 tests."
|
||
echo "You'll be prompted when UI interaction is needed."
|
||
echo ""
|
||
wait_for_user
|
||
|
||
# Pre-flight checks
|
||
print_header "Pre-Flight Checks"
|
||
check_adb_connection
|
||
check_emulator_ready
|
||
|
||
# Build and install
|
||
build_app
|
||
install_app
|
||
|
||
# Clear logs
|
||
clear_logs
|
||
|
||
# ============================================
|
||
# TEST 0: Daily Rollover (Core Contract Verification)
|
||
# ============================================
|
||
# Note: This test verifies the core "one notification per day" contract
|
||
# by checking that after a notification fires, the next day's schedule
|
||
# is correctly computed and scheduled. This is a manual/semi-automated test
|
||
# as it requires either waiting for the alarm to fire or manipulating time.
|
||
#
|
||
# To run: Use test ID "0" or enable manually
|
||
# ============================================
|
||
if should_run_test "0" SELECTED_TESTS; then
|
||
print_header "TEST 0: Daily Rollover Verification"
|
||
echo "Purpose: Verify that after a notification fires, the next day's"
|
||
echo " schedule is correctly computed and only ONE alarm exists."
|
||
echo ""
|
||
echo "Note: This test verifies the core 'one notification per day' contract."
|
||
echo " It requires either:"
|
||
echo " 1. Scheduling a notification for 'now + N seconds' and waiting, OR"
|
||
echo " 2. Manipulating the emulator clock to cross the fire boundary."
|
||
echo ""
|
||
echo " For now, this is a MANUAL test - you'll need to verify the"
|
||
echo " behavior by checking logs and AlarmManager after a notification fires."
|
||
echo ""
|
||
wait_for_user
|
||
|
||
print_step "1" "Schedule a test notification for near-future (or use existing)..."
|
||
launch_app
|
||
ensure_plugin_configured
|
||
|
||
# Capture pre-schedule UI state
|
||
take_screenshot "phase1_test0_daily_rollover" "before_scheduling"
|
||
|
||
INITIAL_COUNT=$(get_plugin_alarm_count)
|
||
SYSTEM_COUNT=$(get_system_alarm_count)
|
||
print_info "Current notification alarms: ${INITIAL_COUNT} (expected before scheduling: 0)"
|
||
print_info "System/other alarms: ${SYSTEM_COUNT} (for context)"
|
||
print_info "Note: Prefetch uses WorkManager (not AlarmManager), so it won't appear in alarm count"
|
||
|
||
if [ "${INITIAL_COUNT}" -eq "0" ] 2>/dev/null; then
|
||
print_success "✅ No existing notification alarms (clean state)"
|
||
wait_for_ui_action "In the app UI, schedule a daily notification.
|
||
|
||
For this test, you may want to schedule it for a time very soon
|
||
(e.g., 1-2 minutes from now) to observe the rollover behavior.
|
||
|
||
This will schedule:
|
||
- 1 notification alarm (AlarmManager) for the specified time
|
||
- 1 prefetch job (WorkManager) for 2 minutes before that time"
|
||
sleep 3 # Give alarm time to be registered in AlarmManager
|
||
|
||
# Capture post-schedule UI state
|
||
take_screenshot "phase1_test0_daily_rollover" "after_scheduling"
|
||
|
||
POST_SCHEDULE_COUNT=$(get_plugin_alarm_count)
|
||
|
||
# Check alarm count after scheduling
|
||
# Note: This is a PRELIMINARY sanity check only - final verdict comes after rollover
|
||
# 0 alarms is likely a race condition (alarm may not be visible yet in dumpsys)
|
||
# Only treat >1 alarms as a real failure (duplicates)
|
||
if [ "${POST_SCHEDULE_COUNT}" -eq "0" ] 2>/dev/null; then
|
||
print_warn "⚠️ Found 0 plugin alarms right after scheduling."
|
||
print_info "ℹ️ Pre-schedule check not reliable; will rely on rollover assertions."
|
||
print_info "ℹ️ The alarm may not be visible in dumpsys yet. Continuing to verify after rollover."
|
||
elif [ "${POST_SCHEDULE_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Found 1 notification alarm (expected: 1) – preliminary check passed."
|
||
print_info "ℹ️ This is preliminary check only; final verdict after rollover."
|
||
else
|
||
# count > 1 - this is a real duplicate bug
|
||
print_warn "⚠️ ⚠️ Found ${POST_SCHEDULE_COUNT} notification alarms (expected: 1) – DUPLICATES DETECTED right after scheduling!"
|
||
print_warn "This indicates duplicate NOTIFICATION alarms were created (BUG DETECTED)"
|
||
print_info "Showing alarm blocks for debugging:"
|
||
show_plugin_alarms_compact
|
||
print_info "For more details, run:"
|
||
print_info " adb shell dumpsys alarm | grep -A 5 'com.timesafari.dailynotification' | sed -n '1,80p'"
|
||
fi
|
||
INITIAL_COUNT="${POST_SCHEDULE_COUNT}"
|
||
fi
|
||
|
||
# Only show alarm details if we found exactly 1 alarm (skip if 0 due to race condition)
|
||
if [ "${INITIAL_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single notification alarm scheduled (one per day)"
|
||
print_info "Note: Prefetch uses WorkManager (not AlarmManager), so it won't appear in alarm count"
|
||
|
||
# Show alarm details
|
||
ALARM_DETAILS=$($ADB_BIN shell dumpsys alarm | grep -A 3 "com.timesafari.dailynotification" | grep -A 3 "com.timesafari.daily.NOTIFICATION" | head -10)
|
||
if [ -n "${ALARM_DETAILS}" ]; then
|
||
print_info "Notification alarm details:"
|
||
echo "${ALARM_DETAILS}" | head -5
|
||
echo ""
|
||
|
||
# Extract trigger time
|
||
ALARM_TRIGGER_MS=$(echo "${ALARM_DETAILS}" | grep -oE "origWhen [0-9]+" | head -1 | awk '{print $2}')
|
||
if [ -n "${ALARM_TRIGGER_MS}" ]; then
|
||
ALARM_TRIGGER_SEC=$((ALARM_TRIGGER_MS / 1000))
|
||
ALARM_READABLE=$(date -d "@${ALARM_TRIGGER_SEC}" 2>/dev/null || echo "${ALARM_TRIGGER_MS} ms")
|
||
print_info "Notification alarm scheduled for: ${ALARM_READABLE}"
|
||
|
||
# Calculate prefetch time (2 minutes before)
|
||
PREFETCH_SEC=$((ALARM_TRIGGER_SEC - 120))
|
||
PREFETCH_READABLE=$(date -d "@${PREFETCH_SEC}" 2>/dev/null || echo "${PREFETCH_SEC}")
|
||
print_info "Prefetch should be scheduled for: ${PREFETCH_READABLE} (2 minutes before, via WorkManager)"
|
||
|
||
# Calculate next day
|
||
NEXT_DAY_SEC=$((ALARM_TRIGGER_SEC + 86400))
|
||
NEXT_DAY_READABLE=$(date -d "@${NEXT_DAY_SEC}" 2>/dev/null || echo "${NEXT_DAY_SEC}")
|
||
print_info "Expected next day notification: ${NEXT_DAY_READABLE} (24 hours later)"
|
||
fi
|
||
fi
|
||
elif [ "${INITIAL_COUNT}" -gt "1" ] 2>/dev/null; then
|
||
print_warn "⚠️ Found ${INITIAL_COUNT} notification alarms (expected: 1) - DUPLICATES DETECTED!"
|
||
print_warn "This indicates the duplicate alarm bug - multiple alarms for the same notification"
|
||
fi
|
||
# Note: We intentionally don't print anything for INITIAL_COUNT == 0 here
|
||
# because we already handled it above with the race condition message
|
||
|
||
print_step "2" "Manual verification steps..."
|
||
echo ""
|
||
echo "To complete this test, you need to:"
|
||
echo " 1. Wait for the notification to fire (or advance emulator clock)"
|
||
echo " 2. Check that the plugin:"
|
||
echo " - Computed the next day's time (24 hours later)"
|
||
echo " - Scheduled exactly ONE notification alarm (AlarmManager) for tomorrow"
|
||
echo " - Scheduled exactly ONE prefetch job (WorkManager) for 2 minutes before tomorrow's notification"
|
||
echo " - Did NOT create duplicate notification alarms"
|
||
echo " 3. Verify in logs:"
|
||
echo " - Next run time calculation shows tomorrow's time"
|
||
echo " - Only one notification alarm scheduled in AlarmManager"
|
||
echo " - Prefetch job scheduled in WorkManager"
|
||
echo ""
|
||
echo "Expected log patterns:"
|
||
echo " DNP-SCHEDULE: Scheduling next daily alarm: ... source=ROLLOVER_ON_FIRE"
|
||
echo " DNP-NOTIFY: Scheduling alarm: triggerTime=<tomorrow's time>"
|
||
echo " DNP-SCHEDULE: Scheduling OS alarm: variant=ALARM_CLOCK, action=com.timesafari.daily.NOTIFICATION, ..."
|
||
echo ""
|
||
echo "When scheduling the test notification, expect:"
|
||
echo " DNP-SCHEDULE: ... source=TEST_NOTIFICATION"
|
||
echo ""
|
||
echo "Alarm shape in dumpsys:"
|
||
echo " RTC_WAKEUP, ALARM_CLOCK, tag=*walarm*:com.timesafari.daily.NOTIFICATION"
|
||
echo ""
|
||
echo "After notification fires, run:"
|
||
echo " adb shell dumpsys alarm | grep -A 3 'com.timesafari.dailynotification'"
|
||
echo " adb logcat -d | grep -E 'DNP-SCHEDULE|DNP-NOTIFY' | tail -20"
|
||
echo ""
|
||
|
||
wait_for_ui_action "After the notification fires (or you advance the clock),
|
||
check the logs and AlarmManager to verify:
|
||
|
||
1. Only ONE alarm exists (one per day)
|
||
2. The alarm time is for tomorrow (24 hours later)
|
||
3. No duplicate alarms were created
|
||
|
||
Press Enter when you've verified this (or to skip this test)."
|
||
|
||
print_info "Polling for stable alarm count (allowing up to ~10 seconds for Android to settle)..."
|
||
POST_ROLLOVER_COUNT=$(wait_for_stable_plugin_alarm_count 5 2)
|
||
SYSTEM_FINAL=$(get_system_alarm_count)
|
||
print_info "Notification alarms after rollover: ${POST_ROLLOVER_COUNT} (expected: 1)"
|
||
print_info "System/other alarms: ${SYSTEM_FINAL} (for context)"
|
||
print_info "Note: Prefetch is scheduled via WorkManager (not AlarmManager), so it won't appear in alarm count"
|
||
|
||
# Capture post-rollover UI state
|
||
take_screenshot "phase1_test0_daily_rollover" "after_rollover_check"
|
||
|
||
# After rollover, the state should be stable - this is the real assertion point
|
||
if [ "${POST_ROLLOVER_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ TEST 0 PASSED: Daily rollover created exactly one NOTIFICATION alarm for tomorrow."
|
||
print_info "Expected state after rollover:"
|
||
echo " ✅ 1 notification alarm (AlarmManager) for tomorrow"
|
||
echo " ✅ 1 prefetch job (WorkManager) for 2 minutes before tomorrow's notification"
|
||
elif [ "${POST_ROLLOVER_COUNT}" -gt "1" ] 2>/dev/null; then
|
||
print_warn "⚠️ ⚠️ TEST 0 FAILED: Daily rollover created ${POST_ROLLOVER_COUNT} NOTIFICATION alarms (duplicates)."
|
||
print_warn "This indicates duplicate NOTIFICATION alarms were created (BUG DETECTED)"
|
||
take_failure_screenshot "phase1_test0_daily_rollover" "duplicate_alarms"
|
||
echo ""
|
||
print_info "Showing alarm blocks for debugging:"
|
||
show_plugin_alarms_compact
|
||
echo ""
|
||
print_info "Check scheduling logs for multiple DNP-SCHEDULE entries:"
|
||
print_info " adb logcat -d | grep 'DNP-SCHEDULE\|DNP-NOTIFY' | tail -20"
|
||
echo ""
|
||
print_info "Possible causes:"
|
||
echo " - Multiple scheduling paths racing (rollover + recovery)"
|
||
echo " - Old alarm wasn't canceled before scheduling new one"
|
||
echo " - Different scheduleIds used for same trigger time"
|
||
echo " - Idempotence check not working correctly"
|
||
else
|
||
# count is 0 or invalid - rollover may have failed
|
||
print_warn "⚠️ TEST 0 INCONCLUSIVE: No NOTIFICATION alarm found after rollover (expected 1, got 0)"
|
||
print_warn "This indicates the rollover did not schedule tomorrow's alarm correctly"
|
||
take_failure_screenshot "phase1_test0_daily_rollover" "no_alarm_after_rollover"
|
||
echo ""
|
||
print_info "Showing alarm blocks for debugging:"
|
||
show_plugin_alarms_compact
|
||
echo ""
|
||
print_info "Expected log pattern after fire:"
|
||
echo " DNP-SCHEDULE: Scheduling next daily alarm: ... source=ROLLOVER_ON_FIRE"
|
||
echo " DNP-NOTIFY: Scheduling alarm: triggerTime=<tomorrow>"
|
||
echo " DNP-SCHEDULE: Scheduling OS alarm: variant=ALARM_CLOCK, action=com.timesafari.daily.NOTIFICATION, ..."
|
||
echo ""
|
||
print_info "Check logs for rollover scheduling errors:"
|
||
print_info " adb logcat -d | grep 'DNP-SCHEDULE\|DNP-NOTIFY' | tail -20"
|
||
echo ""
|
||
print_info "Manual verification:"
|
||
echo " If dumpsys shows a single RTC_WAKEUP alarm with"
|
||
echo " tag=*walarm*:com.timesafari.daily.NOTIFICATION for tomorrow,"
|
||
echo " treat TEST 0 as manually PASSED and update the script expectations later."
|
||
fi
|
||
|
||
wait_for_user
|
||
fi
|
||
|
||
# ============================================
|
||
# TEST 1: Force-Stop Recovery - Database Restoration
|
||
# ============================================
|
||
if should_run_test "1" SELECTED_TESTS; then
|
||
print_header "TEST 1: Force-Stop Recovery - Database Restoration"
|
||
echo "Purpose: Verify that after force-stop (which clears alarms), recovery"
|
||
echo " uses the database to rebuild alarms on app relaunch."
|
||
echo ""
|
||
echo "Note: App supports one alarm per day."
|
||
echo ""
|
||
echo "Test sequence:"
|
||
echo " 1. Clean start - verify no lingering alarms"
|
||
echo " 2. Schedule one alarm → Verify it exists in AlarmManager"
|
||
echo " 3. Force-stop app → Verify alarm is cleared (count = 0)"
|
||
echo " 4. Relaunch app → Verify recovery rebuilds alarm from database"
|
||
echo " 5. Verify alarm actually fires at scheduled time (optional)"
|
||
echo ""
|
||
wait_for_user
|
||
|
||
# ============================================
|
||
# Step 1: Clean start - verify no lingering alarms
|
||
# ============================================
|
||
print_step "1" "Clean start - checking for lingering alarms..."
|
||
LINGERING_COUNT=$(get_plugin_alarm_count)
|
||
SYSTEM_COUNT=$(get_system_alarm_count)
|
||
print_info "Current plugin notification alarms: ${LINGERING_COUNT}"
|
||
print_info "System/other alarms: ${SYSTEM_COUNT} (for context)"
|
||
|
||
if [ "${LINGERING_COUNT}" -gt "0" ] 2>/dev/null; then
|
||
print_warn "Found ${LINGERING_COUNT} lingering plugin alarm(s) - these will interfere with TEST 1."
|
||
print_info "TEST 1 needs a clean state (no existing plugin alarms)."
|
||
print_info "Resetting app state via uninstall + reinstall..."
|
||
|
||
# Uninstall existing app
|
||
print_info "Uninstalling existing app..."
|
||
set +e
|
||
uninstall_output="$($ADB_BIN uninstall "$APP_ID" 2>&1)"
|
||
uninstall_status=$?
|
||
set -e
|
||
|
||
if [ $uninstall_status -eq 0 ]; then
|
||
print_success "App uninstall succeeded (clean slate)."
|
||
else
|
||
if grep -q "DELETE_FAILED_INTERNAL_ERROR" <<<"$uninstall_output"; then
|
||
print_info "No existing app to uninstall (continuing)"
|
||
else
|
||
print_warn "App uninstall reported an error: $uninstall_output (continuing anyway)"
|
||
fi
|
||
fi
|
||
|
||
# Reinstall APK
|
||
print_info "Reinstalling APK..."
|
||
if $ADB_BIN install -r "$APK_PATH" >/dev/null 2>&1; then
|
||
print_success "App reinstall succeeded."
|
||
else
|
||
print_error "App reinstall FAILED - cannot proceed with TEST 1."
|
||
print_warn "Marking TEST 1 as INCONCLUSIVE due to dirty starting state."
|
||
take_failure_screenshot "phase1_test1_force_stop" "dirty_state_reinstall_failed"
|
||
wait_for_user
|
||
# Skip to end of TEST 1 block
|
||
goto_test1_end=true
|
||
fi
|
||
|
||
if [ "${goto_test1_end:-false}" != "true" ]; then
|
||
# Verify install
|
||
if ! $ADB_BIN shell pm list packages | grep -q "$APP_ID"; then
|
||
print_error "App not found in package list after reinstall - aborting TEST 1."
|
||
take_failure_screenshot "phase1_test1_force_stop" "dirty_state_verify_failed"
|
||
wait_for_user
|
||
goto_test1_end=true
|
||
fi
|
||
fi
|
||
|
||
if [ "${goto_test1_end:-false}" != "true" ]; then
|
||
# Clear logs
|
||
print_info "Clearing logcat buffer after reinstall..."
|
||
$ADB_BIN logcat -c || true
|
||
|
||
# Re-check plugin alarms
|
||
print_info "Rechecking plugin alarms after reset..."
|
||
sleep 2 # Give system time to clear alarms
|
||
LINGERING_COUNT=$(get_plugin_alarm_count)
|
||
print_info "Plugin alarms after reset: ${LINGERING_COUNT} (expected: 0)"
|
||
|
||
if [ "${LINGERING_COUNT}" -ne "0" ] 2>/dev/null; then
|
||
print_warn "TEST 1 starting with non-zero alarm count even after reset; treating as INCONCLUSIVE."
|
||
print_info "This may indicate device-specific behavior where alarms persist across uninstall."
|
||
take_failure_screenshot "phase1_test1_force_stop" "unexpected_alarms_after_reset"
|
||
wait_for_user
|
||
goto_test1_end=true
|
||
fi
|
||
fi
|
||
|
||
if [ "${goto_test1_end:-false}" = "true" ]; then
|
||
print_warn "TEST 1 skipped due to inability to achieve clean starting state."
|
||
wait_for_user
|
||
else
|
||
print_success "App state reset complete. TEST 1 starting from clean state."
|
||
fi
|
||
else
|
||
print_success "No lingering plugin alarms found (clean state)"
|
||
fi
|
||
|
||
# Skip remaining TEST 1 steps if we couldn't achieve clean state
|
||
if [ "${goto_test1_end:-false}" != "true" ]; then
|
||
|
||
# ============================================
|
||
# Step 2: Schedule a known future alarm
|
||
# ============================================
|
||
print_step "2" "Launch app and schedule alarm..."
|
||
launch_app
|
||
ensure_plugin_configured
|
||
|
||
wait_for_ui_action "In the app UI, click the 'Test Notification' button.
|
||
|
||
This will schedule ONE notification for 4 minutes in the future.
|
||
(App supports one alarm per day)
|
||
|
||
The alarm will be stored in the database AND scheduled in AlarmManager."
|
||
|
||
print_step "3" "Verifying alarm exists in AlarmManager (BEFORE force-stop)..."
|
||
sleep 2
|
||
|
||
ALARM_COUNT_BEFORE=$(get_plugin_alarm_count)
|
||
SYSTEM_COUNT_BEFORE=$(get_system_alarm_count)
|
||
print_info "Plugin alarms: ${ALARM_COUNT_BEFORE} (expected: 1)"
|
||
print_info "System/other alarms: ${SYSTEM_COUNT_BEFORE} (for context)"
|
||
|
||
if [ "${ALARM_COUNT_BEFORE}" -eq "0" ] 2>/dev/null; then
|
||
print_error "No plugin alarms found in AlarmManager - cannot test force-stop recovery"
|
||
print_info "Make sure you clicked 'Test Notification' and wait a moment"
|
||
wait_for_user
|
||
# Re-check
|
||
ALARM_COUNT_BEFORE=$(get_plugin_alarm_count)
|
||
if [ "${ALARM_COUNT_BEFORE}" -eq "0" ] 2>/dev/null; then
|
||
print_error "Still no alarms found - aborting test"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
if [ "${ALARM_COUNT_BEFORE}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single plugin alarm confirmed in AlarmManager (one per day)"
|
||
else
|
||
print_warn "⚠️ Found ${ALARM_COUNT_BEFORE} plugin alarms (expected: 1) - continuing anyway"
|
||
fi
|
||
|
||
# Capture alarm details for later verification
|
||
ALARM_DETAILS_BEFORE=$($ADB_BIN shell dumpsys alarm | grep -A 3 "$APP_ID" | head -10)
|
||
print_info "Alarm details:"
|
||
echo "${ALARM_DETAILS_BEFORE}" | head -5
|
||
echo ""
|
||
|
||
# Extract trigger time for later verification
|
||
ALARM_TRIGGER_MS=$(echo "${ALARM_DETAILS_BEFORE}" | grep -oE "origWhen [0-9]+" | head -1 | awk '{print $2}')
|
||
if [ -n "${ALARM_TRIGGER_MS}" ]; then
|
||
ALARM_TRIGGER_SEC=$((ALARM_TRIGGER_MS / 1000))
|
||
ALARM_READABLE=$(date -d "@${ALARM_TRIGGER_SEC}" 2>/dev/null || echo "${ALARM_TRIGGER_SEC}")
|
||
print_info "Alarm scheduled for: ${ALARM_READABLE} (${ALARM_TRIGGER_MS} ms)"
|
||
fi
|
||
|
||
print_info "Checking logs for scheduling confirmation..."
|
||
$ADB_BIN logcat -d | grep -E "DN|SCHEDULE|Stored notification content" | tail -5
|
||
echo ""
|
||
|
||
wait_for_user
|
||
|
||
# ============================================
|
||
# Step 3: Force-stop the app (clears alarms)
|
||
# ============================================
|
||
print_step "4" "Force-stopping app (clears all alarms)..."
|
||
print_warn "Force-stop will clear ALL alarms from AlarmManager"
|
||
print_info "Executing: $ADB_BIN shell am force-stop ${APP_ID}"
|
||
force_stop_app
|
||
sleep 2
|
||
|
||
# Verify app is stopped (force_stop_app already handles this)
|
||
|
||
# ============================================
|
||
# Step 4: Verify alarms are MISSING (cleared by OS)
|
||
# ============================================
|
||
print_step "5" "Verifying alarms are MISSING from AlarmManager (AFTER force-stop)..."
|
||
sleep 1
|
||
ALARM_COUNT_AFTER=$(get_plugin_alarm_count)
|
||
SYSTEM_COUNT_AFTER=$(get_system_alarm_count)
|
||
print_info "Plugin alarms after force-stop: ${ALARM_COUNT_AFTER} (expected: 0)"
|
||
print_info "System/other alarms: ${SYSTEM_COUNT_AFTER} (for context)"
|
||
|
||
if [ "${ALARM_COUNT_AFTER}" -eq "0" ] 2>/dev/null; then
|
||
print_success "✅ Plugin alarms cleared by force-stop (count: ${ALARM_COUNT_AFTER})"
|
||
print_info "This confirms: Force-stop cleared alarms from AlarmManager"
|
||
else
|
||
print_warn "⚠️ Plugin alarms still present after force-stop (count: ${ALARM_COUNT_AFTER})"
|
||
print_info "Some devices/OS versions may not clear alarms on force-stop"
|
||
print_info "Continuing test anyway - recovery should still work"
|
||
fi
|
||
|
||
wait_for_user
|
||
|
||
# ============================================
|
||
# Step 5: Relaunch app (triggers recovery from database)
|
||
# ============================================
|
||
print_step "6" "Relaunching app (triggers recovery from database)..."
|
||
clear_logs
|
||
launch_app
|
||
sleep 4 # Give recovery time to run
|
||
|
||
# ============================================
|
||
# Step 6: Verify recovery rebuilt alarms from database
|
||
# ============================================
|
||
print_step "7" "Verifying recovery rebuilt alarms from database..."
|
||
sleep 2
|
||
ALARM_COUNT_RECOVERED=$(get_plugin_alarm_count)
|
||
SYSTEM_COUNT_RECOVERED=$(get_system_alarm_count)
|
||
print_info "Plugin alarms after recovery: ${ALARM_COUNT_RECOVERED} (expected: 1)"
|
||
print_info "System/other alarms: ${SYSTEM_COUNT_RECOVERED} (for context)"
|
||
|
||
print_info "Checking recovery logs..."
|
||
check_recovery_logs
|
||
|
||
print_info "Expected log output:"
|
||
echo " DNP-REACTIVATION: Starting app launch recovery"
|
||
echo " DNP-REACTIVATION: Rescheduled alarm: <id> for <time>"
|
||
echo " DNP-REACTIVATION: Cold start recovery complete: ... rescheduled>=1, ..."
|
||
echo ""
|
||
|
||
# Check recovery logs for rescheduling and recovery source
|
||
RECOVERY_RESULT=$($ADB_BIN logcat -d | grep "Cold start recovery complete\|Boot recovery complete" | tail -1)
|
||
RESCHEDULED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||
VERIFIED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||
|
||
# Check for explicit recovery source indication (if logged)
|
||
RECOVERY_SOURCE=$($ADB_BIN logcat -d | grep -E "recovery source|from database|DATABASE" | tail -1 || echo "")
|
||
|
||
# Pass/fail criteria
|
||
TEST1_PASSED=false
|
||
|
||
if [ "${ALARM_COUNT_RECOVERED}" -gt "0" ] 2>/dev/null; then
|
||
print_success "✅ Alarms restored in AlarmManager (count: ${ALARM_COUNT_RECOVERED})"
|
||
if [ "${RESCHEDULED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||
print_success "✅ Recovery logs confirm rescheduling (rescheduled=${RESCHEDULED_COUNT})"
|
||
TEST1_PASSED=true
|
||
else
|
||
print_warn "⚠️ Alarms restored but logs show rescheduled=0"
|
||
print_info "This might be okay if alarms were verified instead of rescheduled"
|
||
# Still pass if alarms are restored, even if rescheduled=0
|
||
TEST1_PASSED=true
|
||
fi
|
||
else
|
||
print_error "❌ No alarms restored (count: ${ALARM_COUNT_RECOVERED})"
|
||
print_info "Recovery may have failed or alarms were not in database"
|
||
fi
|
||
|
||
if [ "${TEST1_PASSED}" = true ]; then
|
||
print_success "TEST 1 PASSED: Recovery successfully rebuilt alarms from database!"
|
||
print_info "Summary:"
|
||
echo " - Before force-stop: ${ALARM_COUNT_BEFORE} alarm(s)"
|
||
echo " - After force-stop: ${ALARM_COUNT_AFTER} alarm(s) (cleared)"
|
||
echo " - After recovery: ${ALARM_COUNT_RECOVERED} alarm(s) (rebuilt)"
|
||
echo " - Rescheduled: ${RESCHEDULED_COUNT} alarm(s)"
|
||
echo " - Verified: ${VERIFIED_COUNT} alarm(s)"
|
||
if [ -n "${RECOVERY_SOURCE}" ]; then
|
||
echo " - Recovery source: ${RECOVERY_SOURCE}"
|
||
fi
|
||
else
|
||
print_error "TEST 1 FAILED: Recovery did not rebuild alarms correctly"
|
||
print_info "Check logs above for recovery errors"
|
||
fi
|
||
|
||
# ============================================
|
||
# Step 7: Verify alarms actually fire (optional, controlled by VERIFY_FIRE flag)
|
||
# ============================================
|
||
if [ "${VERIFY_FIRE}" = "true" ] && [ -n "${ALARM_TRIGGER_MS}" ]; then
|
||
print_step "8" "Verifying alarm fires at scheduled time..."
|
||
|
||
# Get current time in milliseconds
|
||
CURRENT_TIME_SEC=$(get_current_time)
|
||
CURRENT_TIME_MS=$((CURRENT_TIME_SEC * 1000))
|
||
WAIT_MS=$((ALARM_TRIGGER_MS - CURRENT_TIME_MS))
|
||
|
||
if [ "${WAIT_MS}" -lt 0 ]; then
|
||
print_warn "Alarm time already passed (${WAIT_MS} ms ago); skipping fire verification"
|
||
else
|
||
WAIT_SEC=$((WAIT_MS / 1000))
|
||
|
||
# Clamp upper bound to prevent accidentally waiting 30+ minutes
|
||
if [ "${WAIT_SEC}" -gt 600 ]; then
|
||
print_warn "Alarm is >10 minutes away (${WAIT_SEC}s); skipping fire verification"
|
||
print_info "To test fire verification, schedule alarm closer to current time"
|
||
print_info "Or set a shorter test alarm interval in the app"
|
||
else
|
||
print_info "Alarm scheduled for: ${ALARM_READABLE}"
|
||
print_info "Current time: $(date -d "@${CURRENT_TIME_SEC}" 2>/dev/null || echo "${CURRENT_TIME_SEC}")"
|
||
print_info "Waiting ~${WAIT_SEC} seconds for alarm to fire..."
|
||
|
||
# Clear logs before waiting
|
||
clear_logs
|
||
|
||
# Wait for alarm time (with a small buffer)
|
||
sleep ${WAIT_SEC}
|
||
|
||
# Give alarm a moment to fire and log
|
||
sleep 2
|
||
|
||
print_info "Checking logs for fired alarm..."
|
||
ALARM_FIRED=$($ADB_BIN logcat -d | grep -E "DNP-RECEIVE|DNP-NOTIFY|DNP-WORK|Alarm fired|Notification displayed" | tail -10)
|
||
|
||
if [ -n "${ALARM_FIRED}" ]; then
|
||
print_success "✅ Alarm fired! Logs:"
|
||
echo "${ALARM_FIRED}"
|
||
else
|
||
print_warn "⚠️ No alarm fire logs found"
|
||
print_info "This might mean:"
|
||
echo " - Alarm fired but logs were cleared"
|
||
echo " - Alarm receiver didn't log"
|
||
echo " - Check notification tray manually"
|
||
print_info "Recent logs:"
|
||
$ADB_BIN logcat -d | grep -E "DNP|DailyNotification" | tail -10
|
||
fi
|
||
fi
|
||
fi
|
||
elif [ "${VERIFY_FIRE}" = "true" ]; then
|
||
print_info "Skipping fire verification (alarm trigger time not captured)"
|
||
else
|
||
print_info "Skipping fire verification (VERIFY_FIRE=false, set VERIFY_FIRE=true to enable)"
|
||
fi
|
||
|
||
fi # End of "if goto_test1_end != true" block (wraps all TEST 1 steps after clean state check)
|
||
|
||
wait_for_user
|
||
fi # End of "if should_run_test 1" block
|
||
|
||
# ============================================
|
||
# TEST 2: Schedule Update (One-Per-Day Semantics)
|
||
# ============================================
|
||
if should_run_test "2" SELECTED_TESTS; then
|
||
print_header "TEST 2: Schedule Update Verification"
|
||
echo "Purpose: Verify that updating the schedule time maintains 'one per day' semantics."
|
||
echo ""
|
||
echo "Note: This test verifies that when the schedule time changes, the old alarm"
|
||
echo " is canceled and only the new one remains (one notification per day)."
|
||
echo ""
|
||
wait_for_user
|
||
|
||
print_step "1" "Launch app and verify initial schedule"
|
||
launch_app
|
||
ensure_plugin_configured
|
||
|
||
# Get initial alarm count
|
||
INITIAL_ALARM_COUNT=$(get_plugin_alarm_count)
|
||
SYSTEM_ALARM_COUNT=$(get_system_alarm_count)
|
||
print_info "Plugin alarms: ${INITIAL_ALARM_COUNT} (expected: 1)"
|
||
print_info "System/other alarms: ${SYSTEM_ALARM_COUNT} (for context)"
|
||
|
||
if [ "${INITIAL_ALARM_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Initial alarm confirmed (one per day)"
|
||
elif [ "${INITIAL_ALARM_COUNT}" -eq "0" ] 2>/dev/null; then
|
||
print_warn "⚠️ No initial alarm found - scheduling one first..."
|
||
wait_for_ui_action "In the app UI, schedule a daily notification (e.g., click 'Test Notification')."
|
||
sleep 2
|
||
INITIAL_ALARM_COUNT=$(get_plugin_alarm_count)
|
||
if [ "${INITIAL_ALARM_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Alarm scheduled"
|
||
else
|
||
print_error "Failed to schedule initial alarm"
|
||
wait_for_user
|
||
fi
|
||
else
|
||
print_warn "⚠️ Found ${INITIAL_ALARM_COUNT} plugin alarms (expected: 1) - continuing anyway"
|
||
fi
|
||
|
||
print_step "2" "Updating schedule time"
|
||
wait_for_ui_action "In the app UI, change the schedule time (e.g., from 06:50 to 07:10)
|
||
and apply the schedule.
|
||
|
||
This should cancel the old alarm and schedule a new one at the new time.
|
||
You should still have exactly 1 alarm (one per day)."
|
||
|
||
sleep 3
|
||
check_alarm_status
|
||
|
||
UPDATED_ALARM_COUNT=$(get_plugin_alarm_count)
|
||
print_info "Plugin alarms after update: ${UPDATED_ALARM_COUNT} (expected: 1)"
|
||
|
||
if [ "${UPDATED_ALARM_COUNT}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single alarm confirmed after schedule update (one per day maintained)"
|
||
else
|
||
print_warn "⚠️ Found ${UPDATED_ALARM_COUNT} plugin alarms (expected: 1)"
|
||
if [ "${UPDATED_ALARM_COUNT}" -gt "1" ] 2>/dev/null; then
|
||
print_warn "⚠️ Multiple alarms detected - old alarm may not have been canceled"
|
||
fi
|
||
fi
|
||
|
||
print_step "3" "Killing app and relaunching (triggers recovery)..."
|
||
kill_app
|
||
clear_logs
|
||
launch_app
|
||
|
||
print_step "4" "Checking recovery logs for verification..."
|
||
sleep 3
|
||
check_recovery_logs
|
||
|
||
print_info "Expected log output (either):"
|
||
echo " DNP-REACTIVATION: Verified scheduled alarm: <id> at <time>"
|
||
echo " OR"
|
||
echo " DNP-REACTIVATION: Rescheduled missing alarm: <id> at <time>"
|
||
echo " DNP-REACTIVATION: Cold start recovery complete: ..., verified=1 or rescheduled=1, ..."
|
||
echo ""
|
||
|
||
RECOVERY_RESULT=$($ADB_BIN logcat -d | grep "Cold start recovery complete" | tail -1)
|
||
|
||
# Extract counts from recovery result
|
||
RESCHEDULED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||
VERIFIED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||
|
||
# Verify alarm count after recovery
|
||
ALARM_COUNT_AFTER_RECOVERY=$(get_plugin_alarm_count)
|
||
print_info "Plugin alarms after recovery: ${ALARM_COUNT_AFTER_RECOVERY} (expected: 1)"
|
||
|
||
if [ "${RESCHEDULED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||
print_success "✅ TEST 2 PASSED: Missing alarm was detected and rescheduled (rescheduled=${RESCHEDULED_COUNT})!"
|
||
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single alarm confirmed after recovery (one per day maintained)"
|
||
else
|
||
print_warn "⚠️ Alarm count is ${ALARM_COUNT_AFTER_RECOVERY} (expected: 1)"
|
||
fi
|
||
elif [ "${VERIFIED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||
print_success "✅ TEST 2 PASSED: Alarm verified in AlarmManager (verified=${VERIFIED_COUNT})!"
|
||
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single alarm confirmed after recovery (one per day maintained)"
|
||
else
|
||
print_warn "⚠️ Alarm count is ${ALARM_COUNT_AFTER_RECOVERY} (expected: 1)"
|
||
fi
|
||
elif [ "${RESCHEDULED_COUNT}" -eq "0" ] 2>/dev/null && [ "${VERIFIED_COUNT}" -eq "0" ] 2>/dev/null; then
|
||
print_warn "⚠️ TEST 2: No verification/rescheduling needed (both verified=0 and rescheduled=0)"
|
||
print_info "This might mean:"
|
||
echo " - Alarm was already properly scheduled and didn't need recovery"
|
||
echo " - Recovery didn't detect any issues"
|
||
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||
print_success "✅ Single alarm still present - recovery may have verified it silently"
|
||
fi
|
||
else
|
||
print_error "TEST 2 INCONCLUSIVE: Could not find recovery result"
|
||
print_info "Recovery result: ${RECOVERY_RESULT}"
|
||
fi
|
||
|
||
print_step "5" "Verifying alarms are still scheduled in AlarmManager..."
|
||
check_alarm_status
|
||
|
||
wait_for_user
|
||
fi
|
||
|
||
# ============================================
|
||
# TEST 3: Recovery Timeout
|
||
# ============================================
|
||
if should_run_test "3" SELECTED_TESTS; then
|
||
print_header "TEST 3: Recovery Timeout"
|
||
echo "Purpose: Verify recovery times out gracefully."
|
||
echo ""
|
||
echo "Note: This test requires creating many schedules (100+)."
|
||
echo "For now, we'll verify the timeout mechanism exists."
|
||
echo ""
|
||
wait_for_user
|
||
|
||
print_step "1" "Checking recovery timeout implementation..."
|
||
if grep -q "RECOVERY_TIMEOUT_SECONDS.*2L" "${PROJECT_ROOT}/android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt"; then
|
||
print_success "Timeout is set to 2 seconds"
|
||
else
|
||
print_error "Timeout not found in code"
|
||
fi
|
||
|
||
if grep -q "withTimeout" "${PROJECT_ROOT}/android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt"; then
|
||
print_success "Timeout protection is implemented"
|
||
else
|
||
print_error "Timeout protection not found"
|
||
fi
|
||
|
||
print_info "TEST 3: Timeout mechanism verified in code"
|
||
print_info "Full test (100+ schedules) can be done manually if needed"
|
||
|
||
wait_for_user
|
||
fi
|
||
|
||
# ============================================
|
||
# TEST 4: Invalid Data Handling
|
||
# ============================================
|
||
if should_run_test "4" SELECTED_TESTS; then
|
||
print_header "TEST 4: Invalid Data Handling"
|
||
echo "Purpose: Verify invalid data doesn't crash recovery."
|
||
echo ""
|
||
echo "Note: This requires database access. We'll check if the app is debuggable."
|
||
echo ""
|
||
wait_for_user
|
||
|
||
print_step "1" "Checking if app is debuggable..."
|
||
if $ADB_BIN shell dumpsys package "${APP_ID}" | grep -q "debuggable=true"; then
|
||
print_success "App is debuggable - can access database"
|
||
|
||
print_info "Invalid data handling is tested automatically during recovery."
|
||
print_info "The ReactivationManager code includes checks for:"
|
||
echo " - Empty notification IDs (skipped with warning)"
|
||
echo " - Invalid schedule IDs (skipped with warning)"
|
||
echo " - Database errors (logged, non-fatal)"
|
||
echo ""
|
||
print_info "To manually test invalid data:"
|
||
echo " 1. Use: $ADB_BIN shell run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db"
|
||
echo " 2. Insert invalid notification: INSERT INTO notification_content (id, ...) VALUES ('', ...);"
|
||
echo " 3. Launch app and check logs for 'Skipping invalid notification'"
|
||
else
|
||
print_info "App is not debuggable - cannot access database directly"
|
||
print_info "TEST 4: Code review confirms invalid data handling exists"
|
||
print_info " - ReactivationManager.kt checks for empty IDs"
|
||
print_info " - Errors are logged but don't crash recovery"
|
||
fi
|
||
|
||
wait_for_user
|
||
fi
|
||
|
||
# ============================================
|
||
# Summary
|
||
# ============================================
|
||
print_header "Testing Complete"
|
||
|
||
echo "Test Results Summary:"
|
||
echo ""
|
||
echo "TEST 1: Cold Start Missed Detection"
|
||
echo " - ✅ PASSED if logs show 'missed=1'"
|
||
echo " - ❌ FAILED if logs show 'missed=0' or no recovery logs"
|
||
echo ""
|
||
echo "TEST 2: Future Alarm Verification/Rescheduling"
|
||
echo " - ✅ PASSED if logs show 'rescheduled=1' OR 'verified=1'"
|
||
echo " - ℹ️ INFO if both are 0 (no future alarms to check)"
|
||
echo ""
|
||
echo "TEST 3: Recovery Timeout"
|
||
echo " - Timeout mechanism verified in code"
|
||
echo ""
|
||
echo "TEST 4: Invalid Data Handling"
|
||
echo " - Requires database access (debuggable app or root)"
|
||
echo ""
|
||
|
||
print_info "All recovery logs:"
|
||
echo ""
|
||
$ADB_BIN logcat -d | grep "$REACTIVATION_TAG" | tail -20
|
||
echo ""
|
||
|
||
print_success "Phase 1 testing script complete!"
|
||
echo ""
|
||
echo "Next steps:"
|
||
echo " - Review logs above"
|
||
echo " - Verify all tests passed"
|
||
echo " - Check database if needed (debuggable app)"
|
||
echo " - Update Doc B with test results"
|
||
}
|
||
|
||
# Run main function
|
||
main "$@"
|
||
|