Files
daily-notification-plugin/test-apps/android-test-app/docs/PHASE1_TEST1_GOLDEN.md
Matthew Raymer ca194952e4 test(android): add auto-reset for TEST 1 and create golden run documentation
Add automatic app state reset for TEST 1 to ensure clean starting state when
lingering alarms from TEST 0 are detected. Create PHASE1_TEST1_GOLDEN.md with
actual values from successful run.

TEST 1 Auto-Reset:
- Detect lingering plugin alarms before TEST 1 starts
- Automatically uninstall/reinstall app to clear alarms
- Verify clean state (0 alarms) before proceeding
- Gracefully skip TEST 1 if clean state cannot be achieved
- Take failure screenshots when reset fails
- Wrap all TEST 1 steps in conditional to skip on reset failure

Documentation:
- Create PHASE1_TEST1_GOLDEN.md with actual values from passing run
- Document auto-reset behavior in golden run steps
- Add cross-references between TEST 0 and TEST 1 golden docs
- Include actual timestamps, scheduleIds, and recovery metrics

This ensures TEST 1 always starts from a known clean state, making test
results reliable and reproducible. The golden doc serves as a baseline for
comparing future TEST 1 runs.
2025-12-04 10:22:35 +00:00

12 KiB
Raw Blame History

Phase 1 — TEST 1 Golden Run (Force-Stop Recovery - Database Restoration)

Related Docs:


1. Test Overview

Test Name: TEST 1 — Force-Stop Recovery: Database Restoration
Purpose: Verify that after an Android force-stop (which clears all scheduled alarms), the plugin:

  1. Persists the alarm schedule in its database.
  2. Detects that alarms are missing on app launch.
  3. Rebuilds the missing alarm(s) from the database.
  4. Restores the one-notification-per-day contract:
    • Before force-stop: 1 alarm
    • After force-stop: 0 alarms
    • After recovery: 1 alarm (same trigger time as before force-stop)

This golden run documents a known-good execution on 2025-12-04.


2. Environment & Build Info

  • Date/Time of Run: 2025-12-04 (around 09:5109:55 UTC)

  • Command Used:

    ./test-phase1.sh
    
  • Project: daily-notification-plugintest-apps/android-test-app

  • Gradle:

    • Build invoked via ./gradlew assembleDebug (through the script)

    • Gradle output included:

      Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
      
  • APK Built:

    ./app/build/outputs/apk/debug/app-debug.apk
    
  • App ID: com.timesafari.dailynotification

Note: Device/emulator model & API level can be added here later if desired.


3. Step-by-Step Execution (Golden Run)

