Add regression coverage for Cmd+N workspace creation crash (#2127)
* Add Cmd+N workspace snapshot regression coverage (#2017) * Add dev flag to stress Cmd+N workspace creation
This commit is contained in:
parent
0d8597caf9
commit
da70f3fa47
2 changed files with 93 additions and 1 deletions
|
|
@ -1142,6 +1142,48 @@ class TabManager: ObservableObject {
|
|||
focusedBrowserPanel?.hideFind()
|
||||
}
|
||||
|
||||
func makeWorkspaceForCreation(
|
||||
title: String,
|
||||
workingDirectory: String?,
|
||||
portOrdinal: Int,
|
||||
configTemplate: ghostty_surface_config_s?,
|
||||
initialTerminalCommand: String?,
|
||||
initialTerminalEnvironment: [String: String]
|
||||
) -> Workspace {
|
||||
Workspace(
|
||||
title: title,
|
||||
workingDirectory: workingDirectory,
|
||||
portOrdinal: portOrdinal,
|
||||
configTemplate: configTemplate,
|
||||
initialTerminalCommand: initialTerminalCommand,
|
||||
initialTerminalEnvironment: initialTerminalEnvironment
|
||||
)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private func maybeMutateSelectionDuringWorkspaceCreationForDev(
|
||||
snapshot: WorkspaceCreationSnapshot
|
||||
) {
|
||||
let env = ProcessInfo.processInfo.environment
|
||||
let isEnabled: Bool = {
|
||||
if let raw = env["CMUX_DEV_MUTATE_WORKSPACE_SELECTION_DURING_CREATION"] {
|
||||
return raw == "1" || raw.caseInsensitiveCompare("true") == .orderedSame
|
||||
}
|
||||
return UserDefaults.standard.bool(forKey: "cmuxDevMutateWorkspaceSelectionDuringCreation")
|
||||
}()
|
||||
guard isEnabled,
|
||||
let selectedTabId = snapshot.selectedTabId,
|
||||
let target = snapshot.tabs.first(where: { $0.id != selectedTabId }) else {
|
||||
return
|
||||
}
|
||||
dlog(
|
||||
"workspace.create.devSelectionMutation from=\(selectedTabId.uuidString.prefix(5)) " +
|
||||
"to=\(target.id.uuidString.prefix(5))"
|
||||
)
|
||||
self.selectedTabId = target.id
|
||||
}
|
||||
#endif
|
||||
|
||||
@discardableResult
|
||||
func addWorkspace(
|
||||
workingDirectory overrideWorkingDirectory: String? = nil,
|
||||
|
|
@ -1155,6 +1197,9 @@ class TabManager: ObservableObject {
|
|||
// Snapshot current published state once so workspace creation doesn't repeatedly
|
||||
// bounce through Combine-backed accessors while we're preparing the new workspace.
|
||||
let snapshot = workspaceCreationSnapshot()
|
||||
#if DEBUG
|
||||
maybeMutateSelectionDuringWorkspaceCreationForDev(snapshot: snapshot)
|
||||
#endif
|
||||
let nextTabCount = snapshot.tabs.count + 1
|
||||
sentryBreadcrumb("workspace.create", data: ["tabCount": nextTabCount])
|
||||
let explicitWorkingDirectory = normalizedWorkingDirectory(overrideWorkingDirectory)
|
||||
|
|
@ -1166,7 +1211,7 @@ class TabManager: ObservableObject {
|
|||
let insertIndex = newTabInsertIndex(snapshot: snapshot, placementOverride: placementOverride)
|
||||
let ordinal = Self.nextPortOrdinal
|
||||
Self.nextPortOrdinal += 1
|
||||
let newWorkspace = Workspace(
|
||||
let newWorkspace = makeWorkspaceForCreation(
|
||||
title: "Terminal \(nextTabCount)",
|
||||
workingDirectory: workingDirectory,
|
||||
portOrdinal: ordinal,
|
||||
|
|
|
|||
|
|
@ -294,6 +294,29 @@ final class WorkspacePlacementSettingsTests: XCTestCase {
|
|||
|
||||
@MainActor
|
||||
final class WorkspaceCreationPlacementTests: XCTestCase {
|
||||
private final class SnapshotMutatingTabManager: TabManager {
|
||||
var beforeCreateWorkspace: (() -> Void)?
|
||||
|
||||
override func makeWorkspaceForCreation(
|
||||
title: String,
|
||||
workingDirectory: String?,
|
||||
portOrdinal: Int,
|
||||
configTemplate: ghostty_surface_config_s?,
|
||||
initialTerminalCommand: String?,
|
||||
initialTerminalEnvironment: [String: String]
|
||||
) -> Workspace {
|
||||
beforeCreateWorkspace?()
|
||||
return super.makeWorkspaceForCreation(
|
||||
title: title,
|
||||
workingDirectory: workingDirectory,
|
||||
portOrdinal: portOrdinal,
|
||||
configTemplate: configTemplate,
|
||||
initialTerminalCommand: initialTerminalCommand,
|
||||
initialTerminalEnvironment: initialTerminalEnvironment
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testAddWorkspaceDefaultPlacementMatchesCurrentSetting() {
|
||||
let currentPlacement = WorkspacePlacementSettings.current()
|
||||
|
||||
|
|
@ -352,6 +375,30 @@ final class WorkspaceCreationPlacementTests: XCTestCase {
|
|||
XCTAssertEqual(manager.tabs.last?.id, inserted.id)
|
||||
}
|
||||
|
||||
func testAddWorkspaceAfterCurrentUsesPrecreationSnapshotWhenSelectionMutatesDuringBootstrap() {
|
||||
let manager = SnapshotMutatingTabManager()
|
||||
guard let first = manager.tabs.first else {
|
||||
XCTFail("Expected initial workspace")
|
||||
return
|
||||
}
|
||||
|
||||
manager.setPinned(first, pinned: true)
|
||||
let second = manager.addWorkspace()
|
||||
let third = manager.addWorkspace()
|
||||
manager.selectWorkspace(third)
|
||||
|
||||
let baselineOrder = manager.tabs.map(\.id)
|
||||
manager.beforeCreateWorkspace = {
|
||||
manager.selectWorkspace(first)
|
||||
}
|
||||
|
||||
let inserted = manager.addWorkspace(placementOverride: .afterCurrent)
|
||||
|
||||
XCTAssertEqual(manager.tabs.map(\.id).filter { $0 != inserted.id }, baselineOrder)
|
||||
XCTAssertEqual(manager.tabs.map(\.id), [first.id, second.id, third.id, inserted.id])
|
||||
XCTAssertEqual(manager.selectedTabId, inserted.id)
|
||||
}
|
||||
|
||||
private func makeManagerWithThreeWorkspaces() -> TabManager {
|
||||
let manager = TabManager()
|
||||
_ = manager.addWorkspace()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue