Adds documentation and test harness for Phase 2 (Force Stop Detection & Recovery). Changes: - Add PHASE2-EMULATOR-TESTING.md with detailed test procedures - Add PHASE2-VERIFICATION.md with test matrix and verification template - Add test-phase2.sh automated test harness Test harness features: - 3 test cases: force stop with cleared alarms, intact alarms, empty DB - Automatic force stop simulation via adb - Log parsing for scenario detection and recovery results - UI prompts for plugin configuration and scheduling Related: - Directive: android-implementation-directive-phase2.md - Requirements: docs/alarms/03-plugin-requirements.md §3.1.4 - Testing: docs/alarms/PHASE2-EMULATOR-TESTING.md - Verification: docs/alarms/PHASE2-VERIFICATION.md
9.9 KiB
PHASE 2 – EMULATOR TESTING
Force Stop Detection & Recovery
1. Purpose
Phase 2 verifies that the Daily Notification Plugin correctly:
- Detects force stop scenarios (where alarms may be cleared by the OS).
- Reschedules future notifications when alarms are missing but schedules remain in the database.
- Avoids heavy recovery when alarms are still intact.
- Does not misfire force-stop recovery on first launch / empty database.
This document defines the emulator test procedure for Phase 2 using the script:
test-apps/android-test-app/test-phase2.sh
2. Prerequisites
- Android emulator or device, e.g.:
- Pixel 8 / API 34 (recommended baseline)
- ADB available in
PATH(orADB_BINexported) - Project built with:
- Daily Notification Plugin integrated
- Test app at:
test-apps/android-test-app
- Debug APK path:
app/build/outputs/apk/debug/app-debug.apk
- Phase 1 behavior already implemented and verified:
- Cold start detection
- Missed notification marking
Note: Some OS/device combinations do not clear alarms on
am force-stop. In such cases, TEST 1 may partially skip or only verify scenario logging.
3. How to Run
From the android-test-app directory:
cd test-apps/android-test-app
chmod +x test-phase2.sh # first time only
./test-phase2.sh
The script will:
- Perform pre-flight checks (ADB/emulator).
- Build and install the debug APK.
- Guide you through three tests:
- TEST 1: Force stop – alarms cleared
- TEST 2: Force stop / process stop – alarms intact
- TEST 3: First launch / empty DB safeguard
- Print parsed recovery summaries from
DNP-REACTIVATIONlogs.
You will be prompted for UI actions at each step (e.g., configuring the plugin, pressing "Test Notification").
4. Test Cases
4.1 TEST 1 – Force Stop with Cleared Alarms
Goal:
Verify that when a force stop clears alarms, the plugin:
- Detects the FORCE_STOP scenario.
- Reschedules future notifications.
- Completes recovery with
errors=0.
Steps (Script Flow):
-
Launch & configure plugin
- Script launches the app.
- In the app UI, confirm:
⚙️ Plugin Settings: ✅ Configured🔌 Native Fetcher: ✅ Configured
- If not, press Configure Plugin and wait until both are ✅.
-
Schedule a future notification
- Click Test Notification (or equivalent) to schedule a notification a few minutes in the future.
-
Verify alarms are scheduled
- Script runs:
adb shell dumpsys alarm | grep com.timesafari.dailynotification - Confirm at least one
RTC_WAKEUPalarm forcom.timesafari.dailynotification.
- Script runs:
-
Force stop the app
- Script executes:
adb shell am force-stop com.timesafari.dailynotification
- Script executes:
-
Confirm alarms after force stop
- Script re-runs
dumpsys alarm. - Ideal test case: 0 alarms for
com.timesafari.dailynotification(alarms cleared).
- Script re-runs
-
Trigger recovery
- Script clears logcat and launches the app.
- Wait ~5 seconds for recovery.
-
Collect and inspect logs
- Script collects
DNP-REACTIVATIONlogs and parses:scenario=<value>missed=<n>rescheduled=<n>verified=<n>errors=<n>
- Script collects
Expected Logs (Ideal Case):
-
Scenario:
DNP-REACTIVATION: Detected scenario: FORCE_STOP -
Alarm handling:
DNP-REACTIVATION: Rescheduled alarm: daily_<id> for <time> DNP-REACTIVATION: Rescheduled missing alarm: daily_<id> at <time> -
Summary:
DNP-REACTIVATION: Force stop recovery completed: missed=1, rescheduled=1, verified=0, errors=0
Pass Criteria:
scenario=FORCE_STOPrescheduled > 0errors = 0- Script prints:
✅ TEST 1 PASSED: Force stop detected and alarms rescheduled (scenario=FORCE_STOP, rescheduled=1).
Partial / Edge Cases:
- If alarms remain after
force-stopon this device:- Script warns that FORCE_STOP may not fully trigger.
- Mark this as environment-limited rather than a plugin failure.
4.2 TEST 2 – Force Stop / Process Stop with Intact Alarms
Goal:
Ensure we do not run heavy force-stop recovery when alarms are still intact.
Steps (Script Flow):
-
Launch & configure plugin
- Same as TEST 1 (ensure both plugin statuses are ✅).
-
Schedule another future notification
- Click Test Notification again to create a second schedule.
-
Verify alarms are scheduled
- Confirm multiple alarms for
com.timesafari.dailynotificationviadumpsys alarm.
- Confirm multiple alarms for
-
Simulate a "soft stop"
- Script runs:
adb shell am kill com.timesafari.dailynotification - Intent: stop the process but not clear alarms (actual behavior may vary by OS).
- Script runs:
-
Check alarms after soft stop
- Ensure alarms are still present in
dumpsys alarm.
- Ensure alarms are still present in
-
Trigger recovery
- Script clears logcat and relaunches the app.
- Wait ~5 seconds.
-
Collect logs
- Script parses
DNP-REACTIVATIONlogs for:scenarioreschedulederrors
- Script parses
Expected Behavior:
scenarioshould not beFORCE_STOP(e.g.,COLD_STARTor another non-force-stop scenario, depending on your implementation).rescheduled = 0.errors = 0.
Pass Criteria:
- Alarms still present after soft stop.
- Recovery summary indicates no force-stop-level rescheduling when alarms are intact:
scenario != FORCE_STOPrescheduled = 0- Script prints:
✅ TEST 2 PASSED: No heavy force-stop recovery when alarms intact (scenario=<value>, rescheduled=0).
If scenario=FORCE_STOP and rescheduled>0 here, treat this as a warning and review scenario detection logic.
4.3 TEST 3 – First Launch / Empty DB Safeguard
Goal:
Ensure force-stop recovery is not mis-triggered when the app is freshly installed with no schedules.
Steps (Script Flow):
-
Clear state
- Script uninstalls the app to clear DB/state:
adb uninstall com.timesafari.dailynotification
- Script uninstalls the app to clear DB/state:
-
Reinstall APK
- Script reinstalls
app-debug.apk.
- Script reinstalls
-
Launch app without scheduling anything
- Script launches the app.
- Do not schedule notifications or configure plugin beyond initial display.
-
Collect logs
- Script grabs
DNP-REACTIVATIONlogs and parses:scenariorescheduled
- Script grabs
Expected Behavior:
-
Either:
- No
DNP-REACTIVATIONlogs at all (no recovery run), or - A specific "no schedules" scenario, e.g.:
or
DNP-REACTIVATION: No schedules present — skipping recovery (first launch)DNP-REACTIVATION: Detected scenario: NONE
- No
-
In both cases:
rescheduled = 0.
Pass Criteria:
- If no logs:
- Pass: recovery correctly doesn't run at all on empty DB.
- If logs present:
scenario=NONE(or equivalent) andrescheduled=0.
Script will report success when:
scenario == NONE_SCENARIO_VALUEandrescheduled=0, or- No recovery logs are found.
5. Latest Known Good Run (Template)
Fill this in after your first successful emulator run.
---
## Latest Known Good Run (Emulator)
**Environment**
- Device: Pixel 8 API 34 (Android 14)
- App ID: `com.timesafari.dailynotification`
- Build: Debug APK (`app-debug.apk`) from commit `<GIT_HASH>`
- Script: `./test-phase2.sh`
- Date: 2025-11-XX
**Results**
- ✅ TEST 1: Force Stop – Alarms Cleared
- `scenario=FORCE_STOP`
- `missed=1, rescheduled=1, verified=0, errors=0`
- ✅ TEST 2: Force Stop / Process Stop – Alarms Intact
- `scenario=COLD_START` (or equivalent non-force-stop scenario)
- `rescheduled=0, errors=0`
- ✅ TEST 3: First Launch / No Schedules
- `scenario=NONE` (or no logs)
- `rescheduled=0`
**Conclusion:**
Phase 2 **Force Stop Detection & Recovery** is verified on the emulator using `test-phase2.sh`. This run is the canonical reference for future regression testing.
6. Troubleshooting
Alarms Not Cleared on Force Stop
Symptom: am force-stop doesn't clear alarms in AlarmManager
Cause: Some Android versions/emulators don't clear alarms on force stop
Solution:
- This is expected behavior on some systems
- TEST 1 will run as Phase 1 (cold start) recovery
- For full force stop validation, test on a device/OS that clears alarms
- Script will report this as an environment limitation, not a failure
Scenario Not Detected as FORCE_STOP
Symptom: Logs show COLD_START even when alarms were cleared
Possible Causes:
- Scenario detection logic not implemented (Phase 2 not complete)
- Alarm count check failing (
alarmsExist()returning true when it shouldn't) - Database query timing issue
Solution:
- Verify Phase 2 implementation is complete
- Check
ReactivationManager.detectScenario()implementation - Review logs for alarm existence checks
- Verify
alarmsExist()uses PendingIntent check (notnextAlarmClock)
Recovery Doesn't Reschedule Alarms
Symptom: rescheduled=0 even when alarms were cleared
Possible Causes:
- Schedules not in database
- Reschedule logic failing
- Alarm scheduling permissions missing
Solution:
- Verify schedules exist in database
- Check logs for reschedule errors
- Verify exact alarm permission is granted
- Review
performForceStopRecovery()implementation
7. Related Documentation
- Phase 2 Directive - Implementation details
- Phase 2 Verification - Verification report
- Phase 1 Testing Guide - Prerequisite testing
- Activation Guide - How to use directives
- Plugin Requirements - Requirements Phase 2 implements
Status: Ready for testing (Phase 2 implementation pending)
Last Updated: November 2025