Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/e2e_tests_fdc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -218,21 +218,25 @@ jobs:
- uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5
id: simulator
with:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators
model: "iPhone 16"
- name: Ensure Simulator Ready
env:
SIMULATOR: ${{ steps.simulator.outputs.udid }}
ENSURE_BOOT_IF_NEEDED: "0"
run: .github/workflows/scripts/ensure-simulator-ready.sh
- name: 'E2E Tests'
working-directory: 'packages/firebase_data_connect/firebase_data_connect/example'
env:
SIMULATOR: ${{ steps.simulator.outputs.udid }}
run: |
# Uncomment following line to have simulator logs printed out for debugging purposes.
# xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' &
# The iOS simulator sometimes fails to connect the VM Service. Keep a
# limit around the full test command and retry once with a simulator reboot.
# Retry once after VM Service / simulator flake (reboot + migration-aware wait).
perl -e 'alarm 900; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x || {
echo "First attempt failed or timed out. Rebooting simulator and retrying..."
xcrun simctl shutdown "$SIMULATOR" || true
xcrun simctl boot "$SIMULATOR"
xcrun simctl bootstatus "$SIMULATOR" -b
"${GITHUB_WORKSPACE}/.github/workflows/scripts/ensure-simulator-ready.sh"
flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x
}
- name: Save Firestore Emulator Cache
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/e2e_tests_pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,17 @@ jobs:
- uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5
id: simulator
with:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators
model: "iPhone 16"
- name: Build iOS (simulator)
working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example
run: |
flutter build ios --no-codesign --simulator --debug --target=./integration_test/pipeline/pipeline_live_test.dart --dart-define=CI=true
- name: Ensure Simulator Ready
env:
SIMULATOR: ${{ steps.simulator.outputs.udid }}
ENSURE_BOOT_IF_NEEDED: "0"
run: .github/workflows/scripts/ensure-simulator-ready.sh
- name: Run pipeline E2E tests (iOS)
working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example
env:
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/ios.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,13 @@ jobs:
- uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5
id: simulator
with:
# List of available simulators: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md#installed-simulators
# List of available simulators: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators
model: "iPhone 16"
- name: Ensure Simulator Ready
env:
SIMULATOR: ${{ steps.simulator.outputs.udid }}
ENSURE_BOOT_IF_NEEDED: "0"
run: .github/workflows/scripts/ensure-simulator-ready.sh
- name: 'E2E Tests'
working-directory: ${{ matrix.working_directory }}
env:
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/scripts/drive-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ fi

if [ "$ACTION" == "ios" ]
then
SIMULATOR="iPhone 14"
# Boot simulator and wait for System app to be ready.
xcrun simctl bootstatus "$SIMULATOR" -b
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SIMULATOR="${SIMULATOR:-iPhone 16}"
export SIMULATOR
"${SCRIPT_DIR}/ensure-simulator-ready.sh"
xcrun simctl logverbose "$SIMULATOR" enable
# Sleep to allow simulator to settle.
sleep 15
# Uncomment following line to have simulator logs printed out for debugging purposes.
# xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' &
melos exec -c 1 --fail-fast --scope="$FLUTTERFIRE_PLUGIN_SCOPE_EXAMPLE" --dir-exists=integration_test -- \
Expand Down
182 changes: 182 additions & 0 deletions .github/workflows/scripts/ensure-simulator-ready.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#!/bin/bash

# Wait until an iOS Simulator is fully ready for integration tests (including first-boot
# data migration). Intended to run after futureware-tech/simulator-action (or any step
# that has issued simctl boot) and before flutter test / flutter drive.
#
# Usage:
# SIMULATOR=<udid-or-device-name> ./ensure-simulator-ready.sh
# ./ensure-simulator-ready.sh <udid-or-device-name>
#
# Environment:
# SIMULATOR UDID or device name (required if not passed as arg)
# BOOT_POLL_INTERVAL_SECONDS Poll interval (default: 20)
# BOOT_PROBE_TIMEOUT_SECONDS Per-probe timeout (default: 12)
# BOOT_MAX_WAIT_SECONDS Max wait for full boot (default: 660)
# ENSURE_OPEN_SIMULATOR_APP Open Simulator.app when booting (default: 1)
# ENSURE_BOOT_IF_NEEDED simctl boot when not Booted yet (default: 1)
set -euo pipefail

BOOT_POLL_INTERVAL_SECONDS="${BOOT_POLL_INTERVAL_SECONDS:-20}"
BOOT_PROBE_TIMEOUT_SECONDS="${BOOT_PROBE_TIMEOUT_SECONDS:-12}"
BOOT_MAX_WAIT_SECONDS="${BOOT_MAX_WAIT_SECONDS:-660}"
ENSURE_OPEN_SIMULATOR_APP="${ENSURE_OPEN_SIMULATOR_APP:-1}"
ENSURE_BOOT_IF_NEEDED="${ENSURE_BOOT_IF_NEEDED:-1}"

