From 4a72af694c2e01243488017894cd735f95309782 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 2 Oct 2026 03:10:46 -0400 Subject: [PATCH] fix(ci): restore #15712's non-iOS test-harness hunks dropped by #16709 The squash revert of #15712 (#16709, for iOS toolbar regressions) also reverted its CI/test-harness hunks: the TEST_RUNNER_-aware SwiftTestingAssertions.sourceURL, the console-session allowlist entry for CMUX_CI_RUNTIME_SOURCE_ROOT, and the CLI test fixture isolation. #16352's guard in tests/test_app_host_test_rerun.py asserts those, so CanonicalRootTests fails on main. No iOS file is touched. Co-Authored-By: Claude Opus 5.5 --- CLI/cmux.swift | 7 +++ Sources/AppDelegate.swift | 5 +++ cmuxCLITests/BundledCLITestSupport.swift | 35 +++++++++++++++ .../CLIExplicitSurfaceRoutingTests.swift | 43 +++++++++++++++++++ cmuxTests/SwiftTestingAssertions.swift | 26 ++++++----- .../WorkspaceCloseTabsContextMenuTests.swift | 4 +- scripts/ci/run-in-console-session.sh | 2 +- 7 files changed, 108 insertions(+), 14 deletions(-) diff --git a/CLI/cmux.swift b/CLI/cmux.swift index a60181b78061..0900fc4d60bf 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -40711,6 +40711,13 @@ export default { appendIfExisting(Bundle.main.url(forResource: "opencode-plugin", withExtension: "js")) appendIfExisting(Bundle.main.resourceURL?.appendingPathComponent("opencode-plugin.js", isDirectory: false)) + if let runtimeRoot = ProcessInfo.processInfo.environment["CMUX_CI_RUNTIME_SOURCE_ROOT"], + !runtimeRoot.isEmpty { + appendIfExisting( + URL(fileURLWithPath: runtimeRoot, isDirectory: true) + .appendingPathComponent("src/Resources/opencode-plugin.js") + ) + } if let executableURL = resolvedExecutableURL() { let execDir = executableURL.deletingLastPathComponent().standardizedFileURL for relativePath in ["opencode-plugin.js", "../opencode-plugin.js", "../../Resources/opencode-plugin.js", "../../../Contents/Resources/opencode-plugin.js"] { diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 2d26109334e4..9cf59be8d20e 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -698,6 +698,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent private nonisolated static let persistedWindowGeometryDefaultsKey = SessionSnapshotPersistenceWriter.persistedWindowGeometryDefaultsKey #if DEBUG nonisolated static var debugPersistedWindowGeometryDefaultsKey: String { persistedWindowGeometryDefaultsKey } + + private nonisolated static func forgetPersistedWindowGeometryForTestProcess() { + UserDefaults.standard.removeObject(forKey: persistedWindowGeometryDefaultsKey) + removeLegacyPersistedWindowGeometry() + } #endif weak var tabManager: TabManager? diff --git a/cmuxCLITests/BundledCLITestSupport.swift b/cmuxCLITests/BundledCLITestSupport.swift index 2aebcc02f600..f6deab95c3d9 100644 --- a/cmuxCLITests/BundledCLITestSupport.swift +++ b/cmuxCLITests/BundledCLITestSupport.swift @@ -7,6 +7,41 @@ import XCTest /// has no app host, so it resolves it from `CMUX_CLI_PATH` or relative to the /// built products directory the bundle itself was copied into. enum BundledCLITestSupport { + static func ciRuntimeSourceRootEnvironment() -> [String: String] { + let environment = ProcessInfo.processInfo.environment + guard let runtimeRoot = [ + environment["CMUX_CI_RUNTIME_SOURCE_ROOT"], + environment["TEST_RUNNER_CMUX_CI_RUNTIME_SOURCE_ROOT"], + ].compactMap({ $0 }).first(where: { !$0.isEmpty }) else { + return [:] + } + return [ + "CMUX_CI_RUNTIME_SOURCE_ROOT": runtimeRoot, + "TEST_RUNNER_CMUX_CI_RUNTIME_SOURCE_ROOT": runtimeRoot, + ] + } + + static func repositoryRoot(file: StaticString = #filePath) -> URL { + let environment = ProcessInfo.processInfo.environment + var roots: [URL] = [] + for key in ["CMUX_CI_RUNTIME_SOURCE_ROOT", "TEST_RUNNER_CMUX_CI_RUNTIME_SOURCE_ROOT"] { + if let runtimeRoot = environment[key], !runtimeRoot.isEmpty { + roots.append(URL(fileURLWithPath: runtimeRoot, isDirectory: true) + .appendingPathComponent("src", isDirectory: true)) + } + } + let fileRoot = URL(fileURLWithPath: String(describing: file)) + .deletingLastPathComponent() + .deletingLastPathComponent() + roots.append(fileRoot) + roots.append(URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)) + return roots.first(where: { + FileManager.default.isExecutableFile( + atPath: $0.appendingPathComponent("Resources/bin/cmux-claude-wrapper").path + ) + }) ?? fileRoot + } + /// The daemon handshake must match the CLI under test, not the xctest host. static func appVersion(cliPath: String) throws -> String { let result = CLIHookProcessRunner.run( diff --git a/cmuxCLITests/CLIExplicitSurfaceRoutingTests.swift b/cmuxCLITests/CLIExplicitSurfaceRoutingTests.swift index 930c04ea0342..15f505a676a6 100644 --- a/cmuxCLITests/CLIExplicitSurfaceRoutingTests.swift +++ b/cmuxCLITests/CLIExplicitSurfaceRoutingTests.swift @@ -43,6 +43,49 @@ struct CLIExplicitSurfaceRoutingTests { ) } + @Test func vmTreeUsesCloudLinkErrorMessageInHumanOutput() throws { + let temporaryHome = FileManager.default.temporaryDirectory + .appendingPathComponent("cmux-cli-vm-tree-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: temporaryHome, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: temporaryHome) } + + let execution = try runMockCommand( + arguments: ["vm", "tree"], + socketName: "vm-tree-link-error", + environmentOverrides: [ + "CFFIXED_USER_HOME": temporaryHome.path, + "HOME": temporaryHome.path, + ] + ) { line in + guard let request = Self.jsonObject(line), + let id = request["id"] as? String, + request["method"] as? String == "surface.catalog" else { + return Self.malformedRequestResponse(raw: line) + } + return Self.v2Response( + id: id, + ok: true, + result: [ + "machines": [[ + "id": "brave-otter", + "status": "running", + "link_state": "error", + "link_error": "cloud_api_unavailable", + "link_error_message": "cmux cannot reach the Cloud service for this machine right now." + ]], + "resources": [] + ] + ) + } + + #expect(execution.result.status == 0, Comment(rawValue: execution.result.stderr)) + #expect( + execution.result.stdout.contains("cmux cannot reach the Cloud service"), + Comment(rawValue: execution.result.stdout) + ) + #expect(!execution.result.stdout.contains("cloud_api_unavailable")) + } + @Test func sendKeyCommandsRejectExtraArgumentsWithoutSocketRequest() throws { let cases: [[String]] = [ ["send-key", "--surface", Self.targetSurfaceRef, "ctrl+c", "enter"], diff --git a/cmuxTests/SwiftTestingAssertions.swift b/cmuxTests/SwiftTestingAssertions.swift index fa41cdae638d..93379f9dd2b1 100644 --- a/cmuxTests/SwiftTestingAssertions.swift +++ b/cmuxTests/SwiftTestingAssertions.swift @@ -8,20 +8,24 @@ import Testing struct SwiftTestingAssertions { /// Resolves a test source file from its module-relative identity. static func sourceURL(_ file: StaticString = #fileID) -> URL { - let sourceRoot: URL - if let runtimeRoot = ProcessInfo.processInfo.environment["CMUX_CI_RUNTIME_SOURCE_ROOT"], - !runtimeRoot.isEmpty { - sourceRoot = URL(fileURLWithPath: runtimeRoot, isDirectory: true) - .appendingPathComponent("src", isDirectory: true) - } else { - sourceRoot = URL(fileURLWithPath: #filePath) + let fileID = String(describing: file) + let environment = ProcessInfo.processInfo.environment + var roots: [URL] = [] + for key in ["CMUX_CI_RUNTIME_SOURCE_ROOT", "TEST_RUNNER_CMUX_CI_RUNTIME_SOURCE_ROOT"] { + if let runtimeRoot = environment[key], !runtimeRoot.isEmpty { + roots.append(URL(fileURLWithPath: runtimeRoot, isDirectory: true) + .appendingPathComponent("src", isDirectory: true)) + } + } + roots.append( + URL(fileURLWithPath: #filePath) .deletingLastPathComponent() .deletingLastPathComponent() - } - - let fileID = String(describing: file) + ) + roots.append(URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)) // #fileID is "/"; the module name matches the repo directory. - return sourceRoot.appendingPathComponent(fileID) + let candidates = roots.map { $0.appendingPathComponent(fileID) } + return candidates.first(where: { FileManager.default.fileExists(atPath: $0.path) }) ?? candidates[0] } diff --git a/cmuxTests/WorkspaceCloseTabsContextMenuTests.swift b/cmuxTests/WorkspaceCloseTabsContextMenuTests.swift index 3a5e4d6774c2..065a6b6f0d43 100644 --- a/cmuxTests/WorkspaceCloseTabsContextMenuTests.swift +++ b/cmuxTests/WorkspaceCloseTabsContextMenuTests.swift @@ -96,8 +96,8 @@ struct WorkspaceCloseTabsContextMenuTests { for: tab, inPane: fixture.paneId ) - drainMainQueue() - drainMainQueue() + drainMainQueueForCloseTest(timeout: mainActorTestMainQueueSpin) + drainMainQueueForCloseTest(timeout: mainActorTestMainQueueSpin) #expect(promptCount == 1) #expect(fixture.workspace.panelIdFromSurfaceId(tabId) == nil) diff --git a/scripts/ci/run-in-console-session.sh b/scripts/ci/run-in-console-session.sh index 519282897f54..74bd329154b0 100755 --- a/scripts/ci/run-in-console-session.sh +++ b/scripts/ci/run-in-console-session.sh @@ -240,7 +240,7 @@ if [ -n "$console_user" ] && [ "$console_user" != "root" ] \ CMUX_CI_RUNTIME_SOURCE_ROOT \ CMUX_RENDERER_MEMORY_REGRESSION \ CMUX_APP_HOST_CAPTURE_XCRESULTS CMUX_APP_HOST_RESULT_BUNDLE_ROOT CMUX_APP_HOST_TEST_CASE_TIMEOUT_SECONDS \ - GITHUB_REPOSITORY_ID GITHUB_RUN_ID GITHUB_RUN_ATTEMPT CMUX_APP_HOST_SHARD CMUX_APP_HOST_XCTESTRUN CMUX_NUMERIC_LOCALE_XCTESTRUN CMUX_CI_APP_HOST_ISOLATION_REQUIRED CMUX_APP_HOST_KEY CMUX_APP_HOST_HOME CMUX_APP_HOST_XDG_CONFIG_HOME CMUX_APP_HOST_RECEIPT_DIR CMUX_APP_HOST_CLEANUP_CONFIRMATION CMUX_APP_HOST_CONFIRMATION_FILE \ + GITHUB_REPOSITORY_ID GITHUB_RUN_ID GITHUB_RUN_ATTEMPT CMUX_APP_HOST_SHARD CMUX_APP_HOST_XCTESTRUN CMUX_NUMERIC_LOCALE_XCTESTRUN CMUX_CI_RUNTIME_SOURCE_ROOT CMUX_CI_APP_HOST_ISOLATION_REQUIRED CMUX_APP_HOST_KEY CMUX_APP_HOST_HOME CMUX_APP_HOST_XDG_CONFIG_HOME CMUX_APP_HOST_RECEIPT_DIR CMUX_APP_HOST_CLEANUP_CONFIRMATION CMUX_APP_HOST_CONFIRMATION_FILE \ CFFIXED_USER_HOME XDG_CONFIG_HOME CARGO_HOME RUSTUP_HOME) if [ "${CMUX_CI_APP_HOST_CLEANUP_TEST_HELPER:-0}" = "1" ]; then forward+=(CMUX_CI_APP_HOST_CLEANUP_TEST_HELPER CMUX_APP_HOST_LSOF CMUX_FAKE_LSOF_STATE)