From 75375ab7afa553ca2fbafa11f7597eda801deed1 Mon Sep 17 00:00:00 2001 From: austinpower1258 Date: Mon, 23 Mar 2026 02:19:12 -0700 Subject: [PATCH] Fix display resolution UI test foreground activation on CI XCUIApplication.launch() fails to activate the app on headless CI runners, reporting "Failed to activate application (current state: Running Background)". With continueAfterFailure=false, this kills the test before ensureForegroundAfterLaunch can retry. Fix by temporarily setting continueAfterFailure=true around launch(), then retrying activation via app.activate(). Also add a retry loop in the CI workflow since foreground activation is inherently flaky on headless runners. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 24 ++++++++++++++----- .../DisplayResolutionRegressionUITests.swift | 23 ++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cf24332..c1aa05eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -495,12 +495,24 @@ jobs: {"helperBinaryPath":"$HELPER_PATH"} EOF - xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ - -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ - -disableAutomaticPackageResolution \ - -destination "platform=macOS" \ - -only-testing:cmuxUITests/DisplayResolutionRegressionUITests \ - test + # Retry once — foreground activation on headless CI runners is flaky + for attempt in 1 2; do + if xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \ + -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \ + -disableAutomaticPackageResolution \ + -destination "platform=macOS" \ + -only-testing:cmuxUITests/DisplayResolutionRegressionUITests \ + test; then + exit 0 + fi + if [ "$attempt" -eq 2 ]; then + echo "Display resolution UI regression failed after 2 attempts" >&2 + exit 1 + fi + echo "Attempt $attempt failed, retrying..." + pkill -x "cmux DEV" 2>/dev/null || true + sleep 3 + done - name: Run browser find focus UI regression run: | diff --git a/cmuxUITests/DisplayResolutionRegressionUITests.swift b/cmuxUITests/DisplayResolutionRegressionUITests.swift index 7342c52f..3da4b3b3 100644 --- a/cmuxUITests/DisplayResolutionRegressionUITests.swift +++ b/cmuxUITests/DisplayResolutionRegressionUITests.swift @@ -244,9 +244,21 @@ final class DisplayResolutionRegressionUITests: XCTestCase { for (key, value) in launchEnvironment(targetDisplayID: targetDisplayID) { app.launchEnvironment[key] = value } + + // On headless CI runners, XCUIApplication.launch() may fail to activate + // the app (reporting "Failed to activate application" as a test error). + // Temporarily allow continuation so we can retry activation manually. + continueAfterFailure = true app.launch() + continueAfterFailure = false launchedApp = app + guard ensureForegroundAfterLaunch(app, timeout: 15.0) else { + throw NSError(domain: "DisplayResolutionRegressionUITests", code: 2, userInfo: [ + NSLocalizedDescriptionKey: "App failed to reach foreground. state=\(app.state.rawValue) diagnostics=\(loadDiagnostics() ?? [:])" + ]) + } + if !waitForAppLaunchDiagnostics(timeout: 12.0) { throw NSError(domain: "DisplayResolutionRegressionUITests", code: 2, userInfo: [ NSLocalizedDescriptionKey: "App failed to write launch diagnostics. state=\(app.state.rawValue) diagnostics=\(loadDiagnostics() ?? [:])" @@ -254,6 +266,17 @@ final class DisplayResolutionRegressionUITests: XCTestCase { } } + private func ensureForegroundAfterLaunch(_ app: XCUIApplication, timeout: TimeInterval) -> Bool { + if app.wait(for: .runningForeground, timeout: timeout) { + return true + } + if app.state == .runningBackground { + app.activate() + return app.wait(for: .runningForeground, timeout: 6.0) + } + return false + } + private func launchEnvironment(targetDisplayID: String) -> [String: String] { [ "CMUX_UI_TEST_MODE": "1",