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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user