Files
daily-notification-plugin/docs/alarms/PHASE1-EMULATOR-TESTING.md
Matthew Raymer 3151a1cc31 feat(android): implement Phase 1 cold start recovery
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
2025-11-27 10:01:34 +00:00

687 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 alarm` before kill
- ✅ 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)!`
- ✅ 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
**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** (`emulator` command)
- **ADB** (Android Debug Bridge)
- **Gradle** (via Gradle Wrapper)
- **Java** (JDK 11+)
### Emulator Setup
1. **List available emulators**:
```bash
emulator -list-avds
```
2. **Start emulator** (choose one):
```bash
# Start in background (recommended)
emulator -avd Pixel8_API34 -no-snapshot-load &
# Or start in foreground
emulator -avd Pixel8_API34
```
3. **Wait for emulator to boot**:
```bash
adb wait-for-device
adb shell getprop sys.boot_completed
# Wait until returns "1"
```
4. **Verify emulator is connected**:
```bash
adb devices
# Should show: emulator-5554 device
```
---
## Build and Install Test App
### Option 1: Android Test App (Simpler)
```bash
# 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)
```bash
# 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
```bash
# Clear logcat buffer
adb logcat -c
```
### 2. Monitor Logs in Separate Terminal
**Keep this running in a separate terminal window**:
```bash
# 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)
```bash
# 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
```bash
# 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
```bash
# 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: <id>"
- ✅ Database shows `delivery_status = 'missed'`
- ✅ History table has recovery entry
---
## Test 2: Future Alarm Rescheduling
**Purpose**: Verify missing future alarms are rescheduled.
### Steps
```bash
# 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: <id>"
- ✅ AlarmManager shows rescheduled alarm
- ✅ No duplicate alarms created
---
## Test 3: Recovery Timeout
**Purpose**: Verify recovery times out gracefully.
### Steps
```bash
# 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
```bash
# 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)**
```javascript
// 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)**
```bash
# See "Database Manipulation" section
```
### Time Manipulation (Emulator)
**Fast-forward system time** (for testing without waiting):
```bash
# 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):
```bash
# 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**:
```bash
# 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**:
```bash
#!/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**:
```bash
# Check available AVDs
emulator -list-avds
# Kill existing emulator
pkill -f emulator
# Start with verbose logging
emulator -avd Pixel8_API34 -verbose
```
**Emulator is slow**:
```bash
# 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**:
```bash
# Restart ADB server
adb kill-server
adb start-server
# Check devices
adb devices
```
**Permission denied for database access**:
```bash
# 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**:
```bash
# 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**:
```bash
# 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
```bash
# 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](../android-implementation-directive-phase1.md) - Implementation details
- [Phase 1 Verification](./PHASE1-VERIFICATION.md) - Verification report
- [Activation Guide](./ACTIVATION-GUIDE.md) - How to use directives
- [Standalone Emulator Guide](../standalone-emulator-guide.md) - Emulator setup
---
**Status**: Emulator-verified (test-phase1.sh)
**Last Updated**: 27 November 2025