Skip to content

Add regression coverage for terminal grow resize flush - #8293

Closed
austinywang wants to merge 2 commits into
mainfrom
issue-8285-terminal-width-resize-stuck
Closed

austinywang wants to merge 2 commits into
mainfrom
issue-8285-terminal-width-resize-stuck

Conversation

@austinywang

Copy link
Copy Markdown
Contributor

Summary

  • add a behavior-level regression for a split-divider drag that delivers an intermediate shrink but loses the final grow notification
  • verify the interaction-end transaction flushes the terminal surface back to the authoritative final width
  • keep the runtime change in the already-merged shared sizing fix from Coalesce terminal resizes during split-divider drags #8240; this PR is regression coverage only

Root cause

The affected nightly could apply an intermediate narrow surface size during a divider drag, then lose/coalesce the final wider anchor notification. At that point TerminalWindowPortalRegistry.endInteractiveGeometryResize only ended the interaction; it did not schedule a final geometry reconciliation. Nothing called the final Ghostty surface/PTY resize, so the TUI remained at the narrow width.

The dropped layer was therefore portal interaction end → final surface/PTY resize, not Ghostty grid reflow or scrollback compression.

Merged PR #8240 fixed the shared boundary: the zero crossing of the window-scoped interactive geometry transaction schedules one settled portal synchronization, which applies the exact final renderer and PTY dimensions. That is the architectural invariant this test locks in; no repaint kick, polling, or sleep-based repair is involved.

Regression evidence

The fixture reproduces the report deterministically:

  1. Bind a tracked terminal surface at 420 px.
  2. Begin an interactive divider resize.
  3. Deliver an intermediate shrink to 180 px and drain synchronization.
  4. Restore the anchor to 420 px with frame notifications disabled, modeling a coalesced/lost final grow event.
  5. End the interaction and assert the terminal returns to its initial width.

Against affected nightly commit 1c22c5564, the test fails:

XCTAssertEqual failed: ("180") is not equal to ("420")
Divider drag end must flush the final wider width after an intermediate shrink

Because the runtime fix was already merged through #8240 before this issue branch was finalized, a two-commit red/fix sequence in this PR would be artificial. The red run above is from the exact historical runtime; this PR contains one regression-test commit.

Against current main de74852e5c on an AWS M4 Pro running macOS 15.7.4 / Xcode 26.3, the focused app-host test passes:

Test Case '-[cmuxTests.TerminalWindowPortalLifecycleTests testDividerShrinkThenGrowFlushesFinalWiderSurfaceSize]' passed
Executed 2 tests, with 0 failures

The second selected test initializes the app-host routing fixture required by direct xctest execution.

Link to #8229

This is the grow-direction manifestation of the same interactive sizing transaction fixed for #8229 by #8240. The #8229 investigation ruled out Ghostty scrollback compression; no Ghostty submodule change is needed here.

Validation

  • AWS macOS 15.7.4: cmux-unit build-for-testing succeeded
  • AWS focused direct xctest: 2 tests, 0 failures
  • git diff --check
  • ./scripts/check-pbxproj.sh
  • ./scripts/lint-pbxproj-test-wiring.sh
  • python3 scripts/check-workspace-package-groups.py --check
  • python3 scripts/check-package-resolved-policy.py