This section captures the actual sequence for the golden run.

  1. Ran ./test-phase1.sh.
  2. Pre-flight checks:
    • ADB device connected
    • Emulator ready
  3. Build phase:
    • Debug APK built successfully.
    • APK path: ./app/build/outputs/apk/debug/app-debug.apk
  4. Install phase:
    • Previous app uninstall succeeded
    • APK installed successfully
    • App verified in package list
    • Logcat cleared (twice)
  5. TEST 0 executed:
    • Configured plugin in UI (if needed).
    • Scheduled daily notification.
    • Verified:
      • Before scheduling: 0 plugin alarms.
      • After scheduling: 1 plugin alarm for today.
      • After fire/rollover: 1 plugin alarm for tomorrow.
    • TEST 0 PASSED.
  6. TEST 1 started:
    • Step 1: Detected 1 lingering plugin alarm (tomorrow's alarm from TEST 0).
    • Auto-reset:
      • Uninstalled app
      • Reinstalled APK
      • Cleared logcat
      • Verified plugin alarms = 0
    • Confirmed clean starting state for TEST 1.
  7. Step 2: Launched app and confirmed plugin configured.
  8. In UI: tapped "Test Notification" button (schedules a notification ~4 minutes in the future).
  9. Step 3 (pre-FS verify):
    • Verified 1 plugin alarm exists in AlarmManager.
    • Confirmed alarm details (time, tag, type).
    • Confirmed scheduling logs with source=TEST_NOTIFICATION.
  10. Step 4: Performed force-stop via adb shell am force-stop com.timesafari.dailynotification.
  11. Step 5 (post-FS verify):
    • Verified plugin alarms = 0 (force-stop cleared alarms).
  12. Step 6: Relaunched app (cold start).
  13. Step 7 (recovery verify):
    • Verified plugin alarms = 1 after recovery.
    • Confirmed recovery logs under DNP-REACTIVATION:
      • App launch recovery
      • Boot recovery
      • rescheduled=1
    • Confirmed rescheduled alarm uses the same scheduleId and triggerTime as pre-force-stop.
  14. Fire verification was skipped (VERIFY_FIRE=false).
  15. TEST 1 summary:
    • Before FS: 1 alarm
    • After FS: 0 alarms
    • After recovery: 1 alarm
    • rescheduled=1, errors=0
    • TEST 1 PASSED.

4. Expected Script Output (Key Excerpts)

These are the critical excerpts from the test harness output for a passing TEST 1.

4.1 Clean Start & Auto-Reset

→ Step 1: Clean start - checking for lingering alarms...
  Current plugin notification alarms: 1
  System/other alarms: 17 (for context)
⚠️  Found 1 lingering plugin alarm(s) - these will interfere with TEST 1.
  TEST 1 needs a clean state (no existing plugin alarms).
  Resetting app state via uninstall + reinstall...
  Uninstalling existing app...
✅ App uninstall succeeded (clean slate).
  Reinstalling APK...
✅ App reinstall succeeded.
  Clearing logcat buffer after reinstall...
  Rechecking plugin alarms after reset...
  Plugin alarms after reset: 0 (expected: 0)
✅ App state reset complete. TEST 1 starting from clean state.

4.2 Alarm Scheduled Before Force-Stop

→ Step 3: Verifying alarm exists in AlarmManager (BEFORE force-stop)...
  Plugin alarms: 1 (expected: 1)
  System/other alarms: 19 (for context)
✅ ✅ Single plugin alarm confirmed in AlarmManager (one per day)
  Alarm details:
    RTC_WAKEUP #4: Alarm{161cd2b type 0 origWhen 1764842100000 whenElapsed 13009441 com.timesafari.dailynotification}
      tag=*walarm*:com.timesafari.daily.NOTIFICATION
      type=RTC_WAKEUP origWhen=2025-12-04 09:55:00.000 window=0 exactAllowReason=policy_permission repeatInterval=0 count=0 flags=0x3
      policyWhenElapsed: requester=+3m4s281ms app_standby=-6s566ms device_idle=-- battery_saver=--
--
  Alarm scheduled for: Thu Dec  4 09:55:00 AM UTC 2025 (1764842100000 ms)
  Checking logs for scheduling confirmation...
12-04 09:51:49.150  6803  6867 W DNP-SCHEDULE: Cancelling existing alarm before rescheduling: requestCode=3454, scheduleId=daily_1764841909137, source=TEST_NOTIFICATION
12-04 09:51:49.151  6803  6867 I DNP-NOTIFY: Scheduling alarm: triggerTime=2025-12-04 09:55:00, delayMs=190849, requestCode=3454, scheduleId=daily_1764841909137
12-04 09:51:49.152  6803  6867 I DNP-SCHEDULE: Scheduling OS alarm: variant=ALARM_CLOCK, action=com.timesafari.daily.NOTIFICATION, triggerTime=1764842100000, requestCode=3454, scheduleId=daily_1764841909137, source=TEST_NOTIFICATION, pendingIntentHash=267839060, showIntentHash=256236029
12-04 09:51:49.153  6803  6867 I DNP-NOTIFY: Alarm clock scheduled (setAlarmClock): triggerAt=1764842100000, requestCode=3454

4.3 Force-Stop & Alarm Clearance

→ Step 4: Force-stopping app (clears all alarms)...
⚠️  Force-stop will clear ALL alarms from AlarmManager
  Executing: adb shell am force-stop com.timesafari.dailynotification
  Forcing stop of app process...
✅ Force stop issued
→ Step 5: Verifying alarms are MISSING from AlarmManager (AFTER force-stop)...
  Plugin alarms after force-stop: 0 (expected: 0)
  System/other alarms: 17 (for context)
✅ ✅ Plugin alarms cleared by force-stop (count: 0)
  This confirms: Force-stop cleared alarms from AlarmManager

4.4 Recovery & Rescheduling

→ Step 6: Relaunching app (triggers recovery from database)...
  Clearing logcat buffer...
✅ Logs cleared
  Launching app...
Starting: Intent { cmp=com.timesafari.dailynotification/.MainActivity }
✅ App launched
→ Step 7: Verifying recovery rebuilt alarms from database...
  Plugin alarms after recovery: 1 (expected: 1)
  System/other alarms: 21 (for context)
  Checking recovery logs...

12-04 09:52:21.919  6954  7015 I DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)
12-04 09:52:21.926  6954  7015 I DNP-REACTIVATION: Cold start recovery: checking for missed notifications
12-04 09:52:21.937  6954  7015 I DNP-REACTIVATION: Rescheduled alarm: daily_1764841909137 for 1764842100000
12-04 09:52:21.937  6954  7015 I DNP-REACTIVATION: Rescheduled missing alarm: daily_1764841909137 at 1764842100000
12-04 09:52:21.952  6954  7015 I DNP-REACTIVATION: Cold start recovery complete: missed=0, rescheduled=1, verified=0, errors=0
12-04 09:52:21.952  6954  7015 I DNP-REACTIVATION: App launch recovery completed: missed=0, rescheduled=1, verified=0, errors=0
12-04 09:52:22.077  6954  7015 I DNP-REACTIVATION: Starting boot recovery
12-04 09:52:22.135  6954  7017 I DNP-REACTIVATION: Loaded 1 schedules from DB
12-04 09:52:22.138  6954  7017 I DNP-REACTIVATION: Rescheduled alarm: daily_1764841909137 for 1764842100000
12-04 09:52:22.143  6954  7017 I DNP-REACTIVATION: Boot recovery complete: missed=0, rescheduled=1, verified=0, errors=0

