native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
5.6 kB · 179 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180import Clocksimport Foundationimport GhosttyKitimport ProwlCLISharedimport Testing
@testable import supacode
@MainActor@Suite(.serialized)struct TerminalCloseUndoStackTests { @Test func popsInCloseOrderAndCancelsExpiry() async { let clock = TestClock() let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: clock) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let first = makeView() let second = makeView()
stack.recordClose(makeTabRecord(first)) stack.recordClose(makeTabRecord(second))
#expect(stack.canUndo) #expect(stack.popUndo()?.retainedSurfaces.first === second) #expect(stack.popUndo()?.retainedSurfaces.first === first) #expect(!stack.canUndo)
await clock.advance(by: .seconds(6)) await Task.yield() #expect(expired.isEmpty) }
@Test func eachEntryExpiresOnItsOwnTimer() async { let clock = TestClock() let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: clock) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let first = makeView() let second = makeView()
stack.recordClose(makeTabRecord(first)) await clock.advance(by: .seconds(3)) stack.recordClose(makeTabRecord(second)) await clock.advance(by: .seconds(2)) await settle()
#expect(expired == [first.id]) #expect(stack.canUndo)
await clock.advance(by: .seconds(3)) await settle()
#expect(expired == [first.id, second.id]) #expect(!stack.canUndo) }
@Test func zeroTimeoutExpiresAtOnce() { let stack = TerminalCloseUndoStack(timeout: .zero, clock: TestClock()) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let view = makeView()
stack.recordClose(makeTabRecord(view))
#expect(!stack.isEnabled) #expect(!stack.canUndo) #expect(expired == [view.id]) }
@Test func newCloseClearsRedoUnlessRedoing() { let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: TestClock()) stack.recordReopen(.tabs(worktreeID: "wt", [TerminalTabID()])) #expect(stack.canRedo)
stack.recordClose(makeTabRecord(makeView()), clearingRedo: false) #expect(stack.canRedo)
stack.recordClose(makeTabRecord(makeView())) #expect(!stack.canRedo) }
@Test func discardingAnExitedSurfaceKeepsTheRestOfTheRecord() { let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: TestClock()) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let left = makeView() let right = makeView() let tree = try? SplitTree(view: left).inserting(view: right, at: left, direction: .right) let record = TerminalClosedTabRecord( item: TerminalTabItem(title: "tab", icon: nil), index: 0, wasSelected: true, tree: tree ?? SplitTree(view: left), focusedSurfaceID: right.id, wasRunScriptTab: false, boundDirectoryKey: nil, contexts: [:] ) stack.recordClose(.tabs(worktreeID: "wt", [record]))
stack.discardSurface(id: right.id)
#expect(expired == [right.id]) let remaining = stack.popUndo() #expect(remaining?.retainedSurfaces.map(\.id) == [left.id]) if case .tabs(_, let tabs) = remaining { #expect(tabs.first?.focusedSurfaceID == nil) } else { Issue.record("expected a tab record") } }
@Test func discardingTheOnlySurfaceDropsTheRecord() { let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: TestClock()) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let view = makeView() stack.recordClose( .pane( worktreeID: "wt", TerminalClosedPaneRecord( tabID: TerminalTabID(), view: view, previousTree: SplitTree(view: view), wasFocused: true, context: TerminalRetainedSurfaceContext(launchProfile: nil, hookRegistration: nil))))
stack.discardSurface(id: view.id)
#expect(expired == [view.id]) #expect(!stack.canUndo) }
@Test func discardByWorktreeFreesOnlyThatWorktree() { let stack = TerminalCloseUndoStack(timeout: .seconds(5), clock: TestClock()) var expired: [UUID] = [] stack.onExpire = { expired.append(contentsOf: $0.map(\.id)) } let kept = makeView() let dropped = makeView() stack.recordClose(makeTabRecord(kept, worktreeID: "keep")) stack.recordClose(makeTabRecord(dropped, worktreeID: "drop")) stack.recordReopen(.tabs(worktreeID: "drop", [TerminalTabID()]))
stack.discard { $0 == "drop" }
#expect(expired == [dropped.id]) #expect(!stack.canRedo) #expect(stack.popUndo()?.retainedSurfaces.first === kept) }
private func makeView() -> GhosttySurfaceView { GhosttySurfaceView( runtime: GhosttyRuntime(), workingDirectory: nil, context: GHOSTTY_SURFACE_CONTEXT_TAB, skipsSurfaceCreationForTesting: true ) }
private func makeTabRecord(_ view: GhosttySurfaceView, worktreeID: Worktree.ID = "wt") -> TerminalCloseRecord { .tabs( worktreeID: worktreeID, [ TerminalClosedTabRecord( item: TerminalTabItem(title: "tab", icon: nil), index: 0, wasSelected: true, tree: SplitTree(view: view), focusedSurfaceID: view.id, wasRunScriptTab: false, boundDirectoryKey: nil, contexts: [:] ) ]) }
private func settle() async { for _ in 0..<10 { await Task.yield() } }}