Files
daily-notification-plugin/test-apps/android-test-app/alarm-test-lib.sh
Matthew Raymer 07ace32982 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
2025-11-28 08:53:42 +00:00

365 lines
8.4 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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
}