Final summary:

✅ ✅ Alarms restored in AlarmManager (count: 1)
✅ ✅ Recovery logs confirm rescheduling (rescheduled=1)
✅ TEST 1 PASSED: Recovery successfully rebuilt alarms from database!
  Summary:
  - Before force-stop: 1 alarm(s)
  - After force-stop: 0 alarm(s) (cleared)
  - After recovery: 1 alarm(s) (rebuilt)
  - Rescheduled: 1 alarm(s)
  - Verified: 0 alarm(s)
  Skipping fire verification (VERIFY_FIRE=false, set VERIFY_FIRE=true to enable)

5. Expected dumpsys alarm Shapes

5.1 Before Force-Stop (Scheduled)

  • Plugin alarm count: 1
  • Example block (shape, not necessarily exact handle):
RTC_WAKEUP #4: Alarm{161cd2b type 0 origWhen 1764842100000 whenElapsed 13009441 com.timesafari.dailynotification}
  tag=*walarm*:com.timesafari.daily.NOTIFICATION
  type=RTC_WAKEUP origWhen=2025-12-04 09:55:00.000 window=0 exactAllowReason=policy_permission repeatInterval=0 count=0 flags=0x3
  policyWhenElapsed: requester=+3m4s281ms app_standby=-6s566ms device_idle=-- battery_saver=--

5.2 After Force-Stop

  • Plugin alarm count: 0
  • No *walarm*:com.timesafari.daily.NOTIFICATION entries should appear.

5.3 After Recovery

  • Plugin alarm count: 1
  • Alarm should be restored with:
    • Same scheduleId: daily_1764841909137
    • Same triggerTime: 17648421000002025-12-04 09:55:00

6. Expected logcat Patterns

For a passing run, look for:

  • Scheduling (before FS):

    DNP-NOTIFY: Scheduling alarm: triggerTime=2025-12-04 09:55:00, delayMs=..., requestCode=3454, scheduleId=daily_1764841909137
    DNP-SCHEDULE: Scheduling OS alarm: variant=ALARM_CLOCK, action=com.timesafari.daily.NOTIFICATION, triggerTime=1764842100000, requestCode=3454, scheduleId=daily_1764841909137, source=TEST_NOTIFICATION, ...
    
  • Recovery (after FS + relaunch):

    DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)
    DNP-REACTIVATION: Rescheduled alarm: daily_1764841909137 for 1764842100000
    DNP-REACTIVATION: Cold start recovery complete: missed=0, rescheduled=1, verified=0, errors=0
    DNP-REACTIVATION: App launch recovery completed: missed=0, rescheduled=1, verified=0, errors=0
    
    DNP-REACTIVATION: Starting boot recovery
    DNP-REACTIVATION: Loaded 1 schedules from DB
    DNP-REACTIVATION: Rescheduled alarm: daily_1764841909137 for 1764842100000
    DNP-REACTIVATION: Boot recovery complete: missed=0, rescheduled=1, verified=0, errors=0
    

Key invariants:

  • rescheduled=1
  • errors=0
  • scheduleId and triggerTime match pre-FS values.

7. Quick Pass/Fail Checklist

A TEST 1 run is a PASS if all of the following are true:

  • Starting state:

    • Script auto-resets if lingering alarms exist from TEST 0.
    • After reset: plugin alarm count = 0.
  • Pre-force-stop:

    • Plugin alarm count = 1.
    • Alarm is tagged *walarm*:com.timesafari.daily.NOTIFICATION.
    • triggerTime and origWhen are consistent (e.g. 2025-12-04 09:55:00, 1764842100000).
    • scheduleId looks like daily_<timestamp> (here: daily_1764841909137).
    • Logs show source=TEST_NOTIFICATION.
  • After force-stop:

    • Plugin alarm count = 0.
    • No NOTIFICATION alarms remain in dumpsys.
  • After recovery (relaunch):

    • Plugin alarm count = 1.
    • Logs show DNP-REACTIVATION with:
      • rescheduled=1
      • errors=0
      • Same scheduleId and triggerTime as pre-FS.
  • Script summary:

    • States:
      • Before force-stop: 1 alarm
      • After force-stop: 0 alarms
      • After recovery: 1 alarm
    • Ends with: ✅ TEST 1 PASSED: Recovery successfully rebuilt alarms from database!

If any of these conditions fail, the run is NOT GOLDEN and should not overwrite this reference.


8. Notes / Deviations

  • This golden run skips fire verification (VERIFY_FIRE=false). Future runs may enable fire verification; if that becomes standard, update this doc.
  • It is acceptable for both:
    • Cold start recovery and
    • Boot recovery to run on app launch, as long as:
    • rescheduled=1
    • No duplicate alarms are scheduled.
  • If OS behavior changes (e.g., force-stop no longer clears alarms on a future Android version), this test's expectations may need to be revised; document any such deviations explicitly here before changing the checklist.