Current main no longer contains .github/swift-file-length-budget.tsv or scripts/swift_file_length_budget.py (removed by #8125); this PR does not recreate or modify them. The touched test file remains 224 lines.

No user-facing strings changed, so localization catalogs are unaffected.

Closes #8285

@greptile-apps

greptile-apps Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a focused regression test for #8285, verifying that TerminalWindowPortalRegistry.endInteractiveGeometryResize flushes the terminal surface to its authoritative final width when an intermediate shrink was delivered but the final grow notification was coalesced away. No production code is changed.

  • Adds testDividerShrinkThenGrowFlushesFinalWiderSurfaceSize to TerminalWindowPortalLifecycleHiddenRefreshTests, reproducing the divider-drag scenario deterministically by manually invoking synchronizeForAnchor for the intermediate shrink and suppressing the final-grow notification via postsFrameChangedNotifications = false.
  • Integrates correctly with the existing test-class tracking infrastructure (makeTrackedTerminalSurface, trackedWindows, tearDown), so window and surface teardown is handled automatically without needing an explicit per-test defer block.

Confidence Score: 5/5

This PR is safe to merge — it adds a single regression test with no production code changes.

The change is a pure test addition that integrates cleanly with the existing TerminalWindowPortalLifecycleTests infrastructure. Window and surface teardown are handled automatically by the makeTestWindow tracking system and tearDown(). The two drainMainQueue() calls before realizeWindowLayout correctly model the async-drain pattern used by other tests in the file, and the assertion sequence accurately reproduces the coalesced-notification scenario.

No files require special attention.

Important Files Changed

Filename Overview
cmuxTests/TerminalWindowPortalLifecycleHiddenRefreshTests.swift Adds regression test for the shrink→grow flush invariant; test logic, drainMainQueue sequencing, and resource tracking all look correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Test
    participant Anchor as NSView (anchor)
    participant Registry as TerminalWindowPortalRegistry
    participant Surface as TerminalSurface (PTY)

    Test->>Registry: bind(hostedView, to: anchor)
    Test->>Registry: synchronizeForAnchor(anchor)
    Test->>Test: drainMainQueue() x2
    Test->>Test: realizeWindowLayout(window)
    Test->>Surface: "debugCurrentPixelSize() -> 420px (initial)"

    Note over Test,Anchor: Begin interactive divider drag
    Test->>Anchor: "postsFrameChangedNotifications = false"
    Test->>Registry: beginInteractiveGeometryResize(owner, window)

    Note over Test,Surface: Intermediate shrink - notification delivered manually
    Test->>Anchor: "frame.size.width = 180"
    Test->>Registry: synchronizeForAnchor(anchor)
    Test->>Test: drainMainQueue() x2
    Test->>Surface: "debugCurrentPixelSize() -> 180px (narrow)"

    Note over Test,Surface: Final grow - notification coalesced/lost
    Test->>Anchor: "frame = initialAnchorFrame (420px, no notification)"
    Test->>Surface: "debugCurrentPixelSize() -> still 180px"

    Note over Test,Surface: End interaction - must flush final width
    Test->>Registry: endInteractiveGeometryResize(owner)
    Test->>Test: drainMainQueue() x2
    Test->>Surface: "debugCurrentPixelSize() -> 420px (flushed)"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Test
    participant Anchor as NSView (anchor)
    participant Registry as TerminalWindowPortalRegistry
    participant Surface as TerminalSurface (PTY)

    Test->>Registry: bind(hostedView, to: anchor)
    Test->>Registry: synchronizeForAnchor(anchor)
    Test->>Test: drainMainQueue() x2
    Test->>Test: realizeWindowLayout(window)
    Test->>Surface: "debugCurrentPixelSize() -> 420px (initial)"

    Note over Test,Anchor: Begin interactive divider drag
    Test->>Anchor: "postsFrameChangedNotifications = false"
    Test->>Registry: beginInteractiveGeometryResize(owner, window)

    Note over Test,Surface: Intermediate shrink - notification delivered manually
    Test->>Anchor: "frame.size.width = 180"
    Test->>Registry: synchronizeForAnchor(anchor)
    Test->>Test: drainMainQueue() x2
    Test->>Surface: "debugCurrentPixelSize() -> 180px (narrow)"

    Note over Test,Surface: Final grow - notification coalesced/lost
    Test->>Anchor: "frame = initialAnchorFrame (420px, no notification)"
    Test->>Surface: "debugCurrentPixelSize() -> still 180px"

    Note over Test,Surface: End interaction - must flush final width
    Test->>Registry: endInteractiveGeometryResize(owner)
    Test->>Test: drainMainQueue() x2
    Test->>Surface: "debugCurrentPixelSize() -> 420px (flushed)"
Loading

Reviews (2): Last reviewed commit: "Stabilize terminal resize regression set..." | Re-trigger Greptile

Comment thread cmuxTests/TerminalWindowPortalLifecycleHiddenRefreshTests.swift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nightly 0.64.19: terminal width resize is one-way — TUI stuck at narrowed width, widening never propagates back

1 participant