From 9bb2816e05a05894fcd71c923dcb4f86feb3db15 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:04:33 -0700 Subject: [PATCH] Fix main CI regressions (#1458) Co-authored-by: Lawrence Chen --- Sources/GhosttyTerminalView.swift | 10 ++- cmuxTests/GhosttyConfigTests.swift | 131 +++++++++++++++++++++-------- 2 files changed, 103 insertions(+), 38 deletions(-) diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 765a80e8..013e387f 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -4441,9 +4441,13 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { private func invalidateTextInputCoordinates(selectionChanged: Bool = false) { guard let inputContext else { return } inputContext.invalidateCharacterCoordinates() - if #available(macOS 15.4, *), selectionChanged { - inputContext.textInputClientDidUpdateSelection() - } + guard selectionChanged else { return } + + // `textInputClientDidUpdateSelection` is absent from the Xcode 16.2 AppKit SDK + // used by the macOS 14 compatibility lane, so call it dynamically when present. + let updateSelectionSelector = NSSelectorFromString("textInputClientDidUpdateSelection") + guard inputContext.responds(to: updateSelectionSelector) else { return } + _ = inputContext.perform(updateSelectionSelector) } override var acceptsFirstResponder: Bool { true } diff --git a/cmuxTests/GhosttyConfigTests.swift b/cmuxTests/GhosttyConfigTests.swift index a466343d..40ff7404 100644 --- a/cmuxTests/GhosttyConfigTests.swift +++ b/cmuxTests/GhosttyConfigTests.swift @@ -310,50 +310,84 @@ final class GhosttyConfigTests: XCTestCase { ) } - func testReleaseAppSupportFallbackLoadsForDebugWhenOnlyReleaseConfigExists() { - XCTAssertTrue( - GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig( - currentBundleIdentifier: "com.cmuxterm.app.debug", - currentConfigFileSize: nil, - currentLegacyConfigFileSize: nil, - releaseConfigFileSize: 128, - releaseLegacyConfigFileSize: nil + func testCmuxAppSupportConfigURLsUseReleaseConfigForDebugBundleWithoutCurrentConfig() throws { + try withTemporaryAppSupportDirectory { appSupportDirectory in + let releaseConfigURL = try writeAppSupportConfig( + appSupportDirectory: appSupportDirectory, + bundleIdentifier: "com.cmuxterm.app", + filename: "config", + contents: "font-size = 13\n" ) - ) + + XCTAssertEqual( + GhosttyApp.cmuxAppSupportConfigURLs( + currentBundleIdentifier: "com.cmuxterm.app.debug", + appSupportDirectory: appSupportDirectory + ), + [releaseConfigURL] + ) + } } - func testReleaseAppSupportFallbackSkipsWhenDebugConfigAlreadyExists() { - XCTAssertFalse( - GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig( - currentBundleIdentifier: "com.cmuxterm.app.debug.issue-829", - currentConfigFileSize: nil, - currentLegacyConfigFileSize: 64, - releaseConfigFileSize: 128, - releaseLegacyConfigFileSize: nil + func testCmuxAppSupportConfigURLsPreferCurrentBundleConfigWhenPresent() throws { + try withTemporaryAppSupportDirectory { appSupportDirectory in + _ = try writeAppSupportConfig( + appSupportDirectory: appSupportDirectory, + bundleIdentifier: "com.cmuxterm.app", + filename: "config", + contents: "font-size = 13\n" ) - ) + let currentConfigURL = try writeAppSupportConfig( + appSupportDirectory: appSupportDirectory, + bundleIdentifier: "com.cmuxterm.app.debug.issue-829", + filename: "config.ghostty", + contents: "font-size = 14\n" + ) + + XCTAssertEqual( + GhosttyApp.cmuxAppSupportConfigURLs( + currentBundleIdentifier: "com.cmuxterm.app.debug.issue-829", + appSupportDirectory: appSupportDirectory + ), + [currentConfigURL] + ) + } } - func testReleaseAppSupportFallbackSkipsForNonDebugBundleOrMissingReleaseConfig() { - XCTAssertFalse( - GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig( - currentBundleIdentifier: "com.cmuxterm.app", - currentConfigFileSize: nil, - currentLegacyConfigFileSize: nil, - releaseConfigFileSize: 128, - releaseLegacyConfigFileSize: nil + func testCmuxAppSupportConfigURLsSkipReleaseFallbackForNonDebugBundle() throws { + try withTemporaryAppSupportDirectory { appSupportDirectory in + _ = try writeAppSupportConfig( + appSupportDirectory: appSupportDirectory, + bundleIdentifier: "com.cmuxterm.app", + filename: "config", + contents: "font-size = 13\n" ) - ) - XCTAssertFalse( - GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig( - currentBundleIdentifier: "com.cmuxterm.app.debug", - currentConfigFileSize: nil, - currentLegacyConfigFileSize: nil, - releaseConfigFileSize: nil, - releaseLegacyConfigFileSize: 0 + XCTAssertTrue( + GhosttyApp.cmuxAppSupportConfigURLs( + currentBundleIdentifier: "com.example.other-app", + appSupportDirectory: appSupportDirectory + ).isEmpty ) - ) + } + } + + func testCmuxAppSupportConfigURLsIgnoreMissingOrEmptyFiles() throws { + try withTemporaryAppSupportDirectory { appSupportDirectory in + _ = try writeAppSupportConfig( + appSupportDirectory: appSupportDirectory, + bundleIdentifier: "com.cmuxterm.app", + filename: "config.ghostty", + contents: "" + ) + + XCTAssertTrue( + GhosttyApp.cmuxAppSupportConfigURLs( + currentBundleIdentifier: "com.cmuxterm.app.debug", + appSupportDirectory: appSupportDirectory + ).isEmpty + ) + } } func testDefaultBackgroundUpdateScopePrioritizesSurfaceOverAppAndUnscoped() { @@ -562,6 +596,33 @@ final class GhosttyConfigTests: XCTestCase { blue: Int(round(blue * 255)) ) } + + private func withTemporaryAppSupportDirectory( + _ body: (URL) throws -> Void + ) throws { + let fileManager = FileManager.default + let directory = fileManager.temporaryDirectory + .appendingPathComponent("cmux-app-support-\(UUID().uuidString)", isDirectory: true) + try fileManager.createDirectory(at: directory, withIntermediateDirectories: true) + defer { try? fileManager.removeItem(at: directory) } + try body(directory) + } + + private func writeAppSupportConfig( + appSupportDirectory: URL, + bundleIdentifier: String, + filename: String, + contents: String + ) throws -> URL { + let fileManager = FileManager.default + let bundleDirectory = appSupportDirectory + .appendingPathComponent(bundleIdentifier, isDirectory: true) + try fileManager.createDirectory(at: bundleDirectory, withIntermediateDirectories: true) + + let configURL = bundleDirectory.appendingPathComponent(filename, isDirectory: false) + try contents.write(to: configURL, atomically: true, encoding: .utf8) + return configURL + } } final class WorkspaceChromeThemeTests: XCTestCase {