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
365 lines
8.4 KiB
Bash
365 lines
8.4 KiB
Bash
#!/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
|
||
}
|
||
|