Implements cold start recovery for missed notifications and future alarm verification/rescheduling as specified in Phase 1 directive. Changes: - Add ReactivationManager.kt with cold start recovery logic - Integrate recovery into DailyNotificationPlugin.load() - Fix NotifyReceiver to always store NotificationContentEntity for recovery - Add Phase 1 emulator testing guide and verification doc - Add test-phase1.sh automated test harness Recovery behavior: - Detects missed notifications on app launch - Marks missed notifications in database - Verifies future alarms are scheduled in AlarmManager - Reschedules missing future alarms - Completes within 2-second timeout (non-blocking) Test harness: - Automated script with 4 test cases - UI prompts for plugin configuration - Log parsing for recovery results - Verified on Pixel 8 API 34 emulator Related: - Implements: android-implementation-directive-phase1.md - Requirements: docs/alarms/03-plugin-requirements.md §3.1.2 - Testing: docs/alarms/PHASE1-EMULATOR-TESTING.md - Verification: docs/alarms/PHASE1-VERIFICATION.md
16 KiB
Phase 1 Emulator Testing Guide
Author: Matthew Raymer
Date: November 2025
Status: Testing Guide
Version: 1.0.0
Purpose
This guide provides step-by-step instructions for testing Phase 1 (Cold Start Recovery) implementation on an Android emulator. All Phase 1 tests can be run entirely on an emulator using ADB commands.
Latest Known Good Run (Emulator)
Environment
- Device: Android Emulator – Pixel 8 API 34
- App ID:
com.timesafari.dailynotification - Build: Debug APK from
test-apps/android-test-app - Script:
./test-phase1.sh - Date: 27 November 2025
Observed Results
-
✅ TEST 1: Cold Start Missed Detection
- Logs show:
Marked missed notification: daily_<id>Cold start recovery complete: missed=1, rescheduled=0, verified=0, errors=0
- "Stored notification content in database" present in logs
- Alarm present in
dumpsys alarmbefore kill
- Logs show:
-
✅ TEST 2: Future Alarm Verification / Rescheduling
- Logs show:
Rescheduled alarm: daily_<id> for <time>Rescheduled missing alarm: daily_<id> at <time>Cold start recovery complete: missed=1, rescheduled=1, verified=0, errors=0
- Script output:
✅ TEST 2 PASSED: Missing future alarms were detected and rescheduled (rescheduled=1)!
- Logs show:
-
✅ TEST 3: Recovery Timeout
- Timeout protection confirmed at 2 seconds
- No blocking of app startup
-
✅ TEST 4: Invalid Data Handling
- Confirmed in code review:
- Reactivation code safely skips invalid IDs
- Errors are logged but do not crash recovery
- Confirmed in code review:
Conclusion:
Phase 1 cold-start recovery behavior is successfully verified on emulator using test-phase1.sh. This run is the reference baseline for future regressions.
Prerequisites
Required Software
- Android SDK with command line tools
- Android Emulator (
emulatorcommand) - ADB (Android Debug Bridge)
- Gradle (via Gradle Wrapper)
- Java (JDK 11+)
Emulator Setup
- List available emulators:
emulator -list-avds
- Start emulator (choose one):
# Start in background (recommended)
emulator -avd Pixel8_API34 -no-snapshot-load &
# Or start in foreground
emulator -avd Pixel8_API34
- Wait for emulator to boot:
adb wait-for-device
adb shell getprop sys.boot_completed
# Wait until returns "1"
- Verify emulator is connected:
adb devices
# Should show: emulator-5554 device
Build and Install Test App
Option 1: Android Test App (Simpler)
# Navigate to test app directory
cd test-apps/android-test-app
# Build debug APK (builds plugin automatically)
./gradlew assembleDebug
# Install on emulator
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Verify installation
adb shell pm list packages | grep timesafari
# Should show: package:com.timesafari.dailynotification
Option 2: Vue Test App (More Features)
# Navigate to Vue test app
cd test-apps/daily-notification-test
# Build Vue app
npm run build
# Sync with Capacitor
npx cap sync android
# Build Android APK
cd android
./gradlew assembleDebug
# Install on emulator
adb install -r app/build/outputs/apk/debug/app-debug.apk
Test Setup
1. Clear Logs Before Testing
# Clear logcat buffer
adb logcat -c
2. Monitor Logs in Separate Terminal
Keep this running in a separate terminal window:
# Monitor all plugin-related logs
adb logcat | grep -E "DNP-REACTIVATION|DNP-PLUGIN|DNP-NOTIFY|DailyNotification"
# Or monitor just recovery logs
adb logcat -s DNP-REACTIVATION
# Or save logs to file
adb logcat -s DNP-REACTIVATION > recovery_test.log
3. Launch App Once (Initial Setup)
# Launch app to initialize database
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# Wait a few seconds for initialization
sleep 3
Test 1: Cold Start Missed Detection
Purpose: Verify missed notifications are detected and marked.
Steps
# 1. Clear logs
adb logcat -c
# 2. Launch app
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 3. Schedule notification for 2 minutes in future
# (Use app UI or API - see "Scheduling Notifications" below)
# 4. Wait for app to schedule (check logs)
adb logcat -d | grep "DN|SCHEDULE\|DN|ALARM"
# Should show alarm scheduled
# 5. Verify alarm is scheduled
adb shell dumpsys alarm | grep -i timesafari
# Should show scheduled alarm
# 6. Kill app process (simulates OS kill, NOT force stop)
adb shell am kill com.timesafari.dailynotification
# 7. Verify app is killed
adb shell ps | grep timesafari
# Should return nothing
# 8. Wait 5 minutes (past scheduled time)
# Use: sleep 300 (or wait manually)
# Or: Set system time forward (see "Time Manipulation" below)
# 9. Launch app (cold start)
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 10. Check recovery logs immediately
adb logcat -d | grep DNP-REACTIVATION
Expected Log Output
DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)
DNP-REACTIVATION: Cold start recovery: checking for missed notifications
DNP-REACTIVATION: Marked missed notification: <id>
DNP-REACTIVATION: Cold start recovery complete: missed=1, rescheduled=0, verified=0, errors=0
DNP-REACTIVATION: App launch recovery completed: missed=1, rescheduled=0, verified=0, errors=0
Verification
# Check database (requires root or debug build)
adb shell run-as com.timesafari.dailynotification sqlite3 databases/daily_notification_plugin.db \
"SELECT id, delivery_status, scheduled_time FROM notification_content WHERE delivery_status = 'missed';"
# Or check history table
adb shell run-as com.timesafari.dailynotification sqlite3 databases/daily_notification_plugin.db \
"SELECT * FROM history WHERE kind = 'recovery' ORDER BY occurredAt DESC LIMIT 1;"
Pass Criteria
- ✅ Log shows "Cold start recovery: checking for missed notifications"
- ✅ Log shows "Marked missed notification: "
- ✅ Database shows
delivery_status = 'missed' - ✅ History table has recovery entry
Test 2: Future Alarm Rescheduling
Purpose: Verify missing future alarms are rescheduled.
Steps
# 1. Clear logs
adb logcat -c
# 2. Launch app
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 3. Schedule notification for 10 minutes in future
# (Use app UI or API)
# 4. Verify alarm is scheduled
adb shell dumpsys alarm | grep -i timesafari
# Note the request code or trigger time
# 5. Manually cancel alarm (simulate missing alarm)
# Find the alarm request code from dumpsys output
# Then cancel using PendingIntent (requires root or app code)
# OR: Use app UI to cancel if available
# Alternative: Use app code to cancel
# (This test may require app modification to add cancel button)
# 6. Verify alarm is cancelled
adb shell dumpsys alarm | grep -i timesafari
# Should show no alarms (or fewer alarms)
# 7. Launch app (triggers recovery)
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 8. Check recovery logs
adb logcat -d | grep DNP-REACTIVATION
# 9. Verify alarm is rescheduled
adb shell dumpsys alarm | grep -i timesafari
# Should show rescheduled alarm
Expected Log Output
DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)
DNP-REACTIVATION: Cold start recovery: checking for missed notifications
DNP-REACTIVATION: Rescheduled missing alarm: <id> at <timestamp>
DNP-REACTIVATION: Cold start recovery complete: missed=0, rescheduled=1, verified=0, errors=0
Pass Criteria
- ✅ Log shows "Rescheduled missing alarm: "
- ✅ AlarmManager shows rescheduled alarm
- ✅ No duplicate alarms created
Test 3: Recovery Timeout
Purpose: Verify recovery times out gracefully.
Steps
# 1. Clear logs
adb logcat -c
# 2. Create large number of schedules (100+)
# This requires app modification or database manipulation
# See "Database Manipulation" section below
# 3. Launch app
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 4. Check logs immediately
adb logcat -d | grep DNP-REACTIVATION
Expected Behavior
- ✅ Recovery completes within 2 seconds OR times out
- ✅ App doesn't crash
- ✅ Partial recovery logged if timeout occurs
Pass Criteria
- ✅ Recovery doesn't block app launch
- ✅ No app crash
- ✅ Timeout logged if occurs
Note: This test may be difficult to execute without creating many schedules. Consider testing with smaller numbers first (10, 50 schedules) to verify behavior.
Test 4: Invalid Data Handling
Purpose: Verify invalid data doesn't crash recovery.
Steps
# 1. Clear logs
adb logcat -c
# 2. Manually insert invalid notification (empty ID) into database
# See "Database Manipulation" section below
# 3. Launch app
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# 4. Check logs
adb logcat -d | grep DNP-REACTIVATION
Expected Log Output
DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)
DNP-REACTIVATION: Cold start recovery: checking for missed notifications
DNP-REACTIVATION: Skipping invalid notification: empty ID
DNP-REACTIVATION: Cold start recovery complete: missed=0, rescheduled=0, verified=0, errors=0
Pass Criteria
- ✅ Invalid notification skipped
- ✅ Warning logged
- ✅ Recovery continues normally
- ✅ App doesn't crash
Helper Scripts and Commands
Scheduling Notifications
Option 1: Use App UI
- Launch app
- Use "Schedule Notification" button
- Set time to 2-5 minutes in future
Option 2: Use Capacitor API (if test app has console)
// In browser console or test app
const { DailyNotification } = Plugins.DailyNotification;
await DailyNotification.scheduleDailyNotification({
schedule: "*/2 * * * *", // Every 2 minutes
title: "Test Notification",
body: "Testing Phase 1 recovery"
});
Option 3: Direct Database Insert (Advanced)
# See "Database Manipulation" section
Time Manipulation (Emulator)
Fast-forward system time (for testing without waiting):
# Get current time
adb shell date +%s
# Set time forward (e.g., 5 minutes)
adb shell date -s @$(($(adb shell date +%s) + 300))
# Or set specific time
adb shell date -s "2025-11-15 14:30:00"
Note: Some emulators may not support time changes. Test with actual waiting if time manipulation doesn't work.
Database Manipulation
Access database (requires root or debug build):
# Check if app is debuggable
adb shell dumpsys package com.timesafari.dailynotification | grep debuggable
# Access database
adb shell run-as com.timesafari.dailynotification sqlite3 databases/daily_notification_plugin.db
# Example: Insert test notification
sqlite> INSERT INTO notification_content (
id, plugin_version, title, body, scheduled_time,
delivery_status, delivery_attempts, last_delivery_attempt,
created_at, updated_at, ttl_seconds, priority,
vibration_enabled, sound_enabled
) VALUES (
'test_notification_1', '1.1.0', 'Test', 'Test body',
$(($(date +%s) * 1000 - 300000)), -- 5 minutes ago
'pending', 0, 0,
$(date +%s) * 1000, $(date +%s) * 1000,
604800, 0, 1, 1
);
# Example: Insert invalid notification (empty ID)
sqlite> INSERT INTO notification_content (
id, plugin_version, title, body, scheduled_time,
delivery_status, delivery_attempts, last_delivery_attempt,
created_at, updated_at, ttl_seconds, priority,
vibration_enabled, sound_enabled
) VALUES (
'', '1.1.0', 'Invalid', 'Invalid body',
$(($(date +%s) * 1000 - 300000)),
'pending', 0, 0,
$(date +%s) * 1000, $(date +%s) * 1000,
604800, 0, 1, 1
);
# Example: Create many schedules (for timeout test)
sqlite> .read create_many_schedules.sql
# (Create SQL file with 100+ INSERT statements)
Log Filtering
Useful log filters:
# Recovery-specific logs
adb logcat -s DNP-REACTIVATION
# All plugin logs
adb logcat | grep -E "DNP-|DailyNotification"
# Recovery + scheduling logs
adb logcat | grep -E "DNP-REACTIVATION|DN|SCHEDULE"
# Save logs to file
adb logcat -d > phase1_test_$(date +%Y%m%d_%H%M%S).log
Complete Test Sequence
Run all tests in sequence:
#!/bin/bash
# Phase 1 Complete Test Sequence
PACKAGE="com.timesafari.dailynotification"
ACTIVITY="${PACKAGE}/.MainActivity"
echo "=== Phase 1 Testing on Emulator ==="
echo ""
# Setup
echo "1. Setting up emulator..."
adb wait-for-device
adb logcat -c
# Test 1: Cold Start Missed Detection
echo ""
echo "=== Test 1: Cold Start Missed Detection ==="
echo "1. Launch app and schedule notification for 2 minutes"
adb shell am start -n $ACTIVITY
echo " (Use app UI to schedule notification)"
read -p "Press Enter after scheduling notification..."
echo "2. Killing app process..."
adb shell am kill $PACKAGE
echo "3. Waiting 5 minutes (or set time forward)..."
echo " (You can set time forward: adb shell date -s ...)"
read -p "Press Enter after waiting 5 minutes..."
echo "4. Launching app (cold start)..."
adb shell am start -n $ACTIVITY
sleep 2
echo "5. Checking recovery logs..."
adb logcat -d | grep DNP-REACTIVATION
echo ""
echo "=== Test 1 Complete ==="
read -p "Press Enter to continue to Test 2..."
# Test 2: Future Alarm Rescheduling
echo ""
echo "=== Test 2: Future Alarm Rescheduling ==="
echo "1. Schedule notification for 10 minutes"
adb shell am start -n $ACTIVITY
echo " (Use app UI to schedule notification)"
read -p "Press Enter after scheduling..."
echo "2. Verify alarm scheduled..."
adb shell dumpsys alarm | grep -i timesafari
echo "3. Cancel alarm (use app UI or see Database Manipulation)"
read -p "Press Enter after cancelling alarm..."
echo "4. Launch app (triggers recovery)..."
adb shell am start -n $ACTIVITY
sleep 2
echo "5. Check recovery logs..."
adb logcat -d | grep DNP-REACTIVATION
echo "6. Verify alarm rescheduled..."
adb shell dumpsys alarm | grep -i timesafari
echo ""
echo "=== Test 2 Complete ==="
echo ""
echo "=== All Tests Complete ==="
Troubleshooting
Emulator Issues
Emulator won't start:
# Check available AVDs
emulator -list-avds
# Kill existing emulator
pkill -f emulator
# Start with verbose logging
emulator -avd Pixel8_API34 -verbose
Emulator is slow:
# Use hardware acceleration
emulator -avd Pixel8_API34 -accel on -gpu host
# Allocate more RAM
emulator -avd Pixel8_API34 -memory 4096
ADB Issues
ADB not detecting emulator:
# Restart ADB server
adb kill-server
adb start-server
# Check devices
adb devices
Permission denied for database access:
# Check if app is debuggable
adb shell dumpsys package com.timesafari.dailynotification | grep debuggable
# If not debuggable, rebuild with debug signing
cd test-apps/android-test-app
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
App Issues
App won't launch:
# Check if app is installed
adb shell pm list packages | grep timesafari
# Uninstall and reinstall
adb uninstall com.timesafari.dailynotification
adb install -r app/build/outputs/apk/debug/app-debug.apk
No logs appearing:
# Check logcat buffer size
adb logcat -G 10M
# Clear and monitor
adb logcat -c
adb logcat -s DNP-REACTIVATION
Expected Test Results Summary
| Test | Expected Outcome | Verification Method |
|---|---|---|
| Test 1 | Missed notification detected and marked | Logs + Database query |
| Test 2 | Missing alarm rescheduled | Logs + AlarmManager check |
| Test 3 | Recovery times out gracefully | Logs (timeout message) |
| Test 4 | Invalid data skipped | Logs (warning message) |
Quick Reference
Essential Commands
# Start emulator
emulator -avd Pixel8_API34 &
# Build and install
cd test-apps/android-test-app
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Launch app
adb shell am start -n com.timesafari.dailynotification/.MainActivity
# Kill app
adb shell am kill com.timesafari.dailynotification
# Monitor logs
adb logcat -s DNP-REACTIVATION
# Check alarms
adb shell dumpsys alarm | grep -i timesafari
Related Documentation
- Phase 1 Directive - Implementation details
- Phase 1 Verification - Verification report
- Activation Guide - How to use directives
- Standalone Emulator Guide - Emulator setup
Status: Emulator-verified (test-phase1.sh)
Last Updated: 27 November 2025