#!/bin/bash # run_ui_tests.sh — Three-phase UI test runner with parallel middle phase # # Usage: # ./run_ui_tests.sh # Default: iPhone 17 Pro, 4 workers # ./run_ui_tests.sh "iPhone Air" 3 # Custom device and worker count # ./run_ui_tests.sh --skip-seed # Skip seeding (already done) # ./run_ui_tests.sh --skip-cleanup # Skip cleanup at end # ./run_ui_tests.sh --only-parallel # Only run parallel phase set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT="$SCRIPT_DIR/honeyDue.xcodeproj" SCHEME="HoneyDueUITests" DESTINATION="platform=iOS Simulator,name=iPhone 17 Pro" # 2 workers avoids simulator contention that caused intermittent XCUITest # typing / UI-update races (Suite5/7/8 flakes under 4-worker load). Phase 2b # isolates Suite6 further. WORKERS=2 SKIP_SEED=false SKIP_CLEANUP=false ONLY_PARALLEL=false # Parse flags (positional args for device/workers, flags for skip options) POSITIONAL_ARGS=() for arg in "$@"; do case $arg in --skip-seed) SKIP_SEED=true ;; --skip-cleanup) SKIP_CLEANUP=true ;; --only-parallel) ONLY_PARALLEL=true; SKIP_SEED=true; SKIP_CLEANUP=true ;; *) POSITIONAL_ARGS+=("$arg") ;; esac done if [ ${#POSITIONAL_ARGS[@]} -ge 1 ]; then DESTINATION="platform=iOS Simulator,name=${POSITIONAL_ARGS[0]}" fi if [ ${#POSITIONAL_ARGS[@]} -ge 2 ]; then WORKERS="${POSITIONAL_ARGS[1]}" fi RESULTS_DIR="$SCRIPT_DIR/build/test-results" DERIVED_DATA="$SCRIPT_DIR/build/DerivedData" mkdir -p "$RESULTS_DIR" "$DERIVED_DATA" BOLD='\033[1m' GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m' RESET='\033[0m' phase_header() { echo "" echo -e "${BOLD}════════════════════════════════════════════════════${RESET}" echo -e "${BOLD} $1${RESET}" echo -e "${BOLD}════════════════════════════════════════════════════${RESET}" echo "" } # Seed tests — must run first, sequentially SEED_TESTS=( "-only-testing:HoneyDueUITests/AAA_SeedTests" ) # All parallelizable test classes PARALLEL_TESTS=( "-only-testing:HoneyDueUITests/AuthCriticalPathTests" "-only-testing:HoneyDueUITests/NavigationCriticalPathTests" "-only-testing:HoneyDueUITests/SmokeTests" "-only-testing:HoneyDueUITests/SimpleLoginTest" "-only-testing:HoneyDueUITests/Suite0_OnboardingRebuildTests" "-only-testing:HoneyDueUITests/Suite1_RegistrationTests" "-only-testing:HoneyDueUITests/Suite2_AuthenticationRebuildTests" "-only-testing:HoneyDueUITests/Suite3_ResidenceRebuildTests" "-only-testing:HoneyDueUITests/Suite4_ComprehensiveResidenceTests" "-only-testing:HoneyDueUITests/Suite5_TaskTests" "-only-testing:HoneyDueUITests/Suite7_ContractorTests" "-only-testing:HoneyDueUITests/Suite8_DocumentWarrantyTests" "-only-testing:HoneyDueUITests/Suite9_IntegrationE2ETests" "-only-testing:HoneyDueUITests/Suite10_ComprehensiveE2ETests" ) # Suite6 runs in a smaller-parallel phase of its own. Under 4-worker contention # with 14 other classes, SwiftUI's TextField binding intermittently lags behind # XCUITest typing, leaving the Add-Task form un-submittable. Isolating Suite6 # to 2 workers gives the binding enough time to flush reliably. SUITE6_TESTS=( "-only-testing:HoneyDueUITests/Suite6_ComprehensiveTaskTests" ) # Cleanup tests — must run last, sequentially CLEANUP_TESTS=( "-only-testing:HoneyDueUITests/SuiteZZ_CleanupTests" ) run_phase() { local phase_name="$1" local result_path="$RESULTS_DIR/${phase_name}.xcresult" shift local extra_args=("$@") rm -rf "$result_path" xcodebuild test \ -project "$PROJECT" \ -scheme "$SCHEME" \ -destination "$DESTINATION" \ -derivedDataPath "$DERIVED_DATA" \ -resultBundlePath "$result_path" \ "${extra_args[@]}" \ 2>&1 | tail -30 return ${PIPESTATUS[0]} } OVERALL_START=$(date +%s) # ── Phase 1: Seed ────────────────────────────────────────────── if [ "$SKIP_SEED" = false ]; then phase_header "Phase 1/3: Seeding test data (sequential)" SEED_START=$(date +%s) if run_phase "SeedTests" "${SEED_TESTS[@]}"; then SEED_END=$(date +%s) echo -e "\n${GREEN}✓ Seed phase passed ($(( SEED_END - SEED_START ))s)${RESET}" else SEED_END=$(date +%s) echo -e "\n${RED}✗ Seed phase FAILED ($(( SEED_END - SEED_START ))s)${RESET}" echo -e "${RED} Cannot proceed without seeded data. Aborting.${RESET}" exit 1 fi fi # ── Phase 2: Parallel Tests ─────────────────────────────────── phase_header "Phase 2/3: Running tests in parallel ($WORKERS workers)" PARALLEL_START=$(date +%s) if run_phase "ParallelTests" \ -parallel-testing-enabled YES \ -parallel-testing-worker-count "$WORKERS" \ "${PARALLEL_TESTS[@]}"; then PARALLEL_END=$(date +%s) echo -e "\n${GREEN}✓ Parallel phase passed ($(( PARALLEL_END - PARALLEL_START ))s)${RESET}" PARALLEL_PASSED=true else PARALLEL_END=$(date +%s) echo -e "\n${RED}✗ Parallel phase FAILED ($(( PARALLEL_END - PARALLEL_START ))s)${RESET}" PARALLEL_PASSED=false fi # ── Phase 2b: Suite6 (isolated parallel) ────────────────────── phase_header "Phase 2b: Suite6 task tests (2 workers, isolated)" SUITE6_START=$(date +%s) if run_phase "Suite6Tests" \ -parallel-testing-enabled YES \ -parallel-testing-worker-count 2 \ "${SUITE6_TESTS[@]}"; then SUITE6_END=$(date +%s) echo -e "\n${GREEN}✓ Suite6 phase passed ($(( SUITE6_END - SUITE6_START ))s)${RESET}" SUITE6_PASSED=true else SUITE6_END=$(date +%s) echo -e "\n${RED}✗ Suite6 phase FAILED ($(( SUITE6_END - SUITE6_START ))s)${RESET}" SUITE6_PASSED=false fi # ── Phase 3: Cleanup ────────────────────────────────────────── if [ "$SKIP_CLEANUP" = false ]; then phase_header "Phase 3/3: Cleaning up test data (sequential)" CLEANUP_START=$(date +%s) if run_phase "CleanupTests" "${CLEANUP_TESTS[@]}"; then CLEANUP_END=$(date +%s) echo -e "\n${GREEN}✓ Cleanup phase passed ($(( CLEANUP_END - CLEANUP_START ))s)${RESET}" else CLEANUP_END=$(date +%s) echo -e "\n${YELLOW}⚠ Cleanup phase failed ($(( CLEANUP_END - CLEANUP_START ))s) — non-blocking${RESET}" fi fi # ── Summary ─────────────────────────────────────────────────── OVERALL_END=$(date +%s) TOTAL_TIME=$(( OVERALL_END - OVERALL_START )) phase_header "Summary" echo " Total time: ${TOTAL_TIME}s" echo " Workers: $WORKERS" echo " Results: $RESULTS_DIR/" echo "" if [ "$PARALLEL_PASSED" = true ] && [ "${SUITE6_PASSED:-true}" = true ]; then echo -e " ${GREEN}${BOLD}ALL TESTS PASSED${RESET}" exit 0 else echo -e " ${RED}${BOLD}TESTS FAILED${RESET}" echo -e " Check results: open $RESULTS_DIR/" exit 1 fi