DEVICE="${SIMULATOR:-${1:-}}"
if [[ -z "$DEVICE" ]]; then
echo "[boot-status] ERROR: set SIMULATOR or pass device UDID/name as first argument" >&2
exit 1
fi

run_with_timeout() {
local max="$1"
shift
"$@" &
local cmd_pid=$!
local waited=0
while kill -0 "$cmd_pid" 2>/dev/null && (( waited < max )); do
sleep 1
waited=$((waited + 1))
done
if kill -0 "$cmd_pid" 2>/dev/null; then
kill "$cmd_pid" 2>/dev/null
wait "$cmd_pid" 2>/dev/null || true
return 124
fi
wait "$cmd_pid"
}

log_boot_status() {
echo "[boot-status] $*"
}

is_udid() {
[[ "$1" =~ ^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$ ]]
}

describe_booted_device() {
local device="$1"
if is_udid "$device"; then
xcrun simctl list devices booted 2>/dev/null \
| grep -F "$device" \
| grep -v 'unavailable' \
| head -1 \
|| true
else
xcrun simctl list devices booted 2>/dev/null \
| grep -i "${device} (" \
| grep -v 'Phone:' \
| grep -v 'unavailable' \
| grep -v CoreSimulator \
| head -1 \
|| true
fi
}

is_device_booted() {
[[ -n "$(describe_booted_device "$1")" ]]
}

log_migration_status() {
local device="$1"
local migration_output probe_rc

log_boot_status "probing data migration (bootstatus -d, up to ${BOOT_PROBE_TIMEOUT_SECONDS}s)..."
set +e
migration_output="$(run_with_timeout "$BOOT_PROBE_TIMEOUT_SECONDS" xcrun simctl bootstatus "$device" -d 2>&1)"
probe_rc=$?
set -e

if [[ "$probe_rc" -eq 124 ]]; then
log_boot_status " data migration / system bring-up still in progress"
return 1
fi

if [[ -n "$migration_output" ]]; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
log_boot_status " ${line}"
done <<<"$migration_output"
else
log_boot_status " no migration details reported"
fi
return 0
}

wait_for_simulator_ready() {
local device="$1"
local start=$SECONDS

while (( SECONDS - start < BOOT_MAX_WAIT_SECONDS )); do
local elapsed=$(( SECONDS - start ))
local booted_line ready_rc

log_boot_status "elapsed=${elapsed}s phase=wait_for_full_boot device=\"${device}\""

booted_line="$(describe_booted_device "$device")"
if [[ -z "$booted_line" ]]; then
log_boot_status " simctl list: not in Booted state yet"
else
log_boot_status " simctl list: ${booted_line}"
log_migration_status "$device" || true
fi

set +e
run_with_timeout "$BOOT_PROBE_TIMEOUT_SECONDS" xcrun simctl bootstatus "$device" >/dev/null 2>&1
ready_rc=$?
set -e

if [[ "$ready_rc" -eq 0 ]]; then
log_boot_status "bootstatus: simulator ready after ${elapsed}s"
log_migration_status "$device" || true
return 0
fi

if [[ "$ready_rc" -eq 124 ]]; then
log_boot_status "bootstatus: still booting (probe timed out after ${BOOT_PROBE_TIMEOUT_SECONDS}s)"
else
log_boot_status "bootstatus: probe exited with status ${ready_rc}"
fi

sleep "$BOOT_POLL_INTERVAL_SECONDS"
done

log_boot_status "ERROR: timed out after ${BOOT_MAX_WAIT_SECONDS}s waiting for simulator to become ready"
return 1
}

if is_udid "$DEVICE"; then
log_boot_status "phase=resolve_device udid=\"${DEVICE}\""
else
log_boot_status "phase=resolve_device name=\"${DEVICE}\""
fi

if ! is_device_booted "$DEVICE"; then
if [[ "$ENSURE_BOOT_IF_NEEDED" == "1" ]]; then
log_boot_status "phase=boot_command device not Booted; starting simctl boot..."
set +e
boot_output="$(xcrun simctl boot "$DEVICE" 2>&1)"
boot_rc=$?
set -e
if [[ "$boot_rc" -ne 0 ]]; then
log_boot_status "simctl boot exited ${boot_rc}: ${boot_output}"
else
log_boot_status "simctl boot command returned (device may still be migrating data)"
fi
if [[ "$ENSURE_OPEN_SIMULATOR_APP" == "1" ]]; then
log_boot_status "phase=foreground_simulator opening Simulator.app..."
open -a Simulator.app || true
fi
else
log_boot_status "phase=boot_command skipped (ENSURE_BOOT_IF_NEEDED=0); waiting for existing boot..."
fi
else
log_boot_status "phase=boot_command device already Booted; waiting for full readiness..."
fi

if ! wait_for_simulator_ready "$DEVICE"; then
exit 1
fi

log_boot_status "phase=complete device=\"${DEVICE}\" ready for flutter test"
Loading