rename 'docs' directory to 'doc'
This commit is contained in:
354
doc/testing/BOOT_RECEIVER_GUIDE.md
Normal file
354
doc/testing/BOOT_RECEIVER_GUIDE.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# Boot Receiver Testing Guide for DailyNotification Plugin
|
||||
|
||||
**Created**: 2025-10-14 05:41:27 UTC
|
||||
**Author**: Matthew Raymer
|
||||
**Status**: ✅ **PRODUCTION READY**
|
||||
|
||||
## 🎯 **Overview**
|
||||
|
||||
This guide provides comprehensive testing procedures for the **fixed BootReceiver** that now properly handles Direct Boot and Android 10+ requirements. The BootReceiver works alongside the app startup recovery for maximum reliability.
|
||||
|
||||
## 🔧 **What Was Fixed**
|
||||
|
||||
### **1. AndroidManifest.xml Updates**
|
||||
```xml
|
||||
<receiver
|
||||
android:name="org.timesafari.dailynotification.BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:directBootAware="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<!-- Delivered very early after reboot (before unlock) -->
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
<!-- Delivered after the user unlocks / credential-encrypted storage is available -->
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<!-- Delivered after app update; great for rescheduling alarms without reboot -->
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
```
|
||||
|
||||
**Key Changes:**
|
||||
- ✅ Added `android:exported="true"` (required for API 31+)
|
||||
- ✅ Added `android:directBootAware="true"` (handles Direct Boot)
|
||||
- ✅ Added `LOCKED_BOOT_COMPLETED` (early boot recovery)
|
||||
- ✅ Removed `PACKAGE_REPLACED` (not needed for our use case)
|
||||
|
||||
### **2. BootReceiver Implementation Updates**
|
||||
```java
|
||||
// Now handles three boot events:
|
||||
case ACTION_LOCKED_BOOT_COMPLETED:
|
||||
handleLockedBootCompleted(context);
|
||||
break;
|
||||
|
||||
case ACTION_BOOT_COMPLETED:
|
||||
handleBootCompleted(context);
|
||||
break;
|
||||
|
||||
case ACTION_MY_PACKAGE_REPLACED:
|
||||
handlePackageReplaced(context, intent);
|
||||
break;
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- ✅ **Direct Boot Safe**: Uses device protected storage context
|
||||
- ✅ **Early Recovery**: Handles locked boot completion
|
||||
- ✅ **Full Recovery**: Handles unlocked boot completion
|
||||
- ✅ **Update Recovery**: Handles app updates
|
||||
|
||||
### **3. Exact Alarm Permission Handling**
|
||||
```java
|
||||
// Improved exact alarm settings
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (alarmManager.canScheduleExactAlarms()) {
|
||||
Log.d(TAG, "Exact alarms already allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
||||
intent.setData(Uri.parse("package:" + getContext().getPackageName()));
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 **Testing Procedures**
|
||||
|
||||
### **Test 1: Boot Receiver Registration**
|
||||
|
||||
**Objective**: Verify BootReceiver is properly registered with all required actions
|
||||
|
||||
**Steps**:
|
||||
```bash
|
||||
# Check BootReceiver registration
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A10 -B5 BootReceiver"
|
||||
```
|
||||
|
||||
**Expected Output**:
|
||||
```
|
||||
android.intent.action.LOCKED_BOOT_COMPLETED:
|
||||
a440fcf org.timesafari.dailynotification/.BootReceiver filter 4e5fd5c
|
||||
Action: "android.intent.action.LOCKED_BOOT_COMPLETED"
|
||||
Action: "android.intent.action.BOOT_COMPLETED"
|
||||
Action: "android.intent.action.MY_PACKAGE_REPLACED"
|
||||
mPriority=1000, mOrder=0, mHasStaticPartialTypes=false, mHasDynamicPartialTypes=false
|
||||
```
|
||||
|
||||
**Success Criteria**:
|
||||
- ✅ BootReceiver is registered
|
||||
- ✅ All three actions are present
|
||||
- ✅ Priority is set to 1000
|
||||
- ✅ Component is enabled
|
||||
|
||||
### **Test 2: Real Device Reboot Test**
|
||||
|
||||
**Objective**: Test BootReceiver with actual device reboot
|
||||
|
||||
**Steps**:
|
||||
```bash
|
||||
# 1. Schedule notification
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
# Tap "Test Notification" (5 minutes from now)
|
||||
|
||||
# 2. Verify initial scheduling
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
# Should show scheduled alarm
|
||||
|
||||
# 3. Perform REAL reboot (not emulator soft restart)
|
||||
adb reboot
|
||||
|
||||
# 4. Wait for full boot completion (2-3 minutes)
|
||||
# Wait for boot animation to complete
|
||||
# Wait for home screen to appear
|
||||
|
||||
# 5. Check BootReceiver logs
|
||||
adb logcat -d | grep -i "bootreceiver" | tail -10
|
||||
```
|
||||
|
||||
**Expected Log Messages**:
|
||||
```
|
||||
BootReceiver: Received broadcast: android.intent.action.LOCKED_BOOT_COMPLETED
|
||||
BootReceiver: Locked boot completed - preparing for recovery
|
||||
BootReceiver: Received broadcast: android.intent.action.BOOT_COMPLETED
|
||||
BootReceiver: Device boot completed - restoring notifications
|
||||
BootReceiver: Found X notifications to recover
|
||||
BootReceiver: Notification recovery completed: X/X recovered
|
||||
```
|
||||
|
||||
**Success Criteria**:
|
||||
- ✅ `LOCKED_BOOT_COMPLETED` is received
|
||||
- ✅ `BOOT_COMPLETED` is received
|
||||
- ✅ Recovery process completes successfully
|
||||
- ✅ Alarms are restored
|
||||
|
||||
### **Test 3: App Update Recovery Test**
|
||||
|
||||
**Objective**: Test BootReceiver with app update (simulated)
|
||||
|
||||
**Steps**:
|
||||
```bash
|
||||
# 1. Schedule notification
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
# Tap "Test Notification" (5 minutes from now)
|
||||
|
||||
# 2. Verify initial scheduling
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# 3. Update app (triggers MY_PACKAGE_REPLACED)
|
||||
cd android && ./gradlew assembleDebug
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# 4. Check recovery logs
|
||||
adb logcat -d | grep -i "bootreceiver\|recovery" | tail -10
|
||||
```
|
||||
|
||||
**Expected Log Messages**:
|
||||
```
|
||||
BootReceiver: Received broadcast: android.intent.action.MY_PACKAGE_REPLACED
|
||||
BootReceiver: Package replaced - restoring notifications
|
||||
BootReceiver: Device boot completed - restoring notifications
|
||||
BootReceiver: Found X notifications to recover
|
||||
BootReceiver: Notification recovery completed: X/X recovered
|
||||
```
|
||||
|
||||
**Success Criteria**:
|
||||
- ✅ `MY_PACKAGE_REPLACED` is received
|
||||
- ✅ Recovery process completes successfully
|
||||
- ✅ Alarms are restored after update
|
||||
|
||||
### **Test 4: Direct Boot Compatibility Test**
|
||||
|
||||
**Objective**: Verify Direct Boot handling works correctly
|
||||
|
||||
**Steps**:
|
||||
```bash
|
||||
# 1. Schedule notification
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
# Tap "Test Notification" (5 minutes from now)
|
||||
|
||||
# 2. Reboot device
|
||||
adb reboot
|
||||
|
||||
# 3. Check logs for Direct Boot handling
|
||||
adb logcat -d | grep -i "locked.*boot\|direct.*boot" | tail -5
|
||||
```
|
||||
|
||||
**Expected Log Messages**:
|
||||
```
|
||||
BootReceiver: Received broadcast: android.intent.action.LOCKED_BOOT_COMPLETED
|
||||
BootReceiver: Locked boot completed - preparing for recovery
|
||||
BootReceiver: Locked boot completed - ready for full recovery on unlock
|
||||
```
|
||||
|
||||
**Success Criteria**:
|
||||
- ✅ `LOCKED_BOOT_COMPLETED` is handled
|
||||
- ✅ Direct Boot context is used
|
||||
- ✅ No errors during locked boot phase
|
||||
|
||||
### **Test 5: Exact Alarm Permission Test**
|
||||
|
||||
**Objective**: Test exact alarm permission handling
|
||||
|
||||
**Steps**:
|
||||
```bash
|
||||
# 1. Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# 2. Tap "Exact Alarm Settings" button
|
||||
# Should open exact alarm settings if needed
|
||||
|
||||
# 3. Check permission status
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
**Expected Behavior**:
|
||||
- ✅ Opens exact alarm settings if permission not granted
|
||||
- ✅ Shows current permission status
|
||||
- ✅ Allows scheduling exact alarms
|
||||
|
||||
## 🔍 **Debugging Commands**
|
||||
|
||||
### **Check BootReceiver Status**
|
||||
```bash
|
||||
# Verify registration
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A10 -B5 BootReceiver"
|
||||
|
||||
# Check if enabled
|
||||
adb shell "pm list packages -d | grep timesafari"
|
||||
# Should return nothing (app not disabled)
|
||||
|
||||
# Check permissions
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A5 -B5 permission"
|
||||
```
|
||||
|
||||
### **Monitor Boot Events**
|
||||
```bash
|
||||
# Real-time boot monitoring
|
||||
adb logcat | grep -i "bootreceiver\|recovery"
|
||||
|
||||
# Check boot completion
|
||||
adb shell getprop sys.boot_completed
|
||||
# Should return "1" when boot is complete
|
||||
|
||||
# Check system boot time
|
||||
adb shell "dumpsys alarm | head -20"
|
||||
```
|
||||
|
||||
### **Verify Alarm Restoration**
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
|
||||
# Check alarm manager state
|
||||
adb shell "dumpsys alarm | grep -A5 -B5 timesafari"
|
||||
```
|
||||
|
||||
## 🚨 **Troubleshooting**
|
||||
|
||||
### **Issue 1: BootReceiver Not Triggered**
|
||||
|
||||
**Symptoms**: No boot receiver logs after reboot
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check if receiver is registered
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep BootReceiver"
|
||||
|
||||
# Check if app is disabled
|
||||
adb shell "pm list packages -d | grep timesafari"
|
||||
|
||||
# Check if permissions are granted
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep RECEIVE_BOOT_COMPLETED"
|
||||
```
|
||||
|
||||
### **Issue 2: Direct Boot Errors**
|
||||
|
||||
**Symptoms**: Errors during locked boot completion
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check Direct Boot compatibility
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep directBootAware"
|
||||
|
||||
# Check device protected storage
|
||||
adb shell "ls -la /data/user_de/0/org.timesafari.dailynotification/"
|
||||
```
|
||||
|
||||
### **Issue 3: Exact Alarm Permission Denied**
|
||||
|
||||
**Symptoms**: Cannot schedule exact alarms
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check exact alarm permission
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
|
||||
# Open exact alarm settings
|
||||
adb shell am start -a android.settings.REQUEST_SCHEDULE_EXACT_ALARM --es android.provider.extra.APP_PACKAGE org.timesafari.dailynotification
|
||||
```
|
||||
|
||||
## 📊 **Success Metrics**
|
||||
|
||||
### **Boot Receiver Performance**
|
||||
- **Registration Time**: < 1 second
|
||||
- **Recovery Time**: < 500ms for typical notification sets
|
||||
- **Memory Usage**: Minimal (only loads notification metadata)
|
||||
- **Battery Impact**: Negligible (runs only during boot)
|
||||
|
||||
### **Reliability Metrics**
|
||||
- **Boot Event Detection**: 100% for supported Android versions
|
||||
- **Recovery Success Rate**: 100% for valid notifications
|
||||
- **Direct Boot Compatibility**: 100% on Android 7+ devices
|
||||
- **App Update Recovery**: 100% success rate
|
||||
|
||||
## 🎯 **Best Practices**
|
||||
|
||||
### **Testing Environment**
|
||||
- Use **real devices** for most accurate results
|
||||
- Test on **multiple Android versions** (7, 8, 9, 10, 11, 12, 13+)
|
||||
- Test on **different OEMs** (Samsung, Huawei, Xiaomi, etc.)
|
||||
- Test with **different boot scenarios** (normal boot, recovery boot, etc.)
|
||||
|
||||
### **Production Deployment**
|
||||
- **Monitor boot receiver logs** in production
|
||||
- **Track recovery success rates** across devices
|
||||
- **Handle edge cases** gracefully (corrupted data, storage issues)
|
||||
- **Provide fallback mechanisms** (app startup recovery)
|
||||
|
||||
## 🏆 **Conclusion**
|
||||
|
||||
The **fixed BootReceiver** now provides:
|
||||
|
||||
- ✅ **Universal Compatibility**: Works on all Android versions
|
||||
- ✅ **Direct Boot Support**: Handles locked boot completion
|
||||
- ✅ **App Update Recovery**: Restores alarms after updates
|
||||
- ✅ **Exact Alarm Handling**: Proper permission management
|
||||
- ✅ **Comprehensive Logging**: Full visibility into recovery process
|
||||
|
||||
**Combined with app startup recovery, this creates a rock-solid notification system that survives reboots, updates, and OEM quirks!** 🚀
|
||||
|
||||
## 📚 **Related Documentation**
|
||||
|
||||
- [App Startup Recovery Solution](app-startup-recovery-solution.md)
|
||||
- [Reboot Testing Procedures](reboot-testing-procedure.md)
|
||||
- [Notification Testing Guide](notification-testing-procedures.md)
|
||||
- [Testing Quick Reference](testing-quick-reference.md)
|
||||
1109
doc/testing/COMPREHENSIVE_GUIDE.md
Normal file
1109
doc/testing/COMPREHENSIVE_GUIDE.md
Normal file
File diff suppressed because it is too large
Load Diff
450
doc/testing/EMULATOR_GUIDE.md
Normal file
450
doc/testing/EMULATOR_GUIDE.md
Normal file
@@ -0,0 +1,450 @@
|
||||
# Running Android App in Standalone Emulator (Without Android Studio)
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Last Updated**: 2026-02-05
|
||||
**Version**: 1.1.0
|
||||
|
||||
## Overview
|
||||
|
||||
This guide demonstrates how to run the DailyNotification plugin test app in a standalone Android emulator without using Android Studio. This method is useful for development, CI/CD pipelines, and resource-constrained environments.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Software
|
||||
- **Android SDK** with command line tools
|
||||
- **Android Emulator** (`emulator` command)
|
||||
- **ADB** (Android Debug Bridge)
|
||||
- **Gradle** (via Gradle Wrapper)
|
||||
- **Node.js** and **npm** (for TypeScript compilation)
|
||||
|
||||
### System Requirements
|
||||
- **RAM**: 4GB minimum, 8GB recommended
|
||||
- **Storage**: 2GB free space for emulator
|
||||
- **OS**: Linux, macOS, or Windows with WSL
|
||||
|
||||
## Checking and Installing Prerequisites
|
||||
|
||||
### How to check
|
||||
|
||||
Run these in a terminal. If a command is missing or a check fails, use the install steps below.
|
||||
|
||||
| Requirement | How to check |
|
||||
|------------------|--------------|
|
||||
| **Node.js** | `node --version` (v14+ recommended; test app may require 20+) |
|
||||
| **npm** | `npm --version` |
|
||||
| **Java** | `java -version` (Java 11+; build scripts expect 11+) |
|
||||
| **ANDROID_HOME** | `echo $ANDROID_HOME` (must be set to your Android SDK root) |
|
||||
| **adb** | `adb version` (must be on PATH; usually `$ANDROID_HOME/platform-tools/adb`) |
|
||||
| **emulator** | `emulator -version` (must be on PATH; usually `$ANDROID_HOME/emulator/emulator`) |
|
||||
| **At least one AVD** | `emulator -list-avds` (must list at least one device name) |
|
||||
|
||||
**Project script:** From the repo root you can run:
|
||||
|
||||
```bash
|
||||
node scripts/check-environment.js
|
||||
```
|
||||
|
||||
This checks Node, npm, Java, and `ANDROID_HOME`. It does **not** check `adb`, `emulator`, or AVDs—verify those manually as above.
|
||||
|
||||
### How to install
|
||||
|
||||
- **Node.js and npm**
|
||||
- Install from [nodejs.org](https://nodejs.org/) (LTS), or on macOS: `brew install node`.
|
||||
|
||||
- **Java (JDK 11+)**
|
||||
- macOS: `brew install openjdk@17` and follow the caveats to link (e.g. `sudo ln -sfn $(brew --prefix)/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk`).
|
||||
- Or install [Eclipse Temurin](https://adoptium.net/) / [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) and ensure `java` and `javac` are on your PATH.
|
||||
|
||||
- **Android SDK (without Android Studio)**
|
||||
1. Download the [Command-line tools only](https://developer.android.com/studio#command-tools) package for your OS.
|
||||
2. Create an SDK directory, e.g. `mkdir -p ~/android-sdk` and extract the zip so that you have `~/android-sdk/cmdline-tools/latest/` (the `bin` folder with `sdkmanager` and `avdmanager` must be inside `cmdline-tools/latest/`).
|
||||
3. Set environment variables (add to `~/.zshrc` or `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
export ANDROID_HOME=$HOME/android-sdk
|
||||
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator
|
||||
```
|
||||
|
||||
4. Install required SDK packages (accept licenses when prompted):
|
||||
|
||||
```bash
|
||||
sdkmanager "platform-tools"
|
||||
sdkmanager "emulator"
|
||||
sdkmanager "platforms;android-35"
|
||||
sdkmanager "build-tools;35.0.0"
|
||||
```
|
||||
|
||||
Install a system image that matches your host CPU:
|
||||
- **Apple Silicon (M1/M2/M3, aarch64):** `sdkmanager "system-images;android-35;google_apis;arm64-v8a"`
|
||||
- **Intel Mac / Windows / Linux (x86_64):** `sdkmanager "system-images;android-35;google_apis;x86_64"`
|
||||
|
||||
5. Create at least one AVD (use the same image type you installed):
|
||||
|
||||
**Apple Silicon:**
|
||||
```bash
|
||||
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;arm64-v8a" -d "pixel_8"
|
||||
```
|
||||
|
||||
**Intel / x86_64:**
|
||||
```bash
|
||||
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;x86_64" -d "pixel_8"
|
||||
```
|
||||
|
||||
Then start the emulator with: `emulator -avd Pixel8_API35 -no-snapshot-load &` and use `adb wait-for-device` before building/installing the app.
|
||||
|
||||
- **Gradle**
|
||||
The project uses the Gradle Wrapper (`gradlew`) inside the app’s `android` directory. No separate Gradle install is needed.
|
||||
|
||||
After installing, run the checks again to confirm `adb`, `emulator`, and `emulator -list-avds` work.
|
||||
|
||||
## Step-by-Step Process
|
||||
|
||||
### 1. Check Available Emulators
|
||||
|
||||
```bash
|
||||
# List available Android Virtual Devices (AVDs)
|
||||
emulator -list-avds
|
||||
|
||||
# Example output:
|
||||
# Pixel8_API35
|
||||
```
|
||||
|
||||
### 2. Start the Emulator
|
||||
|
||||
```bash
|
||||
# Start emulator in background (recommended)
|
||||
emulator -avd Pixel8_API35 -no-snapshot-load &
|
||||
|
||||
# Alternative: Start in foreground
|
||||
emulator -avd Pixel8_API35
|
||||
```
|
||||
|
||||
**Flags Explained:**
|
||||
- `-avd Pixel8_API35` - Specifies the AVD to use
|
||||
- `-no-snapshot-load` - Forces fresh boot (recommended for testing)
|
||||
- `&` - Runs in background (optional)
|
||||
|
||||
### 3. Wait for Emulator to Boot
|
||||
|
||||
```bash
|
||||
# Wait for emulator to be ready
|
||||
adb wait-for-device
|
||||
|
||||
# Verify emulator is running
|
||||
adb devices
|
||||
|
||||
# Example output:
|
||||
# List of devices attached
|
||||
# emulator-5554 device
|
||||
```
|
||||
|
||||
### 4. Build the Plugin and Test App
|
||||
|
||||
```bash
|
||||
# Navigate to project directory
|
||||
cd /path/to/daily-notification-plugin
|
||||
|
||||
# Build TypeScript and native code
|
||||
./scripts/build-native.sh --platform android
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Compiles TypeScript to JavaScript
|
||||
- Builds Android native code
|
||||
- Creates plugin AAR files
|
||||
- Builds test app APK
|
||||
|
||||
### 5. Build Debug APK (Required for Installation)
|
||||
|
||||
```bash
|
||||
# Navigate to Android directory
|
||||
cd android
|
||||
|
||||
# Build debug version (includes debug signing)
|
||||
./gradlew :app:assembleDebug
|
||||
```
|
||||
|
||||
**Why Debug APK:**
|
||||
- **Debug signing** - Automatically signed for installation
|
||||
- **No certificates needed** - Uses default debug keystore
|
||||
- **Faster builds** - No optimization, faster compilation
|
||||
|
||||
### 6. Install APK on Emulator
|
||||
|
||||
```bash
|
||||
# Install the debug APK
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# Alternative: Install with replacement
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
**Installation Options:**
|
||||
- `adb install` - Install new app
|
||||
- `adb install -r` - Replace existing app
|
||||
- `adb install -t` - Allow test APKs
|
||||
|
||||
### 7. Launch the App
|
||||
|
||||
```bash
|
||||
# Launch the app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# Alternative: Launch with specific intent
|
||||
adb shell am start -a android.intent.action.MAIN -n org.timesafari.dailynotification/.MainActivity
|
||||
```
|
||||
|
||||
### 8. Monitor App Logs
|
||||
|
||||
```bash
|
||||
# View all logs
|
||||
adb logcat
|
||||
|
||||
# Filter for specific tags
|
||||
adb logcat -s "Capacitor" "DailyNotification" "Console"
|
||||
|
||||
# View logs for specific process
|
||||
adb logcat --pid=<PID>
|
||||
|
||||
# Clear logs and view new ones
|
||||
adb logcat -c && adb logcat
|
||||
```
|
||||
|
||||
## Complete Command Sequence
|
||||
|
||||
### Quick Start (Copy-Paste Ready)
|
||||
|
||||
```bash
|
||||
# 1. Start emulator
|
||||
emulator -avd Pixel8_API35 -no-snapshot-load &
|
||||
|
||||
# 2. Wait for emulator
|
||||
adb wait-for-device
|
||||
|
||||
# 3. Build everything
|
||||
./scripts/build-native.sh --platform android
|
||||
|
||||
# 4. Build debug APK
|
||||
cd android && ./gradlew :app:assembleDebug
|
||||
|
||||
# 5. Install APK
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# 6. Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# 7. Monitor logs
|
||||
adb logcat -s "Capacitor" "DailyNotification" "Console"
|
||||
```
|
||||
|
||||
## Alternative Methods
|
||||
|
||||
### Method 1: Using Capacitor CLI
|
||||
|
||||
```bash
|
||||
# Build and run in one command
|
||||
npx cap run android
|
||||
|
||||
# This will:
|
||||
# - Build the plugin
|
||||
# - Sync web assets
|
||||
# - Build and install APK
|
||||
# - Launch the app
|
||||
```
|
||||
|
||||
### Method 2: Direct Gradle Commands
|
||||
|
||||
```bash
|
||||
# Build and install directly
|
||||
cd android
|
||||
./gradlew :app:assembleDebug
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
```
|
||||
|
||||
### Method 3: Using Monkey (Alternative Launch)
|
||||
|
||||
```bash
|
||||
# Install and launch with Monkey
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell monkey -p org.timesafari.dailynotification -c android.intent.category.LAUNCHER 1
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Emulator Won't Start
|
||||
```bash
|
||||
# Check available AVDs
|
||||
emulator -list-avds
|
||||
|
||||
# Check emulator process
|
||||
ps aux | grep emulator
|
||||
|
||||
# Kill existing emulator
|
||||
pkill -f emulator
|
||||
|
||||
# Start with verbose logging
|
||||
emulator -avd Pixel8_API35 -verbose
|
||||
```
|
||||
|
||||
#### "x86_64 is not supported by the QEMU2 emulator on aarch64 host"
|
||||
On Apple Silicon (M1/M2/M3), the emulator cannot run x86_64 system images. Use an ARM64 image and AVD instead:
|
||||
|
||||
```bash
|
||||
sdkmanager "system-images;android-35;google_apis;arm64-v8a"
|
||||
avdmanager delete avd -n Pixel8_API35 # if you already created an x86_64 AVD
|
||||
avdmanager create avd -n Pixel8_API35 -k "system-images;android-35;google_apis;arm64-v8a" -d "pixel_8"
|
||||
emulator -avd Pixel8_API35 -no-snapshot-load
|
||||
```
|
||||
|
||||
#### ADB Connection Issues
|
||||
```bash
|
||||
# Check ADB connection
|
||||
adb devices
|
||||
|
||||
# Restart ADB server
|
||||
adb kill-server
|
||||
adb start-server
|
||||
|
||||
# Check ADB version
|
||||
adb version
|
||||
```
|
||||
|
||||
#### APK Installation Fails
|
||||
```bash
|
||||
# Check if app is already installed
|
||||
adb shell pm list packages | grep timesafari
|
||||
|
||||
# Uninstall existing app
|
||||
adb uninstall org.timesafari.dailynotification
|
||||
|
||||
# Install with force
|
||||
adb install -r -t app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
#### Build Failures
|
||||
```bash
|
||||
# Clean build
|
||||
cd android && ./gradlew clean
|
||||
|
||||
# Rebuild
|
||||
./gradlew :app:assembleDebug
|
||||
|
||||
# Check Gradle daemon
|
||||
./gradlew --status
|
||||
```
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
#### Emulator Performance
|
||||
```bash
|
||||
# Start with hardware acceleration
|
||||
emulator -avd Pixel8_API35 -accel on
|
||||
|
||||
# Start with specific RAM allocation
|
||||
emulator -avd Pixel8_API35 -memory 2048
|
||||
|
||||
# Start with GPU acceleration
|
||||
emulator -avd Pixel8_API35 -gpu host
|
||||
```
|
||||
|
||||
#### Build Performance
|
||||
```bash
|
||||
# Enable Gradle daemon
|
||||
echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties
|
||||
|
||||
# Increase memory
|
||||
echo "org.gradle.jvmargs=-Xmx4g" >> ~/.gradle/gradle.properties
|
||||
|
||||
# Enable parallel builds
|
||||
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
|
||||
```
|
||||
|
||||
## Expected Results
|
||||
|
||||
### Successful App Launch
|
||||
When the app launches successfully, you should see:
|
||||
|
||||
```bash
|
||||
# ADB output
|
||||
Starting: Intent { cmp=org.timesafari.dailynotification/.MainActivity }
|
||||
|
||||
# Logcat output
|
||||
D Capacitor: Starting BridgeActivity
|
||||
D Capacitor: Registering plugin instance: CapacitorCookies
|
||||
D Capacitor: Registering plugin instance: WebView
|
||||
D Capacitor: Registering plugin instance: CapacitorHttp
|
||||
D Capacitor: Loading app at https://localhost
|
||||
D Capacitor: App started
|
||||
D Capacitor: App resumed
|
||||
I Capacitor/Console: Script loading...
|
||||
I Capacitor/Console: Creating mock DailyNotification plugin...
|
||||
I Capacitor/Console: Functions attached to window: [object Object]
|
||||
```
|
||||
|
||||
### App Interface
|
||||
The app should display:
|
||||
- **Title**: "🔔 DailyNotification Plugin Test"
|
||||
- **Three buttons**: "Test Plugin", "Configure Plugin", "Check Status"
|
||||
- **Status area**: Shows test results and plugin status
|
||||
|
||||
## Benefits of Standalone Approach
|
||||
|
||||
### Advantages
|
||||
- ✅ **No Android Studio** - Pure command line workflow
|
||||
- ✅ **Faster startup** - No IDE overhead
|
||||
- ✅ **CI/CD friendly** - Works in automated environments
|
||||
- ✅ **Resource efficient** - Lower memory usage
|
||||
- ✅ **Scriptable** - Can be automated
|
||||
- ✅ **Remote development** - Works over SSH
|
||||
|
||||
### Use Cases
|
||||
- **Development** - Quick testing and iteration
|
||||
- **CI/CD pipelines** - Automated testing
|
||||
- **Remote development** - SSH-based development
|
||||
- **Resource-constrained environments** - Low-spec machines
|
||||
- **Team environments** - Shared development servers
|
||||
|
||||
## Integration with Development Workflow
|
||||
|
||||
### Daily Development
|
||||
```bash
|
||||
# Quick test cycle
|
||||
./scripts/build-native.sh --platform android
|
||||
cd android && ./gradlew :app:assembleDebug
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
```
|
||||
|
||||
### Automated Testing
|
||||
```bash
|
||||
# CI/CD pipeline
|
||||
emulator -avd Pixel8_API35 -no-snapshot-load &
|
||||
adb wait-for-device
|
||||
./scripts/build-native.sh --platform android
|
||||
cd android && ./gradlew :app:assembleDebug
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
# Run tests...
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Testing the App
|
||||
1. **Click "Test Plugin"** - Tests the mock plugin implementation
|
||||
2. **Click "Configure Plugin"** - Tests plugin configuration
|
||||
3. **Click "Check Status"** - Tests plugin status retrieval
|
||||
4. **Monitor logs** - Check for any errors or issues
|
||||
|
||||
### Development Workflow
|
||||
1. **Make changes** - Edit plugin code or test app
|
||||
2. **Rebuild** - Run the build commands
|
||||
3. **Reinstall** - Install updated APK
|
||||
4. **Test** - Launch and test functionality
|
||||
5. **Iterate** - Repeat as needed
|
||||
|
||||
---
|
||||
|
||||
**This method provides a complete standalone Android development environment without requiring Android Studio!** 🎉
|
||||
283
doc/testing/IOS_LOGGING_GUIDE.md
Normal file
283
doc/testing/IOS_LOGGING_GUIDE.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# iOS Logging Guide - How to Check Logs for Errors
|
||||
|
||||
**Purpose:** Quick reference for viewing iOS app logs during development and debugging
|
||||
|
||||
**Last Updated:** 2025-11-15
|
||||
**Status:** 🎯 **ACTIVE** - Reference guide for iOS debugging
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Most Common Methods (in order of ease):**
|
||||
|
||||
1. **Xcode Console** (when app is running in Xcode) - Easiest
|
||||
2. **Console.app** (macOS system console) - Good for background logs
|
||||
3. **Command-line** (`xcrun simctl`) - Best for automation/scripts
|
||||
|
||||
---
|
||||
|
||||
## Method 1: Xcode Console (Recommended for Development)
|
||||
|
||||
**When to use:** App is running in Xcode (simulator or device)
|
||||
|
||||
### Steps:
|
||||
|
||||
1. **Open Xcode** and run your app (Cmd+R)
|
||||
2. **Open Debug Area:**
|
||||
- Press **Cmd+Shift+Y** (or View → Debug Area → Activate Console)
|
||||
- Or click the bottom panel icon in Xcode
|
||||
3. **Filter logs:**
|
||||
- Click the search box at bottom of console
|
||||
- Type: `DNP-` or `DailyNotification` or `Error`
|
||||
- Press Enter
|
||||
|
||||
### Filter Examples:
|
||||
|
||||
```
|
||||
DNP-PLUGIN # Plugin operations
|
||||
DNP-FETCH # Background fetch operations
|
||||
DNP-SCHEDULER # Scheduling operations
|
||||
DNP-STORAGE # Storage operations
|
||||
Error # All errors
|
||||
```
|
||||
|
||||
### Copy-Paste Commands (LLDB Console):
|
||||
|
||||
When app is running, you can also use LLDB commands in Xcode console:
|
||||
|
||||
```swift
|
||||
// Check pending notifications
|
||||
po UNUserNotificationCenter.current().pendingNotificationRequests()
|
||||
|
||||
// Check permission status
|
||||
po await UNUserNotificationCenter.current().notificationSettings()
|
||||
|
||||
// Manually trigger BGTask (simulator only)
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Method 2: Console.app (macOS System Console)
|
||||
|
||||
**When to use:** App is running in background, or you want to see system-level logs
|
||||
|
||||
### Steps:
|
||||
|
||||
1. **Open Console.app:**
|
||||
- Press **Cmd+Space** (Spotlight)
|
||||
- Type: `Console`
|
||||
- Press Enter
|
||||
- Or: Applications → Utilities → Console
|
||||
|
||||
2. **Select Device/Simulator:**
|
||||
- In left sidebar, expand "Devices"
|
||||
- Select your simulator or connected device
|
||||
- Or select "All Logs" for system-wide logs
|
||||
|
||||
3. **Filter logs:**
|
||||
- Click search box (top right)
|
||||
- Type: `DNP-` or `org.timesafari.dailynotification`
|
||||
- Press Enter
|
||||
|
||||
### Filter by Subsystem (Structured Logging):
|
||||
|
||||
The plugin uses structured logging with subsystems:
|
||||
|
||||
```
|
||||
org.timesafari.dailynotification.plugin # Plugin operations
|
||||
org.timesafari.dailynotification.fetch # Fetch operations
|
||||
org.timesafari.dailynotification.scheduler # Scheduling operations
|
||||
org.timesafari.dailynotification.storage # Storage operations
|
||||
```
|
||||
|
||||
**To filter by subsystem:**
|
||||
- In Console.app search: `subsystem:org.timesafari.dailynotification`
|
||||
|
||||
---
|
||||
|
||||
## Method 3: Command-Line (xcrun simctl)
|
||||
|
||||
**When to use:** Automation, scripts, or when Xcode/Console.app aren't available
|
||||
|
||||
### Stream Live Logs (Simulator):
|
||||
|
||||
```bash
|
||||
# Stream all logs from booted simulator
|
||||
xcrun simctl spawn booted log stream
|
||||
|
||||
# Stream only plugin logs (filtered)
|
||||
xcrun simctl spawn booted log stream --predicate 'subsystem == "org.timesafari.dailynotification"'
|
||||
|
||||
# Stream with DNP- prefix filter
|
||||
xcrun simctl spawn booted log stream | grep "DNP-"
|
||||
```
|
||||
|
||||
### Save Logs to File:
|
||||
|
||||
```bash
|
||||
# Save all logs to file
|
||||
xcrun simctl spawn booted log stream > device.log 2>&1
|
||||
|
||||
# Save filtered logs
|
||||
xcrun simctl spawn booted log stream --predicate 'subsystem == "org.timesafari.dailynotification"' > plugin.log 2>&1
|
||||
|
||||
# Then analyze with grep
|
||||
grep -E "\[DNP-(FETCH|SCHEDULER|PLUGIN)\]" device.log
|
||||
```
|
||||
|
||||
### View Recent Logs (Not Streaming):
|
||||
|
||||
```bash
|
||||
# Show recent logs (last 100 lines)
|
||||
xcrun simctl spawn booted log show --last 1m | grep "DNP-"
|
||||
|
||||
# Show logs for specific time range
|
||||
xcrun simctl spawn booted log show --start "2025-11-15 10:00:00" --end "2025-11-15 11:00:00" | grep "DNP-"
|
||||
```
|
||||
|
||||
### Physical Device Logs:
|
||||
|
||||
```bash
|
||||
# List connected devices
|
||||
xcrun devicectl list devices
|
||||
|
||||
# Stream logs from physical device (requires device UDID)
|
||||
xcrun devicectl device process launch --device <UDID> org.timesafari.dailynotification.test
|
||||
|
||||
# Or use Console.app for physical devices (easier)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Method 4: Validate Log Sequence (Automated)
|
||||
|
||||
**When to use:** Testing prefetch cycles, verifying complete execution
|
||||
|
||||
### Using Validation Script:
|
||||
|
||||
```bash
|
||||
# From log file
|
||||
./scripts/validate-ios-logs.sh device.log
|
||||
|
||||
# From live stream
|
||||
xcrun simctl spawn booted log stream --predicate 'subsystem == "org.timesafari.dailynotification"' | ./scripts/validate-ios-logs.sh
|
||||
|
||||
# From filtered grep
|
||||
grep -E "\[DNP-(FETCH|SCHEDULER|PLUGIN)\]" device.log | ./scripts/validate-ios-logs.sh
|
||||
```
|
||||
|
||||
**See:** `scripts/validate-ios-logs.sh` for complete validation script
|
||||
|
||||
---
|
||||
|
||||
## Common Log Prefixes
|
||||
|
||||
**Plugin Logs (look for these):**
|
||||
|
||||
| Prefix | Meaning | Example |
|
||||
|--------|---------|---------|
|
||||
| `[DNP-PLUGIN]` | Main plugin operations | `[DNP-PLUGIN] configure() called` |
|
||||
| `[DNP-FETCH]` | Background fetch operations | `[DNP-FETCH] BGTask handler invoked` |
|
||||
| `[DNP-SCHEDULER]` | Notification scheduling | `[DNP-SCHEDULER] Scheduling notification` |
|
||||
| `[DNP-STORAGE]` | Storage/DB operations | `[DNP-STORAGE] Persisted schedule` |
|
||||
| `[DNP-DEBUG]` | Debug diagnostics | `[DNP-DEBUG] Plugin class found` |
|
||||
|
||||
**Error Indicators:**
|
||||
|
||||
- `Error:` - System errors
|
||||
- `Failed:` - Operation failures
|
||||
- `❌` - Visual error markers in logs
|
||||
- `⚠️` - Warning markers
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Common Issues
|
||||
|
||||
### Issue: No logs appearing
|
||||
|
||||
**Solutions:**
|
||||
1. **Check app is running:** App must be launched to generate logs
|
||||
2. **Check filter:** Remove filters to see all logs
|
||||
3. **Check log level:** Some logs may be at debug level only
|
||||
4. **Restart logging:** Close and reopen Console.app or restart log stream
|
||||
|
||||
### Issue: Too many logs (noise)
|
||||
|
||||
**Solutions:**
|
||||
1. **Use specific filters:** `DNP-` instead of `DailyNotification`
|
||||
2. **Filter by subsystem:** `subsystem:org.timesafari.dailynotification`
|
||||
3. **Use time range:** Only show logs from last 5 minutes
|
||||
4. **Use validation script:** Automatically filters for important events
|
||||
|
||||
### Issue: Can't see background task logs
|
||||
|
||||
**Solutions:**
|
||||
1. **Use Console.app:** Background tasks show better in system console
|
||||
2. **Check Background App Refresh:** Must be enabled for BGTask logs
|
||||
3. **Use log stream:** `xcrun simctl spawn booted log stream` shows all logs
|
||||
4. **Check predicate:** Use `--predicate` to filter specific subsystems
|
||||
|
||||
### Issue: Physical device logs not showing
|
||||
|
||||
**Solutions:**
|
||||
1. **Use Console.app:** Easiest for physical devices
|
||||
2. **Check device connection:** Device must be connected and trusted
|
||||
3. **Check provisioning:** Device must be provisioned for development
|
||||
4. **Use Xcode:** Xcode → Window → Devices and Simulators → View Device Logs
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Commands
|
||||
|
||||
### Copy-Paste Ready Commands:
|
||||
|
||||
```bash
|
||||
# Stream plugin logs (simulator)
|
||||
xcrun simctl spawn booted log stream --predicate 'subsystem == "org.timesafari.dailynotification"'
|
||||
|
||||
# Save logs to file
|
||||
xcrun simctl spawn booted log stream > device.log 2>&1
|
||||
|
||||
# View recent errors
|
||||
xcrun simctl spawn booted log show --last 5m | grep -i "error\|failed\|DNP-"
|
||||
|
||||
# Validate log sequence
|
||||
grep -E "\[DNP-(FETCH|SCHEDULER|PLUGIN)\]" device.log | ./scripts/validate-ios-logs.sh
|
||||
|
||||
# Check app logs only
|
||||
xcrun simctl spawn booted log stream --predicate 'process == "App"'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log Levels and Filtering
|
||||
|
||||
**iOS Log Levels:**
|
||||
- **Default:** Shows Info, Error, Fault
|
||||
- **Debug:** Shows Debug, Info, Error, Fault
|
||||
- **Error:** Shows Error, Fault only
|
||||
|
||||
**To see debug logs:**
|
||||
- In Xcode: Product → Scheme → Edit Scheme → Run → Arguments → Environment Variables
|
||||
- Add: `OS_ACTIVITY_MODE=disable` (shows all logs including debug)
|
||||
|
||||
**Or use Console.app:**
|
||||
- Action menu → Include Info Messages
|
||||
- Action menu → Include Debug Messages
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Testing Guide:** `doc/test-app-ios/IOS_PREFETCH_TESTING.md` - Comprehensive testing procedures
|
||||
- **Test App Requirements:** `doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md` - Debugging section
|
||||
- **Validation Script:** `scripts/validate-ios-logs.sh` - Automated log sequence validation
|
||||
- **Main Directive:** `doc/directives/0003-iOS-Android-Parity-Directive.md` - Implementation details
|
||||
|
||||
---
|
||||
|
||||
**Status:** 🎯 **READY FOR USE**
|
||||
**Maintainer:** Matthew Raymer
|
||||
|
||||
580
doc/testing/IOS_PHASE1_TESTING_GUIDE.md
Normal file
580
doc/testing/IOS_PHASE1_TESTING_GUIDE.md
Normal file
@@ -0,0 +1,580 @@
|
||||
# iOS Phase 1 Testing Guide
|
||||
|
||||
**Status:** ✅ **READY FOR TESTING**
|
||||
**Phase:** Phase 1 - Core Infrastructure Parity
|
||||
**Target:** iOS Simulator (primary) or Physical Device
|
||||
|
||||
---
|
||||
|
||||
## Quick Start Testing
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Xcode Version:** 15.0 or later
|
||||
- **macOS Version:** 13.0 (Ventura) or later
|
||||
- **iOS Deployment Target:** iOS 15.0 or later
|
||||
- **Test App:** `test-apps/ios-test-app/` (to be created)
|
||||
|
||||
### Testing Environment Setup
|
||||
|
||||
1. **Build Test App:**
|
||||
```bash
|
||||
# From repo root
|
||||
./scripts/build-ios-test-app.sh --simulator
|
||||
```
|
||||
Note: If build script doesn't exist yet, see "Manual Build Steps" below.
|
||||
|
||||
2. **Open in Xcode:**
|
||||
```bash
|
||||
cd test-apps/ios-test-app
|
||||
open App.xcworkspace # or App.xcodeproj
|
||||
```
|
||||
|
||||
3. **Run on Simulator:**
|
||||
- Select target device (iPhone 15, iPhone 15 Pro, etc.)
|
||||
- Press Cmd+R to build and run
|
||||
- Or use Xcode menu: Product → Run
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 Test Cases
|
||||
|
||||
### Test Case 1: Plugin Initialization
|
||||
|
||||
**Objective:** Verify plugin loads and initializes correctly
|
||||
|
||||
**Steps:**
|
||||
1. Launch test app on iOS Simulator
|
||||
2. Check Console.app logs for: `DNP-PLUGIN: Daily Notification Plugin loaded on iOS`
|
||||
3. Verify no initialization errors
|
||||
|
||||
**Expected Results:**
|
||||
- Plugin loads without errors
|
||||
- Storage and scheduler components initialized
|
||||
- State actor created (iOS 13+)
|
||||
|
||||
**Logs to Check:**
|
||||
```
|
||||
DNP-PLUGIN: Daily Notification Plugin loaded on iOS
|
||||
DailyNotificationStorage: Database opened successfully at [path]
|
||||
DailyNotificationScheduler: Notification category setup complete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 2: Configure Method
|
||||
|
||||
**Objective:** Test plugin configuration
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
import { DailyNotification } from '@capacitor-community/daily-notification';
|
||||
|
||||
// Test configure
|
||||
await DailyNotification.configure({
|
||||
options: {
|
||||
storage: 'tiered',
|
||||
ttlSeconds: 3600,
|
||||
prefetchLeadMinutes: 5,
|
||||
maxNotificationsPerDay: 1,
|
||||
retentionDays: 7
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Call `configure()` with options
|
||||
2. Check Console.app for: `DNP-PLUGIN: Plugin configuration completed successfully`
|
||||
3. Verify settings stored in UserDefaults
|
||||
|
||||
**Expected Results:**
|
||||
- Configuration succeeds without errors
|
||||
- Settings stored correctly
|
||||
- Database path set correctly
|
||||
|
||||
**Verification:**
|
||||
```swift
|
||||
// In Xcode debugger or Console.app
|
||||
po UserDefaults.standard.dictionary(forKey: "DailyNotificationPrefs")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 3: Schedule Daily Notification
|
||||
|
||||
**Objective:** Test main scheduling method with prefetch
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
// Schedule notification for 5 minutes from now
|
||||
const now = new Date();
|
||||
const scheduleTime = new Date(now.getTime() + 5 * 60 * 1000);
|
||||
const hour = scheduleTime.getHours();
|
||||
const minute = scheduleTime.getMinutes();
|
||||
const timeString = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
||||
|
||||
await DailyNotification.scheduleDailyNotification({
|
||||
options: {
|
||||
time: timeString,
|
||||
title: "Test Notification",
|
||||
body: "This is a Phase 1 test notification",
|
||||
sound: true,
|
||||
url: null
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Schedule notification 5 minutes from now
|
||||
2. Verify prefetch scheduled 5 minutes before notification time
|
||||
3. Check Console.app logs
|
||||
4. Wait for notification to appear
|
||||
|
||||
**Expected Results:**
|
||||
- Notification scheduled successfully
|
||||
- Prefetch BGTask scheduled 5 minutes before notification
|
||||
- Notification appears at scheduled time (±180s tolerance)
|
||||
|
||||
**Logs to Check:**
|
||||
```
|
||||
DNP-PLUGIN: Scheduling daily notification
|
||||
DNP-PLUGIN: Daily notification scheduled successfully
|
||||
DNP-FETCH-SCHEDULE: Background fetch scheduled for [date]
|
||||
DailyNotificationScheduler: Notification scheduled successfully for [date]
|
||||
```
|
||||
|
||||
**Verification Commands:**
|
||||
```bash
|
||||
# Check pending notifications (in Xcode debugger)
|
||||
po UNUserNotificationCenter.current().pendingNotificationRequests()
|
||||
|
||||
# Check BGTask scheduling (simulator only)
|
||||
# Use LLDB command in Xcode debugger:
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 4: Get Last Notification
|
||||
|
||||
**Objective:** Test last notification retrieval
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
const lastNotification = await DailyNotification.getLastNotification();
|
||||
console.log('Last notification:', lastNotification);
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Schedule a notification
|
||||
2. Wait for it to fire (or manually trigger)
|
||||
3. Call `getLastNotification()`
|
||||
4. Verify returned data structure
|
||||
|
||||
**Expected Results:**
|
||||
- Returns notification object with: `id`, `title`, `body`, `timestamp`, `url`
|
||||
- Returns empty object `{}` if no notifications exist
|
||||
- Thread-safe retrieval via state actor
|
||||
|
||||
**Expected Response:**
|
||||
```json
|
||||
{
|
||||
"id": "daily_1234567890",
|
||||
"title": "Test Notification",
|
||||
"body": "This is a Phase 1 test notification",
|
||||
"timestamp": 1234567890000,
|
||||
"url": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 5: Cancel All Notifications
|
||||
|
||||
**Objective:** Test cancellation of all scheduled notifications
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
// Schedule multiple notifications first
|
||||
await DailyNotification.scheduleDailyNotification({...});
|
||||
await DailyNotification.scheduleDailyNotification({...});
|
||||
|
||||
// Then cancel all
|
||||
await DailyNotification.cancelAllNotifications();
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Schedule 2-3 notifications
|
||||
2. Verify they're scheduled: `getNotificationStatus()`
|
||||
3. Call `cancelAllNotifications()`
|
||||
4. Verify all cancelled
|
||||
|
||||
**Expected Results:**
|
||||
- All notifications cancelled
|
||||
- Storage cleared
|
||||
- Pending count = 0
|
||||
|
||||
**Logs to Check:**
|
||||
```
|
||||
DNP-PLUGIN: All notifications cancelled successfully
|
||||
DailyNotificationScheduler: All notifications cancelled
|
||||
DailyNotificationStorage: All notifications cleared
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 6: Get Notification Status
|
||||
|
||||
**Objective:** Test status retrieval
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
const status = await DailyNotification.getNotificationStatus();
|
||||
console.log('Status:', status);
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Call `getNotificationStatus()`
|
||||
2. Verify response structure
|
||||
3. Check permission status
|
||||
4. Check pending count
|
||||
|
||||
**Expected Results:**
|
||||
- Returns complete status object
|
||||
- Permission status accurate
|
||||
- Pending count accurate
|
||||
- Next notification time calculated
|
||||
|
||||
**Expected Response:**
|
||||
```json
|
||||
{
|
||||
"isEnabled": true,
|
||||
"isScheduled": true,
|
||||
"lastNotificationTime": 1234567890000,
|
||||
"nextNotificationTime": 1234567895000,
|
||||
"pending": 1,
|
||||
"settings": {
|
||||
"storageMode": "tiered",
|
||||
"ttlSeconds": 3600
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 7: Update Settings
|
||||
|
||||
**Objective:** Test settings update
|
||||
|
||||
**JavaScript Test Code:**
|
||||
```javascript
|
||||
await DailyNotification.updateSettings({
|
||||
settings: {
|
||||
sound: false,
|
||||
priority: "high",
|
||||
timezone: "America/New_York"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. Call `updateSettings()` with new settings
|
||||
2. Verify settings stored
|
||||
3. Retrieve settings and verify changes
|
||||
|
||||
**Expected Results:**
|
||||
- Settings updated successfully
|
||||
- Changes persisted
|
||||
- Thread-safe update via state actor
|
||||
|
||||
---
|
||||
|
||||
### Test Case 8: BGTask Miss Detection
|
||||
|
||||
**Objective:** Test BGTask miss detection and rescheduling
|
||||
|
||||
**Steps:**
|
||||
1. Schedule a notification with prefetch
|
||||
2. Note the BGTask `earliestBeginDate` from logs
|
||||
3. Simulate missing the BGTask window:
|
||||
- Wait 15+ minutes after `earliestBeginDate`
|
||||
- Ensure no successful run recorded
|
||||
4. Launch app (triggers `checkForMissedBGTask()`)
|
||||
5. Verify BGTask rescheduled
|
||||
|
||||
**Expected Results:**
|
||||
- Miss detection triggers on app launch
|
||||
- BGTask rescheduled for 1 minute from now
|
||||
- Logs show: `DNP-FETCH: BGTask missed window; rescheduling`
|
||||
|
||||
**Logs to Check:**
|
||||
```
|
||||
DNP-FETCH: BGTask missed window; rescheduling
|
||||
DNP-FETCH: BGTask rescheduled for [date]
|
||||
```
|
||||
|
||||
**Manual Trigger (Simulator Only):**
|
||||
```bash
|
||||
# In Xcode debugger (LLDB)
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 9: Permission Auto-Healing
|
||||
|
||||
**Objective:** Test automatic permission request
|
||||
|
||||
**Steps:**
|
||||
1. Reset notification permissions (Settings → [App] → Notifications → Off)
|
||||
2. Call `scheduleDailyNotification()`
|
||||
3. Verify permission request dialog appears
|
||||
4. Grant permissions
|
||||
5. Verify scheduling succeeds
|
||||
|
||||
**Expected Results:**
|
||||
- Permission request dialog appears automatically
|
||||
- Scheduling succeeds after granting
|
||||
- Error returned if permissions denied
|
||||
|
||||
**Logs to Check:**
|
||||
```
|
||||
DailyNotificationScheduler: Permission request result: true
|
||||
DailyNotificationScheduler: Scheduling notification: [id]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test Case 10: Error Handling
|
||||
|
||||
**Objective:** Test error code responses
|
||||
|
||||
**Test Scenarios:**
|
||||
|
||||
1. **Missing Parameters:**
|
||||
```javascript
|
||||
await DailyNotification.scheduleDailyNotification({
|
||||
options: {} // Missing 'time' parameter
|
||||
});
|
||||
```
|
||||
**Expected Error:**
|
||||
```json
|
||||
{
|
||||
"error": "missing_required_parameter",
|
||||
"message": "Missing required parameter: time"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Invalid Time Format:**
|
||||
```javascript
|
||||
await DailyNotification.scheduleDailyNotification({
|
||||
options: { time: "invalid" }
|
||||
});
|
||||
```
|
||||
**Expected Error:**
|
||||
```json
|
||||
{
|
||||
"error": "invalid_time_format",
|
||||
"message": "Invalid time format. Use HH:mm"
|
||||
}
|
||||
```
|
||||
|
||||
3. **Notifications Denied:**
|
||||
- Deny notification permissions
|
||||
- Try to schedule notification
|
||||
- Verify error code returned
|
||||
|
||||
**Expected Error:**
|
||||
```json
|
||||
{
|
||||
"error": "notifications_denied",
|
||||
"message": "Notification permissions denied"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manual Build Steps (If Build Script Not Available)
|
||||
|
||||
### Step 1: Install Dependencies
|
||||
|
||||
```bash
|
||||
cd ios
|
||||
pod install
|
||||
```
|
||||
|
||||
### Step 2: Open in Xcode
|
||||
|
||||
```bash
|
||||
open DailyNotificationPlugin.xcworkspace
|
||||
# or
|
||||
open DailyNotificationPlugin.xcodeproj
|
||||
```
|
||||
|
||||
### Step 3: Configure Build Settings
|
||||
|
||||
1. Select project in Xcode
|
||||
2. Go to Signing & Capabilities
|
||||
3. Add Background Modes:
|
||||
- Background fetch
|
||||
- Background processing
|
||||
4. Add to Info.plist:
|
||||
```xml
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>org.timesafari.dailynotification.fetch</string>
|
||||
<string>org.timesafari.dailynotification.notify</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
### Step 4: Build and Run
|
||||
|
||||
- Select target device (Simulator or Physical Device)
|
||||
- Press Cmd+R or Product → Run
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tools
|
||||
|
||||
### Console.app Logging
|
||||
|
||||
**View Logs:**
|
||||
1. Open Console.app (Applications → Utilities)
|
||||
2. Select your device/simulator
|
||||
3. Filter by: `DNP-` or `DailyNotification`
|
||||
|
||||
**Key Log Prefixes:**
|
||||
- `DNP-PLUGIN:` - Main plugin operations
|
||||
- `DNP-FETCH:` - Background fetch operations
|
||||
- `DNP-FETCH-SCHEDULE:` - BGTask scheduling
|
||||
- `DailyNotificationStorage:` - Storage operations
|
||||
- `DailyNotificationScheduler:` - Scheduling operations
|
||||
|
||||
### Xcode Debugger Commands
|
||||
|
||||
**Check Pending Notifications:**
|
||||
```swift
|
||||
po UNUserNotificationCenter.current().pendingNotificationRequests()
|
||||
```
|
||||
|
||||
**Check Permission Status:**
|
||||
```swift
|
||||
po await UNUserNotificationCenter.current().notificationSettings()
|
||||
```
|
||||
|
||||
**Check BGTask Status (Simulator Only):**
|
||||
```swift
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
```
|
||||
|
||||
**Check Storage:**
|
||||
```swift
|
||||
po UserDefaults.standard.dictionary(forKey: "DailyNotificationPrefs")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue 1: BGTaskScheduler Not Running
|
||||
|
||||
**Symptoms:**
|
||||
- BGTask never executes
|
||||
- No logs from `handleBackgroundFetch()`
|
||||
|
||||
**Solutions:**
|
||||
1. Verify Info.plist has `BGTaskSchedulerPermittedIdentifiers`
|
||||
2. Check task registered in `setupBackgroundTasks()`
|
||||
3. **Simulator workaround:** Use LLDB command to manually trigger (see above)
|
||||
|
||||
### Issue 2: Notifications Not Delivering
|
||||
|
||||
**Symptoms:**
|
||||
- Notification scheduled but never appears
|
||||
- No notification in notification center
|
||||
|
||||
**Solutions:**
|
||||
1. Check permissions: `UNUserNotificationCenter.current().getNotificationSettings()`
|
||||
2. Verify notification scheduled: `getPendingNotificationRequests()`
|
||||
3. Check notification category registered
|
||||
4. Verify time hasn't passed (iOS may deliver immediately if time passed)
|
||||
|
||||
### Issue 3: Build Failures
|
||||
|
||||
**Symptoms:**
|
||||
- Xcode build errors
|
||||
- Missing dependencies
|
||||
|
||||
**Solutions:**
|
||||
1. Run `pod install` in `ios/` directory
|
||||
2. Clean build folder: Product → Clean Build Folder (Cmd+Shift+K)
|
||||
3. Verify Capacitor plugin path in `capacitor.plugins.json`
|
||||
4. Check Xcode scheme matches workspace
|
||||
|
||||
### Issue 4: Background Tasks Expiring
|
||||
|
||||
**Symptoms:**
|
||||
- BGTask starts but expires before completion
|
||||
- Logs show: `Background fetch task expired`
|
||||
|
||||
**Solutions:**
|
||||
1. Ensure `task.setTaskCompleted(success:)` called before expiration
|
||||
2. Keep processing efficient (< 30 seconds)
|
||||
3. Schedule next task immediately after completion
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Phase 1 Core Methods
|
||||
|
||||
- [ ] `configure()` - Configuration succeeds
|
||||
- [ ] `scheduleDailyNotification()` - Notification schedules correctly
|
||||
- [ ] `getLastNotification()` - Returns correct notification
|
||||
- [ ] `cancelAllNotifications()` - All notifications cancelled
|
||||
- [ ] `getNotificationStatus()` - Status accurate
|
||||
- [ ] `updateSettings()` - Settings updated correctly
|
||||
|
||||
### Background Tasks
|
||||
|
||||
- [ ] BGTask scheduled 5 minutes before notification
|
||||
- [ ] BGTask executes successfully
|
||||
- [ ] BGTask miss detection works
|
||||
- [ ] BGTask rescheduling works
|
||||
|
||||
### Error Handling
|
||||
|
||||
- [ ] Missing parameter errors returned
|
||||
- [ ] Invalid time format errors returned
|
||||
- [ ] Permission denied errors returned
|
||||
- [ ] Error codes match Android format
|
||||
|
||||
### Thread Safety
|
||||
|
||||
- [ ] No race conditions observed
|
||||
- [ ] State actor used for all storage operations
|
||||
- [ ] Background tasks use state actor
|
||||
|
||||
---
|
||||
|
||||
## Next Steps After Testing
|
||||
|
||||
1. **Document Issues:** Create GitHub issues for any bugs found
|
||||
2. **Update Test Cases:** Add test cases for edge cases discovered
|
||||
3. **Performance Testing:** Test with multiple notifications
|
||||
4. **Phase 2 Preparation:** Begin Phase 2 advanced features
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Directive:** `doc/directives/0003-iOS-Android-Parity-Directive.md`
|
||||
- **Phase 1 Summary:** `doc/PHASE1_COMPLETION_SUMMARY.md`
|
||||
- **Android Testing:** `doc/testing/NOTIFICATION_PROCEDURES.md`
|
||||
- **Comprehensive Testing:** `doc/testing/COMPREHENSIVE_GUIDE.md`
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ **READY FOR TESTING**
|
||||
**Last Updated:** 2025-01-XX
|
||||
|
||||
1041
doc/testing/IOS_PREFETCH_TESTING.md
Normal file
1041
doc/testing/IOS_PREFETCH_TESTING.md
Normal file
File diff suppressed because it is too large
Load Diff
802
doc/testing/IOS_TEST_APP_REQUIREMENTS.md
Normal file
802
doc/testing/IOS_TEST_APP_REQUIREMENTS.md
Normal file
@@ -0,0 +1,802 @@
|
||||
# iOS Test App Requirements
|
||||
|
||||
**Purpose:** What the iOS test app must provide so that the testing guide can be executed with parity vs Android
|
||||
|
||||
**Version:** 1.0.1
|
||||
**Scope:** Phase 1 Prefetch MVP
|
||||
**Next Target:** Phase 2 (Rolling Window + TTL Telemetry)
|
||||
**Maintainer:** Matthew Raymer
|
||||
**Status:** 📋 **REQUIRED FOR PHASE 1**
|
||||
**Date:** 2025-11-15
|
||||
**Directive Reference:** `doc/directives/0003-iOS-Android-Parity-Directive.md`
|
||||
|
||||
**Note:** This app exists to support the prefetch testing scenarios in `doc/test-app-ios/IOS_PREFETCH_TESTING.md`.
|
||||
|
||||
**Android parity:** Behavior is aligned with `test-apps/android-test-app` where platform constraints allow. Timing and BGTask heuristics **will differ** from Android's exact alarms:
|
||||
- **Android:** Exact alarms via AlarmManager / WorkManager
|
||||
- **iOS:** Heuristic BGTaskScheduler (see glossary); no hard guarantee of 5-min prefetch
|
||||
|
||||
**Glossary:** See `doc/test-app-ios/IOS_PREFETCH_GLOSSARY.md` for complete terminology definitions.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines the requirements for the iOS test app (`test-apps/ios-test-app/`) that must be created as part of Phase 1 implementation. The iOS test app must provide UI parity with the Android test app (`test-apps/android-test-app/`) while respecting iOS-specific constraints and capabilities.
|
||||
|
||||
## Non-Goals (Phase 1)
|
||||
|
||||
**Out of scope for Phase 1:**
|
||||
|
||||
- ❌ No full UX polish (color, branding)
|
||||
- ❌ No localization / accessibility guarantees (text only for internal QA)
|
||||
- ❌ No production signing / App Store deployment
|
||||
- ❌ No advanced UI features beyond basic functionality testing
|
||||
|
||||
## Security & Privacy Constraints
|
||||
|
||||
**Critical requirements for test app implementation:**
|
||||
|
||||
- Test app MUST use **non-production** endpoints and credentials
|
||||
- JWT / API keys used here are **test-only** and may be rotated or revoked at any time
|
||||
- Logs MUST NOT include real user PII (names, emails, phone numbers)
|
||||
- Any screenshots or shared logs should be scrubbed of secrets before external sharing
|
||||
- Test app should clearly indicate it's a development/testing build (not production)
|
||||
|
||||
## If You Only Have 30 Minutes
|
||||
|
||||
Quick setup checklist:
|
||||
|
||||
1. Copy HTML/JS from Android test app (`test-apps/android-test-app/app/src/main/assets/public/index.html`)
|
||||
2. Wire plugin into Capacitor (`capacitor.config.json`)
|
||||
3. Add Info.plist keys (BGTask identifiers, background modes, notification permissions)
|
||||
4. Build/run (`./scripts/build-ios-test-app.sh --simulator` or Xcode)
|
||||
5. Press buttons: Check Plugin Status → Request Permissions → Schedule Test Notification
|
||||
6. See logs with prefixes `DNP-PLUGIN`, `DNP-FETCH`, `DNP-SCHEDULER`
|
||||
|
||||
---
|
||||
|
||||
## UI Parity Requirements
|
||||
|
||||
### HTML/JS UI
|
||||
|
||||
The iOS test app **MUST** use the same HTML/JS UI as the Android test app to ensure consistent testing experience across platforms.
|
||||
|
||||
**Source:** Copy from `test-apps/android-test-app/app/src/main/assets/public/index.html`
|
||||
|
||||
**Required UI Elements:**
|
||||
- Plugin registration status indicator
|
||||
- Permission status display (✅/❌ indicators)
|
||||
- Test notification button
|
||||
- Check permissions button
|
||||
- Request permissions button
|
||||
- Channel management buttons (Check Channel Status, Open Channel Settings)
|
||||
- Status display area
|
||||
- Log output area (optional, for debugging)
|
||||
|
||||
### UI Functionality
|
||||
|
||||
The test app UI must support:
|
||||
|
||||
1. **Plugin Status Check**
|
||||
- Display plugin availability status
|
||||
- Show "Plugin is loaded and ready!" when available
|
||||
|
||||
2. **Permission Management**
|
||||
- Display current permission status
|
||||
- Request permissions button
|
||||
- Check permissions button
|
||||
- Show ✅/❌ indicators for each permission
|
||||
|
||||
3. **Channel Management** (iOS parity with Android)
|
||||
- Check channel status button (iOS: checks app-wide notification authorization)
|
||||
- Open channel settings button (iOS: opens app Settings, not per-channel)
|
||||
- Note: iOS doesn't have per-channel control like Android; these methods provide app-wide equivalents
|
||||
|
||||
4. **Notification Testing**
|
||||
- Schedule test notification button
|
||||
- Display scheduled time
|
||||
- Show notification status
|
||||
|
||||
5. **Status Display**
|
||||
- Show last notification time
|
||||
- Show pending notification count
|
||||
- Display error messages if any
|
||||
|
||||
### UI Elements to Plugin Methods Mapping
|
||||
|
||||
| UI Element / Button | Plugin Method / API Call | Notes |
|
||||
|---------------------|-------------------------|-------|
|
||||
| "Check Plugin Status" | `DailyNotification.configure()` or status call | Verify plugin load & config |
|
||||
| "Check Permissions" | `checkPermissionStatus()` | Returns current notification permission status |
|
||||
| "Request Permissions" | `requestNotificationPermissions()` | Requests notification permissions (shows system dialog) |
|
||||
| "Schedule Test Notification" | `scheduleDailyNotification()` | Should schedule prefetch + notify |
|
||||
| "Show Last Notification" | `getLastNotification()` | Uses deterministic path (Bucket A) |
|
||||
| "Cancel All Notifications" | `cancelAllNotifications()` | Uses deterministic path (Bucket A) |
|
||||
| "Get Notification Status" | `getNotificationStatus()` | Uses deterministic path (Bucket A) |
|
||||
| "Check Channel Status" | `isChannelEnabled(channelId?)` | Checks if notifications enabled (iOS: app-wide) |
|
||||
| "Open Channel Settings" | `openChannelSettings(channelId?)` | Opens notification settings (iOS: app Settings) |
|
||||
|
||||
**See `IOS_PREFETCH_TESTING.md` Behavior Classification for deterministic vs heuristic methods.**
|
||||
|
||||
---
|
||||
|
||||
## iOS Permissions Configuration
|
||||
|
||||
### Info.plist Requirements
|
||||
|
||||
The test app's `Info.plist` **MUST** include:
|
||||
|
||||
```xml
|
||||
<!-- Background Task Identifiers -->
|
||||
<!-- These identifiers MUST match exactly the values used in IOS_PREFETCH_TESTING.md and the plugin Swift code, otherwise BGTaskScheduler (see glossary) will silently fail -->
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>org.timesafari.dailynotification.fetch</string> <!-- Prefetch task (BGAppRefreshTask - see glossary) -->
|
||||
<string>org.timesafari.dailynotification.notify</string> <!-- Notification maintenance task (if applicable) -->
|
||||
</array>
|
||||
|
||||
<!-- Background Modes -->
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>background-fetch</string>
|
||||
<string>background-processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
|
||||
<!-- Notification Permissions -->
|
||||
<key>NSUserNotificationsUsageDescription</key>
|
||||
<string>This app uses notifications to deliver daily updates and reminders.</string>
|
||||
```
|
||||
|
||||
### Background App Refresh
|
||||
|
||||
- Background App Refresh must be enabled in Settings
|
||||
- Test app should check and report Background App Refresh status
|
||||
- User should be guided to enable Background App Refresh if disabled
|
||||
|
||||
---
|
||||
|
||||
## Build Options
|
||||
|
||||
### Xcode GUI Build
|
||||
|
||||
1. **Open Workspace:**
|
||||
```bash
|
||||
cd test-apps/ios-test-app
|
||||
open App.xcworkspace # or App.xcodeproj
|
||||
```
|
||||
|
||||
2. **Select Target:**
|
||||
- Choose iOS Simulator (iPhone 15, iPhone 15 Pro, etc.)
|
||||
- Or physical device (requires signing)
|
||||
|
||||
3. **Build and Run:**
|
||||
- Press Cmd+R
|
||||
- Or Product → Run
|
||||
|
||||
### Command-Line Build
|
||||
|
||||
Use the build script:
|
||||
|
||||
```bash
|
||||
# From repo root
|
||||
./scripts/build-ios-test-app.sh --simulator
|
||||
|
||||
# Or for device
|
||||
./scripts/build-ios-test-app.sh --device
|
||||
```
|
||||
|
||||
**Phase 2 Enhancement:** Refactor into modular subcommands:
|
||||
|
||||
```bash
|
||||
./scripts/build-ios-test-app.sh setup # pod install + sync
|
||||
./scripts/build-ios-test-app.sh run-sim # build + run simulator
|
||||
./scripts/build-ios-test-app.sh device # build + deploy device
|
||||
```
|
||||
|
||||
**Copy-Paste Commands:**
|
||||
|
||||
```bash
|
||||
# Setup (first time or after dependency changes)
|
||||
cd test-apps/ios-test-app
|
||||
pod install
|
||||
npx cap sync ios
|
||||
|
||||
# Build for simulator
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme ios-test-app \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
build
|
||||
|
||||
# Run on simulator
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme ios-test-app \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
test
|
||||
```
|
||||
|
||||
### Future CI Integration (Optional)
|
||||
|
||||
**Note for Phase 2+:** Consider adding `xcodebuild`-based CI job that:
|
||||
- Builds `test-apps/ios-test-app` for simulator
|
||||
- Runs a minimal UI test that:
|
||||
- Launches app
|
||||
- Calls `configure()` and `getNotificationStatus()`
|
||||
- Validates log sequence + successful fetch simulation via LLDB trigger
|
||||
- This ensures test app remains buildable as plugin evolves
|
||||
|
||||
**Phase 1:** Manual testing only; CI integration is out of scope.
|
||||
|
||||
**CI Readiness (Phase 2):**
|
||||
- Add `xcodebuild` target for "Prefetch Integration Test"
|
||||
- Validate log sequence + successful fetch simulation via LLDB trigger
|
||||
- Use log validation script (`validate-ios-logs.sh`) for automated sequence checking
|
||||
|
||||
### Build Requirements
|
||||
|
||||
**Required Tools:**
|
||||
- **Xcode:** 15.0 or later
|
||||
- **macOS:** 13.0 (Ventura) or later
|
||||
- **iOS Deployment Target:** iOS 15.0 or later
|
||||
- **CocoaPods:** >= 1.13 (must run `pod install` before first build)
|
||||
- **Node.js:** 20.x (recommended)
|
||||
- **npm:** Latest stable (comes with Node.js)
|
||||
- **Xcode Command Line Tools:** Must run `xcode-select --install` if not already installed
|
||||
|
||||
**Note:** Mismatched versions are **out of scope** for Phase 1 support. Use recommended versions to avoid compatibility issues.
|
||||
|
||||
---
|
||||
|
||||
## Capacitor Configuration
|
||||
|
||||
### Plugin Registration
|
||||
|
||||
The test app **MUST** register the DailyNotification plugin:
|
||||
|
||||
**`capacitor.config.json` or `capacitor.config.ts`:**
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"DailyNotification": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Path
|
||||
|
||||
The plugin must be accessible from the test app:
|
||||
|
||||
- **Development:** Plugin source at `../../ios/Plugin/`
|
||||
- **Production:** Plugin installed via npm/CocoaPods
|
||||
|
||||
### Sync Command
|
||||
|
||||
After making changes to plugin or web assets:
|
||||
|
||||
```bash
|
||||
cd test-apps/ios-test-app
|
||||
npx cap sync ios
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Strategy
|
||||
|
||||
**When executing the scenarios in `IOS_PREFETCH_TESTING.md`, use the following commands while looking for logs with prefixes `DNP-PLUGIN`, `DNP-FETCH`, `DNP-SCHEDULER`.**
|
||||
|
||||
### Xcode Debugger
|
||||
|
||||
**Check Pending Notifications:**
|
||||
```swift
|
||||
po UNUserNotificationCenter.current().pendingNotificationRequests()
|
||||
```
|
||||
|
||||
**Check Permission Status:**
|
||||
```swift
|
||||
po await UNUserNotificationCenter.current().notificationSettings()
|
||||
```
|
||||
|
||||
**Manually Trigger BGTask (Simulator Only):**
|
||||
```swift
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
```
|
||||
|
||||
**Copy-Paste Commands:**
|
||||
|
||||
```swift
|
||||
// In Xcode LLDB console:
|
||||
// Check pending notifications
|
||||
po UNUserNotificationCenter.current().pendingNotificationRequests()
|
||||
|
||||
// Check permission status
|
||||
po await UNUserNotificationCenter.current().notificationSettings()
|
||||
|
||||
// Manually trigger BGTask (simulator only)
|
||||
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.timesafari.dailynotification.fetch"]
|
||||
|
||||
// Force reschedule all tasks (test harness)
|
||||
po DailyNotificationBackgroundTaskTestHarness.forceRescheduleAll()
|
||||
|
||||
// Simulate time warp (test harness)
|
||||
po DailyNotificationBackgroundTaskTestHarness.simulateTimeWarp(minutesForward: 60)
|
||||
```
|
||||
|
||||
### Console.app Logging
|
||||
|
||||
1. Open Console.app (Applications → Utilities)
|
||||
2. Select device/simulator
|
||||
3. Filter by: `DNP-` or `DailyNotification`
|
||||
|
||||
**Key Log Prefixes:**
|
||||
- `DNP-PLUGIN:` - Main plugin operations
|
||||
- `DNP-FETCH:` - Background fetch operations
|
||||
- `DNP-SCHEDULER:` - Scheduling operations
|
||||
- `DNP-STORAGE:` - Storage operations
|
||||
|
||||
**Structured Logging (Swift Logger):**
|
||||
|
||||
The plugin uses Swift `Logger` categories for structured logging:
|
||||
- `org.timesafari.dailynotification.plugin` - Plugin operations
|
||||
- `org.timesafari.dailynotification.fetch` - Fetch operations
|
||||
- `org.timesafari.dailynotification.scheduler` - Scheduling operations
|
||||
- `org.timesafari.dailynotification.storage` - Storage operations
|
||||
|
||||
Filter in Console.app by subsystem: `org.timesafari.dailynotification`
|
||||
|
||||
**Phase 2: Log Validation Script**
|
||||
|
||||
Add helper script (`validate-ios-logs.sh`) to grep for required sequence markers:
|
||||
|
||||
```bash
|
||||
grep -E "\[DNP-(FETCH|SCHEDULER|PLUGIN)\]" device.log | ./scripts/validate-ios-logs.sh
|
||||
```
|
||||
|
||||
This confirms that all critical log steps occurred in proper order and flags missing or out-of-order events automatically.
|
||||
|
||||
### Common Debugging Scenarios
|
||||
|
||||
**Scenario: BGTask not running when expected → follow this checklist:**
|
||||
|
||||
1. **BGTask Not Running:**
|
||||
- Check Info.plist has `BGTaskSchedulerPermittedIdentifiers`
|
||||
- Verify identifiers match code exactly (case-sensitive)
|
||||
- Verify task registered in AppDelegate before app finishes launching
|
||||
- Check Background App Refresh enabled in Settings → [Your App]
|
||||
- Verify app not force-quit (iOS won't run BGTask for force-quit apps)
|
||||
- Use simulator-only LLDB command to manually trigger
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Log Checklist: Section 3`
|
||||
|
||||
2. **Notifications Not Delivering:**
|
||||
- Check notification permissions: `UNUserNotificationCenter.current().getNotificationSettings()`
|
||||
- Verify notification scheduled: `UNUserNotificationCenter.current().getPendingNotificationRequests()`
|
||||
- Check notification category registered
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Log Checklist: Section 4`
|
||||
|
||||
3. **BGTaskScheduler Fails on Simulator:**
|
||||
- **Expected Behavior:** BGTaskSchedulerErrorDomain Code=1 (notPermitted) is **normal on simulator**
|
||||
- BGTaskScheduler doesn't work reliably on simulator - this is an iOS limitation, not a plugin bug
|
||||
- Notification scheduling still works; prefetch won't run on simulator but will work on real devices
|
||||
- Error handling logs clear message: "Background fetch scheduling failed (expected on simulator)"
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Known OS Limitations` for details
|
||||
- **Testing:** Use Xcode → Debug → Simulate Background Fetch for simulator testing
|
||||
|
||||
4. **Plugin Not Discovered:**
|
||||
- Check plugin class conforms to `CAPBridgedPlugin` protocol (required for Capacitor discovery)
|
||||
- Verify `@objc extension DailyNotificationPlugin: CAPBridgedPlugin` exists in plugin code
|
||||
- Check plugin framework is force-loaded in AppDelegate before Capacitor initializes
|
||||
- Verify `pluginMethods` array includes all `@objc` methods
|
||||
- **See also:** `doc/directives/0003-iOS-Android-Parity-Directive.md – Plugin Discovery Issue` for detailed troubleshooting
|
||||
|
||||
5. **Build Failures:**
|
||||
- Run `pod install`
|
||||
- Clean build folder (Cmd+Shift+K)
|
||||
- Verify Capacitor plugin path
|
||||
|
||||
---
|
||||
|
||||
## Test App Implementation Checklist
|
||||
|
||||
### Setup
|
||||
|
||||
- [ ] Create `test-apps/ios-test-app/` directory
|
||||
- [ ] Initialize Capacitor iOS project
|
||||
- [ ] Copy HTML/JS UI from Android test app
|
||||
- [ ] Configure Info.plist with BGTask identifiers
|
||||
- [ ] Configure Info.plist with background modes
|
||||
- [ ] Add notification permission description
|
||||
|
||||
### Plugin Integration
|
||||
|
||||
- [ ] Register DailyNotification plugin in Capacitor config
|
||||
- [ ] Ensure plugin path is correct
|
||||
- [ ] Run `npx cap sync ios`
|
||||
- [ ] Verify plugin loads in test app
|
||||
|
||||
### UI Implementation
|
||||
|
||||
- [ ] Copy HTML/JS from Android test app
|
||||
- [ ] Test plugin status display
|
||||
- [ ] Test permission status display
|
||||
- [ ] Test notification scheduling UI
|
||||
- [ ] Test status display
|
||||
|
||||
### Build & Test
|
||||
|
||||
- [ ] Build script works (`./scripts/build-ios-test-app.sh`)
|
||||
- [ ] App builds in Xcode
|
||||
- [ ] App runs on simulator
|
||||
- [ ] Plugin methods work from UI
|
||||
- [ ] Notifications deliver correctly
|
||||
- [ ] BGTask executes (with manual trigger in simulator)
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
test-apps/ios-test-app/
|
||||
├── App.xcworkspace # Xcode workspace (if using CocoaPods)
|
||||
├── App.xcodeproj # Xcode project
|
||||
├── App/ # Main app directory
|
||||
│ ├── App/
|
||||
│ │ ├── AppDelegate.swift
|
||||
│ │ ├── SceneDelegate.swift
|
||||
│ │ ├── Info.plist # Must include BGTask identifiers
|
||||
│ │ └── Assets.xcassets
|
||||
│ └── Public/ # Web assets (HTML/JS)
|
||||
│ └── index.html # Same as Android test app
|
||||
├── Podfile # CocoaPods dependencies
|
||||
├── capacitor.config.json # Capacitor configuration
|
||||
└── package.json # npm dependencies (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Basic Functionality
|
||||
|
||||
**Happy Path Example:**
|
||||
|
||||
When everything is working correctly, your first run should produce logs like this:
|
||||
|
||||
```text
|
||||
[DNP-PLUGIN] configure() called
|
||||
[DNP-PLUGIN] status = ready
|
||||
[DNP-PLUGIN] requestPermissions() → granted
|
||||
[DNP-SCHEDULER] scheduleDailyNotification() → notificationTime=2025-11-15T05:53:00Z
|
||||
[DNP-FETCH] Scheduling prefetch for notification at 2025-11-15T05:53:00Z
|
||||
[DNP-FETCH] BGAppRefreshTask scheduled (earliestBeginDate=2025-11-15T05:48:00Z)
|
||||
[DNP-SCHEDULER] Scheduling notification for 2025-11-15T05:53:00Z
|
||||
[DNP-STORAGE] Persisted schedule to DB (id=..., type=DAILY, ...)
|
||||
```
|
||||
|
||||
**If your first run doesn't look roughly like this, fix that before proceeding to BGTask tests.**
|
||||
|
||||
1. **Plugin Registration** `[P1-Core][SIM+DEV]`
|
||||
- Launch app
|
||||
- Verify plugin status shows "Plugin is loaded and ready!"
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 2`
|
||||
|
||||
2. **Permission Management** `[P1-Core][SIM+DEV]`
|
||||
- Check permissions
|
||||
- Request permissions
|
||||
- Verify permissions granted
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 6 (Negative-Path Tests)`
|
||||
|
||||
3. **Notification Scheduling** `[P1-Prefetch][SIM+DEV]`
|
||||
- Schedule test notification
|
||||
- Verify notification scheduled
|
||||
- Wait for notification to appear
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Simulator Test Plan: Steps 3-5`
|
||||
|
||||
### Background Tasks
|
||||
|
||||
**Sim vs Device Behavior:**
|
||||
|
||||
- `[SIM+DEV]` – can be fully tested on simulator and device (logic correctness)
|
||||
- `[DEV-ONLY]` – timing / heuristic behavior, must be verified on real hardware
|
||||
|
||||
1. **BGTask Scheduling** `[P1-Prefetch][SIM+DEV]`
|
||||
- Schedule notification with prefetch
|
||||
- Verify BGTask scheduled 5 minutes before notification
|
||||
- Manually trigger BGTask (simulator only)
|
||||
- Verify content fetched
|
||||
- **Note:** Logic and logs on sim; real timing on device
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Simulator Test Plan: Steps 3-4`
|
||||
|
||||
2. **BGTask Miss Detection** `[P1-Prefetch][DEV-ONLY]`
|
||||
- Schedule notification
|
||||
- Wait 15+ minutes
|
||||
- Launch app
|
||||
- Verify BGTask rescheduled
|
||||
- **Note:** Only meaningful on device (heuristic timing)
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Real Device Test Plan: Step 4`
|
||||
|
||||
### Error Handling
|
||||
|
||||
1. **Permission Denied** `[P1-Core][SIM+DEV]`
|
||||
- Deny notification permissions
|
||||
- Try to schedule notification
|
||||
- Verify error returned
|
||||
- **See also:** `IOS_PREFETCH_TESTING.md – Simulator Test Plan: Step 7 (Negative-Path Tests)`
|
||||
|
||||
2. **Invalid Parameters** `[P1-Core][SIM+DEV]`
|
||||
- Try to schedule with invalid time format
|
||||
- Verify error returned
|
||||
|
||||
3. **Network Failures** `[P1-Prefetch][SIM+DEV]`
|
||||
- Turn off network during fetch
|
||||
- Verify graceful fallback to on-demand fetch
|
||||
- Test recovery: network off → fail → network on → retry succeeds
|
||||
|
||||
4. **Server Errors / Auth Expiry** `[P1-Prefetch][SIM+DEV]`
|
||||
- Simulate HTTP 401 or 500 error
|
||||
- Verify plugin logs failure with reason (AUTH, NETWORK)
|
||||
- Confirm telemetry counter increments: `dnp_prefetch_failure_total{reason="AUTH"}`
|
||||
- Verify fallback to live fetch at notification time
|
||||
|
||||
5. **Corrupted Cache** `[P1-Prefetch][SIM+DEV]`
|
||||
- Manually tamper with stored cache (invalid JSON or remove entry)
|
||||
- Verify plugin detects invalid cache and falls back
|
||||
- Ensure no crash on reading bad cache (error handling wraps cache read)
|
||||
|
||||
6. **BGTask Execution Failure** `[P1-Prefetch][SIM+DEV]`
|
||||
- Simulate internal failure in BGTask handler
|
||||
- Verify expiration handler or completion still gets called
|
||||
- Ensure task marked complete even on exception
|
||||
|
||||
7. **Repeated Scheduling Calls** `[P1-Prefetch][SIM+DEV]`
|
||||
- Call `scheduleDailyNotification()` multiple times rapidly
|
||||
- Verify no duplicate scheduling (one active task rule enforced)
|
||||
- Confirm only one BGTask is actually submitted
|
||||
|
||||
### Advanced Features `[P2-Advanced]`
|
||||
|
||||
1. **Rolling Window** `[P2-Advanced]`
|
||||
- Schedule multiple notifications
|
||||
- Verify rolling window maintenance
|
||||
- Check notification limits (64 max on iOS)
|
||||
|
||||
2. **TTL Enforcement** `[P2-Advanced]`
|
||||
- Schedule notification with prefetch
|
||||
- Wait for TTL to expire
|
||||
- Verify stale content discarded at delivery
|
||||
|
||||
---
|
||||
|
||||
## Developer/Test Harness Features (Optional but Recommended)
|
||||
|
||||
Since this test app is for internal testing, it can expose more tools:
|
||||
|
||||
### Dev-Only Toggles
|
||||
|
||||
| Button | Action | Purpose |
|
||||
|--------|--------|---------|
|
||||
| "Simulate BGTask Now" | Calls LLDB trigger | Quick sanity test |
|
||||
| "Schedule 1-min Notification" | Auto schedules T-Lead=1 | Edge case testing |
|
||||
| "Simulate DST Shift" | Adds +1 hr offset | DST handling check |
|
||||
| "Show Cached Payload" | Displays JSON cache | Prefetch validation |
|
||||
| "Force Reschedule All" | Calls `forceRescheduleAll()` | BGTask recovery testing |
|
||||
| "Time Warp +N Minutes" | Calls `simulateTimeWarp()` | Accelerated TTL/T-Lead tests |
|
||||
|
||||
1. **Force Schedule Notification N Minutes from Now**
|
||||
- Bypass normal scheduling UI
|
||||
- Directly call `scheduleDailyNotification()` with calculated time
|
||||
- Useful for quick testing scenarios
|
||||
|
||||
2. **Force "Prefetch-Only" Task**
|
||||
- Trigger BGTask without scheduling notification
|
||||
- Useful for testing prefetch logic in isolation
|
||||
- Display raw JSON returned from API (last fetched payload)
|
||||
|
||||
3. **Display Raw API Response**
|
||||
- Show last fetched payload as JSON
|
||||
- Useful for debugging API responses
|
||||
- Can be referenced from `IOS_PREFETCH_TESTING.md` as shortcuts for specific scenarios
|
||||
|
||||
4. **Manual BGTask Trigger (Dev Build Only)**
|
||||
- Button to manually trigger BGTask (simulator only)
|
||||
- Wraps the LLDB command in UI for convenience
|
||||
|
||||
5. **UI Feedback Enhancements**
|
||||
- Add persistent toast/log area showing step-by-step plugin state ("Registered", "Scheduled", "BGTask fired", etc.)
|
||||
- Include color-coded state indicators for each stage (🟢 OK, 🟡 Pending, 🔴 Error)
|
||||
|
||||
These features can then be referenced from `IOS_PREFETCH_TESTING.md` as shortcuts for specific test scenarios.
|
||||
|
||||
### Persistent Schedule Snapshot
|
||||
|
||||
**Phase 2 Enhancement:** Store a simple JSON of the last prefetch state for post-run verification:
|
||||
|
||||
```json
|
||||
{
|
||||
"last_schedule": "2025-11-15T05:48:00Z",
|
||||
"last_prefetch": "2025-11-15T05:50:00Z",
|
||||
"last_notification": "2025-11-15T05:53:00Z",
|
||||
"prefetch_success": true,
|
||||
"cached_content_used": true,
|
||||
"contentHash": "abcdef123456",
|
||||
"scheduleHash": "xyz789"
|
||||
}
|
||||
```
|
||||
|
||||
This can be used for post-run verification and telemetry aggregation. Access via test app UI button "Show Schedule Snapshot" or via UserDefaults key `DNP_ScheduleSnapshot`.
|
||||
|
||||
### UI Indicators for Tests
|
||||
|
||||
**Status Display:**
|
||||
- Status label/icon: 🟢 green when last notification was fetched and delivered from cache
|
||||
- 🔴 red if something failed (network error, etc.)
|
||||
- 🟡 yellow if pending/unknown state
|
||||
|
||||
**Last Operation Summary:**
|
||||
- Display last fetch time
|
||||
- Show whether cached content was used
|
||||
- Display any error message
|
||||
- Show telemetry counter values
|
||||
|
||||
**Dump Prefetch Status Button:**
|
||||
- Triggers plugin to report all relevant info
|
||||
- Shows pending tasks, cache status, telemetry snapshot
|
||||
- Displays in scrollable text view
|
||||
- Useful on devices where attaching debugger is inconvenient
|
||||
|
||||
### In-App Log Viewer (Phase 2)
|
||||
|
||||
**For QA Use:**
|
||||
- Read app's unified logging (OSLog) for entries with subsystem `org.timesafari.dailynotification`
|
||||
- Present logs on screen or allow export to file
|
||||
- Capture Logger output into text buffer during app session
|
||||
- **Security:** Only enable in test builds, not production
|
||||
|
||||
**Export Test Results:**
|
||||
- Save test run summary to file (JSON format)
|
||||
- Include timestamps, outcomes, telemetry counters
|
||||
- Access via "Export Test Results" button
|
||||
- Collect from devices via Xcode or CI pipelines
|
||||
|
||||
## Risks & Gotchas
|
||||
|
||||
**Common pitfalls when working with the test app:**
|
||||
|
||||
1. **Accidentally editing shared HTML/JS in iOS test app instead of Android canonical source**
|
||||
- Always edit Android test app HTML/JS first, then copy to iOS
|
||||
- Keep iOS test app HTML/JS as a copy, not the source of truth
|
||||
|
||||
2. **Forgetting `npx cap sync ios` after plugin or asset changes**
|
||||
- Run `npx cap sync ios` after any plugin code changes
|
||||
- Run `npx cap sync ios` after any web asset changes
|
||||
- Check `capacitor.config.json` is up to date
|
||||
|
||||
3. **Running with stale Pods**
|
||||
- Run `pod repo update` periodically
|
||||
- Run `pod install` after dependency changes
|
||||
- Clean build folder (Cmd+Shift+K) if Pods seem stale
|
||||
|
||||
4. **Changing BGTask identifiers in one place but not the other**
|
||||
- Info.plist `BGTaskSchedulerPermittedIdentifiers` must match Swift code exactly (case-sensitive)
|
||||
- Check both places when updating identifiers
|
||||
- See `IOS_PREFETCH_TESTING.md` for identifier requirements
|
||||
|
||||
5. **Mismatched tooling versions**
|
||||
- Use recommended versions (Node 20.x, CocoaPods >= 1.13, Xcode 15.0+)
|
||||
- Mismatched versions are out of scope for Phase 1 support
|
||||
|
||||
## References
|
||||
|
||||
- **Directive:** `doc/directives/0003-iOS-Android-Parity-Directive.md`
|
||||
- **Testing Guide:** `doc/test-app-ios/IOS_PREFETCH_TESTING.md` - Comprehensive prefetch testing procedures
|
||||
- **Android Test App:** `test-apps/android-test-app/`
|
||||
- **Build Script:** `scripts/build-ios-test-app.sh`
|
||||
|
||||
---
|
||||
|
||||
## Review & Sign-Off Checklist (Phase 1)
|
||||
|
||||
**Use this checklist to verify Phase 1 iOS test app is complete:**
|
||||
|
||||
- [ ] Test app builds on simulator with recommended toolchain (Node 20.x, CocoaPods >= 1.13, Xcode 15.0+)
|
||||
- [ ] Test app builds on at least one real device
|
||||
- [ ] All UI buttons map to plugin methods as per the **UI Elements to Plugin Methods Mapping** table
|
||||
- [ ] Happy-path log sequence matches the example in **Testing Scenarios → Basic Functionality**
|
||||
- [ ] BGTask identifiers are consistent (Info.plist ↔ Swift ↔ doc)
|
||||
- [ ] Risks & Gotchas section has been read and acknowledged by the implementer
|
||||
- [ ] Security & Privacy Constraints have been followed (non-production endpoints, no PII in logs)
|
||||
|
||||
---
|
||||
|
||||
## Technical Correctness Requirements
|
||||
|
||||
### BGTask Scheduling & Lifecycle
|
||||
|
||||
**Validation Requirements:**
|
||||
- Verify `earliestBeginDate` is at least 60 seconds in future (iOS requirement)
|
||||
- Log and handle scheduling errors gracefully (Code=1 on simulator is expected)
|
||||
- Cancel existing pending task before scheduling new (one active task rule)
|
||||
- Use `BGTaskScheduler.shared.getPendingTaskRequests()` in debug to verify only one task pending
|
||||
|
||||
**Schedule Next Task at Execution:**
|
||||
- Adopt Apple's best practice: schedule next task IMMEDIATELY at start of BGTask handler
|
||||
- This ensures continuity even if app is terminated shortly after
|
||||
- Pattern: Schedule next → Initiate async work → Mark complete → Use expiration handler
|
||||
|
||||
**Expiration Handler and Completion:**
|
||||
- Implement expiration handler to cancel ongoing operations if iOS terminates task (~30 seconds)
|
||||
- Always call `task.setTaskCompleted(success:)` exactly once
|
||||
- Use `success: false` if fetch didn't complete (system may reschedule sooner)
|
||||
- Use `success: true` if all went well
|
||||
- Re-schedule next task after marking completion (for recurring use cases)
|
||||
|
||||
**Error Handling & Retry:**
|
||||
- Distinguish recoverable errors (transient network) vs permanent failures
|
||||
- For network failures: log failure reason, set `success: false`, consider cached data if available
|
||||
- For logic errors: log clear message, call `setTaskCompleted(success: false)`, exit cleanly
|
||||
- Ensure fallback to on-demand fetch at notification time if prefetch fails
|
||||
|
||||
**Data Consistency & Cleanup:**
|
||||
- Cross-check `notificationTime` matches payload's `scheduled_for` field
|
||||
- Validate TTL on cached content at notification time (discard if expired)
|
||||
- Ensure content is saved to persistent store before marking BGTask complete
|
||||
- Implement cache cleanup for outdated data
|
||||
- Handle permission changes gracefully (detect failure at delivery, log outcome)
|
||||
|
||||
### Scheduling and Notification Coordination
|
||||
|
||||
**Unified Scheduling Logic:**
|
||||
- Atomically schedule both UNNotificationRequest and BGAppRefreshTaskRequest
|
||||
- If one fails, cancel the other or report partial failure
|
||||
- Return clear status/error code to JS layer
|
||||
|
||||
**BGTask Identifier Constants:**
|
||||
- Verify identifier in code exactly matches Info.plist (case-sensitive)
|
||||
- Test harness should verify on app launch (logs show successful registration)
|
||||
|
||||
**Concurrency Considerations:**
|
||||
- Handle potentially overlapping schedules (Phase 2: multiple notifications)
|
||||
- Use one BGTask to fetch for next upcoming notification only
|
||||
- Store next notification's schedule ID and time in shared place
|
||||
- Use locks or dispatch synchronization to avoid race conditions
|
||||
|
||||
**OS Limits:**
|
||||
- Acknowledge force-quit prevents BGTask execution (can't circumvent)
|
||||
- Tolerate running slightly later than `earliestBeginDate` (iOS heuristics)
|
||||
- Log actual execution time vs scheduled time for analysis
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 Forward Plan
|
||||
|
||||
**Planned enhancements for Phase 2:**
|
||||
|
||||
- Add quick scenario buttons (Simulate BGTask Now, Schedule 1-min Notification, Simulate DST Shift, Show Cached Payload)
|
||||
- Implement persistent schedule snapshot (JSON of last prefetch state)
|
||||
- Add color-coded UI feedback (🟢 OK, 🟡 Pending, 🔴 Error)
|
||||
- Refactor build script into modular subcommands (`setup`, `run-sim`, `device`)
|
||||
- Integrate CI pipeline with `xcodebuild` target for "Prefetch Integration Test"
|
||||
- Add log validation script (`validate-ios-logs.sh`) for automated sequence checking
|
||||
- Implement rolling window & TTL validation
|
||||
- Add telemetry verification for multi-day scenarios
|
||||
- Test on different device models and iOS versions
|
||||
- Add in-app log viewer/export for QA use
|
||||
|
||||
**See also:** `doc/directives/0003-iOS-Android-Parity-Directive.md` for Phase 2 implementation details.
|
||||
|
||||
---
|
||||
|
||||
## Changelog (high-level)
|
||||
|
||||
- 2025-11-15 — Initial Phase 1 version (prefetch MVP, Android parity)
|
||||
|
||||
---
|
||||
|
||||
**Status:** 📋 **REQUIRED FOR PHASE 1**
|
||||
**Last Updated:** 2025-11-15
|
||||
|
||||
210
doc/testing/IOS_TEST_APP_SETUP.md
Normal file
210
doc/testing/IOS_TEST_APP_SETUP.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# iOS Test App Setup Guide
|
||||
|
||||
**Status:** 📋 **SETUP REQUIRED**
|
||||
**Objective:** Create iOS test app for Phase 1 testing
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
The iOS test app (`test-apps/ios-test-app/`) does not exist yet. This guide will help you create it.
|
||||
|
||||
---
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### Option 1: Automated Setup (Recommended)
|
||||
|
||||
Run the setup script:
|
||||
|
||||
```bash
|
||||
./scripts/setup-ios-test-app.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Create basic directory structure
|
||||
- Copy HTML from Android test app
|
||||
- Create `capacitor.config.json` and `package.json`
|
||||
- Set up basic files
|
||||
|
||||
### Option 2: Manual Setup
|
||||
|
||||
Follow the steps below to create the iOS test app manually.
|
||||
|
||||
---
|
||||
|
||||
## Manual Setup Steps
|
||||
|
||||
### Step 1: Create Directory Structure
|
||||
|
||||
```bash
|
||||
cd test-apps
|
||||
mkdir -p ios-test-app/App/App/Public
|
||||
cd ios-test-app
|
||||
```
|
||||
|
||||
### Step 2: Initialize Capacitor
|
||||
|
||||
```bash
|
||||
# Create package.json
|
||||
cat > package.json << 'EOF'
|
||||
{
|
||||
"name": "ios-test-app",
|
||||
"version": "1.0.0",
|
||||
"description": "iOS test app for DailyNotification plugin",
|
||||
"scripts": {
|
||||
"sync": "npx cap sync ios",
|
||||
"open": "npx cap open ios"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor/core": "^5.0.0",
|
||||
"@capacitor/ios": "^5.0.0"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Add iOS platform
|
||||
npx cap add ios
|
||||
```
|
||||
|
||||
### Step 3: Copy HTML from Android Test App
|
||||
|
||||
```bash
|
||||
# Copy HTML file
|
||||
cp ../android-test-app/app/src/main/assets/public/index.html App/App/Public/index.html
|
||||
```
|
||||
|
||||
### Step 4: Configure Capacitor
|
||||
|
||||
Create `capacitor.config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": "org.timesafari.dailynotification.test",
|
||||
"appName": "DailyNotification Test App",
|
||||
"webDir": "App/App/Public",
|
||||
"server": {
|
||||
"iosScheme": "capacitor"
|
||||
},
|
||||
"plugins": {
|
||||
"DailyNotification": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Configure Info.plist
|
||||
|
||||
Edit `App/App/Info.plist` and add:
|
||||
|
||||
```xml
|
||||
<!-- Background Task Identifiers -->
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>org.timesafari.dailynotification.fetch</string>
|
||||
<string>org.timesafari.dailynotification.notify</string>
|
||||
</array>
|
||||
|
||||
<!-- Background Modes -->
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>background-fetch</string>
|
||||
<string>background-processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
|
||||
<!-- Notification Permissions -->
|
||||
<key>NSUserNotificationsUsageDescription</key>
|
||||
<string>This app uses notifications to deliver daily updates and reminders.</string>
|
||||
```
|
||||
|
||||
### Step 6: Link Plugin
|
||||
|
||||
The plugin needs to be accessible. Options:
|
||||
|
||||
**Option A: Local Development (Recommended)**
|
||||
- Ensure plugin is at `../../ios/Plugin/`
|
||||
- Capacitor will auto-detect it during sync
|
||||
|
||||
**Option B: Via npm**
|
||||
- Install plugin: `npm install ../../`
|
||||
- Capacitor will link it automatically
|
||||
|
||||
### Step 7: Sync Capacitor
|
||||
|
||||
```bash
|
||||
npx cap sync ios
|
||||
```
|
||||
|
||||
### Step 8: Build and Run
|
||||
|
||||
```bash
|
||||
# Use build script
|
||||
../../scripts/build-ios-test-app.sh --simulator
|
||||
|
||||
# Or open in Xcode
|
||||
npx cap open ios
|
||||
# Then press Cmd+R in Xcode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "No Xcode workspace or project found"
|
||||
|
||||
**Solution:** Run `npx cap add ios` first to create the Xcode project.
|
||||
|
||||
### Issue: Plugin not found
|
||||
|
||||
**Solution:**
|
||||
1. Ensure plugin exists at `../../ios/Plugin/`
|
||||
2. Run `npx cap sync ios`
|
||||
3. Check `App/App/capacitor.plugins.json` contains DailyNotification entry
|
||||
|
||||
### Issue: BGTask not running
|
||||
|
||||
**Solution:**
|
||||
1. Verify Info.plist has `BGTaskSchedulerPermittedIdentifiers`
|
||||
2. Check task registered in AppDelegate
|
||||
3. Use simulator-only LLDB command to manually trigger (see testing guide)
|
||||
|
||||
### Issue: Build failures
|
||||
|
||||
**Solution:**
|
||||
1. Run `pod install` in `App/` directory
|
||||
2. Clean build folder in Xcode (Cmd+Shift+K)
|
||||
3. Verify Capacitor plugin path
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After setup, verify:
|
||||
|
||||
- [ ] `test-apps/ios-test-app/` directory exists
|
||||
- [ ] `App.xcworkspace` or `App.xcodeproj` exists
|
||||
- [ ] `App/App/Public/index.html` exists
|
||||
- [ ] `capacitor.config.json` exists
|
||||
- [ ] `Info.plist` has BGTask identifiers
|
||||
- [ ] Plugin loads in test app
|
||||
- [ ] Build script works: `./scripts/build-ios-test-app.sh --simulator`
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Requirements:** `doc/test-app-ios/IOS_TEST_APP_REQUIREMENTS.md`
|
||||
- **Testing Guide:** `doc/IOS_PHASE1_TESTING_GUIDE.md`
|
||||
- **Build Script:** `scripts/build-ios-test-app.sh`
|
||||
- **Setup Script:** `scripts/setup-ios-test-app.sh`
|
||||
|
||||
---
|
||||
|
||||
**Status:** 📋 **SETUP REQUIRED**
|
||||
**Last Updated:** 2025-01-XX
|
||||
|
||||
393
doc/testing/LOCALHOST_GUIDE.md
Normal file
393
doc/testing/LOCALHOST_GUIDE.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Localhost Testing Guide
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Version**: 1.0.0
|
||||
**Status**: Testing Setup
|
||||
|
||||
## Overview
|
||||
|
||||
This guide explains how to test the daily notification plugin's prefetch functionality with a localhost development API server running on your host machine.
|
||||
|
||||
## Android Emulator Localhost Access
|
||||
|
||||
On Android emulator, `localhost` (127.0.0.1) refers to the **emulator itself**, not your host machine. To access your host machine's localhost from the Android emulator, use:
|
||||
|
||||
**`10.0.2.2`** - Special alias that maps to host machine's localhost
|
||||
|
||||
### Platform-Specific Localhost Addresses
|
||||
|
||||
- **Android Emulator**: `http://10.0.2.2:PORT`
|
||||
- **iOS Simulator**: `http://localhost:PORT` or `http://127.0.0.1:PORT`
|
||||
- **Web Browser**: `http://localhost:PORT`
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### 1. Start Your Local Development Server
|
||||
|
||||
You have two options:
|
||||
|
||||
#### Option A: Use the Test API Server (Recommended for Quick Testing)
|
||||
|
||||
The plugin includes a ready-to-use test API server with automatic project seeding:
|
||||
|
||||
```bash
|
||||
cd /home/matthew/projects/timesafari/daily-notification-plugin
|
||||
node scripts/test-api-server-with-seed.js [port]
|
||||
# Default port: 3000
|
||||
# Starts on http://localhost:3000
|
||||
```
|
||||
|
||||
This server:
|
||||
- ✅ **Automatically creates plans in-memory on startup** (no fetch errors)
|
||||
- ✅ Implements the `/api/v2/report/plansLastUpdatedBetween` endpoint
|
||||
- ✅ Returns seeded projects when queried by `planIds`
|
||||
- ✅ Ready to use immediately with auto-generated valid URI format IDs
|
||||
- ✅ Provides debugging endpoints to view seeded projects
|
||||
|
||||
**Note**: Plans are stored in-memory only. When the server restarts, plans are re-seeded automatically.
|
||||
|
||||
#### Option B: Use Your Existing TimeSafari API Server
|
||||
|
||||
If you have your own localhost API server, you must create plans first. Plans are created by importing JWT claims containing a `PlanAction`:
|
||||
|
||||
**Route: POST `/api/v2/claim`**
|
||||
|
||||
```json
|
||||
{
|
||||
"jwtEncoded": "<signed JWT with PlanAction claim>"
|
||||
}
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- JWT must contain `@type: "PlanAction"` in the claim payload
|
||||
- JWT must be signed with a valid DID key
|
||||
- Response includes `handleId` and `planId` if creation succeeds
|
||||
|
||||
**Methods to Create Plans**:
|
||||
1. **TimeSafari App UI** (easiest) - Create projects through the app UI and note `handleId` from API responses
|
||||
2. **Import PlanAction JWT** - Generate a signed JWT with `PlanAction` claim and POST to `/api/v2/claim`
|
||||
3. **Direct Database** - Insert plans directly into your database (not recommended for production data integrity)
|
||||
|
||||
**Note**: Creating PlanAction JWTs requires DID signing and proper claim structure, which is complex. For quick testing, **Option A (test API server) is recommended**.
|
||||
|
||||
**Implementation Reference**: If you need to programmatically create PlanAction JWTs, see [`doc/integration/hydrate-plan-implementation-guide.md`](../integration/hydrate-plan-implementation-guide.md) for the complete implementation pattern, including `hydratePlan()` function and helper utilities.
|
||||
|
||||
Then verify your setup:
|
||||
|
||||
```bash
|
||||
# Seed projects to your existing API server
|
||||
node scripts/seed-test-projects.js seed http://localhost:3000
|
||||
|
||||
# Or export projects as JSON for manual import
|
||||
node scripts/seed-test-projects.js export test-projects.json
|
||||
```
|
||||
|
||||
Then ensure your server implements the endpoint as described in the "Localhost API Server Requirements" section below.
|
||||
|
||||
### 2. Configure Test App for Localhost
|
||||
|
||||
Edit `test-apps/daily-notification-test/src/config/test-user-zero.ts`:
|
||||
|
||||
```typescript
|
||||
api: {
|
||||
serverMode: "localhost", // Change from "mock" to "localhost"
|
||||
servers: {
|
||||
localhost: {
|
||||
android: "http://10.0.2.2:3000", // Match your local server port
|
||||
ios: "http://localhost:3000",
|
||||
web: "http://localhost:3000"
|
||||
},
|
||||
// ... other server configs
|
||||
},
|
||||
// ...
|
||||
},
|
||||
testing: {
|
||||
enableMockResponses: false, // Disable mocks to use real localhost API
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Update Starred Plans in Test App
|
||||
|
||||
In the test app UI (UserZeroView), use `updateStarredPlans()` to set which project IDs to query:
|
||||
|
||||
```typescript
|
||||
// In UserZeroView.vue or your test code
|
||||
await DailyNotification.updateStarredPlans({
|
||||
planIds: ["test_project_1", "test_project_2", "test_project_3"]
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Schedule Notification for Testing
|
||||
|
||||
Schedule a notification that will trigger prefetch 5 minutes before:
|
||||
|
||||
```typescript
|
||||
await DailyNotification.scheduleDailyNotification({
|
||||
time: "11:25", // Current time + 5+ minutes for testing
|
||||
title: "Test Notification",
|
||||
body: "Testing prefetch with localhost",
|
||||
priority: "high"
|
||||
});
|
||||
```
|
||||
|
||||
The prefetch will run 5 minutes before (11:20 in this example) and query your localhost API.
|
||||
|
||||
## Testing Prefetch Flow
|
||||
|
||||
### Expected Timeline
|
||||
|
||||
1. **T-0**: You schedule notification for `11:25`
|
||||
2. **T+0**: Plugin schedules prefetch for `11:20` (5 minutes before)
|
||||
3. **T+0**: Plugin logs:
|
||||
```
|
||||
DN|SCHEDULE_FETCH_START time=...
|
||||
DN|WORK_ENQUEUED work_id=... fetch_at=... delay_ms=...
|
||||
```
|
||||
4. **T+~5 minutes**: WorkManager triggers prefetch at `11:20`
|
||||
5. **T+~5 minutes**: Plugin queries `http://10.0.2.2:3000/api/v2/report/plansLastUpdatedBetween`
|
||||
6. **T+~10 minutes**: Notification displays at `11:25`
|
||||
|
||||
### Monitoring Prefetch
|
||||
|
||||
Watch logs for prefetch execution:
|
||||
|
||||
```bash
|
||||
# Filter for prefetch-related logs
|
||||
adb logcat | grep -E "DN|SCHEDULE_FETCH|FETCH_PROJECT|WORK_ENQUEUED"
|
||||
|
||||
# Watch WorkManager queue
|
||||
adb shell dumpsys jobscheduler | grep daily_notification
|
||||
|
||||
# Monitor network requests (if your server logs them)
|
||||
# Check your localhost server logs for POST requests to /api/v2/report/plansLastUpdatedBetween
|
||||
```
|
||||
|
||||
## Localhost API Server Requirements
|
||||
|
||||
Your localhost API server must implement:
|
||||
|
||||
### Endpoint: `POST /api/v2/report/plansLastUpdatedBetween`
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"planIds": ["test_project_1", "test_project_2"],
|
||||
"afterId": "optional_jwt_id_for_pagination"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"planSummary": {
|
||||
"jwtId": "jwt_id_1",
|
||||
"handleId": "test_project_1",
|
||||
"name": "Test Project 1",
|
||||
"description": "Project description",
|
||||
"issuerDid": "did:key:...",
|
||||
"agentDid": "did:key:...",
|
||||
"startTime": "2025-01-01T00:00:00Z",
|
||||
"endTime": "2025-01-31T23:59:59Z"
|
||||
},
|
||||
"previousClaim": {
|
||||
"jwtId": "previous_jwt_id",
|
||||
"claimType": "project_update"
|
||||
}
|
||||
}
|
||||
],
|
||||
"hitLimit": false,
|
||||
"pagination": {
|
||||
"hasMore": false,
|
||||
"nextAfterId": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Headers Required:**
|
||||
- `Authorization: Bearer <JWT_TOKEN>`
|
||||
- `Content-Type: application/json`
|
||||
- `User-Agent: TimeSafari-DailyNotificationPlugin/1.0.0`
|
||||
|
||||
## Seeding Test Projects
|
||||
|
||||
### Using the Test API Server (Automatic)
|
||||
|
||||
If you're using `test-api-server-with-seed.js`, **projects are automatically created on startup** - no additional seeding needed. The server creates plans in-memory, so they're available immediately when the prefetch endpoint is called.
|
||||
|
||||
### Using Your Real TimeSafari API Server
|
||||
|
||||
If your localhost API has no projects, create them via **POST `/api/v2/claim`** with a PlanAction JWT (see "Option B" section above).
|
||||
|
||||
**Note**: The `seed-test-projects.js` script cannot directly create plans because it would require generating signed PlanAction JWTs with DID keys, which is complex. The seed script is designed for the test API server (Option A) or for verifying API responses after plans are created through other means.
|
||||
|
||||
### Generate Test Projects
|
||||
|
||||
```bash
|
||||
# Generate test projects and display as JSON
|
||||
node scripts/seed-test-projects.js generate
|
||||
|
||||
# Export projects to JSON file
|
||||
node scripts/seed-test-projects.js export test-projects.json
|
||||
|
||||
# Seed projects directly to your API server
|
||||
node scripts/seed-test-projects.js seed http://localhost:3000
|
||||
|
||||
# Use custom project IDs
|
||||
node scripts/seed-test-projects.js seed http://localhost:3000 "project_1,project_2,project_3"
|
||||
```
|
||||
|
||||
The seed script generates test projects matching the structure expected by the plugin, including:
|
||||
- `handleId` (from your config)
|
||||
- `jwtId` (timestamp-based for pagination)
|
||||
- `planSummary` (name, description, dates, location)
|
||||
- `previousClaim` (for change detection)
|
||||
|
||||
### Using the Test API Server (Includes Seeding)
|
||||
|
||||
The easiest way is to use the included test API server:
|
||||
|
||||
```bash
|
||||
node scripts/test-api-server-with-seed.js
|
||||
```
|
||||
|
||||
This server:
|
||||
1. Seeds 5 test projects automatically on startup
|
||||
2. Provides the prefetch endpoint ready to use
|
||||
3. Includes debugging endpoints
|
||||
|
||||
## Quick Test Script (Alternative)
|
||||
|
||||
If you want to create your own minimal localhost API server:
|
||||
|
||||
```javascript
|
||||
// test-api-server.js
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.post('/api/v2/report/plansLastUpdatedBetween', (req, res) => {
|
||||
console.log('📥 Prefetch request received:', {
|
||||
planIds: req.body.planIds,
|
||||
afterId: req.body.afterId,
|
||||
headers: req.headers
|
||||
});
|
||||
|
||||
// Return mock response
|
||||
res.json({
|
||||
data: [
|
||||
{
|
||||
planSummary: {
|
||||
jwtId: `${Date.now()}_test_123`,
|
||||
handleId: req.body.planIds[0] || "test_project",
|
||||
name: "Localhost Test Project",
|
||||
description: "Testing prefetch from localhost",
|
||||
issuerDid: "did:test:issuer",
|
||||
agentDid: "did:test:agent",
|
||||
startTime: new Date().toISOString(),
|
||||
endTime: new Date(Date.now() + 86400000).toISOString()
|
||||
},
|
||||
previousClaim: {
|
||||
jwtId: "previous_test_jwt",
|
||||
claimType: "project_update"
|
||||
}
|
||||
}
|
||||
],
|
||||
hitLimit: false,
|
||||
pagination: {
|
||||
hasMore: false,
|
||||
nextAfterId: null
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('🧪 Test API server running on http://localhost:3000');
|
||||
console.log('📡 Android emulator should use: http://10.0.2.2:3000');
|
||||
});
|
||||
```
|
||||
|
||||
Run with:
|
||||
```bash
|
||||
node test-api-server.js
|
||||
```
|
||||
|
||||
**Note**: The included `scripts/test-api-server-with-seed.js` provides the same functionality plus automatic seeding, so you may prefer to use that instead.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Prefetch Not Executing
|
||||
|
||||
1. **Check WorkManager queue:**
|
||||
```bash
|
||||
adb shell dumpsys jobscheduler | grep -A 20 "daily_notification"
|
||||
```
|
||||
|
||||
2. **Verify prefetch was scheduled:**
|
||||
```bash
|
||||
adb logcat | grep "DN|SCHEDULE_FETCH"
|
||||
```
|
||||
|
||||
3. **Check if WorkManager has constraints:**
|
||||
- Battery optimization might be blocking execution
|
||||
- Network might not be available
|
||||
- Device might be in deep sleep
|
||||
|
||||
### Cannot Connect to Localhost
|
||||
|
||||
1. **Android Emulator**: Use `10.0.2.2` not `localhost` or `127.0.0.1`
|
||||
2. **Firewall**: Check if your firewall is blocking port 3000
|
||||
3. **Server not running**: Verify server is listening on correct port
|
||||
4. **Wrong port**: Match the port in config with your server's port
|
||||
|
||||
### Network Timeout
|
||||
|
||||
Increase timeout in config:
|
||||
|
||||
```typescript
|
||||
testing: {
|
||||
timeoutMs: 60000, // Increase from 30000 to 60000
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Errors
|
||||
|
||||
Your localhost server should accept the JWT token generated by the plugin. For testing, you can:
|
||||
|
||||
1. **Disable JWT validation** in your localhost server
|
||||
2. **Use a test JWT secret** that matches plugin config
|
||||
3. **Log the JWT** in your server to see what's being sent
|
||||
|
||||
## Switching Between Mock and Localhost
|
||||
|
||||
You can switch modes without rebuilding:
|
||||
|
||||
1. **Mock Mode** (offline testing):
|
||||
```typescript
|
||||
api.serverMode = "mock";
|
||||
testing.enableMockResponses = true;
|
||||
```
|
||||
|
||||
2. **Localhost Mode** (real API calls to localhost):
|
||||
```typescript
|
||||
api.serverMode = "localhost";
|
||||
testing.enableMockResponses = false;
|
||||
```
|
||||
|
||||
3. **Staging Mode** (real API calls to staging):
|
||||
```typescript
|
||||
api.serverMode = "staging";
|
||||
testing.enableMockResponses = false;
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Test prefetch execution with real localhost API
|
||||
- Monitor network traffic to verify requests
|
||||
- Test with different starred plan IDs
|
||||
- Verify prefetch timing (5 minutes before notification)
|
||||
- Test notification delivery after prefetch completes
|
||||
|
||||
239
doc/testing/MANUAL_SMOKE_TEST.md
Normal file
239
doc/testing/MANUAL_SMOKE_TEST.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Manual Smoke Test Documentation
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Version**: 1.0.0
|
||||
**Created**: 2025-01-27 18:00:00 UTC
|
||||
**Last Updated**: 2025-01-27 18:00:00 UTC
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides step-by-step instructions for manually testing the TimeSafari Daily Notification Plugin across all target platforms (Android, iOS, Electron). This smoke test validates basic functionality and ensures the plugin works correctly in the TimeSafari PWA environment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- TimeSafari PWA project set up and running
|
||||
- Plugin integrated into TimeSafari PWA
|
||||
- Development environment configured for all platforms
|
||||
- Test devices/emulators available for each platform
|
||||
|
||||
## Test Execution Log
|
||||
|
||||
### Latest Test Run
|
||||
|
||||
**Date**: 2025-01-27 18:00:00 UTC
|
||||
**Commit/Tag**: `main` (pre-integration)
|
||||
**Platforms Covered**: Android (Emulator), iOS (Simulator), Electron (Desktop)
|
||||
**Status**: ⏳ **Pending** - To be executed after integration
|
||||
|
||||
## Electron Platform Testing
|
||||
|
||||
### Setup
|
||||
1. Start TimeSafari PWA in development mode
|
||||
2. Open Chrome browser
|
||||
3. Navigate to local development URL
|
||||
4. Open browser developer tools
|
||||
|
||||
### Test Steps
|
||||
|
||||
#### 1. Plugin Installation Verification
|
||||
- [ ] Plugin loads without console errors
|
||||
- [ ] Plugin API is accessible via `window.DailyNotification`
|
||||
- [ ] TypeScript definitions are available
|
||||
|
||||
#### 2. Basic Notification Scheduling
|
||||
- [ ] Schedule a daily notification for 1 minute from now
|
||||
- [ ] Verify notification appears in browser
|
||||
- [ ] Test notification interaction (click/dismiss)
|
||||
- [ ] Verify callback functions are called
|
||||
|
||||
#### 3. Permission Handling
|
||||
- [ ] Request notification permissions
|
||||
- [ ] Handle permission denied gracefully
|
||||
- [ ] Test permission re-request flow
|
||||
|
||||
#### 4. Storage Integration
|
||||
- [ ] Verify notification data persists in native storage
|
||||
- [ ] Test data retrieval after page refresh
|
||||
- [ ] Validate storage adapter integration
|
||||
|
||||
### Expected Results
|
||||
- All notifications appear on schedule
|
||||
- No console errors or warnings
|
||||
- Data persists across page refreshes
|
||||
- Permission flows work correctly
|
||||
|
||||
## Android Platform Testing
|
||||
|
||||
### Setup
|
||||
1. Build TimeSafari PWA for Android
|
||||
2. Install on Android device/emulator
|
||||
3. Enable developer options and USB debugging
|
||||
4. Connect device or start emulator
|
||||
|
||||
### Test Steps
|
||||
|
||||
#### 1. Plugin Registration
|
||||
- [ ] Plugin registers with Capacitor bridge
|
||||
- [ ] Native Android code compiles and loads
|
||||
- [ ] No runtime crashes or errors
|
||||
|
||||
#### 2. Notification Channel Creation
|
||||
- [ ] Notification channels are created on first run
|
||||
- [ ] Channel settings are applied correctly
|
||||
- [ ] Channel importance levels work as expected
|
||||
|
||||
#### 3. Background Scheduling
|
||||
- [ ] WorkManager schedules background tasks
|
||||
- [ ] Notifications fire at scheduled times
|
||||
- [ ] Background execution works with Doze/App Standby
|
||||
|
||||
#### 4. Permission Handling
|
||||
- [ ] Request notification permissions
|
||||
- [ ] Handle permission denied gracefully
|
||||
- [ ] Test battery optimization exemption request
|
||||
|
||||
### Expected Results
|
||||
- Notifications appear on schedule
|
||||
- Background tasks execute reliably
|
||||
- No crashes or ANRs
|
||||
- Permission flows work correctly
|
||||
|
||||
## iOS Platform Testing
|
||||
|
||||
### Setup
|
||||
1. Build TimeSafari PWA for iOS
|
||||
2. Install on iOS device/simulator
|
||||
3. Enable developer mode
|
||||
4. Configure code signing
|
||||
|
||||
### Test Steps
|
||||
|
||||
#### 1. Plugin Registration
|
||||
- [ ] Plugin registers with Capacitor bridge
|
||||
- [ ] Native iOS code compiles and loads
|
||||
- [ ] No runtime crashes or errors
|
||||
|
||||
#### 2. Notification Center Integration
|
||||
- [ ] UNUserNotificationCenter delegates are set
|
||||
- [ ] Notification categories are registered
|
||||
- [ ] Background modes are configured
|
||||
|
||||
#### 3. Background Task Scheduling
|
||||
- [ ] BGTaskScheduler registers background tasks
|
||||
- [ ] Notifications fire at scheduled times
|
||||
- [ ] Background execution works within iOS limits
|
||||
|
||||
#### 4. Permission Handling
|
||||
- [ ] Request notification permissions
|
||||
- [ ] Handle provisional authorization
|
||||
- [ ] Test permission upgrade/downgrade flows
|
||||
|
||||
### Expected Results
|
||||
- Notifications appear on schedule
|
||||
- Background tasks execute within iOS limits
|
||||
- No crashes or memory warnings
|
||||
- Permission flows work correctly
|
||||
|
||||
## Cross-Platform Validation
|
||||
|
||||
### Data Consistency
|
||||
- [ ] Notification data syncs across platforms
|
||||
- [ ] Storage adapter works consistently
|
||||
- [ ] Timezone handling is consistent
|
||||
|
||||
### API Consistency
|
||||
- [ ] Plugin API behaves identically across platforms
|
||||
- [ ] Callback functions work the same way
|
||||
- [ ] Error handling is consistent
|
||||
|
||||
### Performance
|
||||
- [ ] Plugin initialization is fast (< 1 second)
|
||||
- [ ] Memory usage is reasonable
|
||||
- [ ] Battery impact is minimal
|
||||
|
||||
## Test Results Template
|
||||
|
||||
```markdown
|
||||
## Test Run Results
|
||||
|
||||
**Date**: [YYYY-MM-DD HH:MM:SS UTC]
|
||||
**Commit/Tag**: [commit hash or tag]
|
||||
**Tester**: [name]
|
||||
**Environment**: [development/staging/production]
|
||||
|
||||
### Electron Platform
|
||||
- **Status**: [PASS/FAIL/PARTIAL]
|
||||
- **Issues**: [list any issues found]
|
||||
- **Notes**: [additional observations]
|
||||
|
||||
### Android Platform
|
||||
- **Status**: [PASS/FAIL/PARTIAL]
|
||||
- **Device**: [device model/emulator version]
|
||||
- **Issues**: [list any issues found]
|
||||
- **Notes**: [additional observations]
|
||||
|
||||
### iOS Platform
|
||||
- **Status**: [PASS/FAIL/PARTIAL]
|
||||
- **Device**: [device model/simulator version]
|
||||
- **Issues**: [list any issues found]
|
||||
- **Notes**: [additional observations]
|
||||
|
||||
### Overall Assessment
|
||||
- **Integration Status**: [READY/NOT_READY]
|
||||
- **Blocking Issues**: [list any blocking issues]
|
||||
- **Recommendations**: [next steps or improvements]
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Electron Platform
|
||||
- **Notifications not appearing**: Check system notification permissions
|
||||
- **Storage errors**: Verify local storage is available and not blocked
|
||||
- **API not accessible**: Check plugin registration and build output
|
||||
|
||||
#### Android Platform
|
||||
- **Notifications not firing**: Check WorkManager constraints and battery optimization
|
||||
- **Crashes on startup**: Verify native code compilation and permissions
|
||||
- **Background tasks not running**: Check Doze/App Standby settings
|
||||
|
||||
#### iOS Platform
|
||||
- **Notifications not appearing**: Check notification permissions and categories
|
||||
- **Background tasks not running**: Verify BGTaskScheduler registration and iOS limits
|
||||
- **Crashes on startup**: Check native code compilation and capabilities
|
||||
|
||||
### Debug Steps
|
||||
|
||||
1. **Check Logs**: Review console logs, native logs, and system logs
|
||||
2. **Verify Permissions**: Ensure all required permissions are granted
|
||||
3. **Test Isolation**: Test each platform independently
|
||||
4. **Compare with Examples**: Compare behavior with plugin examples
|
||||
5. **Check Dependencies**: Verify all dependencies are correctly installed
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Must Pass
|
||||
- [ ] Plugin loads without errors on all platforms
|
||||
- [ ] Basic notification scheduling works on all platforms
|
||||
- [ ] Permission handling works correctly
|
||||
- [ ] No crashes or memory leaks
|
||||
- [ ] Data persistence works across app restarts
|
||||
|
||||
### Should Pass
|
||||
- [ ] Background execution works reliably
|
||||
- [ ] Performance is acceptable
|
||||
- [ ] Error handling is graceful
|
||||
- [ ] Cross-platform consistency is maintained
|
||||
|
||||
### Nice to Have
|
||||
- [ ] Advanced features work correctly
|
||||
- [ ] Edge cases are handled gracefully
|
||||
- [ ] User experience is smooth
|
||||
- [ ] Integration with TimeSafari features works
|
||||
|
||||
---
|
||||
|
||||
**Next Review**: After first integration test run
|
||||
**Stakeholders**: TimeSafari Development Team, Plugin Development Team
|
||||
**Dependencies**: TimeSafari PWA Integration, Plugin Build Completion
|
||||
546
doc/testing/NOTIFICATION_PROCEDURES.md
Normal file
546
doc/testing/NOTIFICATION_PROCEDURES.md
Normal file
@@ -0,0 +1,546 @@
|
||||
# DailyNotification Plugin Testing Procedures
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides comprehensive testing procedures for the DailyNotification Capacitor plugin, covering both manual and automated testing scenarios.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Manual Testing Procedures](#manual-testing-procedures)
|
||||
2. [Scripted Testing Procedures](#scripted-testing-procedures)
|
||||
3. [ADB Commands Reference](#adb-commands-reference)
|
||||
4. [Troubleshooting](#troubleshooting)
|
||||
5. [Test Scenarios](#test-scenarios)
|
||||
|
||||
---
|
||||
|
||||
## Manual Testing Procedures
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Android device or emulator with ADB enabled
|
||||
- DailyNotification app installed
|
||||
- ADB tools installed on development machine
|
||||
- Notification permissions granted
|
||||
|
||||
### Basic Functionality Test
|
||||
|
||||
#### 1. Plugin Registration Test
|
||||
|
||||
**Objective**: Verify the plugin loads and registers correctly
|
||||
|
||||
**Steps**:
|
||||
1. Launch the app: `adb shell am start -n org.timesafari.dailynotification/.MainActivity`
|
||||
2. Tap "Test Plugin" button
|
||||
3. Verify status shows "Plugin is loaded and ready!"
|
||||
|
||||
**Expected Result**: Green status with plugin loaded confirmation
|
||||
|
||||
#### 2. Permission Management Test
|
||||
|
||||
**Objective**: Test permission request and status checking
|
||||
|
||||
**Steps**:
|
||||
1. Tap "Check Permissions" button
|
||||
2. Note current permission status (✅/❌ indicators)
|
||||
3. Tap "Request Permissions" button
|
||||
4. Grant permissions in system dialog if prompted
|
||||
5. Tap "Check Permissions" again to verify
|
||||
|
||||
**Expected Result**: All permissions show ✅ after granting
|
||||
|
||||
#### 3. Notification Scheduling Test
|
||||
|
||||
**Objective**: Test basic notification scheduling
|
||||
|
||||
**Steps**:
|
||||
1. Tap "Test Notification" button
|
||||
2. Verify status shows "Notification scheduled for [time]!"
|
||||
3. Wait 1 minute for notification to appear
|
||||
4. Check notification panel for the test notification
|
||||
|
||||
**Expected Result**: Notification appears in system notification panel
|
||||
|
||||
### Background Notification Test
|
||||
|
||||
#### 4. App Background Notification Test
|
||||
|
||||
**Objective**: Verify notifications work when app is in background
|
||||
|
||||
**Steps**:
|
||||
1. Schedule a notification using "Test Notification"
|
||||
2. Send app to background: `adb shell input keyevent KEYCODE_HOME`
|
||||
3. Verify app is still running: `adb shell "ps | grep timesafari"`
|
||||
4. Wait for scheduled time
|
||||
5. Check notification panel
|
||||
|
||||
**Expected Result**: Notification appears even with app in background
|
||||
|
||||
#### 5. App Closed Notification Test
|
||||
|
||||
**Objective**: Verify notifications work when app is closed normally
|
||||
|
||||
**Steps**:
|
||||
1. Schedule a notification using "Test Notification"
|
||||
2. Close app normally: `adb shell input keyevent KEYCODE_HOME`
|
||||
3. Verify app process is still running: `adb shell "ps | grep timesafari"`
|
||||
4. Wait for scheduled time
|
||||
5. Check notification panel
|
||||
|
||||
**Expected Result**: Notification appears with app closed
|
||||
|
||||
### Edge Case Testing
|
||||
|
||||
#### 6. Force Stop Test (Expected Failure)
|
||||
|
||||
**Objective**: Verify force-stop cancels notifications (expected behavior)
|
||||
|
||||
**Steps**:
|
||||
1. Schedule a notification using "Test Notification"
|
||||
2. Force stop the app: `adb shell am force-stop org.timesafari.dailynotification`
|
||||
3. Verify app is killed: `adb shell "ps | grep timesafari"` (should return nothing)
|
||||
4. Wait for scheduled time
|
||||
5. Check notification panel
|
||||
|
||||
**Expected Result**: No notification appears (this is correct behavior)
|
||||
|
||||
#### 7. Reboot Recovery Test
|
||||
|
||||
**Objective**: Test notification recovery after device reboot
|
||||
|
||||
**Steps**:
|
||||
1. Schedule a notification for future time
|
||||
2. Reboot device: `adb reboot`
|
||||
3. Wait for device to fully boot
|
||||
4. Check if notification still scheduled: `adb shell "dumpsys alarm | grep timesafari"`
|
||||
5. Wait for scheduled time
|
||||
6. Check notification panel
|
||||
|
||||
**Expected Result**: Notification should still fire after reboot
|
||||
|
||||
---
|
||||
|
||||
## Scripted Testing Procedures
|
||||
|
||||
### Automated Test Script
|
||||
|
||||
Create a test script for automated testing:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# daily-notification-test.sh
|
||||
|
||||
set -e
|
||||
|
||||
APP_PACKAGE="org.timesafari.dailynotification"
|
||||
APP_ACTIVITY=".MainActivity"
|
||||
TEST_TIMEOUT=120 # 2 minutes
|
||||
|
||||
echo "🧪 Starting DailyNotification Plugin Tests"
|
||||
echo "=========================================="
|
||||
|
||||
# Function to check if app is running
|
||||
check_app_running() {
|
||||
adb shell "ps | grep $APP_PACKAGE" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to wait for app to be ready
|
||||
wait_for_app() {
|
||||
echo "⏳ Waiting for app to be ready..."
|
||||
sleep 3
|
||||
}
|
||||
|
||||
# Function to schedule notification
|
||||
schedule_notification() {
|
||||
echo "📅 Scheduling test notification..."
|
||||
# This would need to be implemented with UI automation
|
||||
# For now, we'll assume manual scheduling
|
||||
echo "⚠️ Manual step: Schedule notification in app UI"
|
||||
read -p "Press Enter when notification is scheduled..."
|
||||
}
|
||||
|
||||
# Test 1: App Launch
|
||||
echo "🚀 Test 1: App Launch"
|
||||
adb shell am start -n $APP_PACKAGE/$APP_ACTIVITY
|
||||
wait_for_app
|
||||
|
||||
if check_app_running; then
|
||||
echo "✅ App launched successfully"
|
||||
else
|
||||
echo "❌ App failed to launch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Background Test
|
||||
echo "🔄 Test 2: Background Notification Test"
|
||||
schedule_notification
|
||||
|
||||
echo "📱 Sending app to background..."
|
||||
adb shell input keyevent KEYCODE_HOME
|
||||
sleep 2
|
||||
|
||||
if check_app_running; then
|
||||
echo "✅ App running in background"
|
||||
else
|
||||
echo "❌ App not running in background"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Wait for notification
|
||||
echo "⏰ Test 3: Waiting for notification ($TEST_TIMEOUT seconds)..."
|
||||
echo "👀 Watch for notification in system panel"
|
||||
|
||||
# Check for notification in logs
|
||||
timeout $TEST_TIMEOUT bash -c '
|
||||
while true; do
|
||||
if adb logcat -d | grep -q "Notification displayed successfully"; then
|
||||
echo "✅ Notification displayed successfully"
|
||||
exit 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
'
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Notification test passed"
|
||||
else
|
||||
echo "❌ Notification test failed or timed out"
|
||||
fi
|
||||
|
||||
# Test 4: Force Stop Test
|
||||
echo "🛑 Test 4: Force Stop Test (Expected Failure)"
|
||||
schedule_notification
|
||||
|
||||
echo "💀 Force stopping app..."
|
||||
adb shell am force-stop $APP_PACKAGE
|
||||
sleep 2
|
||||
|
||||
if check_app_running; then
|
||||
echo "❌ App still running after force stop"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ App successfully force stopped"
|
||||
fi
|
||||
|
||||
echo "⏰ Waiting for notification (should NOT appear)..."
|
||||
sleep 60
|
||||
|
||||
if adb logcat -d | grep -q "Notification displayed successfully"; then
|
||||
echo "❌ Notification appeared after force stop (unexpected)"
|
||||
else
|
||||
echo "✅ No notification after force stop (expected)"
|
||||
fi
|
||||
|
||||
echo "🎉 All tests completed!"
|
||||
```
|
||||
|
||||
### Python Test Script
|
||||
|
||||
For more advanced testing with UI automation:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DailyNotification Plugin Automated Test Suite
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import json
|
||||
import sys
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
class DailyNotificationTester:
|
||||
def __init__(self, package: str = "org.timesafari.dailynotification"):
|
||||
self.package = package
|
||||
self.activity = f"{package}/.MainActivity"
|
||||
|
||||
def run_adb_command(self, command: str) -> subprocess.CompletedProcess:
|
||||
"""Run ADB command and return result"""
|
||||
return subprocess.run(
|
||||
f"adb {command}",
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
def is_app_running(self) -> bool:
|
||||
"""Check if app is currently running"""
|
||||
result = self.run_adb_command(f'shell "ps | grep {self.package}"')
|
||||
return result.returncode == 0 and self.package in result.stdout
|
||||
|
||||
def launch_app(self) -> bool:
|
||||
"""Launch the app"""
|
||||
result = self.run_adb_command(f"shell am start -n {self.activity}")
|
||||
time.sleep(3) # Wait for app to load
|
||||
return self.is_app_running()
|
||||
|
||||
def send_to_background(self) -> bool:
|
||||
"""Send app to background"""
|
||||
self.run_adb_command("shell input keyevent KEYCODE_HOME")
|
||||
time.sleep(2)
|
||||
return self.is_app_running()
|
||||
|
||||
def force_stop_app(self) -> bool:
|
||||
"""Force stop the app"""
|
||||
self.run_adb_command(f"shell am force-stop {self.package}")
|
||||
time.sleep(2)
|
||||
return not self.is_app_running()
|
||||
|
||||
def check_notification_logs(self, timeout: int = 60) -> bool:
|
||||
"""Check for notification success in logs"""
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
result = self.run_adb_command("logcat -d")
|
||||
if "Notification displayed successfully" in result.stdout:
|
||||
return True
|
||||
time.sleep(5)
|
||||
return False
|
||||
|
||||
def run_test_suite(self) -> Dict[str, Any]:
|
||||
"""Run complete test suite"""
|
||||
results = {}
|
||||
|
||||
print("🧪 Starting DailyNotification Test Suite")
|
||||
print("=" * 50)
|
||||
|
||||
# Test 1: App Launch
|
||||
print("🚀 Test 1: App Launch")
|
||||
results["app_launch"] = self.launch_app()
|
||||
print(f"Result: {'✅ PASS' if results['app_launch'] else '❌ FAIL'}")
|
||||
|
||||
# Test 2: Background Test
|
||||
print("🔄 Test 2: Background Test")
|
||||
results["background"] = self.send_to_background()
|
||||
print(f"Result: {'✅ PASS' if results['background'] else '❌ FAIL'}")
|
||||
|
||||
# Test 3: Force Stop Test
|
||||
print("🛑 Test 3: Force Stop Test")
|
||||
results["force_stop"] = self.force_stop_app()
|
||||
print(f"Result: {'✅ PASS' if results['force_stop'] else '❌ FAIL'}")
|
||||
|
||||
# Test 4: Notification Test (requires manual scheduling)
|
||||
print("📱 Test 4: Notification Test")
|
||||
print("⚠️ Manual step required: Schedule notification in app")
|
||||
input("Press Enter when notification is scheduled...")
|
||||
|
||||
results["notification"] = self.check_notification_logs()
|
||||
print(f"Result: {'✅ PASS' if results['notification'] else '❌ FAIL'}")
|
||||
|
||||
return results
|
||||
|
||||
def main():
|
||||
tester = DailyNotificationTester()
|
||||
results = tester.run_test_suite()
|
||||
|
||||
print("\n📊 Test Results Summary:")
|
||||
print("=" * 30)
|
||||
for test, passed in results.items():
|
||||
status = "✅ PASS" if passed else "❌ FAIL"
|
||||
print(f"{test.replace('_', ' ').title()}: {status}")
|
||||
|
||||
# Exit with error code if any test failed
|
||||
if not all(results.values()):
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ADB Commands Reference
|
||||
|
||||
### App Management Commands
|
||||
|
||||
```bash
|
||||
# Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# Send to background (normal close)
|
||||
adb shell input keyevent KEYCODE_HOME
|
||||
|
||||
# Force stop app
|
||||
adb shell am force-stop org.timesafari.dailynotification
|
||||
|
||||
# Check if app is running
|
||||
adb shell "ps | grep timesafari"
|
||||
|
||||
# Clear app data
|
||||
adb shell pm clear org.timesafari.dailynotification
|
||||
```
|
||||
|
||||
### Notification Testing Commands
|
||||
|
||||
```bash
|
||||
# Check notification settings
|
||||
adb shell "dumpsys notification | grep -A5 -B5 timesafari"
|
||||
|
||||
# List all notifications
|
||||
adb shell "cmd notification list"
|
||||
|
||||
# Open notification settings
|
||||
adb shell "am start -a android.settings.APP_NOTIFICATION_SETTINGS -e android.provider.extra.APP_PACKAGE org.timesafari.dailynotification"
|
||||
```
|
||||
|
||||
### Alarm Management Commands
|
||||
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep -i alarm"
|
||||
|
||||
# Check specific app alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
### Logging Commands
|
||||
|
||||
```bash
|
||||
# Monitor logs in real-time
|
||||
adb logcat | grep -i "dailynotification\|notification"
|
||||
|
||||
# Get recent logs
|
||||
adb logcat -d | grep -i "dailynotification"
|
||||
|
||||
# Clear logs
|
||||
adb logcat -c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Plugin Not Loading
|
||||
**Symptoms**: "Plugin not available" error
|
||||
**Solutions**:
|
||||
- Check `capacitor.plugins.json` file exists and is valid
|
||||
- Verify plugin is built: `./gradlew assembleDebug`
|
||||
- Reinstall app: `adb install -r app/build/outputs/apk/debug/app-debug.apk`
|
||||
|
||||
#### 2. Notifications Not Appearing
|
||||
**Symptoms**: No notification in system panel
|
||||
**Solutions**:
|
||||
- Check notification permissions: Use "Check Permissions" button
|
||||
- Verify notification importance: `adb shell "dumpsys notification | grep timesafari"`
|
||||
- Check if notifications are enabled in system settings
|
||||
|
||||
#### 3. Alarms Not Firing
|
||||
**Symptoms**: Scheduled notifications don't appear
|
||||
**Solutions**:
|
||||
- Check exact alarm permissions: `adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"`
|
||||
- Verify alarm is scheduled: `adb shell "dumpsys alarm | grep timesafari"`
|
||||
- Check battery optimization settings
|
||||
- Use diagnostic methods to verify alarm status:
|
||||
```typescript
|
||||
// Check if alarm is scheduled
|
||||
const status = await DailyNotification.isAlarmScheduled({
|
||||
triggerAtMillis: scheduledTime
|
||||
});
|
||||
|
||||
// Get next alarm time
|
||||
const nextAlarm = await DailyNotification.getNextAlarmTime();
|
||||
|
||||
// Test alarm delivery
|
||||
await DailyNotification.testAlarm({ secondsFromNow: 10 });
|
||||
```
|
||||
|
||||
#### 4. BroadcastReceiver Not Invoked
|
||||
**Symptoms**: Alarm fires but notification doesn't appear, no logs from `NotifyReceiver`
|
||||
**Solutions**:
|
||||
- **CRITICAL**: Verify `NotifyReceiver` is registered in `AndroidManifest.xml`:
|
||||
```xml
|
||||
<receiver android:name="org.timesafari.dailynotification.NotifyReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
</receiver>
|
||||
```
|
||||
- Check logs for `NotifyReceiver` registration: `adb logcat -d | grep -i "NotifyReceiver"`
|
||||
- Verify the receiver is in your app's manifest, not just the plugin's manifest
|
||||
- Check if app process is killed: `adb shell "ps | grep timesafari"`
|
||||
- Review alarm scheduling logs: `adb logcat -d | grep -E "DNP-NOTIFY|Alarm clock"`
|
||||
|
||||
#### 5. App Crashes on Force Stop
|
||||
**Symptoms**: App crashes when force-stopped
|
||||
**Solutions**:
|
||||
- This is expected behavior - force-stop kills the app
|
||||
- Use normal closure for testing: `adb shell input keyevent KEYCODE_HOME`
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check app permissions
|
||||
adb shell dumpsys package org.timesafari.dailynotification | grep permission
|
||||
|
||||
# Check app info
|
||||
adb shell dumpsys package org.timesafari.dailynotification | grep -A10 "Application Info"
|
||||
|
||||
# Check notification channels
|
||||
adb shell "dumpsys notification | grep -A10 timesafari"
|
||||
|
||||
# Monitor system events
|
||||
adb shell "dumpsys activity activities | grep timesafari"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Scenarios
|
||||
|
||||
### Scenario 1: Basic Functionality
|
||||
- **Objective**: Verify plugin works in normal conditions
|
||||
- **Steps**: Launch app → Test plugin → Schedule notification → Verify delivery
|
||||
- **Expected**: All functions work correctly
|
||||
|
||||
### Scenario 2: Background Operation
|
||||
- **Objective**: Verify notifications work with app in background
|
||||
- **Steps**: Schedule notification → Send to background → Wait for delivery
|
||||
- **Expected**: Notification appears despite app being backgrounded
|
||||
|
||||
### Scenario 3: Permission Management
|
||||
- **Objective**: Test permission request and status
|
||||
- **Steps**: Check permissions → Request permissions → Verify status
|
||||
- **Expected**: Permissions granted and status updated
|
||||
|
||||
### Scenario 4: Edge Cases
|
||||
- **Objective**: Test behavior under extreme conditions
|
||||
- **Steps**: Force stop → Reboot → Battery optimization
|
||||
- **Expected**: Graceful handling of edge cases
|
||||
|
||||
### Scenario 5: Performance Testing
|
||||
- **Objective**: Test under load and stress
|
||||
- **Steps**: Multiple notifications → Rapid scheduling → Memory pressure
|
||||
- **Expected**: Stable performance under load
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Testing Environment
|
||||
- Use physical device for most accurate results
|
||||
- Test on multiple Android versions (API 21+)
|
||||
- Test on different OEMs (Samsung, Huawei, etc.)
|
||||
- Test with different battery optimization settings
|
||||
|
||||
### Test Data
|
||||
- Use realistic notification content
|
||||
- Test with various time intervals
|
||||
- Test with different notification priorities
|
||||
- Test with sound/vibration enabled/disabled
|
||||
|
||||
### Documentation
|
||||
- Record test results with timestamps
|
||||
- Screenshot notification appearances
|
||||
- Log any errors or unexpected behavior
|
||||
- Document device-specific issues
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
This testing procedure ensures the DailyNotification plugin works correctly across all scenarios. Regular testing helps maintain reliability and catch issues early in development.
|
||||
|
||||
For questions or issues, refer to the troubleshooting section or check the plugin logs using the provided ADB commands.
|
||||
519
doc/testing/PHYSICAL_DEVICE_GUIDE.md
Normal file
519
doc/testing/PHYSICAL_DEVICE_GUIDE.md
Normal file
@@ -0,0 +1,519 @@
|
||||
# Running Android App on a Physical Device
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Last Updated**: 2026-02-12
|
||||
**Version**: 1.0.0
|
||||
|
||||
## Overview
|
||||
|
||||
This guide demonstrates how to run the DailyNotification plugin test app on a physical Android device. Physical device testing is essential for validating:
|
||||
|
||||
- **Real notification behavior** — Emulators may not accurately simulate notification delivery timing
|
||||
- **Battery optimization effects** — OEM-specific power management that affects background tasks
|
||||
- **Actual alarm scheduling** — AlarmManager behavior varies between emulators and real hardware
|
||||
- **Device reboot persistence** — Boot receivers and alarm recovery
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Hardware
|
||||
- **Android phone or tablet** running Android 8.0 (API 26) or higher
|
||||
- **USB cable** (data-capable, not charge-only)
|
||||
- **Development computer** with USB port
|
||||
|
||||
### Required Software
|
||||
- **Android SDK** with platform-tools (provides `adb`)
|
||||
- **Gradle** (via Gradle Wrapper)
|
||||
- **Node.js** and **npm** (for TypeScript compilation)
|
||||
|
||||
### How to Check
|
||||
|
||||
| Requirement | How to check |
|
||||
|------------------|--------------|
|
||||
| **Node.js** | `node --version` (v14+ recommended; test app may require 20+) |
|
||||
| **npm** | `npm --version` |
|
||||
| **Java** | `java -version` (Java 11+) |
|
||||
| **ANDROID_HOME** | `echo $ANDROID_HOME` (must be set to your Android SDK root) |
|
||||
| **adb** | `adb version` (must be on PATH) |
|
||||
|
||||
**Project script:** From the repo root:
|
||||
|
||||
```bash
|
||||
node scripts/check-environment.js
|
||||
```
|
||||
|
||||
## Step 1: Enable Developer Options on Your Phone
|
||||
|
||||
Developer Options are hidden by default. To enable them:
|
||||
|
||||
### Android 8.0 - 14 (Most Devices)
|
||||
|
||||
1. Open **Settings**
|
||||
2. Scroll down to **About phone** (or **About device**)
|
||||
3. Find **Build number**
|
||||
4. **Tap Build number 7 times** rapidly
|
||||
5. You'll see "You are now a developer!" toast message
|
||||
|
||||
### Samsung Devices
|
||||
|
||||
1. **Settings** → **About phone** → **Software information**
|
||||
2. Tap **Build number** 7 times
|
||||
|
||||
### Xiaomi/MIUI Devices
|
||||
|
||||
1. **Settings** → **About phone**
|
||||
2. Tap **MIUI version** 7 times
|
||||
|
||||
### OnePlus Devices
|
||||
|
||||
1. **Settings** → **About phone**
|
||||
2. Tap **Build number** 7 times
|
||||
|
||||
## Step 2: Enable USB Debugging
|
||||
|
||||
After enabling Developer Options:
|
||||
|
||||
1. Go to **Settings** → **System** → **Developer options**
|
||||
- On some phones: **Settings** → **Developer options** directly
|
||||
2. Scroll to find **USB debugging**
|
||||
3. Toggle **USB debugging ON**
|
||||
4. Confirm when prompted
|
||||
|
||||
### Optional but Recommended Settings
|
||||
|
||||
While in Developer Options, also enable:
|
||||
|
||||
- **Stay awake** — Screen stays on while charging (useful during development)
|
||||
- **Allow mock locations** — If testing location features
|
||||
|
||||
## Step 3: Connect and Authorize Your Device
|
||||
|
||||
### Physical Connection
|
||||
|
||||
1. Connect your phone to your computer via USB
|
||||
2. On your phone, change USB mode:
|
||||
- Pull down notification shade
|
||||
- Tap the USB notification ("Charging this device via USB")
|
||||
- Select **File transfer / Android Auto** or **PTP** (not "Charge only")
|
||||
|
||||
### Authorize Computer
|
||||
|
||||
1. On your phone, you'll see a dialog: **"Allow USB debugging?"**
|
||||
2. Check **"Always allow from this computer"** (recommended)
|
||||
3. Tap **Allow**
|
||||
|
||||
### Verify Connection
|
||||
|
||||
```bash
|
||||
# List connected devices
|
||||
adb devices
|
||||
|
||||
# Expected output:
|
||||
# List of devices attached
|
||||
# ABC123DEF456 device
|
||||
```
|
||||
|
||||
**Troubleshooting connection states:**
|
||||
|
||||
| State | Meaning | Solution |
|
||||
|-------|---------|----------|
|
||||
| `device` | Connected and authorized | Ready to use |
|
||||
| `unauthorized` | USB debugging not authorized | Check phone for auth dialog |
|
||||
| `offline` | Connection issues | Unplug, replug, restart adb |
|
||||
| (empty) | Device not detected | Check USB cable, USB mode |
|
||||
|
||||
## Step 4: Build and Install the App
|
||||
|
||||
### Option A: Using Build Script (Recommended)
|
||||
|
||||
From the `test-apps/daily-notification-test` directory:
|
||||
|
||||
```bash
|
||||
# Build and run on connected device
|
||||
./scripts/build.sh --run-android
|
||||
```
|
||||
|
||||
### Option B: Manual Build
|
||||
|
||||
```bash
|
||||
# 1. Navigate to test app directory
|
||||
cd test-apps/daily-notification-test
|
||||
|
||||
# 2. Build web assets
|
||||
npm run build
|
||||
|
||||
# 3. Sync with Capacitor
|
||||
npm run cap:sync:android
|
||||
|
||||
# 4. Build APK
|
||||
cd android
|
||||
./gradlew :app:assembleDebug
|
||||
|
||||
# 5. Install on device
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# 6. Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification.test/.MainActivity
|
||||
```
|
||||
|
||||
### Option C: Using Capacitor CLI
|
||||
|
||||
```bash
|
||||
# Build, install, and launch in one command
|
||||
npx cap run android --target <device-id>
|
||||
|
||||
# Get device ID from:
|
||||
adb devices
|
||||
```
|
||||
|
||||
## Step 5: Configure Battery Optimization (Critical!)
|
||||
|
||||
**This is the most important step for notification testing.** Android OEMs aggressively kill background apps to save battery. Without proper configuration, your alarms and notifications may not fire.
|
||||
|
||||
### Disable Battery Optimization for Test App
|
||||
|
||||
1. **Settings** → **Apps** → **DailyNotification Test** (or your app name)
|
||||
2. **Battery** → **Unrestricted** or **Don't optimize**
|
||||
|
||||
### Manufacturer-Specific Settings
|
||||
|
||||
#### Samsung (One UI)
|
||||
|
||||
1. **Settings** → **Battery** → **Background usage limits**
|
||||
2. Remove app from "Sleeping apps" and "Deep sleeping apps"
|
||||
3. Add app to "Never sleeping apps"
|
||||
|
||||
#### Xiaomi (MIUI)
|
||||
|
||||
1. **Settings** → **Apps** → **Manage apps** → Select app
|
||||
2. Enable **Autostart**
|
||||
3. **Battery saver** → **No restrictions**
|
||||
4. **Security** app → **Permissions** → **Autostart** → Enable for app
|
||||
|
||||
#### OnePlus (OxygenOS)
|
||||
|
||||
1. **Settings** → **Battery** → **Battery optimization**
|
||||
2. Select app → **Don't optimize**
|
||||
3. **Settings** → **Apps** → Select app → **Advanced** → **Optimize battery usage** → Off
|
||||
|
||||
#### Huawei/Honor (EMUI)
|
||||
|
||||
1. **Settings** → **Battery** → **App launch**
|
||||
2. Disable automatic management for the app
|
||||
3. Enable all three toggles: Auto-launch, Secondary launch, Run in background
|
||||
|
||||
#### Oppo/Realme (ColorOS)
|
||||
|
||||
1. **Settings** → **Battery** → **More battery settings**
|
||||
2. **Optimize battery use** → Select app → **Don't optimize**
|
||||
3. Enable **Allow auto-start** and **Allow background activity**
|
||||
|
||||
### Verify Battery Settings
|
||||
|
||||
```bash
|
||||
# Check if app is whitelisted from battery optimization
|
||||
adb shell dumpsys deviceidle whitelist
|
||||
|
||||
# Should include your package name
|
||||
```
|
||||
|
||||
## Step 6: Monitor Logs
|
||||
|
||||
### Real-time Log Streaming
|
||||
|
||||
```bash
|
||||
# All logs from the app
|
||||
adb logcat | grep -E "DailyNotification|Capacitor|Console"
|
||||
|
||||
# Specific tags only
|
||||
adb logcat -s "DailyNotification" "Capacitor" "Console"
|
||||
|
||||
# Clear logs and start fresh
|
||||
adb logcat -c && adb logcat -s "DailyNotification"
|
||||
```
|
||||
|
||||
### Filter by Log Level
|
||||
|
||||
```bash
|
||||
# Errors only
|
||||
adb logcat *:E | grep DailyNotification
|
||||
|
||||
# Warnings and above
|
||||
adb logcat *:W | grep DailyNotification
|
||||
|
||||
# Verbose (all levels)
|
||||
adb logcat *:V | grep DailyNotification
|
||||
```
|
||||
|
||||
### Save Logs to File
|
||||
|
||||
```bash
|
||||
# Stream logs to file
|
||||
adb logcat -s "DailyNotification" > device_logs.txt
|
||||
|
||||
# Press Ctrl+C to stop
|
||||
```
|
||||
|
||||
### Check Alarm Scheduling
|
||||
|
||||
```bash
|
||||
# View scheduled alarms (requires root or debuggable build)
|
||||
adb shell dumpsys alarm | grep -A 5 "org.timesafari"
|
||||
|
||||
# View alarm statistics
|
||||
adb shell dumpsys alarm | grep -i "daily"
|
||||
```
|
||||
|
||||
## Step 7: Testing Notification Features
|
||||
|
||||
### Test Immediate Notification
|
||||
|
||||
1. Open the app
|
||||
2. Navigate to notification testing section
|
||||
3. Trigger an immediate notification
|
||||
4. Verify it appears in the notification tray
|
||||
|
||||
### Test Scheduled Notification
|
||||
|
||||
1. Schedule a notification for 1-2 minutes in the future
|
||||
2. Lock the phone or put app in background
|
||||
3. Wait for notification to fire
|
||||
4. Check logs if notification doesn't appear
|
||||
|
||||
### Test Alarm Persistence
|
||||
|
||||
1. Schedule a notification
|
||||
2. Reboot the device:
|
||||
```bash
|
||||
adb reboot
|
||||
```
|
||||
3. After reboot, check if alarm was restored:
|
||||
```bash
|
||||
adb shell dumpsys alarm | grep -A 5 "org.timesafari"
|
||||
```
|
||||
|
||||
### Test Force Stop Recovery
|
||||
|
||||
1. Schedule a notification
|
||||
2. Force stop the app:
|
||||
```bash
|
||||
adb shell am force-stop org.timesafari.dailynotification.test
|
||||
```
|
||||
3. Check if alarms are recovered (implementation dependent)
|
||||
|
||||
## Complete Command Sequence
|
||||
|
||||
### Quick Start (Copy-Paste Ready)
|
||||
|
||||
```bash
|
||||
# 1. Verify device connection
|
||||
adb devices
|
||||
|
||||
# 2. Navigate to test app
|
||||
cd test-apps/daily-notification-test
|
||||
|
||||
# 3. Build everything
|
||||
npm run build
|
||||
npm run cap:sync:android
|
||||
|
||||
# 4. Build and install APK
|
||||
cd android
|
||||
./gradlew :app:assembleDebug
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# 5. Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification.test/.MainActivity
|
||||
|
||||
# 6. Monitor logs (in separate terminal)
|
||||
adb logcat -s "DailyNotification" "Capacitor" "Console"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Device Not Detected
|
||||
|
||||
```bash
|
||||
# Restart ADB server
|
||||
adb kill-server
|
||||
adb start-server
|
||||
adb devices
|
||||
|
||||
# Check USB connection
|
||||
# - Try different USB cable (use data cable, not charge-only)
|
||||
# - Try different USB port
|
||||
# - Check USB mode on phone (should be File transfer, not Charge only)
|
||||
```
|
||||
|
||||
### "Unauthorized" Device
|
||||
|
||||
```bash
|
||||
# Revoke USB debugging authorizations on phone:
|
||||
# Settings → Developer options → Revoke USB debugging authorizations
|
||||
|
||||
# Then reconnect and re-authorize
|
||||
adb kill-server
|
||||
adb start-server
|
||||
# Accept authorization dialog on phone
|
||||
```
|
||||
|
||||
### APK Installation Fails
|
||||
|
||||
```bash
|
||||
# Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE
|
||||
# Solution: Uninstall existing app first
|
||||
adb uninstall org.timesafari.dailynotification.test
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# Error: INSTALL_FAILED_USER_RESTRICTED
|
||||
# Solution: Enable "Install via USB" in Developer options
|
||||
```
|
||||
|
||||
### Notifications Not Appearing
|
||||
|
||||
1. **Check notification permissions:**
|
||||
```bash
|
||||
adb shell dumpsys notification | grep -A 10 "org.timesafari"
|
||||
```
|
||||
|
||||
2. **Check battery optimization:**
|
||||
- Ensure app is set to "Unrestricted" or "Don't optimize"
|
||||
- Check manufacturer-specific settings (see Step 5)
|
||||
|
||||
3. **Check Do Not Disturb:**
|
||||
- Ensure DND is off, or app is allowed through DND
|
||||
|
||||
4. **Check notification channel:**
|
||||
```bash
|
||||
adb shell dumpsys notification | grep -B 5 -A 10 "channel"
|
||||
```
|
||||
|
||||
### Alarms Not Firing
|
||||
|
||||
1. **Check if alarms are scheduled:**
|
||||
```bash
|
||||
adb shell dumpsys alarm | grep -A 10 "org.timesafari"
|
||||
```
|
||||
|
||||
2. **Check Doze mode:**
|
||||
```bash
|
||||
# Check current Doze state
|
||||
adb shell dumpsys deviceidle
|
||||
|
||||
# Force device out of Doze for testing
|
||||
adb shell dumpsys deviceidle unforce
|
||||
```
|
||||
|
||||
3. **Check exact alarm permission (Android 12+):**
|
||||
```bash
|
||||
adb shell appops get org.timesafari.dailynotification.test SCHEDULE_EXACT_ALARM
|
||||
```
|
||||
|
||||
### Build Failures
|
||||
|
||||
```bash
|
||||
# Clean build
|
||||
cd android
|
||||
./gradlew clean
|
||||
./gradlew :app:assembleDebug
|
||||
|
||||
# If still failing, clean Gradle cache
|
||||
rm -rf ~/.gradle/caches
|
||||
./gradlew :app:assembleDebug
|
||||
```
|
||||
|
||||
## Benefits of Physical Device Testing
|
||||
|
||||
### Advantages Over Emulator
|
||||
|
||||
- ✅ **Accurate notification timing** — Real hardware scheduler behavior
|
||||
- ✅ **Real battery optimization** — Test against actual OEM restrictions
|
||||
- ✅ **True Doze mode** — Emulators simulate but don't fully replicate
|
||||
- ✅ **Boot receiver testing** — Actual device reboot behavior
|
||||
- ✅ **Performance metrics** — Real CPU/memory usage
|
||||
- ✅ **User experience** — How notifications actually feel
|
||||
|
||||
### When to Use Physical Device
|
||||
|
||||
- **Final validation** — Before release
|
||||
- **Notification timing tests** — Alarm accuracy verification
|
||||
- **Battery impact testing** — Real power consumption
|
||||
- **Reboot persistence tests** — Boot receiver validation
|
||||
- **OEM-specific testing** — Samsung, Xiaomi, etc. quirks
|
||||
|
||||
### When Emulator is Sufficient
|
||||
|
||||
- **Basic functionality** — Core feature development
|
||||
- **UI testing** — Layout and interaction testing
|
||||
- **Quick iteration** — Fast build-test cycles
|
||||
- **CI/CD pipelines** — Automated testing
|
||||
|
||||
## Multiple Device Management
|
||||
|
||||
### List All Connected Devices
|
||||
|
||||
```bash
|
||||
adb devices -l
|
||||
|
||||
# Example output:
|
||||
# ABC123DEF456 device usb:1-1 product:walleye model:Pixel_2 device:walleye
|
||||
# XYZ789GHI012 device usb:1-2 product:star2lte model:SM_G965F device:star2lte
|
||||
```
|
||||
|
||||
### Target Specific Device
|
||||
|
||||
```bash
|
||||
# Install on specific device
|
||||
adb -s ABC123DEF456 install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# View logs from specific device
|
||||
adb -s ABC123DEF456 logcat -s "DailyNotification"
|
||||
|
||||
# Launch app on specific device
|
||||
adb -s ABC123DEF456 shell am start -n org.timesafari.dailynotification.test/.MainActivity
|
||||
```
|
||||
|
||||
## Wireless ADB (Optional)
|
||||
|
||||
For cable-free development after initial setup:
|
||||
|
||||
```bash
|
||||
# 1. Connect device via USB first
|
||||
# 2. Enable TCP/IP mode on port 5555
|
||||
adb tcpip 5555
|
||||
|
||||
# 3. Find device IP (Settings → About phone → Status → IP address)
|
||||
# Or:
|
||||
adb shell ip addr show wlan0 | grep inet
|
||||
|
||||
# 4. Disconnect USB and connect wirelessly
|
||||
adb connect 192.168.1.100:5555
|
||||
|
||||
# 5. Verify connection
|
||||
adb devices
|
||||
# Should show: 192.168.1.100:5555 device
|
||||
```
|
||||
|
||||
**Note:** Wireless ADB is slower than USB and may disconnect. Use USB for large APK transfers.
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Testing Workflow
|
||||
|
||||
1. **Build** → Make changes, rebuild APK
|
||||
2. **Install** → Push to device with `adb install -r`
|
||||
3. **Test** → Exercise notification features
|
||||
4. **Monitor** → Watch logs for issues
|
||||
5. **Iterate** → Fix and repeat
|
||||
|
||||
### Recommended Test Sequence
|
||||
|
||||
1. ✅ Immediate notification display
|
||||
2. ✅ Scheduled notification (1-2 min delay)
|
||||
3. ✅ App backgrounded notification
|
||||
4. ✅ Screen off notification
|
||||
5. ✅ Device reboot alarm persistence
|
||||
6. ✅ Force stop recovery (if implemented)
|
||||
7. ✅ Battery optimization scenarios
|
||||
|
||||
---
|
||||
|
||||
**Physical device testing is essential for production-quality notification behavior.** While emulators are great for development, only real hardware reveals the true behavior of Android's notification and alarm systems. 📱
|
||||
224
doc/testing/QUICK_REFERENCE.md
Normal file
224
doc/testing/QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# DailyNotification Testing Quick Reference
|
||||
|
||||
> **Note:** For P0 production-grade features testing, see [QUICK_REFERENCE_V2.md](./QUICK_REFERENCE_V2.md)
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Manual Testing
|
||||
```bash
|
||||
# 1. Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# 2. Schedule notification (in app UI)
|
||||
# Tap "Test Notification" button
|
||||
|
||||
# 3. Close app normally
|
||||
adb shell input keyevent KEYCODE_HOME
|
||||
|
||||
# 4. Wait for notification (1 minute)
|
||||
# Check notification panel
|
||||
```
|
||||
|
||||
### Automated Testing
|
||||
```bash
|
||||
# Run bash test script
|
||||
./scripts/daily-notification-test.sh
|
||||
|
||||
# Run Python test script
|
||||
python3 scripts/daily-notification-test.py
|
||||
|
||||
# Run with verbose output
|
||||
python3 scripts/daily-notification-test.py -v
|
||||
```
|
||||
|
||||
## 🔧 Essential ADB Commands
|
||||
|
||||
### App Management
|
||||
```bash
|
||||
# Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# Normal close (background)
|
||||
adb shell input keyevent KEYCODE_HOME
|
||||
|
||||
# Force stop (kills app)
|
||||
adb shell am force-stop org.timesafari.dailynotification
|
||||
|
||||
# Check if running
|
||||
adb shell "ps | grep timesafari"
|
||||
```
|
||||
|
||||
### Notification Testing
|
||||
```bash
|
||||
# Check notification settings
|
||||
adb shell "dumpsys notification | grep -A5 -B5 timesafari"
|
||||
|
||||
# Open notification settings
|
||||
adb shell "am start -a android.settings.APP_NOTIFICATION_SETTINGS -e android.provider.extra.APP_PACKAGE org.timesafari.dailynotification"
|
||||
|
||||
# List notifications
|
||||
adb shell "cmd notification list"
|
||||
```
|
||||
|
||||
### Alarm Management
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
### Logging
|
||||
```bash
|
||||
# Monitor logs
|
||||
adb logcat | grep -i "dailynotification\|notification"
|
||||
|
||||
# Get recent logs
|
||||
adb logcat -d | grep -i "dailynotification"
|
||||
|
||||
# Clear logs
|
||||
adb logcat -c
|
||||
```
|
||||
|
||||
## 🧪 Test Scenarios
|
||||
|
||||
### Scenario 1: Basic Functionality
|
||||
1. Launch app → Test plugin → Schedule notification → Verify delivery
|
||||
2. **Expected**: All functions work correctly
|
||||
|
||||
### Scenario 2: Background Operation
|
||||
1. Schedule notification → Send to background → Wait for delivery
|
||||
2. **Expected**: Notification appears despite app being backgrounded
|
||||
|
||||
### Scenario 3: Force Stop (Expected Failure)
|
||||
1. Schedule notification → Force stop → Wait for delivery
|
||||
2. **Expected**: No notification appears (this is correct behavior)
|
||||
|
||||
### Scenario 4: Permission Management
|
||||
1. Check permissions → Request permissions → Verify status
|
||||
2. **Expected**: Permissions granted and status updated
|
||||
|
||||
## 🚨 Common Issues
|
||||
|
||||
### Plugin Not Loading
|
||||
- Check `capacitor.plugins.json` exists and is valid
|
||||
- Rebuild: `./gradlew assembleDebug`
|
||||
- Reinstall: `adb install -r app/build/outputs/apk/debug/app-debug.apk`
|
||||
|
||||
### Notifications Not Appearing
|
||||
- Check notification permissions in app
|
||||
- Verify notification importance: `adb shell "dumpsys notification | grep timesafari"`
|
||||
- Check if notifications enabled in system settings
|
||||
|
||||
### Alarms Not Firing
|
||||
- Check exact alarm permissions
|
||||
- Verify alarm scheduled: `adb shell "dumpsys alarm | grep timesafari"`
|
||||
- Check battery optimization settings
|
||||
|
||||
## 📱 Testing Checklist
|
||||
|
||||
### Pre-Test Setup
|
||||
- [ ] Android device/emulator connected via ADB
|
||||
- [ ] App installed and launched
|
||||
- [ ] Notification permissions granted
|
||||
- [ ] Battery optimization disabled (if needed)
|
||||
|
||||
### Test Execution
|
||||
- [ ] Plugin loads successfully
|
||||
- [ ] Notification scheduling works
|
||||
- [ ] Background operation works
|
||||
- [ ] Force stop behavior is correct
|
||||
- [ ] Permission management works
|
||||
|
||||
### Post-Test Verification
|
||||
- [ ] All expected notifications appeared
|
||||
- [ ] No unexpected errors in logs
|
||||
- [ ] App behavior is consistent
|
||||
- [ ] Performance is acceptable
|
||||
|
||||
## 🎯 Key Differences
|
||||
|
||||
| Action | ADB Command | App Status | Alarms Survive? |
|
||||
|--------|-------------|------------|-----------------|
|
||||
| **Normal Close** | `KEYCODE_HOME` | Background | ✅ Yes |
|
||||
| **Force Stop** | `am force-stop` | Killed | ❌ No |
|
||||
| **Back Button** | `KEYCODE_BACK` | Background | ✅ Yes |
|
||||
|
||||
## 📊 Success Criteria
|
||||
|
||||
### ✅ Test Passes When:
|
||||
- Plugin loads and is ready for use
|
||||
- Notifications appear at scheduled time
|
||||
- Background operation works correctly
|
||||
- Force stop behaves as expected
|
||||
- Permissions are managed properly
|
||||
|
||||
### ❌ Test Fails When:
|
||||
- Plugin doesn't load
|
||||
- Notifications don't appear
|
||||
- App crashes or behaves unexpectedly
|
||||
- Permissions aren't handled correctly
|
||||
- Performance is unacceptable
|
||||
|
||||
## 🔍 Debugging Tips
|
||||
|
||||
### Check App Status
|
||||
```bash
|
||||
# Is app running?
|
||||
adb shell "ps | grep timesafari"
|
||||
|
||||
# What's the app doing?
|
||||
adb shell "dumpsys activity activities | grep timesafari"
|
||||
```
|
||||
|
||||
### Check Notifications
|
||||
```bash
|
||||
# Are notifications enabled?
|
||||
adb shell "dumpsys notification | grep timesafari"
|
||||
|
||||
# What notifications are active?
|
||||
adb shell "cmd notification list"
|
||||
```
|
||||
|
||||
### Check Alarms
|
||||
```bash
|
||||
# Are alarms scheduled?
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# What alarms are pending?
|
||||
adb shell "dumpsys alarm | grep -A5 -B5 timesafari"
|
||||
```
|
||||
|
||||
### Check Logs
|
||||
```bash
|
||||
# Recent plugin activity
|
||||
adb logcat -d | grep -i "dailynotification"
|
||||
|
||||
# Real-time monitoring
|
||||
adb logcat | grep -i "dailynotification\|notification"
|
||||
```
|
||||
|
||||
## 🚀 Production Testing
|
||||
|
||||
### Real Device Testing
|
||||
- Test on multiple Android versions
|
||||
- Test on different OEMs (Samsung, Huawei, etc.)
|
||||
- Test with different battery optimization settings
|
||||
- Test under various network conditions
|
||||
|
||||
### Performance Testing
|
||||
- Test with multiple scheduled notifications
|
||||
- Test rapid scheduling/canceling
|
||||
- Test under memory pressure
|
||||
- Test battery impact
|
||||
|
||||
### Edge Case Testing
|
||||
- Test after device reboot
|
||||
- Test with low battery
|
||||
- Test with airplane mode
|
||||
- Test with timezone changes
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Force-stop is not a real-world scenario. Focus testing on normal app closure and background operation! 🎯
|
||||
282
doc/testing/QUICK_REFERENCE_V2.md
Normal file
282
doc/testing/QUICK_REFERENCE_V2.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# Testing Quick Reference - P0 Production-Grade Features
|
||||
|
||||
> **Note:** For general testing commands, see [QUICK_REFERENCE.md](./QUICK_REFERENCE.md)
|
||||
|
||||
## Current Version Features
|
||||
|
||||
✅ **P0 Priority 1**: Channel Management (ChannelManager)
|
||||
✅ **P0 Priority 2**: PendingIntent & Exact Alarms (PendingIntentManager)
|
||||
✅ **P0 Priority 3**: JIT Freshness Re-check (Soft TTL)
|
||||
✅ **P0 Priority 4**: Recovery Coexistence (RecoveryManager)
|
||||
|
||||
## Quick Test Commands
|
||||
|
||||
### 1. Basic Functionality Test
|
||||
|
||||
```bash
|
||||
# Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# Check channel status
|
||||
adb shell "dumpsys notification | grep -A5 daily_default"
|
||||
|
||||
# Check alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permission
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
### 2. Channel Management Test
|
||||
|
||||
```bash
|
||||
# Check channel exists and is enabled
|
||||
adb shell "dumpsys notification | grep daily_default"
|
||||
|
||||
# Check channel importance (should be 3 = IMPORTANCE_HIGH)
|
||||
adb shell "dumpsys notification | grep -A5 daily_default | grep importance"
|
||||
|
||||
# Open channel settings
|
||||
adb shell "am start -a android.settings.CHANNEL_NOTIFICATION_SETTINGS -e android.provider.extra.APP_PACKAGE org.timesafari.dailynotification -e android.provider.extra.CHANNEL_ID daily_default"
|
||||
```
|
||||
|
||||
### 3. PendingIntent & Exact Alarms Test
|
||||
|
||||
```bash
|
||||
# Check PendingIntent flags in alarms
|
||||
adb shell "dumpsys alarm | grep -A10 -B5 timesafari"
|
||||
|
||||
# Check exact alarm permission
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
|
||||
# Open exact alarm settings
|
||||
adb shell "am start -a android.settings.REQUEST_SCHEDULE_EXACT_ALARM -d package:org.timesafari.dailynotification"
|
||||
```
|
||||
|
||||
### 4. JIT Freshness Re-check Test
|
||||
|
||||
```bash
|
||||
# Clear logs
|
||||
adb logcat -c
|
||||
|
||||
# Schedule notification in app UI, then monitor logs
|
||||
adb logcat | grep -i "jit\|freshness\|stale"
|
||||
|
||||
# Look for:
|
||||
# - "Content is fresh (age: X minutes), skipping JIT refresh"
|
||||
# - "Content is stale (age: X minutes), attempting JIT refresh"
|
||||
# - "JIT refresh succeeded" or "JIT refresh failed, using original content"
|
||||
```
|
||||
|
||||
### 5. Recovery Coexistence Test
|
||||
|
||||
```bash
|
||||
# Clear logs
|
||||
adb logcat -c
|
||||
|
||||
# Launch app multiple times to test cooldown
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
sleep 2
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
sleep 2
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "recovery.*requested.*app_startup"
|
||||
adb logcat -d | grep -i "recovery.*performed.*recently.*skipping"
|
||||
```
|
||||
|
||||
### 6. Reboot Recovery Test
|
||||
|
||||
```bash
|
||||
# Schedule notification in app UI first
|
||||
# Then reboot
|
||||
adb reboot
|
||||
|
||||
# Wait for boot
|
||||
adb wait-for-device
|
||||
sleep 30
|
||||
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "recovery.*requested.*boot_completed"
|
||||
|
||||
# Check alarms restored
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
|
||||
## Automated Test Scripts
|
||||
|
||||
### Comprehensive Test Suite
|
||||
```bash
|
||||
# Run comprehensive test suite
|
||||
./scripts/comprehensive-test-v2.sh
|
||||
```
|
||||
|
||||
### Reboot Test Suite
|
||||
```bash
|
||||
# Run reboot test suite
|
||||
./scripts/reboot-test-v2.sh
|
||||
```
|
||||
|
||||
### Python Test Suite
|
||||
```bash
|
||||
# Run Python test suite
|
||||
python3 scripts/daily-notification-test.py
|
||||
```
|
||||
|
||||
## Expected Log Patterns
|
||||
|
||||
### Channel Management
|
||||
```
|
||||
ChannelManager: Creating new notification channel: daily_default
|
||||
ChannelManager: Notification channel created with IMPORTANCE_HIGH
|
||||
```
|
||||
|
||||
### PendingIntent & Exact Alarms
|
||||
```
|
||||
PendingIntentManager: Scheduled exact alarm with setExactAndAllowWhileIdle
|
||||
PendingIntentManager: AlarmStatus{exactAlarmsSupported=true, exactAlarmsGranted=true}
|
||||
```
|
||||
|
||||
### JIT Freshness Re-check
|
||||
```
|
||||
DailyNotificationReceiver: Content is fresh (age: 5 minutes), skipping JIT refresh
|
||||
DailyNotificationReceiver: Content is stale (age: 7 hours), attempting JIT refresh
|
||||
DailyNotificationReceiver: JIT refresh succeeded, using fresh content
|
||||
```
|
||||
|
||||
### Recovery Coexistence
|
||||
```
|
||||
RecoveryManager: Recovery requested from: APP_STARTUP
|
||||
RecoveryManager: Recovery performed recently (2s ago), skipping
|
||||
RecoveryManager: Recovery requested from: BOOT_COMPLETED
|
||||
RecoveryManager: Recovery completed from BOOT_COMPLETED: 3/5 recovered, 2 skipped
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### ✅ All Tests Pass When:
|
||||
|
||||
1. **Channel Management**:
|
||||
- Channel exists with ID `daily_default`
|
||||
- Channel importance is `IMPORTANCE_HIGH` (3)
|
||||
- Channel blocking detection works
|
||||
- Settings deep linking works
|
||||
|
||||
2. **PendingIntent & Exact Alarms**:
|
||||
- PendingIntent uses `FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE`
|
||||
- Exact alarm permission detection works
|
||||
- Settings deep linking works
|
||||
- Alarms schedule correctly
|
||||
|
||||
3. **JIT Freshness Re-check**:
|
||||
- Fresh content (< 6 hours) skips refresh
|
||||
- Stale content (> 6 hours) attempts refresh
|
||||
- Graceful fallback on refresh failure
|
||||
- Original content preserved on failure
|
||||
|
||||
4. **Recovery Coexistence**:
|
||||
- App startup recovery works
|
||||
- Boot recovery works
|
||||
- 5-minute cooldown prevents duplicate recovery
|
||||
- Recovery statistics tracked correctly
|
||||
- State persisted in SharedPreferences
|
||||
|
||||
### ❌ Tests Fail When:
|
||||
|
||||
- Channel not created or blocked
|
||||
- PendingIntent flags incorrect
|
||||
- JIT refresh not working
|
||||
- Recovery operations conflict
|
||||
- Boot recovery not triggered
|
||||
- Alarms not restored after reboot
|
||||
|
||||
## Troubleshooting Commands
|
||||
|
||||
### Debug Channel Issues
|
||||
```bash
|
||||
# Check channel status
|
||||
adb shell "dumpsys notification | grep -A10 daily_default"
|
||||
|
||||
# Check channel importance
|
||||
adb shell "dumpsys notification | grep -A5 daily_default | grep importance"
|
||||
```
|
||||
|
||||
### Debug Alarm Issues
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permission
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
|
||||
# Check alarm manager state
|
||||
adb shell "dumpsys alarm | head -20"
|
||||
```
|
||||
|
||||
### Debug Recovery Issues
|
||||
```bash
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "recovery.*requested"
|
||||
|
||||
# Check recovery state
|
||||
adb shell "run-as org.timesafari.dailynotification ls -la /data/data/org.timesafari.dailynotification/shared_prefs/"
|
||||
|
||||
# Check boot receiver registration
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A5 -B5 receiver"
|
||||
```
|
||||
|
||||
### Debug JIT Freshness Issues
|
||||
```bash
|
||||
# Check JIT refresh logs
|
||||
adb logcat -d | grep -i "jit\|freshness\|stale"
|
||||
|
||||
# Check content age calculation
|
||||
adb logcat -d | grep -i "age.*minutes"
|
||||
```
|
||||
|
||||
## Manual Testing Checklist
|
||||
|
||||
### Pre-Test Setup
|
||||
- [ ] Device/emulator connected via ADB
|
||||
- [ ] App installed and permissions granted
|
||||
- [ ] Notification permissions enabled
|
||||
- [ ] Exact alarm permissions granted (Android 12+)
|
||||
|
||||
### Basic Tests
|
||||
- [ ] App launches successfully
|
||||
- [ ] Plugin loads without errors
|
||||
- [ ] Channel created with correct settings
|
||||
- [ ] Notifications schedule correctly
|
||||
- [ ] Notifications appear at scheduled time
|
||||
|
||||
### P0 Feature Tests
|
||||
- [ ] Channel management works
|
||||
- [ ] PendingIntent flags correct
|
||||
- [ ] Exact alarm permission detection
|
||||
- [ ] JIT freshness re-check works
|
||||
- [ ] Recovery coexistence works
|
||||
- [ ] Boot recovery works
|
||||
|
||||
### Edge Case Tests
|
||||
- [ ] App background notification
|
||||
- [ ] App closed notification
|
||||
- [ ] Force stop (expected failure)
|
||||
- [ ] Reboot recovery
|
||||
- [ ] Multiple notifications
|
||||
- [ ] Network connectivity issues
|
||||
|
||||
### Performance Tests
|
||||
- [ ] Memory usage stable
|
||||
- [ ] CPU usage reasonable
|
||||
- [ ] No memory leaks
|
||||
- [ ] Efficient recovery operations
|
||||
|
||||
## Quick Status Check
|
||||
|
||||
```bash
|
||||
# One-liner to check all systems
|
||||
echo "=== Channel Status ===" && adb shell "dumpsys notification | grep -A3 daily_default" && echo "=== Alarm Status ===" && adb shell "dumpsys alarm | grep timesafari" && echo "=== Recovery Status ===" && adb logcat -d | grep -i "recovery.*requested" | tail -3
|
||||
```
|
||||
|
||||
This quick reference covers all the essential testing procedures for the current P0 production-grade version of the DailyNotification plugin.
|
||||
463
doc/testing/REBOOT_PROCEDURE.md
Normal file
463
doc/testing/REBOOT_PROCEDURE.md
Normal file
@@ -0,0 +1,463 @@
|
||||
# Reboot Testing Procedure for DailyNotification Plugin
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides a comprehensive procedure for testing notification recovery after device reboots. The DailyNotification plugin implements a robust reboot recovery system that restores scheduled notifications after system events.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Android device or emulator with ADB enabled
|
||||
- DailyNotification app installed
|
||||
- ADB tools installed on development machine
|
||||
- Notification permissions granted
|
||||
- Boot receiver permissions granted
|
||||
|
||||
## Reboot Recovery System
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Boot Receiver Registration**: The `BootReceiver` is registered in `AndroidManifest.xml` to listen for:
|
||||
- `BOOT_COMPLETED` - Device boot completion
|
||||
- `MY_PACKAGE_REPLACED` - App update
|
||||
- `PACKAGE_REPLACED` - Any package update
|
||||
|
||||
2. **Recovery Process**: When triggered, the boot receiver:
|
||||
- Initializes the `DailyNotificationRebootRecoveryManager`
|
||||
- Loads stored notification data from SharedPreferences
|
||||
- Reschedules all pending notifications
|
||||
- Validates notification integrity
|
||||
|
||||
3. **Data Persistence**: Notification data is stored in:
|
||||
- SharedPreferences for notification content
|
||||
- AlarmManager for scheduling
|
||||
- Internal storage for recovery metadata
|
||||
|
||||
## Manual Reboot Testing Procedure
|
||||
|
||||
### Test 1: Basic Reboot Recovery
|
||||
|
||||
**Objective**: Verify notifications are restored after device reboot
|
||||
|
||||
**Steps**:
|
||||
1. **Schedule Notification**:
|
||||
```bash
|
||||
# Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
```
|
||||
- Tap "Test Notification" button
|
||||
- Verify notification scheduled for 5 minutes from now
|
||||
- Note the scheduled time
|
||||
|
||||
2. **Verify Alarm is Scheduled**:
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
- Should show scheduled alarm with correct timestamp
|
||||
|
||||
3. **Reboot Device**:
|
||||
```bash
|
||||
# Reboot device
|
||||
adb reboot
|
||||
```
|
||||
- Wait for device to fully boot (2-3 minutes)
|
||||
- Wait for boot animation to complete
|
||||
|
||||
4. **Verify Recovery**:
|
||||
```bash
|
||||
# Check if app recovered notifications
|
||||
adb logcat -d | grep -i "bootreceiver\|recovery"
|
||||
```
|
||||
- Look for "Device boot completed - restoring notifications"
|
||||
- Look for "Notification recovery completed successfully"
|
||||
|
||||
5. **Verify Alarm Restored**:
|
||||
```bash
|
||||
# Check if alarm was restored
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
- Should show the same scheduled alarm
|
||||
|
||||
6. **Wait for Notification**:
|
||||
- Wait for the originally scheduled time
|
||||
- Check notification panel for the test notification
|
||||
|
||||
**Expected Result**: Notification appears at the originally scheduled time
|
||||
|
||||
### Test 2: App Update Recovery
|
||||
|
||||
**Objective**: Verify notifications are restored after app update
|
||||
|
||||
**Steps**:
|
||||
1. **Schedule Notification**:
|
||||
- Schedule a notification for 5 minutes from now
|
||||
- Verify it's scheduled correctly
|
||||
|
||||
2. **Update App**:
|
||||
```bash
|
||||
# Build and install updated app
|
||||
cd android && ./gradlew assembleDebug
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
3. **Verify Recovery**:
|
||||
```bash
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "package.*replaced\|recovery"
|
||||
```
|
||||
- Look for "Our app was updated - restoring notifications"
|
||||
|
||||
4. **Verify Alarm Restored**:
|
||||
```bash
|
||||
# Check if alarm was restored
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
|
||||
5. **Wait for Notification**:
|
||||
- Wait for the scheduled time
|
||||
- Verify notification appears
|
||||
|
||||
**Expected Result**: Notification appears after app update
|
||||
|
||||
### Test 3: Multiple Notifications Recovery
|
||||
|
||||
**Objective**: Verify multiple notifications are restored correctly
|
||||
|
||||
**Steps**:
|
||||
1. **Schedule Multiple Notifications**:
|
||||
- Schedule 3-4 notifications at different times
|
||||
- Note all scheduled times
|
||||
|
||||
2. **Reboot Device**:
|
||||
```bash
|
||||
adb reboot
|
||||
```
|
||||
|
||||
3. **Verify All Recovered**:
|
||||
```bash
|
||||
# Check all alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
- Should show all scheduled alarms
|
||||
|
||||
4. **Test Each Notification**:
|
||||
- Wait for each scheduled time
|
||||
- Verify each notification appears
|
||||
|
||||
**Expected Result**: All notifications appear at their scheduled times
|
||||
|
||||
## Automated Reboot Testing
|
||||
|
||||
### Bash Script for Reboot Testing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# reboot-test.sh
|
||||
|
||||
set -e
|
||||
|
||||
APP_PACKAGE="org.timesafari.dailynotification"
|
||||
APP_ACTIVITY=".MainActivity"
|
||||
|
||||
echo "🔄 Starting Reboot Recovery Test"
|
||||
echo "================================"
|
||||
|
||||
# Function to wait for device
|
||||
wait_for_device() {
|
||||
echo "⏳ Waiting for device to be ready..."
|
||||
adb wait-for-device
|
||||
sleep 10 # Additional wait for boot completion
|
||||
}
|
||||
|
||||
# Function to schedule notification
|
||||
schedule_notification() {
|
||||
echo "📅 Scheduling test notification..."
|
||||
adb shell am start -n $APP_PACKAGE/$APP_ACTIVITY
|
||||
sleep 3
|
||||
|
||||
echo "⚠️ Manual step: Schedule notification in app"
|
||||
echo " - Tap 'Test Notification' button"
|
||||
echo " - Wait for 'Notification scheduled' message"
|
||||
read -p "Press Enter when notification is scheduled..."
|
||||
}
|
||||
|
||||
# Function to check recovery logs
|
||||
check_recovery_logs() {
|
||||
echo "🔍 Checking recovery logs..."
|
||||
if adb logcat -d | grep -q "Notification recovery completed successfully"; then
|
||||
echo "✅ Recovery completed successfully"
|
||||
return 0
|
||||
else
|
||||
echo "❌ Recovery not found in logs"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check scheduled alarms
|
||||
check_scheduled_alarms() {
|
||||
echo "⏰ Checking scheduled alarms..."
|
||||
if adb shell "dumpsys alarm | grep timesafari" | grep -q "RTC_WAKEUP"; then
|
||||
echo "✅ Alarms scheduled correctly"
|
||||
return 0
|
||||
else
|
||||
echo "❌ No alarms found"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main test
|
||||
main() {
|
||||
# Step 1: Schedule notification
|
||||
schedule_notification
|
||||
|
||||
# Step 2: Verify initial scheduling
|
||||
if check_scheduled_alarms; then
|
||||
echo "✅ Initial scheduling successful"
|
||||
else
|
||||
echo "❌ Initial scheduling failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Reboot device
|
||||
echo "🔄 Rebooting device..."
|
||||
adb reboot
|
||||
wait_for_device
|
||||
|
||||
# Step 4: Check recovery
|
||||
if check_recovery_logs; then
|
||||
echo "✅ Recovery successful"
|
||||
else
|
||||
echo "❌ Recovery failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 5: Verify alarms restored
|
||||
if check_scheduled_alarms; then
|
||||
echo "✅ Alarms restored successfully"
|
||||
else
|
||||
echo "❌ Alarms not restored"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🎉 Reboot recovery test completed successfully!"
|
||||
echo "⏰ Wait for the scheduled notification to appear"
|
||||
}
|
||||
|
||||
main
|
||||
```
|
||||
|
||||
### Python Script for Reboot Testing
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Reboot Recovery Test Script
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
|
||||
class RebootTester:
|
||||
def __init__(self):
|
||||
self.package = "org.timesafari.dailynotification"
|
||||
self.activity = f"{self.package}/.MainActivity"
|
||||
|
||||
def run_command(self, command):
|
||||
"""Run ADB command"""
|
||||
return subprocess.run(f"adb {command}", shell=True, capture_output=True, text=True)
|
||||
|
||||
def wait_for_device(self):
|
||||
"""Wait for device to be ready"""
|
||||
print("⏳ Waiting for device...")
|
||||
self.run_command("wait-for-device")
|
||||
time.sleep(10) # Additional wait for boot completion
|
||||
|
||||
def schedule_notification(self):
|
||||
"""Schedule notification (manual step)"""
|
||||
print("📅 Scheduling notification...")
|
||||
self.run_command(f"shell am start -n {self.activity}")
|
||||
time.sleep(3)
|
||||
|
||||
print("⚠️ Manual step: Schedule notification in app")
|
||||
input("Press Enter when notification is scheduled...")
|
||||
|
||||
def check_recovery_logs(self):
|
||||
"""Check if recovery was successful"""
|
||||
result = self.run_command("logcat -d")
|
||||
return "Notification recovery completed successfully" in result.stdout
|
||||
|
||||
def check_scheduled_alarms(self):
|
||||
"""Check if alarms are scheduled"""
|
||||
result = self.run_command('shell "dumpsys alarm | grep timesafari"')
|
||||
return "RTC_WAKEUP" in result.stdout
|
||||
|
||||
def reboot_device(self):
|
||||
"""Reboot the device"""
|
||||
print("🔄 Rebooting device...")
|
||||
self.run_command("reboot")
|
||||
self.wait_for_device()
|
||||
|
||||
def run_test(self):
|
||||
"""Run complete reboot test"""
|
||||
print("🔄 Starting Reboot Recovery Test")
|
||||
print("=" * 40)
|
||||
|
||||
# Schedule notification
|
||||
self.schedule_notification()
|
||||
|
||||
# Verify initial scheduling
|
||||
if self.check_scheduled_alarms():
|
||||
print("✅ Initial scheduling successful")
|
||||
else:
|
||||
print("❌ Initial scheduling failed")
|
||||
return False
|
||||
|
||||
# Reboot device
|
||||
self.reboot_device()
|
||||
|
||||
# Check recovery
|
||||
if self.check_recovery_logs():
|
||||
print("✅ Recovery successful")
|
||||
else:
|
||||
print("❌ Recovery failed")
|
||||
return False
|
||||
|
||||
# Verify alarms restored
|
||||
if self.check_scheduled_alarms():
|
||||
print("✅ Alarms restored successfully")
|
||||
else:
|
||||
print("❌ Alarms not restored")
|
||||
return False
|
||||
|
||||
print("🎉 Reboot recovery test completed!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
tester = RebootTester()
|
||||
if tester.run_test():
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## ADB Commands for Reboot Testing
|
||||
|
||||
### Device Management
|
||||
```bash
|
||||
# Reboot device
|
||||
adb reboot
|
||||
|
||||
# Wait for device to be ready
|
||||
adb wait-for-device
|
||||
|
||||
# Check if device is ready
|
||||
adb shell getprop sys.boot_completed
|
||||
```
|
||||
|
||||
### Recovery Monitoring
|
||||
```bash
|
||||
# Monitor recovery logs in real-time
|
||||
adb logcat | grep -i "bootreceiver\|recovery"
|
||||
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "bootreceiver\|recovery"
|
||||
|
||||
# Check boot receiver registration
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A10 -B10 receiver"
|
||||
```
|
||||
|
||||
### Alarm Verification
|
||||
```bash
|
||||
# Check scheduled alarms before reboot
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check scheduled alarms after reboot
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
|
||||
# Check exact alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
## Troubleshooting Reboot Recovery
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Boot Receiver Not Triggered
|
||||
**Symptoms**: No recovery logs after reboot
|
||||
**Solutions**:
|
||||
- Check boot receiver registration in manifest
|
||||
- Verify `BOOT_COMPLETED` permission is granted
|
||||
- Check if device supports boot receiver (some OEMs disable it)
|
||||
|
||||
#### 2. Recovery Fails
|
||||
**Symptoms**: Recovery logs show errors
|
||||
**Solutions**:
|
||||
- Check SharedPreferences data integrity
|
||||
- Verify notification content is valid
|
||||
- Check alarm scheduling permissions
|
||||
|
||||
#### 3. Alarms Not Restored
|
||||
**Symptoms**: No alarms after recovery
|
||||
**Solutions**:
|
||||
- Check exact alarm permissions
|
||||
- Verify alarm scheduling logic
|
||||
- Check battery optimization settings
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check boot receiver status
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A5 -B5 receiver"
|
||||
|
||||
# Check recovery manager logs
|
||||
adb logcat -d | grep -i "rebootrecovery"
|
||||
|
||||
# Check notification storage
|
||||
adb shell "run-as org.timesafari.dailynotification ls -la /data/data/org.timesafari.dailynotification/shared_prefs/"
|
||||
|
||||
# Check alarm manager state
|
||||
adb shell "dumpsys alarm | head -20"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Testing Environment
|
||||
- Use physical device for most accurate results
|
||||
- Test on multiple Android versions
|
||||
- Test on different OEMs (Samsung, Huawei, etc.)
|
||||
- Test with different battery optimization settings
|
||||
|
||||
### Test Data
|
||||
- Use realistic notification content
|
||||
- Test with multiple notifications
|
||||
- Test with different time intervals
|
||||
- Test with various notification priorities
|
||||
|
||||
### Documentation
|
||||
- Record test results with timestamps
|
||||
- Screenshot notification appearances
|
||||
- Log any errors or unexpected behavior
|
||||
- Document device-specific issues
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### ✅ Test Passes When:
|
||||
- Boot receiver is triggered after reboot
|
||||
- Recovery logs show successful restoration
|
||||
- Alarms are rescheduled correctly
|
||||
- Notifications appear at scheduled times
|
||||
- Multiple notifications are handled correctly
|
||||
|
||||
### ❌ Test Fails When:
|
||||
- Boot receiver is not triggered
|
||||
- Recovery fails with errors
|
||||
- Alarms are not restored
|
||||
- Notifications don't appear
|
||||
- App crashes during recovery
|
||||
|
||||
## Conclusion
|
||||
|
||||
The reboot recovery system ensures that scheduled notifications survive device reboots and app updates. Regular testing of this functionality is crucial for maintaining reliable notification delivery in production environments.
|
||||
|
||||
For questions or issues, refer to the troubleshooting section or check the plugin logs using the provided ADB commands.
|
||||
216
doc/testing/REBOOT_STEPS.md
Normal file
216
doc/testing/REBOOT_STEPS.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Reboot Testing Steps for DailyNotification Plugin
|
||||
|
||||
## 🎯 **Objective**
|
||||
Test that scheduled notifications survive device reboots and are properly restored by the BootReceiver.
|
||||
|
||||
## ⏰ **Extended Testing Time**
|
||||
- **Notification Delay**: 5 minutes (instead of 1 minute)
|
||||
- **More Realistic**: Allows time for proper testing and verification
|
||||
- **Better for Reboot Testing**: Gives time to reboot and verify recovery
|
||||
|
||||
## 🔄 **Complete Reboot Testing Procedure**
|
||||
|
||||
### **Step 1: Schedule Notification**
|
||||
```bash
|
||||
# Launch app
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity
|
||||
```
|
||||
- Tap **"Test Notification"** button
|
||||
- Verify message: **"Notification scheduled for [time]! Check your notification bar in 5 minutes."**
|
||||
- **Note the scheduled time** (5 minutes from now)
|
||||
|
||||
### **Step 2: Verify Initial Scheduling**
|
||||
```bash
|
||||
# Check scheduled alarms
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
- Should show scheduled alarm with correct timestamp
|
||||
- Note the alarm details
|
||||
|
||||
### **Step 3: Reboot Device**
|
||||
```bash
|
||||
# Reboot device
|
||||
adb reboot
|
||||
```
|
||||
- **Wait 2-3 minutes** for device to fully boot
|
||||
- Wait for boot animation to complete
|
||||
- Wait for home screen to appear
|
||||
|
||||
### **Step 4: Verify Boot Recovery**
|
||||
```bash
|
||||
# Check recovery logs
|
||||
adb logcat -d | grep -i "bootreceiver\|recovery"
|
||||
```
|
||||
**Look for these log messages:**
|
||||
- `"Device boot completed - restoring notifications"`
|
||||
- `"Found X notifications to recover"`
|
||||
- `"Notification recovery completed: X/X recovered"`
|
||||
|
||||
### **Step 5: Verify Alarm Restoration**
|
||||
```bash
|
||||
# Check if alarm was restored
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
- Should show the same scheduled alarm as before reboot
|
||||
- Alarm timestamp should match original schedule
|
||||
|
||||
### **Step 6: Wait for Notification**
|
||||
- **Wait for the originally scheduled time** (5 minutes from when you scheduled it)
|
||||
- **Check notification panel** for the test notification
|
||||
- **Verify notification appears** with correct content
|
||||
|
||||
## 🧪 **Automated Reboot Test**
|
||||
|
||||
### **Run the Reboot Test Script:**
|
||||
```bash
|
||||
# Run automated reboot test
|
||||
./scripts/reboot-test.sh
|
||||
```
|
||||
|
||||
**What the script does:**
|
||||
1. ✅ Checks boot receiver registration
|
||||
2. 📅 Prompts you to schedule notification
|
||||
3. 🔍 Verifies initial scheduling
|
||||
4. 🔄 Reboots device automatically
|
||||
5. ⏳ Waits for boot completion
|
||||
6. 🔍 Checks recovery logs
|
||||
7. ⏰ Verifies alarm restoration
|
||||
8. 🎉 Reports success/failure
|
||||
|
||||
## 🔍 **Key Log Messages to Look For**
|
||||
|
||||
### **Successful Recovery:**
|
||||
```
|
||||
BootReceiver: Device boot completed - restoring notifications
|
||||
BootReceiver: Found X notifications to recover
|
||||
BootReceiver: Recovered notification: [notification-id]
|
||||
BootReceiver: Notification recovery completed: X/X recovered
|
||||
```
|
||||
|
||||
### **Recovery Issues:**
|
||||
```
|
||||
BootReceiver: No notifications to recover
|
||||
BootReceiver: Failed to recover notification: [notification-id]
|
||||
BootReceiver: Error during boot recovery
|
||||
```
|
||||
|
||||
## 🚨 **Troubleshooting Reboot Recovery**
|
||||
|
||||
### **Issue 1: Boot Receiver Not Triggered**
|
||||
**Symptoms**: No recovery logs after reboot
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check boot receiver registration
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep -A10 -B10 receiver"
|
||||
|
||||
# Check BOOT_COMPLETED permission
|
||||
adb shell "dumpsys package org.timesafari.dailynotification | grep permission"
|
||||
```
|
||||
|
||||
### **Issue 2: Recovery Fails**
|
||||
**Symptoms**: Recovery logs show errors
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check notification storage
|
||||
adb shell "run-as org.timesafari.dailynotification ls -la /data/data/org.timesafari.dailynotification/shared_prefs/"
|
||||
|
||||
# Check alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
```
|
||||
|
||||
### **Issue 3: Alarms Not Restored**
|
||||
**Symptoms**: No alarms after recovery
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Check exact alarm permissions
|
||||
adb shell "dumpsys alarm | grep SCHEDULE_EXACT_ALARM"
|
||||
|
||||
# Check battery optimization
|
||||
adb shell "dumpsys deviceidle | grep timesafari"
|
||||
```
|
||||
|
||||
## 📊 **Success Criteria**
|
||||
|
||||
### ✅ **Test Passes When:**
|
||||
- Boot receiver is triggered after reboot
|
||||
- Recovery logs show successful restoration
|
||||
- Alarms are rescheduled correctly
|
||||
- Notification appears at the originally scheduled time
|
||||
- All recovery log messages are present
|
||||
|
||||
### ❌ **Test Fails When:**
|
||||
- Boot receiver is not triggered
|
||||
- Recovery fails with errors
|
||||
- Alarms are not restored
|
||||
- Notification doesn't appear
|
||||
- Recovery logs show failures
|
||||
|
||||
## 🎯 **Quick Test Commands**
|
||||
|
||||
### **One-Line Reboot Test:**
|
||||
```bash
|
||||
# Schedule notification, reboot, and verify
|
||||
adb shell am start -n org.timesafari.dailynotification/.MainActivity && echo "Schedule notification, then:" && read -p "Press Enter to reboot..." && adb reboot && sleep 120 && adb logcat -d | grep -i "bootreceiver\|recovery"
|
||||
```
|
||||
|
||||
### **Check Recovery Status:**
|
||||
```bash
|
||||
# Quick recovery check
|
||||
adb logcat -d | grep -i "bootreceiver" | tail -10
|
||||
```
|
||||
|
||||
### **Verify Alarms:**
|
||||
```bash
|
||||
# Quick alarm check
|
||||
adb shell "dumpsys alarm | grep timesafari"
|
||||
```
|
||||
|
||||
## 🔧 **Advanced Testing**
|
||||
|
||||
### **Test Multiple Reboots:**
|
||||
```bash
|
||||
# Test multiple reboot cycles
|
||||
for i in {1..3}; do
|
||||
echo "Reboot test $i/3"
|
||||
./scripts/reboot-test.sh
|
||||
sleep 60
|
||||
done
|
||||
```
|
||||
|
||||
### **Test with Different Notification Types:**
|
||||
- Test with sound enabled/disabled
|
||||
- Test with different priorities
|
||||
- Test with different content
|
||||
- Test with multiple notifications
|
||||
|
||||
### **Test Edge Cases:**
|
||||
- Test with low battery
|
||||
- Test with airplane mode
|
||||
- Test with timezone changes
|
||||
- Test with system updates
|
||||
|
||||
## 📱 **Production Considerations**
|
||||
|
||||
### **Real-World Scenarios:**
|
||||
- Users rarely force-stop apps
|
||||
- Device reboots are common (updates, crashes, etc.)
|
||||
- App updates should preserve notifications
|
||||
- Battery optimization can affect recovery
|
||||
|
||||
### **Best Practices:**
|
||||
- Test on multiple Android versions
|
||||
- Test on different OEMs
|
||||
- Test with various battery optimization settings
|
||||
- Test under different network conditions
|
||||
|
||||
## 🎉 **Expected Results**
|
||||
|
||||
After implementing the reboot recovery system:
|
||||
|
||||
1. **Notifications survive reboots** ✅
|
||||
2. **Boot receiver activates automatically** ✅
|
||||
3. **Recovery logs show success** ✅
|
||||
4. **Alarms are properly restored** ✅
|
||||
5. **Notifications appear at scheduled times** ✅
|
||||
|
||||
**The system is now robust and production-ready!** 🚀
|
||||
Reference in New Issue
Block a user