240 lines
7.9 KiB
Bash
Executable File
240 lines
7.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# Run Robo Tests on Firebase Test Lab
|
|
# =============================================================================
|
|
# This script runs Robo exploratory tests on Firebase Test Lab across the
|
|
# configured device matrix. Robo tests automatically crawl the app UI to
|
|
# find crashes and ANRs without requiring instrumented test code.
|
|
#
|
|
# Prerequisites:
|
|
# 1. gcloud CLI installed and authenticated (gcloud auth login)
|
|
# 2. Firebase project created and Blaze plan enabled
|
|
# 3. Google Play Console linked to Firebase project (optional, for deep links)
|
|
# 4. Service account with Firebase Test Lab admin role
|
|
#
|
|
# Usage:
|
|
# ./run_robo_tests.sh [options]
|
|
#
|
|
# Options:
|
|
# --project-id Firebase project ID (default: kordant-android)
|
|
# --app-aab Path to app AAB (default: auto-detected)
|
|
# --app-apk Path to app APK (default: auto-detected)
|
|
# --robo-script Path to robo script JSON (default: robo_script.json)
|
|
# --timeout Max robo crawl time in seconds (default: 600)
|
|
# --dry-run Print gcloud command without executing
|
|
# --help Show this help message
|
|
#
|
|
# Reference: https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
# Default values
|
|
PROJECT_ID="${FIREBASE_PROJECT_ID:-kordant-android}"
|
|
APP_PATH=""
|
|
ROBO_SCRIPT="${SCRIPT_DIR}/robo_script.json"
|
|
MAX_CRAWL_TIME=600
|
|
DRY_RUN=false
|
|
|
|
# Device matrix from test_matrix_config.yaml (generated via gcloud --device flags)
|
|
# Each device runs with each orientation and locale combination
|
|
declare -a DEVICE_ARGS=(
|
|
# Pixel 6 - Primary target device (API 33)
|
|
"--device model=Pixel6,version=33,locale=en_US,orientation=portrait"
|
|
"--device model=Pixel6,version=33,locale=en_US,orientation=landscape"
|
|
"--device model=Pixel6,version=33,locale=es_ES,orientation=portrait"
|
|
"--device model=Pixel6,version=33,locale=es_ES,orientation=landscape"
|
|
|
|
# Pixel 4 - Older Pixel device (API 30)
|
|
"--device model=Pixel4,version=30,locale=en_US,orientation=portrait"
|
|
"--device model=Pixel4,version=30,locale=en_US,orientation=landscape"
|
|
"--device model=Pixel4,version=30,locale=es_ES,orientation=portrait"
|
|
"--device model=Pixel4,version=30,locale=es_ES,orientation=landscape"
|
|
|
|
# Samsung Galaxy S21 - Popular Samsung device (API 31)
|
|
"--device model=GalaxyS21,version=31,locale=en_US,orientation=portrait"
|
|
"--device model=GalaxyS21,version=31,locale=en_US,orientation=landscape"
|
|
"--device model=GalaxyS21,version=31,locale=es_ES,orientation=portrait"
|
|
"--device model=GalaxyS21,version=31,locale=es_ES,orientation=landscape"
|
|
|
|
# Xiaomi Redmi Note 8 - Budget device (API 29)
|
|
"--device model=RedmiNote8,version=29,locale=en_US,orientation=portrait"
|
|
"--device model=RedmiNote8,version=29,locale=en_US,orientation=landscape"
|
|
"--device model=RedmiNote8,version=29,locale=es_ES,orientation=portrait"
|
|
"--device model=RedmiNote8,version=29,locale=es_ES,orientation=landscape"
|
|
|
|
# Low-end device - Minimum spec target (API 28, 2GB RAM equivalent)
|
|
"--device model=AquestM2,version=28,locale=en_US,orientation=portrait"
|
|
"--device model=AquestM2,version=28,locale=en_US,orientation=landscape"
|
|
"--device model=AquestM2,version=28,locale=es_ES,orientation=portrait"
|
|
"--device model=AquestM2,version=28,locale=es_ES,orientation=landscape"
|
|
)
|
|
|
|
# ============================================================
|
|
# Helper: Print usage
|
|
# ============================================================
|
|
usage() {
|
|
grep "^#" "$0" | grep -v "^#!/" | sed 's/^# //'
|
|
exit 0
|
|
}
|
|
|
|
# ============================================================
|
|
# Parse arguments
|
|
# ============================================================
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--project-id)
|
|
PROJECT_ID="$2"
|
|
shift 2
|
|
;;
|
|
--app-aab)
|
|
APP_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--app-apk)
|
|
APP_PATH="$2"
|
|
shift 2
|
|
;;
|
|
--robo-script)
|
|
ROBO_SCRIPT="$2"
|
|
shift 2
|
|
;;
|
|
--timeout)
|
|
MAX_CRAWL_TIME="$2"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--help)
|
|
usage
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
echo "Usage: $0 --help"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ============================================================
|
|
# Auto-detect APK/AAB path if not provided
|
|
# ============================================================
|
|
if [ -z "$APP_PATH" ]; then
|
|
# Prefer AAB for more accurate Play Store representation
|
|
APP_PATH=$(find "$PROJECT_DIR/android/app/build/outputs/bundle" -name "*-release.aab" 2>/dev/null | head -1)
|
|
|
|
# Fall back to APK if AAB not found
|
|
if [ -z "$APP_PATH" ]; then
|
|
APP_PATH=$(find "$PROJECT_DIR/android/app/build/outputs/apk" -name "*-release.apk" 2>/dev/null | head -1)
|
|
fi
|
|
|
|
# Last resort: any APK
|
|
if [ -z "$APP_PATH" ]; then
|
|
APP_PATH=$(find "$PROJECT_DIR/android/app/build/outputs/apk" -name "*.apk" 2>/dev/null | head -1)
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$APP_PATH" ]; then
|
|
echo "Error: Could not find app APK or AAB."
|
|
echo ""
|
|
echo "Build the app first:"
|
|
echo " ./gradlew :app:assembleProdRelease"
|
|
echo " # or for AAB:"
|
|
echo " ./gradlew :app:bundleProdRelease"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$ROBO_SCRIPT" ]; then
|
|
echo "Warning: Robo script not found at $ROBO_SCRIPT"
|
|
echo "Robo will run without guided script (fully autonomous crawl)."
|
|
ROBO_SCRIPT=""
|
|
fi
|
|
|
|
# ============================================================
|
|
# Determine type flag based on file extension
|
|
# ============================================================
|
|
if [[ "$APP_PATH" == *.aab ]]; then
|
|
TYPE_FLAG="--type robo"
|
|
APP_FLAG="--app-package com.kordant.android"
|
|
AAB_FLAG="--aab \"$APP_PATH\""
|
|
APK_FLAG=""
|
|
else
|
|
TYPE_FLAG="--type robo"
|
|
APP_FLAG=""
|
|
AAB_FLAG=""
|
|
APK_FLAG="--app \"$APP_PATH\""
|
|
fi
|
|
|
|
# ============================================================
|
|
# Print configuration
|
|
# ============================================================
|
|
echo "=========================================="
|
|
echo "Firebase Test Lab - Robo Tests"
|
|
echo "=========================================="
|
|
echo "Project ID: $PROJECT_ID"
|
|
echo "App: $APP_PATH"
|
|
echo "Robo Script: ${ROBO_SCRIPT:-none (fully autonomous)}"
|
|
echo "Max Crawl Time: ${MAX_CRAWL_TIME}s"
|
|
echo "Devices: ${#DEVICE_ARGS[@]} configurations"
|
|
echo ""
|
|
|
|
# ============================================================
|
|
# Build gcloud command
|
|
# ============================================================
|
|
GCLOUD_CMD="gcloud firebase test android run \
|
|
$TYPE_FLAG \
|
|
--project \"$PROJECT_ID\" \
|
|
$APP_FLAG \
|
|
$AAB_FLAG \
|
|
$APK_FLAG \
|
|
--timeout 60m \
|
|
--max-crawl-time $MAX_CRAWL_TIME \
|
|
--record-video \
|
|
--performance-metrics \
|
|
--results-history-name \"Kordant Android Robo Tests\""
|
|
|
|
# Add robo script if available
|
|
if [ -n "$ROBO_SCRIPT" ]; then
|
|
GCLOUD_CMD="$GCLOUD_CMD --robo-script \"$ROBO_SCRIPT\""
|
|
fi
|
|
|
|
# Add device configurations
|
|
for device in "${DEVICE_ARGS[@]}"; do
|
|
GCLOUD_CMD="$GCLOUD_CMD $device"
|
|
done
|
|
|
|
echo "Command:"
|
|
echo "$GCLOUD_CMD"
|
|
echo ""
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "DRY RUN - Command not executed."
|
|
exit 0
|
|
fi
|
|
|
|
# ============================================================
|
|
# Execute
|
|
# ============================================================
|
|
echo "Starting Robo tests..."
|
|
echo ""
|
|
eval "$GCLOUD_CMD"
|
|
|
|
EXIT_CODE=$?
|
|
if [ $EXIT_CODE -eq 0 ]; then
|
|
echo ""
|
|
echo "✅ Robo tests completed successfully!"
|
|
echo "View results in Firebase Console: https://console.firebase.google.com/project/$PROJECT_ID/testlab"
|
|
echo "Review crawl maps, screenshots, and videos for each device."
|
|
else
|
|
echo ""
|
|
echo "❌ Robo tests failed with exit code $EXIT_CODE"
|
|
echo "View results in Firebase Console: https://console.firebase.google.com/project/$PROJECT_ID/testlab"
|
|
fi
|
|
|
|
exit $EXIT_CODE
|