refactor(test): extract shared helpers into alarm-test-lib.sh
Extract common helper functions from test-phase1.sh, test-phase2.sh, and test-phase3.sh into a shared library (alarm-test-lib.sh) to reduce code duplication and improve maintainability. Changes: - Create alarm-test-lib.sh with shared configuration, UI helpers, ADB helpers, log parsing, and test selection logic - Refactor all three phase test scripts to source the shared library - Remove ~200 lines of duplicated code across the three scripts - Preserve all existing behavior, CLI arguments, and test semantics - Maintain Phase 1 compatibility (print_* functions, VERIFY_FIRE flag) - Update all adb references to use $ADB_BIN variable - Standardize alarm counting to use shared count_alarms() function Benefits: - Single source of truth for shared helpers - Easier maintenance (fix once, benefits all scripts) - Consistent behavior across all test phases - No functional changes to test execution or results
This commit is contained in:
364
test-apps/android-test-app/alarm-test-lib.sh
Normal file
364
test-apps/android-test-app/alarm-test-lib.sh
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Shared Alarm Test Library
|
||||||
|
# ========================================
|
||||||
|
#
|
||||||
|
# Common helpers for Phase 1, 2, and 3 test scripts
|
||||||
|
#
|
||||||
|
# Usage: source this file in your test script:
|
||||||
|
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||||||
|
#
|
||||||
|
# Configuration can be overridden before sourcing:
|
||||||
|
# APP_ID="custom.package" source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||||||
|
|
||||||
|
# --- Config Defaults (can be overridden before sourcing) ---
|
||||||
|
|
||||||
|
: "${APP_ID:=com.timesafari.dailynotification}"
|
||||||
|
: "${APK_PATH:=./app/build/outputs/apk/debug/app-debug.apk}"
|
||||||
|
: "${ADB_BIN:=adb}"
|
||||||
|
|
||||||
|
# Reactivation log tag (common across phases)
|
||||||
|
: "${REACTIVATION_TAG:=DNP-REACTIVATION}"
|
||||||
|
: "${SCENARIO_KEY:=Detected scenario: }"
|
||||||
|
|
||||||
|
# Derived config (for backward compatibility with Phase 1)
|
||||||
|
PACKAGE="${APP_ID}"
|
||||||
|
ACTIVITY="${APP_ID}/.MainActivity"
|
||||||
|
|
||||||
|
# Colors for output (used by Phase 1 print_* functions)
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# --- UI/Log 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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 1 compatibility aliases (print_* functions)
|
||||||
|
print_header() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}$1${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
print_step() {
|
||||||
|
echo -e "${GREEN}→ Step $1:${NC} $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_wait() {
|
||||||
|
echo -e "${YELLOW}⏳ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}✅ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}❌ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_info() {
|
||||||
|
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warn() {
|
||||||
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_user() {
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter when ready to continue..."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_ui_action() {
|
||||||
|
ui_prompt "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- ADB/Build Helpers ---
|
||||||
|
|
||||||
|
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 2>/dev/null | grep -q "1"; then
|
||||||
|
info "Waiting for emulator to boot..."
|
||||||
|
$ADB_BIN wait-for-device
|
||||||
|
while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
ok "Emulator is ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 1 compatibility: check_adb_connection + check_emulator_ready
|
||||||
|
check_adb_connection() {
|
||||||
|
if ! $ADB_BIN devices | grep -q "device$"; then
|
||||||
|
print_error "No Android device/emulator connected"
|
||||||
|
echo "Please connect a device or start an emulator, then run:"
|
||||||
|
echo " adb devices"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_success "ADB device connected"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_emulator_ready() {
|
||||||
|
print_info "Checking emulator status..."
|
||||||
|
if ! $ADB_BIN shell getprop sys.boot_completed 2>/dev/null | grep -q "1"; then
|
||||||
|
print_wait "Waiting for emulator to boot..."
|
||||||
|
$ADB_BIN wait-for-device
|
||||||
|
while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
print_success "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 "Logcat cleared"
|
||||||
|
}
|
||||||
|
|
||||||
|
launch_app() {
|
||||||
|
# Check if we're in Phase 1 context (has print_info function)
|
||||||
|
if type print_info >/dev/null 2>&1; then
|
||||||
|
print_info "Launching app..."
|
||||||
|
$ADB_BIN shell am start -n "${ACTIVITY}"
|
||||||
|
sleep 3 # Give app time to load and check status
|
||||||
|
print_success "App launched"
|
||||||
|
else
|
||||||
|
substep "Launching app main activity..."
|
||||||
|
$ADB_BIN shell am start -n "${ACTIVITY}" >/dev/null 2>&1
|
||||||
|
sleep 3 # Give app time to load
|
||||||
|
ok "App launched"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (cleaned of newlines)
|
||||||
|
local count
|
||||||
|
count="$($ADB_BIN shell dumpsys alarm | grep -c "$APP_ID" 2>/dev/null || echo "0")"
|
||||||
|
echo "$count" | tr -d '\n\r' | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
force_stop_app() {
|
||||||
|
info "Forcing stop of app process..."
|
||||||
|
$ADB_BIN shell am force-stop "$APP_ID" || true
|
||||||
|
sleep 2
|
||||||
|
ok "Force stop issued"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 1 compatibility alias
|
||||||
|
kill_app() {
|
||||||
|
print_info "Killing app process..."
|
||||||
|
$ADB_BIN shell am kill "$APP_ID"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Verify process is killed
|
||||||
|
if $ADB_BIN shell ps | grep -q "$APP_ID"; then
|
||||||
|
print_wait "Process still running, using force-stop..."
|
||||||
|
$ADB_BIN shell am force-stop "$APP_ID"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! $ADB_BIN shell ps | grep -q "$APP_ID"; then
|
||||||
|
print_success "App process terminated"
|
||||||
|
else
|
||||||
|
print_error "App process still running"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reboot_emulator() {
|
||||||
|
info "Rebooting emulator..."
|
||||||
|
$ADB_BIN reboot
|
||||||
|
ok "Reboot initiated"
|
||||||
|
|
||||||
|
info "Waiting for emulator to come back online..."
|
||||||
|
$ADB_BIN wait-for-device
|
||||||
|
|
||||||
|
info "Waiting for system to fully boot..."
|
||||||
|
while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
sleep 5 # Additional buffer for system services
|
||||||
|
ok "Emulator back online"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Log Parsing Helpers ---
|
||||||
|
|
||||||
|
get_recovery_logs() {
|
||||||
|
# Collect only the relevant reactivation logs
|
||||||
|
$ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_field_from_logs() {
|
||||||
|
# Usage: extract_field_from_logs "$logs" "rescheduled"
|
||||||
|
local logs="$1"
|
||||||
|
local key="$2"
|
||||||
|
echo "$logs" | grep -E "$key=" | sed -E "s/.*$key=([0-9]+).*/\1/" | tail -n1 || echo "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_scenario_from_logs() {
|
||||||
|
local logs="$1"
|
||||||
|
echo "$logs" | grep -E "$SCENARIO_KEY" | sed -E "s/.*$SCENARIO_KEY([A-Z_]+).*/\1/" | tail -n1 || echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 1 compatibility: check_recovery_logs
|
||||||
|
check_recovery_logs() {
|
||||||
|
print_info "Checking recovery logs..."
|
||||||
|
echo ""
|
||||||
|
$ADB_BIN logcat -d | grep -E "$REACTIVATION_TAG" | tail -10
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Phase 1 compatibility: check_alarm_status
|
||||||
|
check_alarm_status() {
|
||||||
|
print_info "Checking AlarmManager status..."
|
||||||
|
echo ""
|
||||||
|
$ADB_BIN shell dumpsys alarm | grep -i "$APP_ID" | head -5
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Test Selection Helper ---
|
||||||
|
|
||||||
|
should_run_test() {
|
||||||
|
# Usage: should_run_test "1" SELECTED_TESTS
|
||||||
|
# Returns 0 (success) if test should run, 1 (failure) if not
|
||||||
|
local id="$1"
|
||||||
|
local -n selected_tests_ref="$2"
|
||||||
|
|
||||||
|
# If no tests are specified, run all tests
|
||||||
|
if [[ "${#selected_tests_ref[@]}" -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for t in "${selected_tests_ref[@]}"; do
|
||||||
|
if [[ "$t" == "$id" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,155 +5,17 @@
|
|||||||
|
|
||||||
set -e # Exit on error
|
set -e # Exit on error
|
||||||
|
|
||||||
# Colors for output
|
# Source shared library
|
||||||
RED='\033[0;31m'
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
GREEN='\033[0;32m'
|
source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
# Configuration
|
# Phase 1 specific configuration
|
||||||
PACKAGE="com.timesafari.dailynotification"
|
VERIFY_FIRE=${VERIFY_FIRE:-false} # Set VERIFY_FIRE=true to enable alarm fire verification
|
||||||
ACTIVITY="${PACKAGE}/.MainActivity"
|
APP_DIR="${SCRIPT_DIR}"
|
||||||
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "${APP_DIR}/../.." && pwd)"
|
PROJECT_ROOT="$(cd "${APP_DIR}/../.." && pwd)"
|
||||||
|
|
||||||
# Functions
|
# Allow selecting specific tests on the command line (e.g. ./test-phase1.sh 1 2)
|
||||||
print_header() {
|
SELECTED_TESTS=()
|
||||||
echo ""
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
echo -e "${BLUE}$1${NC}"
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
print_step() {
|
|
||||||
echo -e "${GREEN}→ Step $1:${NC} $2"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_wait() {
|
|
||||||
echo -e "${YELLOW}⏳ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_success() {
|
|
||||||
echo -e "${GREEN}✅ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_error() {
|
|
||||||
echo -e "${RED}❌ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info() {
|
|
||||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_user() {
|
|
||||||
echo ""
|
|
||||||
read -p "Press Enter when ready to continue..."
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_ui_action() {
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
||||||
echo -e "${YELLOW}👆 UI ACTION REQUIRED${NC}"
|
|
||||||
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
||||||
echo -e "${BLUE}$1${NC}"
|
|
||||||
echo ""
|
|
||||||
read -p "Press Enter after completing the action above..."
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
check_adb_connection() {
|
|
||||||
if ! adb devices | grep -q "device$"; then
|
|
||||||
print_error "No Android device/emulator connected"
|
|
||||||
echo "Please connect a device or start an emulator, then run:"
|
|
||||||
echo " adb devices"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
print_success "ADB device connected"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_emulator_ready() {
|
|
||||||
print_info "Checking emulator status..."
|
|
||||||
if ! adb shell getprop sys.boot_completed | grep -q "1"; then
|
|
||||||
print_wait "Waiting for emulator to boot..."
|
|
||||||
adb wait-for-device
|
|
||||||
while [ "$(adb shell getprop sys.boot_completed)" != "1" ]; do
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
print_success "Emulator is ready"
|
|
||||||
}
|
|
||||||
|
|
||||||
build_app() {
|
|
||||||
print_header "Building Test App"
|
|
||||||
|
|
||||||
cd "${APP_DIR}"
|
|
||||||
|
|
||||||
print_step "1" "Building debug APK..."
|
|
||||||
if ./gradlew assembleDebug; then
|
|
||||||
print_success "Build successful"
|
|
||||||
else
|
|
||||||
print_error "Build failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
APK_PATH="${APP_DIR}/app/build/outputs/apk/debug/app-debug.apk"
|
|
||||||
if [ ! -f "${APK_PATH}" ]; then
|
|
||||||
print_error "APK not found at ${APK_PATH}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_success "APK ready: ${APK_PATH}"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_app() {
|
|
||||||
print_header "Installing App"
|
|
||||||
|
|
||||||
print_step "1" "Uninstalling existing app (if present)..."
|
|
||||||
UNINSTALL_OUTPUT=$(adb uninstall "${PACKAGE}" 2>&1)
|
|
||||||
UNINSTALL_EXIT=$?
|
|
||||||
|
|
||||||
if [ ${UNINSTALL_EXIT} -eq 0 ]; then
|
|
||||||
print_success "Existing app uninstalled"
|
|
||||||
elif echo "${UNINSTALL_OUTPUT}" | grep -q "DELETE_FAILED_INTERNAL_ERROR"; then
|
|
||||||
print_info "No existing app to uninstall (continuing)"
|
|
||||||
elif echo "${UNINSTALL_OUTPUT}" | grep -q "Failure"; then
|
|
||||||
print_info "Uninstall failed (app may not exist) - continuing with install"
|
|
||||||
else
|
|
||||||
print_info "Uninstall result unclear - continuing with install"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_step "2" "Installing new APK..."
|
|
||||||
if adb install -r "${APP_DIR}/app/build/outputs/apk/debug/app-debug.apk"; then
|
|
||||||
print_success "App installed successfully"
|
|
||||||
else
|
|
||||||
print_error "Installation failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_step "3" "Verifying installation..."
|
|
||||||
if adb shell pm list packages | grep -q "${PACKAGE}"; then
|
|
||||||
print_success "App verified in package list"
|
|
||||||
else
|
|
||||||
print_error "App not found in package list"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_logs() {
|
|
||||||
print_info "Clearing logcat buffer..."
|
|
||||||
adb logcat -c
|
|
||||||
print_success "Logs cleared"
|
|
||||||
}
|
|
||||||
|
|
||||||
launch_app() {
|
|
||||||
print_info "Launching app..."
|
|
||||||
adb shell am start -n "${ACTIVITY}"
|
|
||||||
sleep 3 # Give app time to load and check status
|
|
||||||
print_success "App launched"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_plugin_configured() {
|
check_plugin_configured() {
|
||||||
print_info "Checking if plugin is already configured..."
|
print_info "Checking if plugin is already configured..."
|
||||||
@@ -162,14 +24,14 @@ check_plugin_configured() {
|
|||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
# Check if database exists (indicates plugin has been used)
|
# Check if database exists (indicates plugin has been used)
|
||||||
DB_EXISTS=$(adb shell run-as "${PACKAGE}" ls databases/ 2>/dev/null | grep -c "daily_notification" || echo "0")
|
DB_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls databases/ 2>/dev/null | grep -c "daily_notification" || echo "0")
|
||||||
|
|
||||||
# Check if SharedPreferences has configuration (more reliable)
|
# Check if SharedPreferences has configuration (more reliable)
|
||||||
# The plugin stores config in SharedPreferences
|
# The plugin stores config in SharedPreferences
|
||||||
PREFS_EXISTS=$(adb shell run-as "${PACKAGE}" ls shared_prefs/ 2>/dev/null | grep -c "DailyNotification" || echo "0")
|
PREFS_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls shared_prefs/ 2>/dev/null | grep -c "DailyNotification" || echo "0")
|
||||||
|
|
||||||
# Check recent logs for configuration activity
|
# Check recent logs for configuration activity
|
||||||
RECENT_CONFIG=$(adb logcat -d -t 50 | grep -E "Plugin configured|configurePlugin|Configuration" | tail -3)
|
RECENT_CONFIG=$($ADB_BIN logcat -d -t 50 | grep -E "Plugin configured|configurePlugin|Configuration" | tail -3)
|
||||||
|
|
||||||
if [ "${DB_EXISTS}" -gt "0" ] || [ "${PREFS_EXISTS}" -gt "0" ]; then
|
if [ "${DB_EXISTS}" -gt "0" ] || [ "${PREFS_EXISTS}" -gt "0" ]; then
|
||||||
print_success "Plugin appears to be configured (database or preferences exist)"
|
print_success "Plugin appears to be configured (database or preferences exist)"
|
||||||
@@ -191,6 +53,64 @@ check_plugin_configured() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_permissions() {
|
||||||
|
print_info "Checking notification permissions..."
|
||||||
|
|
||||||
|
# Check if POST_NOTIFICATIONS permission is granted (Android 13+)
|
||||||
|
PERM_CHECK=$($ADB_BIN shell dumpsys package "${APP_ID}" | grep -A 5 "granted=true" | grep -c "android.permission.POST_NOTIFICATIONS" || echo "0")
|
||||||
|
|
||||||
|
# Also check via app's permission status if available
|
||||||
|
PERM_GRANTED=false
|
||||||
|
if [ "${PERM_CHECK}" -gt "0" ]; then
|
||||||
|
PERM_GRANTED=true
|
||||||
|
else
|
||||||
|
# Check if we're on Android 12 or below (permission not required)
|
||||||
|
SDK_VERSION=$($ADB_BIN shell getprop ro.build.version.sdk)
|
||||||
|
if [ "${SDK_VERSION}" -lt "33" ]; then
|
||||||
|
PERM_GRANTED=true # Pre-Android 13 doesn't need runtime permission
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${PERM_GRANTED}" = true ]; then
|
||||||
|
print_success "Notification permissions granted"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_warn "Notification permissions may not be granted"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_permissions() {
|
||||||
|
if check_permissions; then
|
||||||
|
print_success "Permissions already granted"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_info "Notification permissions needed"
|
||||||
|
wait_for_ui_action "In the app UI, click the 'Request Permissions' button.
|
||||||
|
|
||||||
|
This will show a system permission dialog.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Click 'Request Permissions' button
|
||||||
|
2. In the system dialog, tap 'Allow' to grant notification permission
|
||||||
|
3. Return to the app and verify the status shows:
|
||||||
|
- 🔔 Notifications: ✅ Granted (or similar)
|
||||||
|
|
||||||
|
Once permission is granted, press Enter to continue."
|
||||||
|
|
||||||
|
# Re-check permissions
|
||||||
|
sleep 2
|
||||||
|
if check_permissions; then
|
||||||
|
print_success "Permissions granted"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_warn "Permissions may still not be granted - continuing anyway"
|
||||||
|
print_info "If notifications fail, you may need to grant permissions manually"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
ensure_plugin_configured() {
|
ensure_plugin_configured() {
|
||||||
if check_plugin_configured; then
|
if check_plugin_configured; then
|
||||||
# Plugin might be configured, but let user verify
|
# Plugin might be configured, but let user verify
|
||||||
@@ -199,11 +119,14 @@ ensure_plugin_configured() {
|
|||||||
If you see:
|
If you see:
|
||||||
- ⚙️ Plugin Settings: ✅ Configured
|
- ⚙️ Plugin Settings: ✅ Configured
|
||||||
- 🔌 Native Fetcher: ✅ Configured
|
- 🔌 Native Fetcher: ✅ Configured
|
||||||
|
- 🔔 Notifications: ✅ Granted (or similar)
|
||||||
|
|
||||||
Then the plugin is already configured - just press Enter to continue.
|
Then the plugin is already configured - just press Enter to continue.
|
||||||
|
|
||||||
If either shows ❌ or 'Not configured', click 'Configure Plugin' button first,
|
If any show ❌ or 'Not configured':
|
||||||
wait for both to show ✅, then press Enter."
|
- Click 'Request Permissions' if notifications are not granted
|
||||||
|
- Click 'Configure Plugin' if settings/fetcher are not configured
|
||||||
|
- Wait for all to show ✅, then press Enter."
|
||||||
|
|
||||||
# Give a moment for any configuration that just happened
|
# Give a moment for any configuration that just happened
|
||||||
sleep 2
|
sleep 2
|
||||||
@@ -212,6 +135,10 @@ ensure_plugin_configured() {
|
|||||||
else
|
else
|
||||||
# Plugin definitely needs configuration
|
# Plugin definitely needs configuration
|
||||||
print_info "Plugin needs configuration"
|
print_info "Plugin needs configuration"
|
||||||
|
|
||||||
|
# First ensure permissions
|
||||||
|
ensure_permissions
|
||||||
|
|
||||||
wait_for_ui_action "Click the 'Configure Plugin' button in the app UI.
|
wait_for_ui_action "Click the 'Configure Plugin' button in the app UI.
|
||||||
|
|
||||||
Wait for the status to update:
|
Wait for the status to update:
|
||||||
@@ -226,48 +153,34 @@ ensure_plugin_configured() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
kill_app() {
|
# kill_app is now provided by the library, but Phase 1 uses it with PACKAGE variable
|
||||||
print_info "Killing app process..."
|
# The library version uses APP_ID, which is set to PACKAGE, so it should work
|
||||||
adb shell am kill "${PACKAGE}"
|
# But we keep this as a compatibility wrapper if needed
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Verify process is killed
|
|
||||||
if adb shell ps | grep -q "${PACKAGE}"; then
|
|
||||||
print_wait "Process still running, using force-stop..."
|
|
||||||
adb shell am force-stop "${PACKAGE}"
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! adb shell ps | grep -q "${PACKAGE}"; then
|
|
||||||
print_success "App process terminated"
|
|
||||||
else
|
|
||||||
print_error "App process still running"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_recovery_logs() {
|
|
||||||
print_info "Checking recovery logs..."
|
|
||||||
echo ""
|
|
||||||
adb logcat -d | grep -E "DNP-REACTIVATION" | tail -10
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
check_alarm_status() {
|
|
||||||
print_info "Checking AlarmManager status..."
|
|
||||||
echo ""
|
|
||||||
adb shell dumpsys alarm | grep -i timesafari | head -5
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# Phase 1 specific helper: get_current_time (used for fire verification)
|
||||||
get_current_time() {
|
get_current_time() {
|
||||||
adb shell date +%s
|
$ADB_BIN shell date +%s
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main test execution
|
# Main test execution
|
||||||
main() {
|
main() {
|
||||||
|
# Allow selecting specific tests: e.g. `./test-phase1.sh 1 2`
|
||||||
|
if [[ "$#" -gt 0 && ( "$1" == "-h" || "$1" == "--help" ) ]]; then
|
||||||
|
echo "Usage: $0 [TEST_IDS...]"
|
||||||
|
echo ""
|
||||||
|
echo "If no TEST_IDS are given, all tests (1, 2, 3, 4) will run."
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 # run all tests"
|
||||||
|
echo " $0 1 # run only TEST 1"
|
||||||
|
echo " $0 2 3 # run only TEST 2 and TEST 3"
|
||||||
|
echo " $0 4 # run only TEST 4"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
SELECTED_TESTS=("$@")
|
||||||
|
|
||||||
print_header "Phase 1 Testing Script"
|
print_header "Phase 1 Testing Script"
|
||||||
echo "This script will guide you through all Phase 1 tests."
|
echo "This script will guide you through Phase 1 tests."
|
||||||
echo "You'll be prompted when UI interaction is needed."
|
echo "You'll be prompted when UI interaction is needed."
|
||||||
echo ""
|
echo ""
|
||||||
wait_for_user
|
wait_for_user
|
||||||
@@ -285,112 +198,259 @@ main() {
|
|||||||
clear_logs
|
clear_logs
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# TEST 1: Cold Start Missed Detection
|
# TEST 1: Force-Stop Recovery - Database Restoration
|
||||||
# ============================================
|
# ============================================
|
||||||
print_header "TEST 1: Cold Start Missed Detection"
|
if should_run_test "1" SELECTED_TESTS; then
|
||||||
echo "Purpose: Verify missed notifications are detected and marked."
|
print_header "TEST 1: Force-Stop Recovery - Database Restoration"
|
||||||
|
echo "Purpose: Verify that after force-stop (which clears alarms), recovery"
|
||||||
|
echo " uses the database to rebuild alarms on app relaunch."
|
||||||
|
echo ""
|
||||||
|
echo "Note: App supports one alarm per day."
|
||||||
|
echo ""
|
||||||
|
echo "Test sequence:"
|
||||||
|
echo " 1. Clean start - verify no lingering alarms"
|
||||||
|
echo " 2. Schedule one alarm → Verify it exists in AlarmManager"
|
||||||
|
echo " 3. Force-stop app → Verify alarm is cleared (count = 0)"
|
||||||
|
echo " 4. Relaunch app → Verify recovery rebuilds alarm from database"
|
||||||
|
echo " 5. Verify alarm actually fires at scheduled time (optional)"
|
||||||
echo ""
|
echo ""
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
|
||||||
print_step "1" "Launch app and check plugin status"
|
# ============================================
|
||||||
|
# Step 1: Clean start - verify no lingering alarms
|
||||||
|
# ============================================
|
||||||
|
print_step "1" "Clean start - checking for lingering alarms..."
|
||||||
|
LINGERING_COUNT=$(count_alarms)
|
||||||
|
if [ "${LINGERING_COUNT}" -gt "0" ] 2>/dev/null; then
|
||||||
|
print_warn "Found ${LINGERING_COUNT} lingering alarm(s) - these may interfere with test"
|
||||||
|
print_info "Consider uninstalling/reinstalling app for clean state"
|
||||||
|
wait_for_user
|
||||||
|
else
|
||||||
|
print_success "No lingering alarms found (clean state)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Step 2: Schedule a known future alarm
|
||||||
|
# ============================================
|
||||||
|
print_step "2" "Launch app and schedule alarm..."
|
||||||
launch_app
|
launch_app
|
||||||
ensure_plugin_configured
|
ensure_plugin_configured
|
||||||
|
|
||||||
wait_for_ui_action "In the app UI, click the 'Test Notification' button.
|
wait_for_ui_action "In the app UI, click the 'Test Notification' button.
|
||||||
|
|
||||||
This will schedule a notification for 4 minutes in the future.
|
This will schedule ONE notification for 4 minutes in the future.
|
||||||
(The test app automatically schedules for 4 minutes from now)"
|
(App supports one alarm per day)
|
||||||
|
|
||||||
print_step "2" "Verifying notification was scheduled..."
|
The alarm will be stored in the database AND scheduled in AlarmManager."
|
||||||
|
|
||||||
|
print_step "3" "Verifying alarm exists in AlarmManager (BEFORE force-stop)..."
|
||||||
sleep 2
|
sleep 2
|
||||||
check_alarm_status
|
|
||||||
|
ALARM_COUNT_BEFORE=$(count_alarms)
|
||||||
|
print_info "Alarm count in AlarmManager: ${ALARM_COUNT_BEFORE}"
|
||||||
|
|
||||||
|
if [ "${ALARM_COUNT_BEFORE}" -eq "0" ] 2>/dev/null; then
|
||||||
|
print_error "No alarms found in AlarmManager - cannot test force-stop recovery"
|
||||||
|
print_info "Make sure you clicked 'Test Notification' and wait a moment"
|
||||||
|
wait_for_user
|
||||||
|
# Re-check
|
||||||
|
ALARM_COUNT_BEFORE=$(count_alarms)
|
||||||
|
if [ "${ALARM_COUNT_BEFORE}" -eq "0" ] 2>/dev/null; then
|
||||||
|
print_error "Still no alarms found - aborting test"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "✅ Alarms confirmed in AlarmManager (count: ${ALARM_COUNT_BEFORE})"
|
||||||
|
|
||||||
|
# Capture alarm details for later verification
|
||||||
|
ALARM_DETAILS_BEFORE=$($ADB_BIN shell dumpsys alarm | grep -A 3 "$APP_ID" | head -10)
|
||||||
|
print_info "Alarm details:"
|
||||||
|
echo "${ALARM_DETAILS_BEFORE}" | head -5
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Extract trigger time for later verification
|
||||||
|
ALARM_TRIGGER_MS=$(echo "${ALARM_DETAILS_BEFORE}" | grep -oE "origWhen [0-9]+" | head -1 | awk '{print $2}')
|
||||||
|
if [ -n "${ALARM_TRIGGER_MS}" ]; then
|
||||||
|
ALARM_TRIGGER_SEC=$((ALARM_TRIGGER_MS / 1000))
|
||||||
|
ALARM_READABLE=$(date -d "@${ALARM_TRIGGER_SEC}" 2>/dev/null || echo "${ALARM_TRIGGER_SEC}")
|
||||||
|
print_info "Alarm scheduled for: ${ALARM_READABLE} (${ALARM_TRIGGER_MS} ms)"
|
||||||
|
fi
|
||||||
|
|
||||||
print_info "Checking logs for scheduling confirmation..."
|
print_info "Checking logs for scheduling confirmation..."
|
||||||
adb logcat -d | grep -E "DN|SCHEDULE|Stored notification content" | tail -5
|
$ADB_BIN logcat -d | grep -E "DN|SCHEDULE|Stored notification content" | tail -5
|
||||||
|
echo ""
|
||||||
wait_for_ui_action "Verify in the logs above that you see:
|
|
||||||
- 'Stored notification content in database' (NEW - should appear now)
|
|
||||||
- Alarm scheduled in AlarmManager
|
|
||||||
|
|
||||||
If you don't see 'Stored notification content', the fix may not be working."
|
|
||||||
|
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
|
||||||
print_step "3" "Killing app process (simulates OS kill)..."
|
# ============================================
|
||||||
kill_app
|
# Step 3: Force-stop the app (clears alarms)
|
||||||
|
# ============================================
|
||||||
|
print_step "4" "Force-stopping app (clears all alarms)..."
|
||||||
|
print_warn "Force-stop will clear ALL alarms from AlarmManager"
|
||||||
|
print_info "Executing: $ADB_BIN shell am force-stop ${APP_ID}"
|
||||||
|
force_stop_app
|
||||||
|
sleep 2
|
||||||
|
|
||||||
print_step "4" "Getting alarm scheduled time..."
|
# Verify app is stopped (force_stop_app already handles this)
|
||||||
ALARM_INFO=$(adb shell dumpsys alarm | grep -i timesafari | grep "origWhen" | head -1)
|
|
||||||
if [ -n "${ALARM_INFO}" ]; then
|
|
||||||
# Extract alarm time (origWhen is in milliseconds)
|
|
||||||
ALARM_TIME_MS=$(echo "${ALARM_INFO}" | grep -oE 'origWhen [0-9]+' | awk '{print $2}')
|
|
||||||
if [ -n "${ALARM_TIME_MS}" ]; then
|
|
||||||
CURRENT_TIME=$(get_current_time)
|
|
||||||
ALARM_TIME_SEC=$((ALARM_TIME_MS / 1000))
|
|
||||||
WAIT_SECONDS=$((ALARM_TIME_SEC - CURRENT_TIME + 60)) # Wait 1 minute past alarm
|
|
||||||
|
|
||||||
if [ ${WAIT_SECONDS} -gt 0 ] && [ ${WAIT_SECONDS} -lt 600 ]; then
|
# ============================================
|
||||||
ALARM_READABLE=$(date -d "@${ALARM_TIME_SEC}" 2>/dev/null || echo "${ALARM_TIME_SEC}")
|
# Step 4: Verify alarms are MISSING (cleared by OS)
|
||||||
CURRENT_READABLE=$(date -d "@${CURRENT_TIME}" 2>/dev/null || echo "${CURRENT_TIME}")
|
# ============================================
|
||||||
print_info "Alarm scheduled for: ${ALARM_READABLE}"
|
print_step "5" "Verifying alarms are MISSING from AlarmManager (AFTER force-stop)..."
|
||||||
print_info "Current time: ${CURRENT_READABLE}"
|
sleep 1
|
||||||
print_wait "Waiting ${WAIT_SECONDS} seconds for alarm time to pass..."
|
ALARM_COUNT_AFTER=$(count_alarms)
|
||||||
sleep ${WAIT_SECONDS}
|
print_info "Alarm count in AlarmManager after force-stop: ${ALARM_COUNT_AFTER}"
|
||||||
elif [ ${WAIT_SECONDS} -le 0 ]; then
|
|
||||||
print_info "Alarm time has already passed"
|
if [ "${ALARM_COUNT_AFTER}" -eq "0" ] 2>/dev/null; then
|
||||||
print_wait "Waiting 2 minutes to ensure we're well past alarm time..."
|
print_success "✅ Alarms cleared by force-stop (count: ${ALARM_COUNT_AFTER})"
|
||||||
sleep 120
|
print_info "This confirms: Force-stop cleared alarms from AlarmManager"
|
||||||
else
|
else
|
||||||
print_wait "Alarm is more than 10 minutes away. Waiting 5 minutes (you can adjust this)..."
|
print_warn "⚠️ Alarms still present after force-stop (count: ${ALARM_COUNT_AFTER})"
|
||||||
sleep 300
|
print_info "Some devices/OS versions may not clear alarms on force-stop"
|
||||||
fi
|
print_info "Continuing test anyway - recovery should still work"
|
||||||
else
|
|
||||||
print_wait "Could not parse alarm time. Waiting 5 minutes..."
|
|
||||||
sleep 300
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_wait "Could not find alarm in AlarmManager. Waiting 5 minutes..."
|
|
||||||
sleep 300
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_step "5" "Launching app (cold start - triggers recovery)..."
|
wait_for_user
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Step 5: Relaunch app (triggers recovery from database)
|
||||||
|
# ============================================
|
||||||
|
print_step "6" "Relaunching app (triggers recovery from database)..."
|
||||||
clear_logs
|
clear_logs
|
||||||
launch_app
|
launch_app
|
||||||
|
sleep 4 # Give recovery time to run
|
||||||
|
|
||||||
print_step "6" "Checking recovery logs..."
|
# ============================================
|
||||||
sleep 3
|
# Step 6: Verify recovery rebuilt alarms from database
|
||||||
|
# ============================================
|
||||||
|
print_step "7" "Verifying recovery rebuilt alarms from database..."
|
||||||
|
sleep 2
|
||||||
|
ALARM_COUNT_RECOVERED=$(count_alarms)
|
||||||
|
print_info "Alarm count in AlarmManager after recovery: ${ALARM_COUNT_RECOVERED}"
|
||||||
|
|
||||||
|
print_info "Checking recovery logs..."
|
||||||
check_recovery_logs
|
check_recovery_logs
|
||||||
|
|
||||||
print_info "Expected log output:"
|
print_info "Expected log output:"
|
||||||
echo " DNP-REACTIVATION: Starting app launch recovery (Phase 1: cold start only)"
|
echo " DNP-REACTIVATION: Starting app launch recovery"
|
||||||
echo " DNP-REACTIVATION: Cold start recovery: checking for missed notifications"
|
echo " DNP-REACTIVATION: Rescheduled alarm: <id> for <time>"
|
||||||
echo " DNP-REACTIVATION: Marked missed notification: <id>"
|
echo " DNP-REACTIVATION: Cold start recovery complete: ... rescheduled>=1, ..."
|
||||||
echo " DNP-REACTIVATION: Cold start recovery complete: missed=1, ..."
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
RECOVERY_RESULT=$(adb logcat -d | grep "Cold start recovery complete" | tail -1)
|
# Check recovery logs for rescheduling and recovery source
|
||||||
if echo "${RECOVERY_RESULT}" | grep -q "missed=[1-9]"; then
|
RECOVERY_RESULT=$($ADB_BIN logcat -d | grep "Cold start recovery complete\|Boot recovery complete" | tail -1)
|
||||||
print_success "TEST 1 PASSED: Missed notification detected!"
|
RESCHEDULED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||||||
elif echo "${RECOVERY_RESULT}" | grep -q "missed=0"; then
|
VERIFIED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||||||
print_error "TEST 1 FAILED: No missed notifications detected (missed=0)"
|
|
||||||
print_info "This might mean:"
|
# Check for explicit recovery source indication (if logged)
|
||||||
echo " - Notification was already delivered"
|
RECOVERY_SOURCE=$($ADB_BIN logcat -d | grep -E "recovery source|from database|DATABASE" | tail -1 || echo "")
|
||||||
echo " - NotificationContentEntity was not created"
|
|
||||||
echo " - Alarm fired before app was killed"
|
# Pass/fail criteria
|
||||||
|
TEST1_PASSED=false
|
||||||
|
|
||||||
|
if [ "${ALARM_COUNT_RECOVERED}" -gt "0" ] 2>/dev/null; then
|
||||||
|
print_success "✅ Alarms restored in AlarmManager (count: ${ALARM_COUNT_RECOVERED})"
|
||||||
|
if [ "${RESCHEDULED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||||||
|
print_success "✅ Recovery logs confirm rescheduling (rescheduled=${RESCHEDULED_COUNT})"
|
||||||
|
TEST1_PASSED=true
|
||||||
else
|
else
|
||||||
print_error "TEST 1 INCONCLUSIVE: Could not find recovery result"
|
print_warn "⚠️ Alarms restored but logs show rescheduled=0"
|
||||||
|
print_info "This might be okay if alarms were verified instead of rescheduled"
|
||||||
|
# Still pass if alarms are restored, even if rescheduled=0
|
||||||
|
TEST1_PASSED=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "❌ No alarms restored (count: ${ALARM_COUNT_RECOVERED})"
|
||||||
|
print_info "Recovery may have failed or alarms were not in database"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${TEST1_PASSED}" = true ]; then
|
||||||
|
print_success "TEST 1 PASSED: Recovery successfully rebuilt alarms from database!"
|
||||||
|
print_info "Summary:"
|
||||||
|
echo " - Before force-stop: ${ALARM_COUNT_BEFORE} alarm(s)"
|
||||||
|
echo " - After force-stop: ${ALARM_COUNT_AFTER} alarm(s) (cleared)"
|
||||||
|
echo " - After recovery: ${ALARM_COUNT_RECOVERED} alarm(s) (rebuilt)"
|
||||||
|
echo " - Rescheduled: ${RESCHEDULED_COUNT} alarm(s)"
|
||||||
|
echo " - Verified: ${VERIFIED_COUNT} alarm(s)"
|
||||||
|
if [ -n "${RECOVERY_SOURCE}" ]; then
|
||||||
|
echo " - Recovery source: ${RECOVERY_SOURCE}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "TEST 1 FAILED: Recovery did not rebuild alarms correctly"
|
||||||
|
print_info "Check logs above for recovery errors"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Step 7: Verify alarms actually fire (optional, controlled by VERIFY_FIRE flag)
|
||||||
|
# ============================================
|
||||||
|
if [ "${VERIFY_FIRE}" = "true" ] && [ -n "${ALARM_TRIGGER_MS}" ]; then
|
||||||
|
print_step "8" "Verifying alarm fires at scheduled time..."
|
||||||
|
|
||||||
|
# Get current time in milliseconds
|
||||||
|
CURRENT_TIME_SEC=$(get_current_time)
|
||||||
|
CURRENT_TIME_MS=$((CURRENT_TIME_SEC * 1000))
|
||||||
|
WAIT_MS=$((ALARM_TRIGGER_MS - CURRENT_TIME_MS))
|
||||||
|
|
||||||
|
if [ "${WAIT_MS}" -lt 0 ]; then
|
||||||
|
print_warn "Alarm time already passed (${WAIT_MS} ms ago); skipping fire verification"
|
||||||
|
else
|
||||||
|
WAIT_SEC=$((WAIT_MS / 1000))
|
||||||
|
|
||||||
|
# Clamp upper bound to prevent accidentally waiting 30+ minutes
|
||||||
|
if [ "${WAIT_SEC}" -gt 600 ]; then
|
||||||
|
print_warn "Alarm is >10 minutes away (${WAIT_SEC}s); skipping fire verification"
|
||||||
|
print_info "To test fire verification, schedule alarm closer to current time"
|
||||||
|
print_info "Or set a shorter test alarm interval in the app"
|
||||||
|
else
|
||||||
|
print_info "Alarm scheduled for: ${ALARM_READABLE}"
|
||||||
|
print_info "Current time: $(date -d "@${CURRENT_TIME_SEC}" 2>/dev/null || echo "${CURRENT_TIME_SEC}")"
|
||||||
|
print_info "Waiting ~${WAIT_SEC} seconds for alarm to fire..."
|
||||||
|
|
||||||
|
# Clear logs before waiting
|
||||||
|
clear_logs
|
||||||
|
|
||||||
|
# Wait for alarm time (with a small buffer)
|
||||||
|
sleep ${WAIT_SEC}
|
||||||
|
|
||||||
|
# Give alarm a moment to fire and log
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
print_info "Checking logs for fired alarm..."
|
||||||
|
ALARM_FIRED=$($ADB_BIN logcat -d | grep -E "DNP-RECEIVE|DNP-NOTIFY|DNP-WORK|Alarm fired|Notification displayed" | tail -10)
|
||||||
|
|
||||||
|
if [ -n "${ALARM_FIRED}" ]; then
|
||||||
|
print_success "✅ Alarm fired! Logs:"
|
||||||
|
echo "${ALARM_FIRED}"
|
||||||
|
else
|
||||||
|
print_warn "⚠️ No alarm fire logs found"
|
||||||
|
print_info "This might mean:"
|
||||||
|
echo " - Alarm fired but logs were cleared"
|
||||||
|
echo " - Alarm receiver didn't log"
|
||||||
|
echo " - Check notification tray manually"
|
||||||
|
print_info "Recent logs:"
|
||||||
|
$ADB_BIN logcat -d | grep -E "DNP|DailyNotification" | tail -10
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [ "${VERIFY_FIRE}" = "true" ]; then
|
||||||
|
print_info "Skipping fire verification (alarm trigger time not captured)"
|
||||||
|
else
|
||||||
|
print_info "Skipping fire verification (VERIFY_FIRE=false, set VERIFY_FIRE=true to enable)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
fi
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# TEST 2: Future Alarm Verification
|
# TEST 2: Future Alarm Verification
|
||||||
# ============================================
|
# ============================================
|
||||||
|
if should_run_test "2" SELECTED_TESTS; then
|
||||||
print_header "TEST 2: Future Alarm Verification"
|
print_header "TEST 2: Future Alarm Verification"
|
||||||
echo "Purpose: Verify future alarms are verified/rescheduled if missing."
|
echo "Purpose: Verify future alarms are verified/rescheduled if missing."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Note: The test app doesn't have a cancel button, so we'll test"
|
echo "Note: This test verifies that recovery correctly handles multiple alarms."
|
||||||
echo " verification of existing alarms instead."
|
echo " We'll schedule a second alarm to test recovery with multiple schedules."
|
||||||
echo ""
|
echo ""
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
|
||||||
@@ -398,18 +458,21 @@ main() {
|
|||||||
launch_app
|
launch_app
|
||||||
ensure_plugin_configured
|
ensure_plugin_configured
|
||||||
|
|
||||||
wait_for_ui_action "In the app UI, click 'Test Notification' to schedule another notification.
|
wait_for_ui_action "In the app UI, click 'Test Notification' to schedule a SECOND notification.
|
||||||
|
|
||||||
This creates a second scheduled notification for testing verification."
|
This creates an additional scheduled notification (you should now have 2 total).
|
||||||
|
This tests recovery behavior when multiple alarms exist in the database."
|
||||||
|
|
||||||
print_step "2" "Verifying alarms are scheduled..."
|
print_step "2" "Verifying alarms are scheduled..."
|
||||||
sleep 2
|
sleep 2
|
||||||
check_alarm_status
|
check_alarm_status
|
||||||
|
|
||||||
ALARM_COUNT=$(adb shell dumpsys alarm | grep -c "timesafari" || echo "0")
|
ALARM_COUNT=$(count_alarms)
|
||||||
print_info "Found ${ALARM_COUNT} scheduled alarm(s)"
|
print_info "Found ${ALARM_COUNT} scheduled alarm(s) (expected: 1)"
|
||||||
|
|
||||||
if [ "${ALARM_COUNT}" -gt "0" ]; then
|
if [ "${ALARM_COUNT}" -eq "1" ] 2>/dev/null; then
|
||||||
|
print_success "✅ Single alarm confirmed in AlarmManager"
|
||||||
|
elif [ "${ALARM_COUNT}" -gt "1" ] 2>/dev/null; then
|
||||||
print_success "Alarms are scheduled in AlarmManager"
|
print_success "Alarms are scheduled in AlarmManager"
|
||||||
else
|
else
|
||||||
print_error "No alarms found in AlarmManager"
|
print_error "No alarms found in AlarmManager"
|
||||||
@@ -432,21 +495,34 @@ main() {
|
|||||||
echo " DNP-REACTIVATION: Cold start recovery complete: ..., verified=1 or rescheduled=1, ..."
|
echo " DNP-REACTIVATION: Cold start recovery complete: ..., verified=1 or rescheduled=1, ..."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
RECOVERY_RESULT=$(adb logcat -d | grep "Cold start recovery complete" | tail -1)
|
RECOVERY_RESULT=$($ADB_BIN logcat -d | grep "Cold start recovery complete" | tail -1)
|
||||||
|
|
||||||
# Extract counts from recovery result
|
# Extract counts from recovery result
|
||||||
RESCHEDULED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
RESCHEDULED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||||||
VERIFIED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
VERIFIED_COUNT=$(echo "${RECOVERY_RESULT}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
|
||||||
|
|
||||||
if [ "${RESCHEDULED_COUNT}" -gt "0" ]; then
|
# Verify alarm count after recovery
|
||||||
print_success "TEST 2 PASSED: Missing future alarms were detected and rescheduled (rescheduled=${RESCHEDULED_COUNT})!"
|
ALARM_COUNT_AFTER_RECOVERY=$(count_alarms)
|
||||||
elif [ "${VERIFIED_COUNT}" -gt "0" ]; then
|
print_info "Alarm count after recovery: ${ALARM_COUNT_AFTER_RECOVERY} (expected: 1)"
|
||||||
print_success "TEST 2 PASSED: Future alarms verified in AlarmManager (verified=${VERIFIED_COUNT})!"
|
|
||||||
elif [ "${RESCHEDULED_COUNT}" -eq "0" ] && [ "${VERIFIED_COUNT}" -eq "0" ]; then
|
if [ "${RESCHEDULED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||||||
print_info "TEST 2: No verification/rescheduling needed"
|
print_success "✅ TEST 2 PASSED: Missing future alarm was detected and rescheduled (rescheduled=${RESCHEDULED_COUNT})!"
|
||||||
print_info "This is OK if:"
|
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||||||
echo " - All alarms were in the past (marked as missed)"
|
print_success "✅ Single alarm confirmed in AlarmManager after recovery"
|
||||||
echo " - All future alarms were already correctly scheduled"
|
fi
|
||||||
|
elif [ "${VERIFIED_COUNT}" -gt "0" ] 2>/dev/null; then
|
||||||
|
print_success "✅ TEST 2 PASSED: Future alarm verified in AlarmManager (verified=${VERIFIED_COUNT})!"
|
||||||
|
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||||||
|
print_success "✅ Single alarm confirmed in AlarmManager after recovery"
|
||||||
|
fi
|
||||||
|
elif [ "${RESCHEDULED_COUNT}" -eq "0" ] 2>/dev/null && [ "${VERIFIED_COUNT}" -eq "0" ] 2>/dev/null; then
|
||||||
|
print_warn "⚠️ TEST 2: No verification/rescheduling needed (both verified=0 and rescheduled=0)"
|
||||||
|
print_info "This might mean:"
|
||||||
|
echo " - Alarm was already properly scheduled and didn't need recovery"
|
||||||
|
echo " - Recovery didn't detect any issues"
|
||||||
|
if [ "${ALARM_COUNT_AFTER_RECOVERY}" -eq "1" ] 2>/dev/null; then
|
||||||
|
print_success "✅ Single alarm still present - recovery may have verified it silently"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
print_error "TEST 2 INCONCLUSIVE: Could not find recovery result"
|
print_error "TEST 2 INCONCLUSIVE: Could not find recovery result"
|
||||||
print_info "Recovery result: ${RECOVERY_RESULT}"
|
print_info "Recovery result: ${RECOVERY_RESULT}"
|
||||||
@@ -456,10 +532,12 @@ main() {
|
|||||||
check_alarm_status
|
check_alarm_status
|
||||||
|
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
fi
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# TEST 3: Recovery Timeout
|
# TEST 3: Recovery Timeout
|
||||||
# ============================================
|
# ============================================
|
||||||
|
if should_run_test "3" SELECTED_TESTS; then
|
||||||
print_header "TEST 3: Recovery Timeout"
|
print_header "TEST 3: Recovery Timeout"
|
||||||
echo "Purpose: Verify recovery times out gracefully."
|
echo "Purpose: Verify recovery times out gracefully."
|
||||||
echo ""
|
echo ""
|
||||||
@@ -469,7 +547,7 @@ main() {
|
|||||||
wait_for_user
|
wait_for_user
|
||||||
|
|
||||||
print_step "1" "Checking recovery timeout implementation..."
|
print_step "1" "Checking recovery timeout implementation..."
|
||||||
if grep -q "RECOVERY_TIMEOUT_SECONDS = 2L" "${PROJECT_ROOT}/android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt"; then
|
if grep -q "RECOVERY_TIMEOUT_SECONDS.*2L" "${PROJECT_ROOT}/android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt"; then
|
||||||
print_success "Timeout is set to 2 seconds"
|
print_success "Timeout is set to 2 seconds"
|
||||||
else
|
else
|
||||||
print_error "Timeout not found in code"
|
print_error "Timeout not found in code"
|
||||||
@@ -485,10 +563,12 @@ main() {
|
|||||||
print_info "Full test (100+ schedules) can be done manually if needed"
|
print_info "Full test (100+ schedules) can be done manually if needed"
|
||||||
|
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
fi
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# TEST 4: Invalid Data Handling
|
# TEST 4: Invalid Data Handling
|
||||||
# ============================================
|
# ============================================
|
||||||
|
if should_run_test "4" SELECTED_TESTS; then
|
||||||
print_header "TEST 4: Invalid Data Handling"
|
print_header "TEST 4: Invalid Data Handling"
|
||||||
echo "Purpose: Verify invalid data doesn't crash recovery."
|
echo "Purpose: Verify invalid data doesn't crash recovery."
|
||||||
echo ""
|
echo ""
|
||||||
@@ -497,7 +577,7 @@ main() {
|
|||||||
wait_for_user
|
wait_for_user
|
||||||
|
|
||||||
print_step "1" "Checking if app is debuggable..."
|
print_step "1" "Checking if app is debuggable..."
|
||||||
if adb shell dumpsys package "${PACKAGE}" | grep -q "debuggable=true"; then
|
if $ADB_BIN shell dumpsys package "${APP_ID}" | grep -q "debuggable=true"; then
|
||||||
print_success "App is debuggable - can access database"
|
print_success "App is debuggable - can access database"
|
||||||
|
|
||||||
print_info "Invalid data handling is tested automatically during recovery."
|
print_info "Invalid data handling is tested automatically during recovery."
|
||||||
@@ -507,7 +587,7 @@ main() {
|
|||||||
echo " - Database errors (logged, non-fatal)"
|
echo " - Database errors (logged, non-fatal)"
|
||||||
echo ""
|
echo ""
|
||||||
print_info "To manually test invalid data:"
|
print_info "To manually test invalid data:"
|
||||||
echo " 1. Use: adb shell run-as ${PACKAGE} sqlite3 databases/daily_notification_plugin.db"
|
echo " 1. Use: $ADB_BIN shell run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db"
|
||||||
echo " 2. Insert invalid notification: INSERT INTO notification_content (id, ...) VALUES ('', ...);"
|
echo " 2. Insert invalid notification: INSERT INTO notification_content (id, ...) VALUES ('', ...);"
|
||||||
echo " 3. Launch app and check logs for 'Skipping invalid notification'"
|
echo " 3. Launch app and check logs for 'Skipping invalid notification'"
|
||||||
else
|
else
|
||||||
@@ -518,6 +598,7 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
wait_for_user
|
wait_for_user
|
||||||
|
fi
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Summary
|
# Summary
|
||||||
@@ -543,7 +624,7 @@ main() {
|
|||||||
|
|
||||||
print_info "All recovery logs:"
|
print_info "All recovery logs:"
|
||||||
echo ""
|
echo ""
|
||||||
adb logcat -d | grep "DNP-REACTIVATION" | tail -20
|
$ADB_BIN logcat -d | grep "$REACTIVATION_TAG" | tail -20
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
print_success "Phase 1 testing script complete!"
|
print_success "Phase 1 testing script complete!"
|
||||||
|
|||||||
@@ -6,219 +6,19 @@ set -euo pipefail
|
|||||||
# Phase 2 Testing Script – Force Stop Recovery
|
# Phase 2 Testing Script – Force Stop Recovery
|
||||||
# ========================================
|
# ========================================
|
||||||
|
|
||||||
# --- Config -------------------------------------------------------------------
|
# Source shared library
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
APP_ID="com.timesafari.dailynotification"
|
source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||||||
APK_PATH="./app/build/outputs/apk/debug/app-debug.apk"
|
|
||||||
ADB_BIN="${ADB_BIN:-adb}"
|
|
||||||
|
|
||||||
|
# Phase 2 specific configuration
|
||||||
# Log tags / patterns (matched to actual ReactivationManager logs)
|
# Log tags / patterns (matched to actual ReactivationManager logs)
|
||||||
REACTIVATION_TAG="DNP-REACTIVATION"
|
|
||||||
SCENARIO_KEY="Detected scenario: "
|
|
||||||
FORCE_STOP_SCENARIO_VALUE="FORCE_STOP"
|
FORCE_STOP_SCENARIO_VALUE="FORCE_STOP"
|
||||||
COLD_START_SCENARIO_VALUE="COLD_START"
|
COLD_START_SCENARIO_VALUE="COLD_START"
|
||||||
NONE_SCENARIO_VALUE="NONE"
|
NONE_SCENARIO_VALUE="NONE"
|
||||||
BOOT_SCENARIO_VALUE="BOOT"
|
BOOT_SCENARIO_VALUE="BOOT"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# Allow selecting specific tests on the command line (e.g. ./test-phase2.sh 2 3)
|
||||||
# Helpers
|
SELECTED_TESTS=()
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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
|
# TEST 1 – Force Stop with Cleared Alarms
|
||||||
@@ -276,17 +76,15 @@ test1_force_stop_cleared_alarms() {
|
|||||||
logs="$(get_recovery_logs)"
|
logs="$(get_recovery_logs)"
|
||||||
echo "$logs"
|
echo "$logs"
|
||||||
|
|
||||||
local missed rescheduled verified errors scenario
|
local scenario rescheduled verified errors
|
||||||
missed="$(extract_field_from_logs "$logs" "missed")"
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
verified="$(extract_field_from_logs "$logs" "verified")"
|
verified="$(extract_field_from_logs "$logs" "verified")"
|
||||||
errors="$(extract_field_from_logs "$logs" "errors")"
|
errors="$(extract_field_from_logs "$logs" "errors")"
|
||||||
scenario="$(extract_scenario_from_logs "$logs")"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
info "Parsed recovery summary:"
|
info "Parsed recovery summary:"
|
||||||
echo " scenario = ${scenario:-<none>}"
|
echo " scenario = ${scenario:-<none>}"
|
||||||
echo " missed = ${missed}"
|
|
||||||
echo " rescheduled= ${rescheduled}"
|
echo " rescheduled= ${rescheduled}"
|
||||||
echo " verified = ${verified}"
|
echo " verified = ${verified}"
|
||||||
echo " errors = ${errors}"
|
echo " errors = ${errors}"
|
||||||
@@ -321,16 +119,13 @@ test1_force_stop_cleared_alarms() {
|
|||||||
test2_force_stop_intact_alarms() {
|
test2_force_stop_intact_alarms() {
|
||||||
section "TEST 2: Force Stop / Process Stop – Alarms Intact"
|
section "TEST 2: Force Stop / Process Stop – Alarms Intact"
|
||||||
|
|
||||||
echo "Purpose: Ensure we do NOT run heavy force-stop recovery when alarms are intact."
|
echo "Purpose: Verify that heavy FORCE_STOP recovery does not run when alarms are still present."
|
||||||
|
|
||||||
pause
|
pause
|
||||||
|
|
||||||
substep "Step 1: Launch app & ensure plugin configured"
|
substep "Step 1: Launch app & schedule notifications"
|
||||||
launch_app
|
launch_app
|
||||||
|
ui_prompt "In the app UI, ensure plugin is configured and schedule at least one future notification.\n\nPress Enter when done."
|
||||||
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"
|
substep "Step 2: Verify alarms are scheduled"
|
||||||
show_alarms
|
show_alarms
|
||||||
@@ -362,7 +157,7 @@ test2_force_stop_intact_alarms() {
|
|||||||
|
|
||||||
pause
|
pause
|
||||||
|
|
||||||
substep "Step 5: Launch app (triggers recovery) and capture logs"
|
substep "Step 5: Relaunch app and check recovery logs"
|
||||||
clear_logs
|
clear_logs
|
||||||
launch_app
|
launch_app
|
||||||
sleep 5
|
sleep 5
|
||||||
@@ -372,18 +167,18 @@ test2_force_stop_intact_alarms() {
|
|||||||
logs="$(get_recovery_logs)"
|
logs="$(get_recovery_logs)"
|
||||||
echo "$logs"
|
echo "$logs"
|
||||||
|
|
||||||
local missed rescheduled verified errors scenario
|
local scenario rescheduled missed verified errors
|
||||||
missed="$(extract_field_from_logs "$logs" "missed")"
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
|
missed="$(extract_field_from_logs "$logs" "missed")"
|
||||||
verified="$(extract_field_from_logs "$logs" "verified")"
|
verified="$(extract_field_from_logs "$logs" "verified")"
|
||||||
errors="$(extract_field_from_logs "$logs" "errors")"
|
errors="$(extract_field_from_logs "$logs" "errors")"
|
||||||
scenario="$(extract_scenario_from_logs "$logs")"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
info "Parsed recovery summary:"
|
info "Parsed recovery summary:"
|
||||||
echo " scenario = ${scenario:-<none>}"
|
echo " scenario = ${scenario:-<none>}"
|
||||||
echo " missed = ${missed}"
|
|
||||||
echo " rescheduled= ${rescheduled}"
|
echo " rescheduled= ${rescheduled}"
|
||||||
|
echo " missed = ${missed}"
|
||||||
echo " verified = ${verified}"
|
echo " verified = ${verified}"
|
||||||
echo " errors = ${errors}"
|
echo " errors = ${errors}"
|
||||||
echo
|
echo
|
||||||
@@ -392,12 +187,11 @@ test2_force_stop_intact_alarms() {
|
|||||||
error "Recovery reported errors>0 (errors=$errors)"
|
error "Recovery reported errors>0 (errors=$errors)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$scenario" != "$FORCE_STOP_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
if [[ "$after" -gt 0 && "$rescheduled" -eq 0 && "$scenario" != "$FORCE_STOP_SCENARIO_VALUE" ]]; then
|
||||||
ok "TEST 2 PASSED: No heavy force-stop recovery when alarms intact (scenario=$scenario, rescheduled=$rescheduled)."
|
ok "TEST 2 PASSED: Alarms remained intact, and FORCE_STOP scenario did not run (scenario=$scenario, rescheduled=0)."
|
||||||
elif [[ "$scenario" == "$FORCE_STOP_SCENARIO_VALUE" ]]; then
|
|
||||||
warn "TEST 2: scenario=FORCE_STOP detected but alarms were intact. Check scenario detection logic."
|
|
||||||
else
|
else
|
||||||
info "TEST 2: Some rescheduling occurred (rescheduled=$rescheduled). This might be OK if alarms were actually missing."
|
warn "TEST 2: Verify that FORCE_STOP recovery didn't misfire when alarms were intact."
|
||||||
|
info "Scenario=${scenario:-<none>}, rescheduled=$rescheduled, after_count=$after"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,43 +226,29 @@ test3_first_launch_no_schedules() {
|
|||||||
|
|
||||||
pause
|
pause
|
||||||
|
|
||||||
substep "Step 3: Launch app WITHOUT scheduling anything"
|
substep "Step 3: Launch app for the first time"
|
||||||
launch_app
|
launch_app
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
info "Collecting recovery logs..."
|
substep "Step 4: Collect logs and ensure no force-stop recovery ran"
|
||||||
local logs
|
local logs
|
||||||
logs="$($ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true)"
|
logs="$(get_recovery_logs)"
|
||||||
echo "$logs"
|
echo "$logs"
|
||||||
|
|
||||||
local scenario rescheduled missed
|
local scenario rescheduled
|
||||||
scenario="$(extract_scenario_from_logs "$logs")"
|
scenario="$(extract_scenario_from_logs "$logs")"
|
||||||
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
rescheduled="$(extract_field_from_logs "$logs" "rescheduled")"
|
||||||
missed="$(extract_field_from_logs "$logs" "missed")"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
info "Parsed recovery summary:"
|
info "Parsed summary:"
|
||||||
echo " scenario = ${scenario:-<none>}"
|
echo " scenario = ${scenario:-<none>}"
|
||||||
echo " rescheduled= ${rescheduled}"
|
echo " rescheduled= ${rescheduled}"
|
||||||
echo " missed = ${missed}"
|
|
||||||
echo
|
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
|
if [[ -z "$logs" ]]; then
|
||||||
ok "TEST 3 PASSED: No recovery logs when there are no schedules (safe behavior)."
|
ok "TEST 3 PASSED: No force-stop recovery logs on first launch."
|
||||||
return
|
elif [[ "$scenario" == "$NONE_SCENARIO_VALUE" && "$rescheduled" -eq 0 ]]; then
|
||||||
fi
|
ok "TEST 3 PASSED: NONE scenario logged with rescheduled=0 on first launch."
|
||||||
|
|
||||||
# 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
|
elif [[ "$rescheduled" -gt 0 ]]; then
|
||||||
warn "TEST 3: rescheduled>0 on first launch / empty DB. Check that force-stop recovery isn't misfiring."
|
warn "TEST 3: rescheduled>0 on first launch / empty DB. Check that force-stop recovery isn't misfiring."
|
||||||
else
|
else
|
||||||
@@ -481,12 +261,26 @@ test3_first_launch_no_schedules() {
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
# Allow selecting specific tests: e.g. `./test-phase2.sh 1 3`
|
||||||
|
if [[ "$#" -gt 0 && ( "$1" == "-h" || "$1" == "--help" ) ]]; then
|
||||||
|
echo "Usage: $0 [TEST_IDS...]"
|
||||||
|
echo
|
||||||
|
echo "If no TEST_IDS are given, all tests (1, 2, 3) will run."
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 # run all tests"
|
||||||
|
echo " $0 1 # run only TEST 1"
|
||||||
|
echo " $0 2 3 # run only TEST 2 and TEST 3"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
SELECTED_TESTS=("$@")
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo "Phase 2 Testing Script – Force Stop Recovery"
|
echo "Phase 2 Testing Script – Force Stop Recovery"
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo
|
echo
|
||||||
echo "This script will guide you through all Phase 2 tests."
|
echo "This script will guide you through Phase 2 tests."
|
||||||
echo "You'll be prompted when UI interaction is needed."
|
echo "You'll be prompted when UI interaction is needed."
|
||||||
echo
|
echo
|
||||||
|
|
||||||
@@ -496,13 +290,19 @@ main() {
|
|||||||
build_app
|
build_app
|
||||||
install_app
|
install_app
|
||||||
|
|
||||||
|
if should_run_test "1" SELECTED_TESTS; then
|
||||||
test1_force_stop_cleared_alarms
|
test1_force_stop_cleared_alarms
|
||||||
pause
|
pause
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_run_test "2" SELECTED_TESTS; then
|
||||||
test2_force_stop_intact_alarms
|
test2_force_stop_intact_alarms
|
||||||
pause
|
pause
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_run_test "3" SELECTED_TESTS; then
|
||||||
test3_first_launch_no_schedules
|
test3_first_launch_no_schedules
|
||||||
|
fi
|
||||||
|
|
||||||
section "Testing Complete"
|
section "Testing Complete"
|
||||||
|
|
||||||
@@ -512,10 +312,10 @@ main() {
|
|||||||
echo " - Check logs for scenario=$FORCE_STOP_SCENARIO_VALUE and rescheduled>0"
|
echo " - Check logs for scenario=$FORCE_STOP_SCENARIO_VALUE and rescheduled>0"
|
||||||
echo
|
echo
|
||||||
echo "TEST 2: Force Stop / Process Stop – Alarms Intact"
|
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 " - Verify FORCE_STOP scenario is not incorrectly triggered when alarms are still present"
|
||||||
echo
|
echo
|
||||||
echo "TEST 3: First Launch / No Schedules"
|
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 " - Confirm that no force-stop recovery runs, or that NONE/FIRST_LAUNCH scenario is logged with rescheduled=0"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
ok "Phase 2 testing script complete!"
|
ok "Phase 2 testing script complete!"
|
||||||
|
|||||||
@@ -6,202 +6,19 @@ set -euo pipefail
|
|||||||
# Phase 3 Testing Script – Boot Recovery
|
# Phase 3 Testing Script – Boot Recovery
|
||||||
# ========================================
|
# ========================================
|
||||||
|
|
||||||
# --- Config -------------------------------------------------------------------
|
# Source shared library
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
APP_ID="com.timesafari.dailynotification"
|
source "${SCRIPT_DIR}/alarm-test-lib.sh"
|
||||||
APK_PATH="./app/build/outputs/apk/debug/app-debug.apk"
|
|
||||||
ADB_BIN="${ADB_BIN:-adb}"
|
|
||||||
|
|
||||||
|
# Phase 3 specific configuration
|
||||||
# Log tags / patterns (matched to actual ReactivationManager logs)
|
# Log tags / patterns (matched to actual ReactivationManager logs)
|
||||||
REACTIVATION_TAG="DNP-REACTIVATION"
|
|
||||||
SCENARIO_KEY="Detected scenario: "
|
|
||||||
BOOT_SCENARIO_VALUE="BOOT"
|
BOOT_SCENARIO_VALUE="BOOT"
|
||||||
NONE_SCENARIO_VALUE="NONE"
|
NONE_SCENARIO_VALUE="NONE"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# Allow selecting specific tests on the command line (e.g. ./test-phase3.sh 1 3)
|
||||||
# Helpers
|
SELECTED_TESTS=()
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
reboot_emulator() {
|
|
||||||
info "Rebooting emulator..."
|
|
||||||
$ADB_BIN reboot
|
|
||||||
ok "Reboot initiated"
|
|
||||||
|
|
||||||
info "Waiting for emulator to come back online..."
|
|
||||||
$ADB_BIN wait-for-device
|
|
||||||
while [ "$($ADB_BIN shell getprop sys.boot_completed)" != "1" ]; do
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
ok "Emulator boot completed"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# Phase 3 specific: override extract_scenario_from_logs to handle boot recovery
|
||||||
extract_scenario_from_logs() {
|
extract_scenario_from_logs() {
|
||||||
local logs="$1"
|
local logs="$1"
|
||||||
local scen
|
local scen
|
||||||
@@ -209,6 +26,7 @@ extract_scenario_from_logs() {
|
|||||||
if echo "$logs" | grep -qi "Starting boot recovery\|boot recovery"; then
|
if echo "$logs" | grep -qi "Starting boot recovery\|boot recovery"; then
|
||||||
echo "$BOOT_SCENARIO_VALUE"
|
echo "$BOOT_SCENARIO_VALUE"
|
||||||
else
|
else
|
||||||
|
# Use shared library function as fallback
|
||||||
scen="$(grep -oE "${SCENARIO_KEY}[A-Z_]+" <<<"$logs" | tail -n1 | sed "s/${SCENARIO_KEY}//" || true)"
|
scen="$(grep -oE "${SCENARIO_KEY}[A-Z_]+" <<<"$logs" | tail -n1 | sed "s/${SCENARIO_KEY}//" || true)"
|
||||||
echo "$scen"
|
echo "$scen"
|
||||||
fi
|
fi
|
||||||
@@ -407,7 +225,7 @@ test3_boot_no_schedules() {
|
|||||||
info "Collecting recovery logs from boot..."
|
info "Collecting recovery logs from boot..."
|
||||||
sleep 2
|
sleep 2
|
||||||
local logs
|
local logs
|
||||||
logs="$($ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true)"
|
logs="$(get_recovery_logs)"
|
||||||
echo "$logs"
|
echo "$logs"
|
||||||
|
|
||||||
local scenario rescheduled missed
|
local scenario rescheduled missed
|
||||||
@@ -519,12 +337,27 @@ test4_silent_boot_recovery() {
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
# Allow selecting specific tests: e.g. `./test-phase3.sh 1 3`
|
||||||
|
if [[ "$#" -gt 0 && ( "$1" == "-h" || "$1" == "--help" ) ]]; then
|
||||||
|
echo "Usage: $0 [TEST_IDS...]"
|
||||||
|
echo
|
||||||
|
echo "If no TEST_IDS are given, all tests (1, 2, 3, 4) will run."
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 # run all tests"
|
||||||
|
echo " $0 1 # run only TEST 1"
|
||||||
|
echo " $0 2 3 # run only TEST 2 and TEST 3"
|
||||||
|
echo " $0 4 # run only TEST 4 (silent boot recovery)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
SELECTED_TESTS=("$@")
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo "Phase 3 Testing Script – Boot Recovery"
|
echo "Phase 3 Testing Script – Boot Recovery"
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo
|
echo
|
||||||
echo "This script will guide you through all Phase 3 tests."
|
echo "This script will guide you through Phase 3 tests."
|
||||||
echo "You'll be prompted when UI interaction is needed."
|
echo "You'll be prompted when UI interaction is needed."
|
||||||
echo
|
echo
|
||||||
echo "⚠️ WARNING: This script will reboot the emulator multiple times."
|
echo "⚠️ WARNING: This script will reboot the emulator multiple times."
|
||||||
@@ -537,16 +370,24 @@ main() {
|
|||||||
build_app
|
build_app
|
||||||
install_app
|
install_app
|
||||||
|
|
||||||
|
if should_run_test "1" SELECTED_TESTS; then
|
||||||
test1_boot_future_alarms
|
test1_boot_future_alarms
|
||||||
pause
|
pause
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_run_test "2" SELECTED_TESTS; then
|
||||||
test2_boot_past_alarms
|
test2_boot_past_alarms
|
||||||
pause
|
pause
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_run_test "3" SELECTED_TESTS; then
|
||||||
test3_boot_no_schedules
|
test3_boot_no_schedules
|
||||||
pause
|
pause
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_run_test "4" SELECTED_TESTS; then
|
||||||
test4_silent_boot_recovery
|
test4_silent_boot_recovery
|
||||||
|
fi
|
||||||
|
|
||||||
section "Testing Complete"
|
section "Testing Complete"
|
||||||
|
|
||||||
@@ -575,4 +416,3 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user