Files
honeyDueKMP/iosApp/run_ui_tests.sh
T
Trey T 091248f30f Fix per-test isolation flakiness: relaunch instead of UI logout; 4 workers
The first full 8-worker run surfaced 52 failures, 28 of them "Failed to log out"
(UITestHelpers:86) — forcing a profile-navigation logout between every test (each
test = new account) is fragile, and 8 parallel simulator clones thrashed the
machine (the remaining failures were UI timeouts under that load).

- AuthenticatedUITestCase: relaunchBetweenTests = true. A fresh app launch with
  --reset-state lands on the login screen, so each test logs in as its own account
  with NO UI logout between tests. Removed the ensureLoggedOut call.
- run_ui_tests.sh: default workers 8 -> 4 (reliable on a Mac mini; each test now
  relaunches + creates an account, so the bottleneck is CPU/simulator).

Verified: ContractorUITests (was ~15 logout failures) now passes at 4 workers,
0 leaked accounts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 17:42:38 -05:00

160 lines
7.3 KiB
Bash
Executable File

#!/bin/bash
# run_ui_tests.sh — Phased test runner for the HoneyDue iOS suites.
#
# Architecture: every UI test mints its OWN isolated Kratos account (see
# Core/Fixtures/TestAccount.swift + AuthenticatedUITestCase), so suites are
# fully independent and the parallel phase scales to many workers with no
# cross-suite data races. Pure-API tests live in a separate standalone target
# (HoneyDueAPITests) that runs in seconds without launching the app.
#
# Phases:
# 0. Smoke gate — fast launch/login sanity. Abort the run if it fails.
# 1. Seed — ensure baseline accounts exist (AAA_SeedTests).
# 1b. API — standalone HoneyDueAPITests (no app launch; ~seconds).
# 2. Parallel — the WHOLE UI target minus the four phase-managed suites,
# via -skip-testing. New suites are auto-included (no hand-
# maintained list to drift), run at $WORKERS workers.
# 3. Sweep — clear-all-data + delete leaked uit_* Kratos identities
# (SuiteZZ_CleanupTests). Non-blocking.
#
# Usage:
# ./run_ui_tests.sh # iPhone 17 Pro, 8 workers
# ./run_ui_tests.sh "iPhone Air" 6 # custom device + worker count
# ./run_ui_tests.sh --skip-seed # skip phase 1
# ./run_ui_tests.sh --skip-cleanup # skip phase 3
# ./run_ui_tests.sh --only-parallel # only phase 2
# ./run_ui_tests.sh --smoke # only phase 0
# ./run_ui_tests.sh --only-api # only phase 1b
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT="$SCRIPT_DIR/honeyDue.xcodeproj"
SCHEME="HoneyDueUITests"
API_SCHEME="HoneyDueAPITests"
TARGET="HoneyDueUITests"
DESTINATION="platform=iOS Simulator,name=iPhone 17 Pro"
# Data isolation removed cross-suite races, but each test now relaunches the
# app + creates an account, so the bottleneck is CPU/simulator. 4 is the reliable
# sweet spot on a Mac mini (8 thrashed the simulators -> UI timeouts). Override via arg 2.
WORKERS=4
# Suites that run in their own phases — excluded from the parallel phase.
PHASE_MANAGED=(
"$TARGET/AAA_SeedTests"
"$TARGET/SuiteZZ_CleanupTests"
"$TARGET/SmokeUITests"
"$TARGET/AppLaunchUITests"
)
SKIP_SEED=false; SKIP_CLEANUP=false; ONLY_PARALLEL=false; ONLY_SMOKE=false; ONLY_API=false
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 ;;
--smoke) ONLY_SMOKE=true ;;
--only-api) ONLY_API=true ;;
*) POSITIONAL_ARGS+=("$arg") ;;
esac
done
[ ${#POSITIONAL_ARGS[@]} -ge 1 ] && DESTINATION="platform=iOS Simulator,name=${POSITIONAL_ARGS[0]}"
[ ${#POSITIONAL_ARGS[@]} -ge 2 ] && WORKERS="${POSITIONAL_ARGS[1]}"
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 ""; }
# run_xcodebuild <result-name> <scheme> [extra xcodebuild args...]
run_xcodebuild() {
local result_path="$RESULTS_DIR/$1.xcresult"; local scheme="$2"; shift 2
rm -rf "$result_path"
xcodebuild test \
-project "$PROJECT" -scheme "$scheme" -destination "$DESTINATION" \
-derivedDataPath "$DERIVED_DATA" -resultBundlePath "$result_path" \
"$@" 2>&1 | tail -40
return ${PIPESTATUS[0]}
}
OVERALL_START=$(date +%s)
# ── Phase 1b only ──────────────────────────────────────────────
if [ "$ONLY_API" = true ]; then
phase_header "API tests (standalone)"
run_xcodebuild "API" "$API_SCHEME" && exit 0 || exit 1
fi
# ── Phase 0: Smoke gate ────────────────────────────────────────
if [ "$ONLY_PARALLEL" = false ]; then
phase_header "Phase 0: Smoke gate"
if run_xcodebuild "Smoke" "$SCHEME" \
-only-testing:"$TARGET/SmokeUITests" -only-testing:"$TARGET/AppLaunchUITests"; then
echo -e "${GREEN}✓ Smoke passed${RESET}"
else
echo -e "${RED}✗ Smoke FAILED — aborting (app can't launch/log in).${RESET}"; exit 1
fi
[ "$ONLY_SMOKE" = true ] && exit 0
fi
# ── Phase 1: Seed ──────────────────────────────────────────────
if [ "$SKIP_SEED" = false ]; then
phase_header "Phase 1: Seed baseline accounts"
if run_xcodebuild "Seed" "$SCHEME" -only-testing:"$TARGET/AAA_SeedTests"; then
echo -e "${GREEN}✓ Seed passed${RESET}"
else
echo -e "${RED}✗ Seed FAILED — aborting.${RESET}"; exit 1
fi
fi
# ── Phase 1b: API contract tests (fast, standalone) ────────────
API_PASSED=true
if [ "$ONLY_PARALLEL" = false ]; then
phase_header "Phase 1b: API tests (standalone bundle, no app launch)"
if run_xcodebuild "API" "$API_SCHEME"; then
echo -e "${GREEN}✓ API tests passed${RESET}"
else
API_PASSED=false; echo -e "${RED}✗ API tests FAILED${RESET}"
fi
fi
# ── Phase 2: Parallel (whole UI target minus phase-managed) ────
phase_header "Phase 2: Parallel UI suite ($WORKERS workers)"
SKIP_ARGS=()
for t in "${PHASE_MANAGED[@]}"; do SKIP_ARGS+=( -skip-testing:"$t" ); done
PARALLEL_START=$(date +%s)
if run_xcodebuild "Parallel" "$SCHEME" \
-only-testing:"$TARGET" "${SKIP_ARGS[@]}" \
-parallel-testing-enabled YES -parallel-testing-worker-count "$WORKERS"; then
PARALLEL_PASSED=true; echo -e "${GREEN}✓ Parallel phase passed${RESET}"
else
PARALLEL_PASSED=false; echo -e "${RED}✗ Parallel phase FAILED${RESET}"
fi
PARALLEL_END=$(date +%s)
# ── Phase 3: Sweep ─────────────────────────────────────────────
if [ "$SKIP_CLEANUP" = false ]; then
phase_header "Phase 3: Sweep leaked accounts + data"
if run_xcodebuild "Sweep" "$SCHEME" -only-testing:"$TARGET/SuiteZZ_CleanupTests"; then
echo -e "${GREEN}✓ Sweep passed${RESET}"
else
echo -e "${YELLOW}⚠ Sweep failed (non-blocking)${RESET}"
fi
fi
# ── Summary ────────────────────────────────────────────────────
phase_header "Summary"
echo " Total time: $(( $(date +%s) - OVERALL_START ))s"
echo " Parallel: $(( PARALLEL_END - PARALLEL_START ))s @ $WORKERS workers"
echo " API tests: $([ "$API_PASSED" = true ] && echo passed || echo FAILED)"
echo " Results: $RESULTS_DIR/"
echo ""
if [ "${PARALLEL_PASSED:-false}" = true ] && [ "$API_PASSED" = true ]; then
echo -e " ${GREEN}${BOLD}ALL TESTS PASSED${RESET}"; exit 0
else
echo -e " ${RED}${BOLD}TESTS FAILED${RESET} — open $RESULTS_DIR/"; exit 1
fi