docs(test): add Phase 2 force stop recovery testing infrastructure
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
This commit is contained in:
358
docs/alarms/PHASE2-EMULATOR-TESTING.md
Normal file
358
docs/alarms/PHASE2-EMULATOR-TESTING.md
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
# PHASE 2 – EMULATOR TESTING
|
||||||
|
|
||||||
|
**Force Stop Detection & Recovery**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
Phase 2 verifies that the Daily Notification Plugin correctly:
|
||||||
|
|
||||||
|
1. Detects **force stop** scenarios (where alarms may be cleared by the OS).
|
||||||
|
2. **Reschedules** future notifications when alarms are missing but schedules remain in the database.
|
||||||
|
3. **Avoids heavy recovery** when alarms are still intact.
|
||||||
|
4. **Does not misfire** force-stop recovery on first launch / empty database.
|
||||||
|
|
||||||
|
This document defines the emulator test procedure for Phase 2 using the script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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` (or `ADB_BIN` exported)
|
||||||
|
* 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd test-apps/android-test-app
|
||||||
|
chmod +x test-phase2.sh # first time only
|
||||||
|
./test-phase2.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will:
|
||||||
|
|
||||||
|
1. Perform pre-flight checks (ADB/emulator).
|
||||||
|
2. Build and install the debug APK.
|
||||||
|
3. 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
|
||||||
|
4. Print parsed recovery summaries from `DNP-REACTIVATION` logs.
|
||||||
|
|
||||||
|
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):**
|
||||||
|
|
||||||
|
1. **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 ✅.
|
||||||
|
|
||||||
|
2. **Schedule a future notification**
|
||||||
|
* Click **Test Notification** (or equivalent) to schedule a notification a few minutes in the future.
|
||||||
|
|
||||||
|
3. **Verify alarms are scheduled**
|
||||||
|
* Script runs:
|
||||||
|
```bash
|
||||||
|
adb shell dumpsys alarm | grep com.timesafari.dailynotification
|
||||||
|
```
|
||||||
|
* Confirm at least one `RTC_WAKEUP` alarm for `com.timesafari.dailynotification`.
|
||||||
|
|
||||||
|
4. **Force stop the app**
|
||||||
|
* Script executes:
|
||||||
|
```bash
|
||||||
|
adb shell am force-stop com.timesafari.dailynotification
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Confirm alarms after force stop**
|
||||||
|
* Script re-runs `dumpsys alarm`.
|
||||||
|
* Ideal test case: **0** alarms for `com.timesafari.dailynotification` (alarms cleared).
|
||||||
|
|
||||||
|
6. **Trigger recovery**
|
||||||
|
* Script clears logcat and launches the app.
|
||||||
|
* Wait ~5 seconds for recovery.
|
||||||
|
|
||||||
|
7. **Collect and inspect logs**
|
||||||
|
* Script collects `DNP-REACTIVATION` logs and parses:
|
||||||
|
* `scenario=<value>`
|
||||||
|
* `missed=<n>`
|
||||||
|
* `rescheduled=<n>`
|
||||||
|
* `verified=<n>`
|
||||||
|
* `errors=<n>`
|
||||||
|
|
||||||
|
**Expected Logs (Ideal Case):**
|
||||||
|
|
||||||
|
* Scenario:
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Detected scenario: FORCE_STOP
|
||||||
|
```
|
||||||
|
|
||||||
|
* Alarm handling:
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Rescheduled alarm: daily_<id> for <time>
|
||||||
|
DNP-REACTIVATION: Rescheduled missing alarm: daily_<id> at <time>
|
||||||
|
```
|
||||||
|
|
||||||
|
* Summary:
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Force stop recovery completed: missed=1, rescheduled=1, verified=0, errors=0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pass Criteria:**
|
||||||
|
|
||||||
|
* `scenario=FORCE_STOP`
|
||||||
|
* `rescheduled > 0`
|
||||||
|
* `errors = 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-stop` on 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):**
|
||||||
|
|
||||||
|
1. **Launch & configure plugin**
|
||||||
|
* Same as TEST 1 (ensure both plugin statuses are ✅).
|
||||||
|
|
||||||
|
2. **Schedule another future notification**
|
||||||
|
* Click **Test Notification** again to create a second schedule.
|
||||||
|
|
||||||
|
3. **Verify alarms are scheduled**
|
||||||
|
* Confirm multiple alarms for `com.timesafari.dailynotification` via `dumpsys alarm`.
|
||||||
|
|
||||||
|
4. **Simulate a "soft stop"**
|
||||||
|
* Script runs:
|
||||||
|
```bash
|
||||||
|
adb shell am kill com.timesafari.dailynotification
|
||||||
|
```
|
||||||
|
* Intent: stop the process but **not** clear alarms (actual behavior may vary by OS).
|
||||||
|
|
||||||
|
5. **Check alarms after soft stop**
|
||||||
|
* Ensure alarms are still present in `dumpsys alarm`.
|
||||||
|
|
||||||
|
6. **Trigger recovery**
|
||||||
|
* Script clears logcat and relaunches the app.
|
||||||
|
* Wait ~5 seconds.
|
||||||
|
|
||||||
|
7. **Collect logs**
|
||||||
|
* Script parses `DNP-REACTIVATION` logs for:
|
||||||
|
* `scenario`
|
||||||
|
* `rescheduled`
|
||||||
|
* `errors`
|
||||||
|
|
||||||
|
**Expected Behavior:**
|
||||||
|
|
||||||
|
* `scenario` should **not** be `FORCE_STOP` (e.g., `COLD_START` or 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_STOP`
|
||||||
|
* `rescheduled = 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):**
|
||||||
|
|
||||||
|
1. **Clear state**
|
||||||
|
* Script uninstalls the app to clear DB/state:
|
||||||
|
```bash
|
||||||
|
adb uninstall com.timesafari.dailynotification
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Reinstall APK**
|
||||||
|
* Script reinstalls `app-debug.apk`.
|
||||||
|
|
||||||
|
3. **Launch app without scheduling anything**
|
||||||
|
* Script launches the app.
|
||||||
|
* Do **not** schedule notifications or configure plugin beyond initial display.
|
||||||
|
|
||||||
|
4. **Collect logs**
|
||||||
|
* Script grabs `DNP-REACTIVATION` logs and parses:
|
||||||
|
* `scenario`
|
||||||
|
* `rescheduled`
|
||||||
|
|
||||||
|
**Expected Behavior:**
|
||||||
|
|
||||||
|
* Either:
|
||||||
|
* No `DNP-REACTIVATION` logs at all (no recovery run), **or**
|
||||||
|
* A specific "no schedules" scenario, e.g.:
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: No schedules present — skipping recovery (first launch)
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Detected scenario: NONE
|
||||||
|
```
|
||||||
|
|
||||||
|
* 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) **and** `rescheduled=0`.
|
||||||
|
|
||||||
|
Script will report success when:
|
||||||
|
|
||||||
|
* `scenario == NONE_SCENARIO_VALUE` and `rescheduled=0`, or
|
||||||
|
* No recovery logs are found.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Latest Known Good Run (Template)
|
||||||
|
|
||||||
|
Fill this in after your first successful emulator run.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
## 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**:
|
||||||
|
1. Scenario detection logic not implemented (Phase 2 not complete)
|
||||||
|
2. Alarm count check failing (`alarmsExist()` returning true when it shouldn't)
|
||||||
|
3. 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 (not `nextAlarmClock`)
|
||||||
|
|
||||||
|
### Recovery Doesn't Reschedule Alarms
|
||||||
|
|
||||||
|
**Symptom**: `rescheduled=0` even when alarms were cleared
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. Schedules not in database
|
||||||
|
2. Reschedule logic failing
|
||||||
|
3. 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](../android-implementation-directive-phase2.md) - Implementation details
|
||||||
|
- [Phase 2 Verification](./PHASE2-VERIFICATION.md) - Verification report
|
||||||
|
- [Phase 1 Testing Guide](./PHASE1-EMULATOR-TESTING.md) - Prerequisite testing
|
||||||
|
- [Activation Guide](./ACTIVATION-GUIDE.md) - How to use directives
|
||||||
|
- [Plugin Requirements](./03-plugin-requirements.md) - Requirements Phase 2 implements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: Ready for testing (Phase 2 implementation pending)
|
||||||
|
**Last Updated**: November 2025
|
||||||
195
docs/alarms/PHASE2-VERIFICATION.md
Normal file
195
docs/alarms/PHASE2-VERIFICATION.md
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
# Phase 2 – Force Stop Recovery Verification
|
||||||
|
|
||||||
|
**Plugin:** Daily Notification Plugin
|
||||||
|
**Scope:** Force stop detection & recovery (App ID: `com.timesafari.dailynotification`)
|
||||||
|
**Related Docs:**
|
||||||
|
|
||||||
|
- `android-implementation-directive-phase2.md`
|
||||||
|
- `03-plugin-requirements.md` (Force Stop & App Termination behavior)
|
||||||
|
- `PHASE2-EMULATOR-TESTING.md`
|
||||||
|
- `000-UNIFIED-ALARM-DIRECTIVE.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Objective
|
||||||
|
|
||||||
|
Phase 2 verifies that the Daily Notification Plugin:
|
||||||
|
|
||||||
|
1. Correctly detects **force stop** conditions where alarms may have been cleared.
|
||||||
|
2. **Reschedules** future notifications when schedules exist in the database but alarms are missing.
|
||||||
|
3. **Avoids heavy recovery** when alarms are intact (no false positives).
|
||||||
|
4. **Does not misfire** force-stop recovery on first launch / empty database.
|
||||||
|
|
||||||
|
Phase 2 builds on Phase 1, which already covers:
|
||||||
|
|
||||||
|
- Cold start detection
|
||||||
|
- Missed notification marking
|
||||||
|
- Basic alarm verification
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Test Method
|
||||||
|
|
||||||
|
Verification is performed using the emulator test harness:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd test-apps/android-test-app
|
||||||
|
./test-phase2.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This script:
|
||||||
|
|
||||||
|
* Builds and installs the debug APK (`app/build/outputs/apk/debug/app-debug.apk`).
|
||||||
|
* Guides the tester through UI steps for scheduling notifications and configuring the plugin.
|
||||||
|
* Simulates:
|
||||||
|
* `force-stop` behavior via `adb shell am force-stop ...`
|
||||||
|
* "Soft stop" / process kill via `adb shell am kill ...`
|
||||||
|
* First launch / empty DB via uninstall + reinstall
|
||||||
|
* Collects and parses `DNP-REACTIVATION` log lines, extracting:
|
||||||
|
* `scenario`
|
||||||
|
* `missed`
|
||||||
|
* `rescheduled`
|
||||||
|
* `verified`
|
||||||
|
* `errors`
|
||||||
|
|
||||||
|
Detailed steps and expectations are documented in `PHASE2-EMULATOR-TESTING.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Test Matrix
|
||||||
|
|
||||||
|
| ID | Scenario | Method / Script Step | Expected Behavior | Result | Notes |
|
||||||
|
| --- | ---------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ------ | ----- |
|
||||||
|
| 2.1 | Force stop clears alarms | `test-phase2.sh` – TEST 1: Force Stop – Alarms Cleared | `scenario=FORCE_STOP`, `rescheduled>0`, `errors=0` | ☐ | |
|
||||||
|
| 2.2 | Force stop / process stop with alarms intact | `test-phase2.sh` – TEST 2: Soft Stop – Alarms Intact | `scenario != FORCE_STOP`, `rescheduled=0`, `errors=0` | ☐ | |
|
||||||
|
| 2.3 | First launch / empty DB (no schedules present) | `test-phase2.sh` – TEST 3: First Launch / No Schedules | Either no recovery logs **or** `scenario=NONE` (or equivalent) and `rescheduled=0`, `errors=0` | ☐ | |
|
||||||
|
|
||||||
|
> Fill in **Result** and **Notes** after executing the script on your baseline emulator/device.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Expected Log Patterns
|
||||||
|
|
||||||
|
### 4.1 Force Stop – Alarms Cleared (Test 2.1)
|
||||||
|
|
||||||
|
Typical expected `DNP-REACTIVATION` log patterns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Detected scenario: FORCE_STOP
|
||||||
|
DNP-REACTIVATION: Rescheduled alarm: daily_<id> for <time>
|
||||||
|
DNP-REACTIVATION: Rescheduled missing alarm: daily_<id> at <time>
|
||||||
|
DNP-REACTIVATION: Force stop recovery completed: missed=1, rescheduled=1, verified=0, errors=0
|
||||||
|
```
|
||||||
|
|
||||||
|
The **script** will report:
|
||||||
|
|
||||||
|
```text
|
||||||
|
✅ TEST 1 PASSED: Force stop detected and alarms rescheduled (scenario=FORCE_STOP, rescheduled=1).
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Soft Stop – Alarms Intact (Test 2.2)
|
||||||
|
|
||||||
|
Typical expected patterns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Detected scenario: COLD_START
|
||||||
|
DNP-REACTIVATION: Cold start recovery completed: missed=0, rescheduled=0, verified>=0, errors=0
|
||||||
|
```
|
||||||
|
|
||||||
|
The script should **not** treat this as a force-stop recovery:
|
||||||
|
|
||||||
|
```text
|
||||||
|
✅ TEST 2 PASSED: No heavy force-stop recovery when alarms are intact (scenario=COLD_START, rescheduled=0).
|
||||||
|
```
|
||||||
|
|
||||||
|
(Adjust `scenario` name to match your actual implementation.)
|
||||||
|
|
||||||
|
### 4.3 First Launch / Empty DB (Test 2.3)
|
||||||
|
|
||||||
|
Two acceptable patterns:
|
||||||
|
|
||||||
|
1. **No recovery logs at all** (`DNP-REACTIVATION` absent), or
|
||||||
|
2. Explicit "no schedules" scenario, e.g.:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: No schedules present — skipping recovery (first launch)
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```text
|
||||||
|
DNP-REACTIVATION: Detected scenario: NONE
|
||||||
|
```
|
||||||
|
|
||||||
|
Script-level success message might be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
✅ TEST 3 PASSED: NONE scenario detected with no rescheduling.
|
||||||
|
```
|
||||||
|
|
||||||
|
or:
|
||||||
|
|
||||||
|
```text
|
||||||
|
✅ TEST 3 PASSED: No recovery logs when there are no schedules (safe behavior).
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Latest Known Good Run (Emulator) – Placeholder
|
||||||
|
|
||||||
|
> Update this section after your first successful run.
|
||||||
|
|
||||||
|
**Environment**
|
||||||
|
|
||||||
|
* Device: Pixel 8 API 34 (Android 14)
|
||||||
|
* App ID: `com.timesafari.dailynotification`
|
||||||
|
* Build: Debug `app-debug.apk` from commit `<GIT_HASH>`
|
||||||
|
* Script: `./test-phase2.sh`
|
||||||
|
* Date: 2025-11-XX
|
||||||
|
|
||||||
|
**Observed Results**
|
||||||
|
|
||||||
|
* ☐ **2.1 – Force Stop / Alarms Cleared**
|
||||||
|
* `scenario=FORCE_STOP`
|
||||||
|
* `missed=1, rescheduled=1, verified=0, errors=0`
|
||||||
|
|
||||||
|
* ☐ **2.2 – Soft Stop / Alarms Intact**
|
||||||
|
* `scenario=COLD_START` (or equivalent non-force-stop scenario)
|
||||||
|
* `rescheduled=0, errors=0`
|
||||||
|
|
||||||
|
* ☐ **2.3 – First Launch / Empty DB**
|
||||||
|
* `scenario=NONE` (or no logs)
|
||||||
|
* `rescheduled=0, errors=0`
|
||||||
|
|
||||||
|
**Conclusion:**
|
||||||
|
|
||||||
|
> To be filled after first successful emulator run.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Overall Status
|
||||||
|
|
||||||
|
> To be updated once the first emulator pass is complete.
|
||||||
|
|
||||||
|
* **Implementation Status:** ☐ Pending / ✅ Implemented in `ReactivationManager` (Android plugin)
|
||||||
|
* **Test Harness:** ✅ `test-phase2.sh` in `test-apps/android-test-app`
|
||||||
|
* **Emulator Verification:** ☐ Pending / ✅ Completed (update when done)
|
||||||
|
|
||||||
|
Once all boxes are checked:
|
||||||
|
|
||||||
|
> **Overall Status:** ✅ **VERIFIED** – Phase 2 behavior is implemented, emulator-tested, and aligned with `03-plugin-requirements.md` and `android-implementation-directive-phase2.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Related Documentation
|
||||||
|
|
||||||
|
- [Phase 2 Directive](../android-implementation-directive-phase2.md) - Implementation details
|
||||||
|
- [Phase 2 Emulator Testing](./PHASE2-EMULATOR-TESTING.md) - Test procedures
|
||||||
|
- [Phase 1 Verification](./PHASE1-VERIFICATION.md) - Prerequisite verification
|
||||||
|
- [Plugin Requirements](./03-plugin-requirements.md) - Requirements this phase implements
|
||||||
|
- [Platform Capability Reference](./01-platform-capability-reference.md) - OS-level facts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: ☐ **PENDING** – Phase 2 implementation and testing pending
|
||||||
|
**Last Updated**: November 2025
|
||||||
530
test-apps/android-test-app/test-phase2.sh
Executable file
530
test-apps/android-test-app/test-phase2.sh
Executable file
@@ -0,0 +1,530 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Phase 2 Testing Script – Force Stop Recovery
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
# --- Config -------------------------------------------------------------------
|
||||||
|
|
||||||
|
APP_ID="com.timesafari.dailynotification"
|
||||||
|
APK_PATH="./app/build/outputs/apk/debug/app-debug.apk"
|
||||||
|
ADB_BIN="${ADB_BIN:-adb}"
|
||||||
|
|
||||||
|
# Log tags / patterns (matched to actual ReactivationManager logs)
|
||||||
|
REACTIVATION_TAG="DNP-REACTIVATION"
|
||||||
|
SCENARIO_KEY="Detected scenario: "
|
||||||
|
FORCE_STOP_SCENARIO_VALUE="FORCE_STOP"
|
||||||
|
COLD_START_SCENARIO_VALUE="COLD_START"
|
||||||
|
NONE_SCENARIO_VALUE="NONE"
|
||||||
|
BOOT_SCENARIO_VALUE="BOOT"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
section() {
|
||||||
|
echo
|
||||||
|
echo "========================================"
|
||||||
|
echo "$1"
|
||||||
|
echo "========================================"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
substep() {
|
||||||
|
echo "→ $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo -e "ℹ️ $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
ok() {
|
||||||
|
echo -e "✅ $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "⚠️ $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "❌ $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
echo
|
||||||
|
read -rp "Press Enter when ready to continue..."
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_prompt() {
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "👆 UI ACTION REQUIRED"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo -e "$1"
|
||||||
|
echo
|
||||||
|
read -rp "Press Enter after completing the action above..."
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
# Wrapper so output is visible but failures stop the script
|
||||||
|
# Usage: run_cmd "description" cmd...
|
||||||
|
local desc="$1"; shift
|
||||||
|
substep "$desc"
|
||||||
|
echo " $*"
|
||||||
|
if "$@"; then
|
||||||
|
ok "$desc"
|
||||||
|
else
|
||||||
|
error "$desc failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_adb_device() {
|
||||||
|
section "Pre-Flight Checks"
|
||||||
|
|
||||||
|
if ! $ADB_BIN devices | awk 'NR>1 && $2=="device"{found=1} END{exit !found}'; then
|
||||||
|
error "No emulator/device in 'device' state. Start your emulator first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "ADB device connected"
|
||||||
|
|
||||||
|
info "Checking emulator status..."
|
||||||
|
if ! $ADB_BIN shell getprop sys.boot_completed | grep -q "1"; then
|
||||||
|
info "Waiting for emulator to boot..."
|
||||||
|
$ADB_BIN wait-for-device
|
||||||
|
while [ "$($ADB_BIN shell getprop sys.boot_completed)" != "1" ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
ok "Emulator is ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_app() {
|
||||||
|
section "Building Test App"
|
||||||
|
|
||||||
|
substep "Step 1: Building debug APK..."
|
||||||
|
if ./gradlew :app:assembleDebug; then
|
||||||
|
ok "Build successful"
|
||||||
|
else
|
||||||
|
error "Build failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$APK_PATH" ]]; then
|
||||||
|
ok "APK ready: $APK_PATH"
|
||||||
|
else
|
||||||
|
error "APK not found at $APK_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_app() {
|
||||||
|
section "Installing App"
|
||||||
|
|
||||||
|
substep "Step 1: Uninstalling existing app (if present)..."
|
||||||
|
set +e
|
||||||
|
uninstall_output="$($ADB_BIN uninstall "$APP_ID" 2>&1)"
|
||||||
|
uninstall_status=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ $uninstall_status -ne 0 ]]; then
|
||||||
|
if grep -q "DELETE_FAILED_INTERNAL_ERROR" <<<"$uninstall_output"; then
|
||||||
|
info "No existing app to uninstall (continuing)"
|
||||||
|
else
|
||||||
|
warn "Uninstall returned non-zero status: $uninstall_output (continuing anyway)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ok "Previous app uninstall succeeded"
|
||||||
|
fi
|
||||||
|
|
||||||
|
substep "Step 2: Installing new APK..."
|
||||||
|
if $ADB_BIN install -r "$APK_PATH"; then
|
||||||
|
ok "App installed successfully"
|
||||||
|
else
|
||||||
|
error "App installation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
substep "Step 3: Verifying installation..."
|
||||||
|
if $ADB_BIN shell pm list packages | grep -q "$APP_ID"; then
|
||||||
|
ok "App verified in package list"
|
||||||
|
else
|
||||||
|
error "App not found in package list"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Clearing logcat buffer..."
|
||||||
|
$ADB_BIN logcat -c
|
||||||
|
ok "Logs cleared"
|
||||||
|
}
|
||||||
|
|
||||||
|
launch_app() {
|
||||||
|
info "Launching app..."
|
||||||
|
$ADB_BIN shell am start -n "${APP_ID}/.MainActivity" >/dev/null 2>&1
|
||||||
|
sleep 3 # Give app time to load
|
||||||
|
ok "App launched"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_logs() {
|
||||||
|
info "Clearing logcat buffer..."
|
||||||
|
$ADB_BIN logcat -c
|
||||||
|
ok "Logs cleared"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_alarms() {
|
||||||
|
info "Checking AlarmManager status..."
|
||||||
|
echo
|
||||||
|
$ADB_BIN shell dumpsys alarm | grep -A3 "$APP_ID" || true
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
count_alarms() {
|
||||||
|
# Returns count of alarms for our app
|
||||||
|
$ADB_BIN shell dumpsys alarm | grep -c "$APP_ID" || echo "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
force_stop_app() {
|
||||||
|
info "Force-stopping app..."
|
||||||
|
$ADB_BIN shell am force-stop "$APP_ID"
|
||||||
|
sleep 2
|
||||||
|
ok "Force stop requested"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_recovery_logs() {
|
||||||
|
# Collect recent reactivation logs
|
||||||
|
$ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_field_from_logs() {
|
||||||
|
# Usage: extract_field_from_logs "<logs>" "<field_name>"
|
||||||
|
local logs="$1"
|
||||||
|
local field="$2"
|
||||||
|
# Looks for patterns like "field=NUMBER" and returns NUMBER (or 0)
|
||||||
|
local value
|
||||||
|
value="$(grep -oE "${field}=[0-9]+" <<<"$logs" | tail -n1 | sed "s/${field}=//" || true)"
|
||||||
|
if [[ -z "$value" ]]; then
|
||||||
|
echo "0"
|
||||||
|
else
|
||||||
|
echo "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_scenario_from_logs() {
|
||||||
|
local logs="$1"
|
||||||
|
local scen
|
||||||
|
# Looks for "Detected scenario: FORCE_STOP" format
|
||||||
|
scen="$(grep -oE "${SCENARIO_KEY}[A-Z_]+" <<<"$logs" | tail -n1 | sed "s/${SCENARIO_KEY}//" || true)"
|
||||||
|
echo "$scen"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# TEST 1 – Force Stop with Cleared Alarms
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
test1_force_stop_cleared_alarms() {
|
||||||
|
section "TEST 1: Force Stop – Alarms Cleared"
|
||||||
|
|
||||||
|
echo "Purpose: Verify force stop detection and alarm rescheduling when alarms are cleared."
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 1: Launch app & check plugin status"
|
||||||
|
launch_app
|
||||||
|
|
||||||
|
ui_prompt "In the app UI, verify plugin status:\n\n ⚙️ Plugin Settings: ✅ Configured\n 🔌 Native Fetcher: ✅ Configured\n\nIf either shows ❌ or 'Not configured', click 'Configure Plugin', wait until both are ✅, then press Enter."
|
||||||
|
|
||||||
|
ui_prompt "Now schedule at least one future notification (e.g., click 'Test Notification' to schedule for a few minutes in the future)."
|
||||||
|
|
||||||
|
substep "Step 2: Verify alarms are scheduled"
|
||||||
|
show_alarms
|
||||||
|
local before_count
|
||||||
|
before_count="$(count_alarms)"
|
||||||
|
info "Alarm count before force stop: $before_count"
|
||||||
|
|
||||||
|
if [[ "$before_count" -eq 0 ]]; then
|
||||||
|
warn "No alarms found before force stop; TEST 1 may not be meaningful."
|
||||||
|
fi
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 3: Force stop app (should clear alarms on many devices)"
|
||||||
|
force_stop_app
|
||||||
|
|
||||||
|
substep "Step 4: Check alarms after force stop"
|
||||||
|
local after_count
|
||||||
|
after_count="$(count_alarms)"
|
||||||
|
info "Alarm count after force stop: $after_count"
|
||||||
|
show_alarms
|
||||||
|
|
||||||
|
if [[ "$after_count" -gt 0 ]]; then
|
||||||
|
warn "Alarms still present after force stop. This device/OS may not clear alarms on force stop."
|
||||||
|
warn "TEST 1 will continue but may not fully validate FORCE_STOP scenario."
|
||||||
|
fi
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 5: Launch app (triggers recovery) and capture logs"
|
||||||
|
clear_logs
|
||||||
|
launch_app
|
||||||
|
sleep 5 # give recovery a moment to run
|
||||||
|
|
||||||
|
info "Collecting recovery logs..."
|
||||||
|
local logs
|
||||||
|
logs="$(get_recovery_logs)"
|
||||||
|
echo "$logs"
|
||||||
|
|
||||||
|
local missed rescheduled verified errors scenario
|
||||||
|
missed="$(extract_field_from_logs "$logs" "missed")"
|
||||||
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
|
verified="$(extract_field_from_logs "$logs" "verified")"
|
||||||
|
errors="$(extract_field_from_logs "$logs" "errors")"
|
||||||
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
|
|
||||||
|
echo
|
||||||
|
info "Parsed recovery summary:"
|
||||||
|
echo " scenario = ${scenario:-<none>}"
|
||||||
|
echo " missed = ${missed}"
|
||||||
|
echo " rescheduled= ${rescheduled}"
|
||||||
|
echo " verified = ${verified}"
|
||||||
|
echo " errors = ${errors}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$errors" -gt 0 ]]; then
|
||||||
|
error "Recovery reported errors>0 (errors=$errors)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$scenario" == "$FORCE_STOP_SCENARIO_VALUE" && "$rescheduled" -gt 0 ]]; then
|
||||||
|
ok "TEST 1 PASSED: Force stop detected and alarms rescheduled (scenario=$scenario, rescheduled=$rescheduled)."
|
||||||
|
elif [[ "$scenario" == "$FORCE_STOP_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
||||||
|
warn "TEST 1: scenario=FORCE_STOP but rescheduled=0. Check implementation or logs."
|
||||||
|
elif [[ "$after_count" -gt 0 ]]; then
|
||||||
|
info "TEST 1: Device/emulator kept alarms after force stop; FORCE_STOP scenario may not trigger here."
|
||||||
|
if [[ "$rescheduled" -gt 0 ]]; then
|
||||||
|
info "Recovery still worked (rescheduled=$rescheduled), but scenario was ${scenario:-COLD_START} instead of FORCE_STOP"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "TEST 1: Expected FORCE_STOP scenario not clearly detected. Review logs and scenario detection logic."
|
||||||
|
info "Scenario detected: ${scenario:-<none>}, rescheduled=$rescheduled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
substep "Step 6: Verify alarms are rescheduled in AlarmManager"
|
||||||
|
show_alarms
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# TEST 2 – Force Stop / Process Stop with Intact Alarms
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
test2_force_stop_intact_alarms() {
|
||||||
|
section "TEST 2: Force Stop / Process Stop – Alarms Intact"
|
||||||
|
|
||||||
|
echo "Purpose: Ensure we do NOT run heavy force-stop recovery when alarms are intact."
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 1: Launch app & ensure plugin configured"
|
||||||
|
launch_app
|
||||||
|
|
||||||
|
ui_prompt "In the app UI, verify plugin status:\n\n ⚙️ Plugin Settings: ✅ Configured\n 🔌 Native Fetcher: ✅ Configured\n\nIf needed, click 'Configure Plugin', then press Enter."
|
||||||
|
|
||||||
|
ui_prompt "Click 'Test Notification' to schedule another notification a few minutes in the future."
|
||||||
|
|
||||||
|
substep "Step 2: Verify alarms are scheduled"
|
||||||
|
show_alarms
|
||||||
|
local before
|
||||||
|
before="$(count_alarms)"
|
||||||
|
info "Alarm count before stop: $before"
|
||||||
|
|
||||||
|
if [[ "$before" -eq 0 ]]; then
|
||||||
|
warn "No alarms found; TEST 2 may not be meaningful."
|
||||||
|
fi
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 3: Simulate a 'soft' stop or process kill that does NOT clear alarms"
|
||||||
|
info "Killing app process (non-destructive - may not clear alarms)..."
|
||||||
|
$ADB_BIN shell am kill "$APP_ID" || true
|
||||||
|
sleep 2
|
||||||
|
ok "Kill signal sent (soft stop)"
|
||||||
|
|
||||||
|
substep "Step 4: Verify alarms are still scheduled"
|
||||||
|
local after
|
||||||
|
after="$(count_alarms)"
|
||||||
|
info "Alarm count after soft stop: $after"
|
||||||
|
show_alarms
|
||||||
|
|
||||||
|
if [[ "$after" -eq 0 ]]; then
|
||||||
|
warn "Alarms appear cleared after soft stop; this environment may not distinguish TEST 2 well."
|
||||||
|
fi
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 5: Launch app (triggers recovery) and capture logs"
|
||||||
|
clear_logs
|
||||||
|
launch_app
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
info "Collecting recovery logs..."
|
||||||
|
local logs
|
||||||
|
logs="$(get_recovery_logs)"
|
||||||
|
echo "$logs"
|
||||||
|
|
||||||
|
local missed rescheduled verified errors scenario
|
||||||
|
missed="$(extract_field_from_logs "$logs" "missed")"
|
||||||
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
|
verified="$(extract_field_from_logs "$logs" "verified")"
|
||||||
|
errors="$(extract_field_from_logs "$logs" "errors")"
|
||||||
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
|
|
||||||
|
echo
|
||||||
|
info "Parsed recovery summary:"
|
||||||
|
echo " scenario = ${scenario:-<none>}"
|
||||||
|
echo " missed = ${missed}"
|
||||||
|
echo " rescheduled= ${rescheduled}"
|
||||||
|
echo " verified = ${verified}"
|
||||||
|
echo " errors = ${errors}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ "$errors" -gt 0 ]]; then
|
||||||
|
error "Recovery reported errors>0 (errors=$errors)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$scenario" != "$FORCE_STOP_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
||||||
|
ok "TEST 2 PASSED: No heavy force-stop recovery when alarms intact (scenario=$scenario, rescheduled=$rescheduled)."
|
||||||
|
elif [[ "$scenario" == "$FORCE_STOP_SCENARIO_VALUE" ]]; then
|
||||||
|
warn "TEST 2: scenario=FORCE_STOP detected but alarms were intact. Check scenario detection logic."
|
||||||
|
else
|
||||||
|
info "TEST 2: Some rescheduling occurred (rescheduled=$rescheduled). This might be OK if alarms were actually missing."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# TEST 3 – First Launch / Empty DB Safeguard
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
test3_first_launch_no_schedules() {
|
||||||
|
section "TEST 3: First Launch / No Schedules Safeguard"
|
||||||
|
|
||||||
|
echo "Purpose: Ensure force-stop recovery is NOT triggered when DB is empty or plugin isn't configured."
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 1: Uninstall app to clear DB/state"
|
||||||
|
set +e
|
||||||
|
$ADB_BIN uninstall "$APP_ID" >/dev/null 2>&1
|
||||||
|
set -e
|
||||||
|
ok "App uninstalled (state cleared)"
|
||||||
|
|
||||||
|
substep "Step 2: Reinstall app"
|
||||||
|
if $ADB_BIN install -r "$APK_PATH"; then
|
||||||
|
ok "App installed"
|
||||||
|
else
|
||||||
|
error "Reinstall failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Clearing logcat..."
|
||||||
|
$ADB_BIN logcat -c
|
||||||
|
ok "Logs cleared"
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
substep "Step 3: Launch app WITHOUT scheduling anything"
|
||||||
|
launch_app
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
info "Collecting recovery logs..."
|
||||||
|
local logs
|
||||||
|
logs="$($ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true)"
|
||||||
|
echo "$logs"
|
||||||
|
|
||||||
|
local scenario rescheduled missed
|
||||||
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
|
missed="$(extract_field_from_logs "$logs" "missed")"
|
||||||
|
|
||||||
|
echo
|
||||||
|
info "Parsed recovery summary:"
|
||||||
|
echo " scenario = ${scenario:-<none>}"
|
||||||
|
echo " rescheduled= ${rescheduled}"
|
||||||
|
echo " missed = ${missed}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check for explicit "No schedules" or "skipping recovery" messages
|
||||||
|
if echo "$logs" | grep -qiE "No schedules|skipping recovery|first launch"; then
|
||||||
|
ok "TEST 3 PASSED: No recovery logs or explicit skip message when there are no schedules (safe behavior)."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$logs" ]]; then
|
||||||
|
ok "TEST 3 PASSED: No recovery logs when there are no schedules (safe behavior)."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If you explicitly log a NONE scenario, check for that:
|
||||||
|
if [[ "$scenario" == "$NONE_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
||||||
|
ok "TEST 3 PASSED: NONE scenario detected with no rescheduling."
|
||||||
|
elif [[ "$scenario" == "$FORCE_STOP_SCENARIO_VALUE" ]]; then
|
||||||
|
error "TEST 3 FAILED: FORCE_STOP scenario detected on first launch / empty DB. This should not happen."
|
||||||
|
elif [[ "$rescheduled" -gt 0 ]]; then
|
||||||
|
warn "TEST 3: rescheduled>0 on first launch / empty DB. Check that force-stop recovery isn't misfiring."
|
||||||
|
else
|
||||||
|
info "TEST 3: Logs present but no rescheduling; review scenario handling to ensure it's explicit about NONE / FIRST_LAUNCH."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
main() {
|
||||||
|
echo
|
||||||
|
echo "========================================"
|
||||||
|
echo "Phase 2 Testing Script – Force Stop Recovery"
|
||||||
|
echo "========================================"
|
||||||
|
echo
|
||||||
|
echo "This script will guide you through all Phase 2 tests."
|
||||||
|
echo "You'll be prompted when UI interaction is needed."
|
||||||
|
echo
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
require_adb_device
|
||||||
|
build_app
|
||||||
|
install_app
|
||||||
|
|
||||||
|
test1_force_stop_cleared_alarms
|
||||||
|
pause
|
||||||
|
|
||||||
|
test2_force_stop_intact_alarms
|
||||||
|
pause
|
||||||
|
|
||||||
|
test3_first_launch_no_schedules
|
||||||
|
|
||||||
|
section "Testing Complete"
|
||||||
|
|
||||||
|
echo "Test Results Summary (see logs above for details):"
|
||||||
|
echo
|
||||||
|
echo "TEST 1: Force Stop – Alarms Cleared"
|
||||||
|
echo " - Check logs for scenario=$FORCE_STOP_SCENARIO_VALUE and rescheduled>0"
|
||||||
|
echo
|
||||||
|
echo "TEST 2: Force Stop / Process Stop – Alarms Intact"
|
||||||
|
echo " - Check that heavy FORCE_STOP recovery did NOT run when alarms are still present"
|
||||||
|
echo
|
||||||
|
echo "TEST 3: First Launch / No Schedules"
|
||||||
|
echo " - Check that no force-stop recovery runs, or that NONE scenario is logged with rescheduled=0"
|
||||||
|
echo
|
||||||
|
|
||||||
|
ok "Phase 2 testing script complete!"
|
||||||
|
echo
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " - Review logs above"
|
||||||
|
echo " - Capture snippets into PHASE2-EMULATOR-TESTING.md"
|
||||||
|
echo " - Update PHASE2-VERIFICATION.md and unified directive status matrix"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user