From 4d2fd30143f4d961b128beaf7bad62fb96265e57 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:28:34 -0800 Subject: [PATCH] Fix UI test hang: bypass launch guard for XCUITest apps XCUITest launches the app as a separate process that doesn't inherit XCTest env vars (XCTestConfigurationFilePath, etc.), so isRunningUnderXCTest() returns false. The app then hits shouldBlockUntaggedDebugLaunch() and exits with code 64, causing the test runner to hang waiting for the app to launch. Fix: detect CMUX_UI_TEST_* env vars set via XCUIApplication.launchEnvironment and skip the launch guard. Also revert the failed CMUX_TAG ci.yml workaround. --- .github/workflows/ci.yml | 6 +----- Sources/SocketControlSettings.swift | 5 +++++ cmuxTests/GhosttyConfigTests.swift | 12 ++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de3289bf..a3654ce1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,11 +91,7 @@ jobs: rm -rf ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-* - name: Run UI tests - env: - # Debug builds require CMUX_TAG to prevent accidental untagged launches. - # XCUITest launches the app as a separate process without XCTest env vars, - # so the app's isRunningUnderXCTest() check fails. Provide a tag explicitly. - CMUX_TAG: ci run: | set -euo pipefail + # Run directly on the self-hosted macOS runner. xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" -only-testing:cmuxUITests/UpdatePillUITests test diff --git a/Sources/SocketControlSettings.swift b/Sources/SocketControlSettings.swift index da220b15..b9705095 100644 --- a/Sources/SocketControlSettings.swift +++ b/Sources/SocketControlSettings.swift @@ -230,6 +230,11 @@ struct SocketControlSettings { if isRunningUnderXCTest(environment: environment) { return false } + // XCUITest launches the app as a separate process without XCTest env vars, + // so isRunningUnderXCTest() misses it. Check for any CMUX_UI_TEST_ env var. + if environment.keys.contains(where: { $0.hasPrefix("CMUX_UI_TEST_") }) { + return false + } guard let bundleIdentifier = bundleIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines), !bundleIdentifier.isEmpty else { diff --git a/cmuxTests/GhosttyConfigTests.swift b/cmuxTests/GhosttyConfigTests.swift index a788043b..0d912bb7 100644 --- a/cmuxTests/GhosttyConfigTests.swift +++ b/cmuxTests/GhosttyConfigTests.swift @@ -741,6 +741,18 @@ final class SocketControlSettingsTests: XCTestCase { ) ) } + + func testXCUITestLaunchEnvironmentIgnoresLaunchTagGate() { + // XCUITest launches the app as a separate process without XCTest env vars. + // The app receives CMUX_UI_TEST_* vars via XCUIApplication.launchEnvironment. + XCTAssertFalse( + SocketControlSettings.shouldBlockUntaggedDebugLaunch( + environment: ["CMUX_UI_TEST_MODE": "1"], + bundleIdentifier: "com.cmuxterm.app.debug", + isDebugBuild: true + ) + ) + } } final class PostHogAnalyticsPropertiesTests: XCTestCase {