diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index cd21690c11e7..785655cce3a3 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -1766,10 +1766,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent #if DEBUG // UI tests run on a shared VM user profile, so persisted shortcuts can drift and make - // key-equivalent routing flaky. Force defaults for deterministic tests. + // key-equivalent routing flaky. Force defaults for deterministic tests. The same + // profile carries the last closed window's frame, which sizes the launch window. if isRunningUnderXCTest { SystemWideHotkeySettings.reset() KeyboardShortcutSettings.resetAll() + Self.forgetPersistedWindowGeometryForTestProcess() } #endif @@ -3890,6 +3892,23 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent legacyPersistedWindowGeometryDefaultsKeys.forEach { defaults.removeObjectIfPresent(forKey: $0) } } + /// Forgets the last closed main window's frame so a test process opens its + /// first window at the default size. + /// + /// Every main-window close writes its frame to the app's standard + /// defaults, and the launch window and any window created without a source + /// window read it back. App-host test processes on one machine share that + /// domain, so without this reset a process inherits whatever window an + /// earlier process closed last, often a 320-point fixture. Every later + /// `createMainWindow()` copies that launch window, and split admission then + /// refuses side-by-side splits (#15392). + nonisolated static func forgetPersistedWindowGeometryForTestProcess( + defaults: UserDefaults = .standard + ) { + removeLegacyPersistedWindowGeometry(defaults: defaults) + defaults.removeObjectIfPresent(forKey: persistedWindowGeometryDefaultsKey) + } + private func persistWindowGeometry(from window: NSWindow?) { guard let window else { return } persistWindowGeometry( diff --git a/cmuxTests/AppDelegateBareSpaceShortcutRoutingTests.swift b/cmuxTests/AppDelegateBareSpaceShortcutRoutingTests.swift index 843401bab4d4..f5ca20149474 100644 --- a/cmuxTests/AppDelegateBareSpaceShortcutRoutingTests.swift +++ b/cmuxTests/AppDelegateBareSpaceShortcutRoutingTests.swift @@ -186,6 +186,61 @@ final class AppDelegateBareSpaceShortcutRoutingTests: XCTestCase { XCTAssertEqual(window.frame.height, savedFrame.height, accuracy: 1) } + /// App-host test processes share the app's standard defaults, so a + /// 320-point fixture closed by an earlier process used to size this + /// process's launch window, and every window copied from it was too narrow + /// for a side-by-side split. The test-process reset must restore the + /// default size. + func testTestProcessResetIgnoresWindowGeometryPersistedByEarlierProcess() throws { + let previousShared = AppDelegate.shared + let appDelegate = AppDelegate() + defer { AppDelegate.shared = previousShared } + + let defaults = UserDefaults.standard + let persistedGeometryKey = AppDelegate.debugPersistedWindowGeometryDefaultsKey + let previousPersistedGeometry = defaults.object(forKey: persistedGeometryKey) + var windowId: UUID? + defer { + if let windowId { + closeWindow(withId: windowId) + } + restoreDefaultsValue( + previousPersistedGeometry, + forKey: persistedGeometryKey, + defaults: defaults + ) + } + + let screen = try XCTUnwrap(NSScreen.main ?? NSScreen.screens.first) + let fixtureFrame = CGRect( + x: screen.visibleFrame.minX, + y: screen.visibleFrame.minY, + width: 320, + height: 268 + ) + let payload = AppDelegate.PersistedWindowGeometry( + version: AppDelegate.persistedWindowGeometrySchemaVersion, + frame: SessionRectSnapshot(fixtureFrame), + display: SessionDisplaySnapshot( + displayID: screen.cmuxDisplayID, + frame: SessionRectSnapshot(screen.frame), + visibleFrame: SessionRectSnapshot(screen.visibleFrame) + ) + ) + defaults.set(try JSONEncoder().encode(payload), forKey: persistedGeometryKey) + + AppDelegate.forgetPersistedWindowGeometryForTestProcess() + + let createdWindowId = appDelegate.createMainWindow(shouldActivate: false, sourceWindow: nil) + windowId = createdWindowId + let window = try XCTUnwrap(window(withId: createdWindowId)) + let styleMask = window.styleMask + let expectedContentSize = CmuxMainWindow.defaultContentRect(styleMask: styleMask).size + let contentSize = window.contentRect(forFrameRect: window.frame).size + XCTAssertEqual(contentSize.width, expectedContentSize.width, accuracy: 1) + XCTAssertEqual(contentSize.height, expectedContentSize.height, accuracy: 1) + } + private func makeKeyDownEvent( key: String, keyCode: UInt16,