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:
Matthew Raymer
2025-11-28 08:53:42 +00:00
parent 73301f7d1d
commit 07ace32982
4 changed files with 813 additions and 728 deletions

View 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
}