Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions cmuxTests/PlainPastePTYFixture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class PlainPastePTYFixture {
let surface: TerminalSurface
let window: NSWindow
private let previousMenu: NSMenu?
private let workerClient: TerminalPastePreparationWorkerClient
var view: GhosttyNSView { surface.hostedView.surfaceView }

init(optimized: Bool) throws {
Expand All @@ -40,6 +41,7 @@ final class PlainPastePTYFixture {
executableURL: fullWrapper, pasteboardService: owner,
plainTextExecutableURL: optimized ? textWrapper : nil
)
workerClient = client
let service = TerminalImageTransferPreparationService(
operation: { try await client.prepare($0) },
cleanup: { $0.cleanupTransferredTemporaryFiles(using: owner) }
Expand Down Expand Up @@ -118,6 +120,35 @@ final class PlainPastePTYFixture {
try #require(surface.readText(region: .screen)?.contains("PASTE_READY") == true)
}

/// Warms this fixture's worker launch path before the timed trials.
///
/// The launch-counting wrapper is a fresh shell script per fixture, and
/// its first launch of the app binary is slow on the owned Mac runners:
/// 0.86-1.1 s on an idle mini, over the paste service's 5 s deadline on a
/// loaded one, which drops trial 0 as `deadlineExceeded`. The same binary
/// launched directly, as the app does, answered the same general-pasteboard
/// paste request in ~50 ms just before, and every later wrapper launch
/// takes ~15-50 ms. So run one preparation through the wrapper, outside the
/// service and its deadline, then clear the launch log so the per-trial
/// counts are unchanged and trial 0 measures a fresh worker per paste.
func warmWorkerLaunchPath() async throws {
NSPasteboard.general.clearContents()
try #require(NSPasteboard.general.setString("cmux-paste-pty-warmup", forType: .string))
let started = ContinuousClock.now
let result = try await workerClient.prepare(TerminalPastePreparationRequest(
pasteboard: TerminalPasteboardReadRequest(pasteboard: NSPasteboard.general),
mode: .paste,
destination: .terminal
))
let elapsed = started.duration(to: .now)
guard case .terminal(.insertText("cmux-paste-pty-warmup")) = result else {
Issue.record("Worker warm-up did not read the warm-up text: \(result)")
return
}
try Data().write(to: launches)
print("PASTE_PTY_WARMUP duration=\(elapsed)")
}

func receipt(trial: Int) async throws -> [String: Any] {
let url = root.appendingPathComponent("receipt-\(trial).json")
let deadline = ContinuousClock.now + .seconds(20)
Expand Down
13 changes: 12 additions & 1 deletion cmuxTests/TerminalPlainTextPastePTYTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension TerminalPlainTextPasteStartupTests {
let fixture = try PlainPastePTYFixture(optimized: optimized)
defer { fixture.close() }
try await fixture.waitUntilReady()
try await fixture.warmWorkerLaunchPath()
for trial in 0..<6 {
let text = "paste-\(trial) 日本語 🦀 e\u{301}\nsecond\tline\n"
NSPasteboard.general.clearContents()
Expand All @@ -32,7 +33,17 @@ extension TerminalPlainTextPasteStartupTests {
windowNumber: fixture.window.windowNumber, context: nil,
characters: "v", charactersIgnoringModifiers: "v", isARepeat: false, keyCode: 9
))
try #require(fixture.view.performKeyEquivalent(with: event))
// A real Cmd+V reaches the terminal through its key window,
// where the terminal is first responder. On a live window
// server (the owned Mac runners) this fixture window is not
// key, and cmux's focus handling has yielded the terminal's
// responder to the window by the time the key is sent.
// Restore the key-window precondition per keystroke.
try #require(fixture.window.makeFirstResponder(fixture.view))
try #require(
fixture.view.performKeyEquivalent(with: event),
"Cmd+V was not handled; firstResponder=\(String(describing: fixture.window.firstResponder))"
)
case 1:
fixture.view.paste(nil)
default:
Expand Down
Loading