From dded4de389daa04dc273d5a4f9a166d4dff650af Mon Sep 17 00:00:00 2001 From: vahid torkaman <692343+vahidlazio@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:38:54 +0200 Subject: [PATCH 1/2] ci: wait for the iOS simulator to be ready before running integration tests `xcrun simctl boot` returns as soon as boot is initiated, not when the device is usable. Booting inline in the test step therefore let `flutter test` race the Dart VM debug connection against a still-booting simulator. When it lost that race the app built, installed and launched but the harness never attached, so the job sat silent until the 30 minute timeout with "No tests ran." and "Error waiting for a debug connection: The log reader failed unexpectedly". Measured locally: `simctl boot` returned after 2s while the device needed 3s more when warm and 17s more when cold, most of that "Waiting on Data Migration" running migrator plugins. Since every CI job starts cold, the outcome was effectively a coin flip per run. `bootstatus -b` boots the device if needed and blocks until it reports Finished, and exits non-zero for an invalid device, so a boot failure now fails its own step rather than surfacing as a test timeout later. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ios-test.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ios-test.yaml b/.github/workflows/ios-test.yaml index 56cf0ef..cf13e34 100644 --- a/.github/workflows/ios-test.yaml +++ b/.github/workflows/ios-test.yaml @@ -48,9 +48,25 @@ jobs: channel: ${{ env.FLUTTER_CHANNEL }} - run: echo API_KEY=${{ secrets.TEST_API_KEY }} > example/.env + - name: Boot iOS simulator and wait until it is ready + run: | + # `simctl boot` returns as soon as boot is *initiated*, not when the + # device is usable, so booting inline let `flutter test` race the Dart + # VM debug connection against a still-booting simulator. When it lost + # that race the app built, installed and launched but the harness + # never attached, so the job sat silent until the 30 minute timeout + # with "No tests ran." and "Error waiting for a debug connection". + # + # Measured locally: `boot` returned after 2s while the device needed + # 3s more warm and 17s more cold, most of it "Waiting on Data + # Migration". `bootstatus -b` boots if needed and blocks until the + # device reports Finished. It also exits non-zero for an invalid + # device, so a boot failure fails here instead of masquerading as a + # test timeout further down. + xcrun simctl bootstatus "${{ steps.simulator.outputs.udid }}" -b + - name: Run iOS integration tests timeout-minutes: 30 run: | flutter config --no-enable-swift-package-manager - xcrun simctl boot "${{ steps.simulator.outputs.udid }}" cd example && flutter test -d "${{ steps.simulator.outputs.udid }}" integration_test/widget_test.dart From 501397fb1e1dde74acc06181b53fe60f93e5308f Mon Sep 17 00:00:00 2001 From: vahid torkaman <692343+vahidlazio@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:14:33 +0200 Subject: [PATCH 2/2] ci: retry the iOS integration test step once on a stalled attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The simulator readiness gate did not stop the stall. On the first run of this branch the gate succeeded in 94s and the device was fully booted, and `flutter test` still hung after "Xcode build done." until the 30 minute timeout. That rules out the boot race: the failure is `flutter test` failing to attach to the Dart VM, which readiness does not affect. It is intermittent rather than deterministic — the same commit passes on some runs and stalls on others — and a second attempt with the app already built and the simulator warm has consistently succeeded. Two bounded 13 minute attempts fit inside the job's 45 minute budget. The previous single 30 minute attempt consumed almost the entire budget before failing, leaving no room to recover. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ios-test.yaml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ios-test.yaml b/.github/workflows/ios-test.yaml index cf13e34..5c7ea4b 100644 --- a/.github/workflows/ios-test.yaml +++ b/.github/workflows/ios-test.yaml @@ -65,8 +65,26 @@ jobs: # test timeout further down. xcrun simctl bootstatus "${{ steps.simulator.outputs.udid }}" -b + - name: Configure Flutter to use CocoaPods + run: flutter config --no-enable-swift-package-manager + + # `flutter test` intermittently fails to attach to the Dart VM on a cold + # runner: the app builds, installs and launches, but the harness never + # connects and the step sits silent until it is killed, reporting + # "No tests ran." and "Error waiting for a debug connection: The log + # reader failed unexpectedly". Booting the simulator to readiness first + # (see the step above) did not stop it, so this is an attach race rather + # than a boot race. + # + # It is intermittent, not deterministic — the same commit passes on some + # runs and stalls on others — and a second attempt, with the app already + # built and the simulator already warm, has consistently succeeded. Two + # bounded attempts fit inside the job's 45 minute budget, whereas the + # previous single 30 minute attempt burned almost the whole budget before + # failing. - name: Run iOS integration tests - timeout-minutes: 30 - run: | - flutter config --no-enable-swift-package-manager - cd example && flutter test -d "${{ steps.simulator.outputs.udid }}" integration_test/widget_test.dart + uses: nick-fields/retry@v3 + with: + timeout_minutes: 13 + max_attempts: 2 + command: cd example && flutter test -d "${{ steps.simulator.outputs.udid }}" integration_test/widget_test.dart