From 2f73775ffc2cab8d07ba4fcf2e9b90ae7c88547f Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Sat, 1 Aug 2026 23:38:46 -0700 Subject: [PATCH] fix(terminal): bound fullscreen atlas recovery (#12061) --- config/reliability-gates.jsonc | 123 ++++++++++++++++++ .../terminal-pane/pty-connection.test.ts | 18 +-- .../terminal-pane/pty-connection.ts | 20 +-- .../use-terminal-pane-global-effects.test.ts | 8 +- .../use-terminal-pane-global-effects.ts | 1 + .../use-terminal-window-wake-recovery.test.ts | 16 +++ .../use-terminal-window-wake-recovery.ts | 2 +- .../pane-manager-registry.test.ts | 28 ++++ .../lib/pane-manager/pane-manager-registry.ts | 11 +- .../src/lib/pane-manager/pane-manager.ts | 10 ++ ...document-visibility-webgl-recovery.spec.ts | 25 ++-- 11 files changed, 222 insertions(+), 40 deletions(-) diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index ded4255d7..4ce779f47 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -7476,6 +7476,129 @@ ], "demotionRule": "Disable the probe if it exceeds its per-reveal budget or produces false-positive anomaly noise." }, + { + "id": "terminal-render.atlas-recovery-fanout", + "title": "Terminal atlas recovery stays bounded to visible renderers", + "maturity": "experimental", + "protection": "partial", + "owner": "terminal-rendering", + "layer": "renderer-unit-and-electron", + "surfaces": [ + "WebGL rendering", + "document visibility", + "hidden terminal output", + "paired terminal traffic" + ], + "platforms": ["macos", "linux", "windows"], + "providers": ["local", "daemon", "ssh", "remote-runtime"], + "coveredPlatforms": ["macos"], + "coveredProviders": ["local"], + "coverageNotes": "A deterministic renderer contract models one visible manager plus 64 mounted hidden managers and hidden synchronized/TUI output. A macOS Electron run verifies that a document visibility cycle preserves real WebGL atlases and terminal pixels. Production v1.4.163 evidence linked the same 49-manager fanout to paired traffic, but the candidate has not been rerun against an isolated live paired server.", + "motivatingLinks": [ + "https://github.com/stablyai/orca/pull/7054", + "https://github.com/stablyai/orca/pull/7604" + ], + "invariant": "Ordinary document visibility transitions and hidden terminal output must not clear the shared WebGL glyph atlas. Heavy reset-and-refresh recovery may touch only managers with visible terminal surfaces; hidden managers recover when revealed. Genuine OS resume remains a heavy recovery trigger.", + "oracle": "Dispatch a visible document visibilitychange and require atlas-preserving wake recovery; register one visible manager and 64 hidden managers and require exactly one reset and one refresh; parse hidden synchronized and high-confidence TUI output and require zero global atlas-recovery schedules. Separately, drive an Electron visibility cycle with two real WebGL panes, require zero atlas clears, and retain at least 85% of each pane's baseline ink pixels.", + "commands": [ + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts", + "pnpm run ensure:electron-runtime && pnpm exec playwright test tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", + "pnpm run ensure:electron-runtime && pnpm exec playwright test tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1" + ], + "testFiles": [ + "src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts", + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts", + "src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts", + "src/renderer/src/components/terminal-pane/pty-connection.test.ts", + "tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts" + ], + "assertionRefs": [ + { + "file": "src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts", + "assertions": ["preserves the glyph atlas when a fullscreen Space becomes visible"] + }, + { + "file": "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts", + "assertions": [ + "preserves WebGL texture atlases when the active terminal document becomes visible" + ] + }, + { + "file": "src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts", + "assertions": ["bounds atlas recovery to visible managers"] + }, + { + "file": "src/renderer/src/components/terminal-pane/pty-connection.test.ts", + "assertions": [ + "defers hidden synchronized-output atlas recovery until reveal", + "defers hidden high-confidence TUI redraw recovery until reveal", + "advances hidden rewrite state without scheduling atlas recovery" + ] + }, + { + "file": "tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts", + "assertions": [ + "preserves the WebGL atlas and keeps terminal text painted after document visibility resumes" + ] + } + ], + "evidenceRuns": [ + { + "date": "2026-08-01", + "runner": "local", + "platform": "macos", + "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts", + "result": "passed", + "durationSeconds": 26, + "summary": "All 577 focused renderer contracts passed, including atlas-preserving visibility, visible-only recovery fanout, and hidden-output rewrite-state coverage." + }, + { + "date": "2026-08-01", + "runner": "local", + "platform": "macos", + "command": "pnpm run ensure:electron-runtime && pnpm exec playwright test tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", + "result": "passed", + "durationSeconds": 18, + "summary": "The changed-spec CI topology retained both real WebGL terminal panes with zero atlas clears after a deterministic document visibility cycle." + }, + { + "date": "2026-08-01", + "runner": "local", + "platform": "macos", + "command": "pnpm run ensure:electron-runtime && pnpm exec playwright test tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1", + "result": "passed", + "durationSeconds": 23, + "summary": "Two real WebGL terminal panes retained painted glyphs with zero atlas clears after a deterministic document visibility cycle. BrowserWindow.hide did not change document visibility in the harness, so the test used its explicit visibility-event fallback." + } + ], + "runtimeBudget": { + "p95Seconds": 60, + "scope": "focused renderer contracts plus one prebuilt Electron visibility test" + }, + "flakeHistory": { + "status": "unknown", + "evidence": "The focused unit oracle and one local Electron run passed; CI soak history is not yet available." + }, + "redGreenEvidence": { + "status": "complete", + "evidence": "With the fix disabled, the byte-identical unit oracle observed clearGlyphAtlases=true, reset/refreshed all 65 managers, and scheduled hidden synchronized/TUI recovery one to three times. Restoring the fix made every assertion pass." + }, + "performanceBudget": { + "required": true, + "evidence": "A recovery with one visible and 64 hidden managers performs one reset and one refresh instead of 65 of each. Hidden synchronized/TUI output schedules zero global recovery work; no polling, cooldown, provider call, or parking change is added." + }, + "promotionCriteria": [ + "Accumulate stable macOS Electron runs with real BrowserWindow visibility transitions.", + "Run an isolated headed paired-server terminal flood and verify bounded renderer CPU and atlas diagnostics.", + "Add Linux and Windows WebGL visibility evidence before claiming cross-platform visual coverage." + ], + "knownGaps": [ + "The Electron harness used a deterministic visibility-event fallback because BrowserWindow.hide did not change document.visibilityState.", + "No isolated live paired-server candidate run is recorded; remote-runtime coverage is a provider-agnostic renderer contract plus production incident evidence.", + "The gate counts recovery fanout and pixel retention but does not impose an end-to-end renderer frame-latency threshold." + ], + "demotionRule": "Demote if hidden managers re-enter reset/refresh recovery, ordinary visibility clears the atlas, real WebGL pixels regress, or the focused Electron test cannot remain deterministic." + }, { "id": "terminal-render.atlas-identity-invalidation", "title": "A WebGL atlas identity change rebuilds cached glyph vertices", diff --git a/src/renderer/src/components/terminal-pane/pty-connection.test.ts b/src/renderer/src/components/terminal-pane/pty-connection.test.ts index 2e0ce2b21..478f69392 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.test.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.test.ts @@ -12072,7 +12072,7 @@ describe('connectPanePty', () => { }) }) - it('schedules WebGL atlas recovery after hidden synchronized output parses', async () => { + it('defers hidden synchronized-output atlas recovery until reveal', async () => { const { connectPanePty } = await import('./pty-connection') const transport = createMockTransport('pty-id') const capturedDataCallback: { current: ((data: string) => void) | null } = { current: null } @@ -12112,13 +12112,13 @@ describe('connectPanePty', () => { parseCallbacks[0]?.() - expect(scheduleTerminalWebglAtlasRecovery).toHaveBeenCalledTimes(3) + expect(scheduleTerminalWebglAtlasRecovery).not.toHaveBeenCalled() } finally { vi.useRealTimers() } }) - it('recognizes hidden synchronized output markers split across PTY chunks', async () => { + it('defers split hidden synchronized-output markers until reveal', async () => { const { connectPanePty } = await import('./pty-connection') const transport = createMockTransport('pty-id') const capturedDataCallback: { current: ((data: string) => void) | null } = { current: null } @@ -12154,7 +12154,7 @@ describe('connectPanePty', () => { parseCallbacks[0]?.() - expect(scheduleTerminalWebglAtlasRecovery).toHaveBeenCalledTimes(3) + expect(scheduleTerminalWebglAtlasRecovery).not.toHaveBeenCalled() } finally { vi.useRealTimers() } @@ -12199,7 +12199,7 @@ describe('connectPanePty', () => { } }) - it('schedules hidden atlas recovery for high-confidence TUI redraw controls', async () => { + it('defers hidden high-confidence TUI redraw recovery until reveal', async () => { const { connectPanePty } = await import('./pty-connection') const transport = createMockTransport('pty-id') const capturedDataCallback: { current: ((data: string) => void) | null } = { current: null } @@ -12230,13 +12230,13 @@ describe('connectPanePty', () => { parseCallbacks[0]?.() - expect(scheduleTerminalWebglAtlasRecovery).toHaveBeenCalledTimes(1) + expect(scheduleTerminalWebglAtlasRecovery).not.toHaveBeenCalled() } finally { vi.useRealTimers() } }) - it('advances hidden rewrite state when synchronized output already requests recovery', async () => { + it('advances hidden rewrite state without scheduling atlas recovery', async () => { const { connectPanePty } = await import('./pty-connection') const transport = createMockTransport('pty-id') const capturedDataCallback: { current: ((data: string) => void) | null } = { current: null } @@ -12270,7 +12270,7 @@ describe('connectPanePty', () => { vi.advanceTimersByTime(50) expect(writes).toEqual(['prompt rewrite\r', '\x1b[?2026hredraw frame\x1b[?2026l']) parseCallbacks.shift()?.() - expect(scheduleTerminalWebglAtlasRecovery).toHaveBeenCalledTimes(1) + expect(scheduleTerminalWebglAtlasRecovery).not.toHaveBeenCalled() scheduleTerminalWebglAtlasRecovery.mockClear() capturedDataCallback.current?.('plain after frame') @@ -12313,7 +12313,7 @@ describe('connectPanePty', () => { capturedDataCallback.current?.('\x1b[?2026h') vi.advanceTimersByTime(50) parseCallbacks.shift()?.() - expect(scheduleTerminalWebglAtlasRecovery).toHaveBeenCalledTimes(1) + expect(scheduleTerminalWebglAtlasRecovery).not.toHaveBeenCalled() scheduleTerminalWebglAtlasRecovery.mockClear() writes.length = 0 diff --git a/src/renderer/src/components/terminal-pane/pty-connection.ts b/src/renderer/src/components/terminal-pane/pty-connection.ts index dfa0a237e..00c5f0940 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.ts @@ -6250,16 +6250,20 @@ export function connectPanePty( const renderRefreshDecision = foregroundOutput ? shouldForceForegroundRenderRefresh(data) : { refresh: false, inPlaceRewrite: false, recoverWebglAtlasAfterParse: false } - const recoverHiddenWebglAtlasAfterParse = - !foregroundOutput && hiddenOutputNeedsAtlasRecoveryAfterParse(data) + if (!foregroundOutput) { + // Advance hidden rewrite state; reveal owns atlas recovery. + void hiddenOutputNeedsAtlasRecoveryAfterParse(data) + } const recoverWebglAtlasAfterParse = - renderRefreshDecision.recoverWebglAtlasAfterParse || recoverHiddenWebglAtlasAfterParse + foreground && renderRefreshDecision.recoverWebglAtlasAfterParse // Why: atlas recovery must repaint from the parsed xterm buffer, not a pre-write snapshot a late TUI redraw can stale. - const onParsedAtlasRecovery = recoverWebglAtlasAfterParse - ? scheduleTerminalWebglAtlasRecovery - : renderRefreshDecision.inPlaceRewrite - ? alternateScreenRewriteAtlasRecoveryOnParsed() - : undefined + const onParsedAtlasRecovery = foreground + ? recoverWebglAtlasAfterParse + ? scheduleTerminalWebglAtlasRecovery + : renderRefreshDecision.inPlaceRewrite + ? alternateScreenRewriteAtlasRecoveryOnParsed() + : undefined + : undefined const foregroundRenderRefreshNeeded = renderRefreshDecision.refresh // Why: Claude Code's in-place prompt redraws on Windows ConPTY can paint one frame late; a follow-up repaint fixes the column desync without a resize. const nativeWindowsInPlaceRewriteFollowup = nativeWindowsRewriteNeedsFollowupRenderRefresh({ diff --git a/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts b/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts index f271ab092..0c5fdae0c 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts @@ -974,7 +974,7 @@ describe('useTerminalPaneGlobalEffects', () => { expect(manager.refreshAllPanes).toHaveBeenCalledTimes(1) }) - it('clears WebGL texture atlases when the active visible terminal document becomes visible', () => { + it('preserves WebGL texture atlases when the active terminal document becomes visible', () => { let visibilityState: DocumentVisibilityState = 'hidden' const documentListeners = new Map() vi.stubGlobal('document', { @@ -1024,6 +1024,7 @@ describe('useTerminalPaneGlobalEffects', () => { throw new Error('expected visibilitychange listener') } manager.resetWebglTextureAtlases.mockClear() + manager.scheduleRevealPresent.mockClear() siblingManager.resetWebglTextureAtlases.mockClear() listener(new Event('visibilitychange')) expect(manager.resetWebglTextureAtlases).not.toHaveBeenCalled() @@ -1032,8 +1033,9 @@ describe('useTerminalPaneGlobalEffects', () => { visibilityState = 'visible' listener(new Event('visibilitychange')) - expect(manager.resetWebglTextureAtlases).toHaveBeenCalledTimes(1) - expect(siblingManager.resetWebglTextureAtlases).toHaveBeenCalledTimes(1) + expect(manager.resetWebglTextureAtlases).not.toHaveBeenCalled() + expect(siblingManager.resetWebglTextureAtlases).not.toHaveBeenCalled() + expect(manager.scheduleRevealPresent).toHaveBeenCalledTimes(1) }) it('registers document visibility recovery for visible inactive terminals but not hidden ones', () => { diff --git a/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.ts b/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.ts index 95f9198e7..f9a6e5441 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.ts @@ -142,6 +142,7 @@ export function useTerminalPaneGlobalEffects({ if (!manager) { return } + manager.setAtlasRecoveryVisible?.(rendererVisible) const wasVisible = wasVisibleRef.current const wasWorktreeActive = wasWorktreeActiveRef.current isActiveRef.current = isActive diff --git a/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts b/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts index 4862bc145..b866c90af 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts @@ -79,6 +79,22 @@ describe('useTerminalWindowWakeRecovery', () => { }) }) + it('preserves the glyph atlas when a fullscreen Space becomes visible', () => { + renderWakeRecoveryHook() + Object.defineProperty(document, 'visibilityState', { + configurable: true, + value: 'visible' + }) + + document.dispatchEvent(new Event('visibilitychange')) + + expect(recoverVisibleTerminalWindowWakeMock).toHaveBeenLastCalledWith({ + manager, + isActive: true, + clearGlyphAtlases: false + }) + }) + it('records a wake-recovery breadcrumb with the trigger source and atlas decision', () => { // Why: a post-wake garble report attributes to the trigger that ran (or its // absence). Pin that focus records source=focus/atlas=false and system diff --git a/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.ts b/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.ts index 7405f0434..ba8102a5f 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.ts @@ -101,7 +101,7 @@ export function useTerminalWindowWakeRecovery({ const onFocus = (): void => recoverVisibleWake(false, 'focus') const onVisibilityChange = (): void => { if (typeof document !== 'undefined' && document.visibilityState === 'visible') { - recoverVisibleWake(true, 'visibilitychange') + recoverVisibleWake(false, 'visibilitychange') } } // Why: Linux has no window-occlusion tracking, so visibilitychange never diff --git a/src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts b/src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts index c423004e0..3dcd0482f 100644 --- a/src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts @@ -82,6 +82,34 @@ describe('pane manager registry', () => { expect(order).toEqual(['first-reset', 'second-reset', 'first-refresh', 'second-refresh']) }) + it('bounds atlas recovery to visible managers', () => { + const visible = { + resetWebglTextureAtlases: vi.fn<() => void>(), + refreshAllPanes: vi.fn<() => void>(), + isVisibleForAtlasRecovery: () => true + } + registerLivePaneManager(visible) + registeredManagers.push(visible) + const hidden = Array.from({ length: 64 }, () => ({ + resetWebglTextureAtlases: vi.fn<() => void>(), + refreshAllPanes: vi.fn<() => void>(), + isVisibleForAtlasRecovery: () => false + })) + for (const manager of hidden) { + registerLivePaneManager(manager) + registeredManagers.push(manager) + } + + resetAndRefreshAllTerminalWebglAtlases() + + expect(visible.resetWebglTextureAtlases).toHaveBeenCalledOnce() + expect(visible.refreshAllPanes).toHaveBeenCalledOnce() + expect( + hidden.every((manager) => manager.resetWebglTextureAtlases.mock.calls.length === 0) + ).toBe(true) + expect(hidden.every((manager) => manager.refreshAllPanes.mock.calls.length === 0)).toBe(true) + }) + it('continues reset-and-refresh recovery when one manager throws', () => { const broken = { resetWebglTextureAtlases: vi.fn<() => void>(() => { diff --git a/src/renderer/src/lib/pane-manager/pane-manager-registry.ts b/src/renderer/src/lib/pane-manager/pane-manager-registry.ts index 6ebe2b492..b48a0d6d4 100644 --- a/src/renderer/src/lib/pane-manager/pane-manager-registry.ts +++ b/src/renderer/src/lib/pane-manager/pane-manager-registry.ts @@ -8,6 +8,7 @@ type RegisteredPaneManager = { getRenderingDiagnostics?: () => PaneRenderingDiagnostics[] getPanes?: () => { id: number; terminal: unknown }[] getPaneCount?: () => number + isVisibleForAtlasRecovery?: () => boolean } const liveManagers = new Set() @@ -48,9 +49,15 @@ export function resetAllTerminalWebglAtlases(): void { export function resetAndRefreshAllTerminalWebglAtlases(): void { // Why: the atlas wipe is the heavy recovery path; recording it lets a freeze // report show whether a post-wake repaint actually ran. Silent breadcrumb. - recordTerminalWebglDiagnostic('webgl-atlas-reset', { managers: liveManagers.size }) + const recoveryManagers = Array.from(liveManagers).filter( + (manager) => manager.isVisibleForAtlasRecovery?.() !== false + ) + recordTerminalWebglDiagnostic('webgl-atlas-reset', { + managers: recoveryManagers.length, + mountedManagers: liveManagers.size + }) const resetManagers: RegisteredPaneManager[] = [] - for (const manager of liveManagers) { + for (const manager of recoveryManagers) { try { manager.resetWebglTextureAtlases() resetManagers.push(manager) diff --git a/src/renderer/src/lib/pane-manager/pane-manager.ts b/src/renderer/src/lib/pane-manager/pane-manager.ts index f4ab728ad..c653cd633 100644 --- a/src/renderer/src/lib/pane-manager/pane-manager.ts +++ b/src/renderer/src/lib/pane-manager/pane-manager.ts @@ -72,6 +72,7 @@ export class PaneManager { private styleOptions: PaneStyleOptions = {} private destroyed = false private renderingSuspended: boolean + private atlasRecoveryVisible: boolean private identities = new PaneIdentityRegistry() private pendingPaneReparentFrameIds = new Set() @@ -82,6 +83,7 @@ export class PaneManager { this.root = root this.options = options this.renderingSuspended = options.initialRenderingSuspended === true + this.atlasRecoveryVisible = !this.renderingSuspended // Why: atlas recovery must reach every live manager — see // resetAllTerminalWebglAtlases for the shared-atlas rationale. registerLivePaneManager(this) @@ -358,6 +360,14 @@ export class PaneManager { resetPaneWebglTextureAtlases(this.panes.values()) } + setAtlasRecoveryVisible(visible: boolean): void { + this.atlasRecoveryVisible = visible + } + + isVisibleForAtlasRecovery(): boolean { + return this.atlasRecoveryVisible && !this.destroyed + } + scheduleRevealRepaint(): void { // Why: the settled-frame callback can fire after destroy(); repainting // disposed panes could throw in attach and latch the global WebGL diff --git a/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts b/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts index cfcdddecd..1aa355b31 100644 --- a/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts +++ b/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts @@ -280,8 +280,8 @@ async function dispatchDocumentVisibilityCycle(page: Page): Promise { }) } -test.describe('terminal document visibility WebGL recovery @headful', () => { - test('clears the WebGL atlas and keeps terminal text painted after document visibility resumes', async ({ +test.describe('terminal document visibility WebGL recovery', () => { + test('preserves the WebGL atlas and keeps terminal text painted after document visibility resumes', async ({ electronApp, orcaPage }, testInfo) => { @@ -293,7 +293,7 @@ test.describe('terminal document visibility WebGL recovery @headful', () => { await waitForPaneCount(orcaPage, 2) const webglActive = await forceWebgl(orcaPage) - test.skip(!webglActive, 'WebGL was not active in this headful environment') + test.skip(!webglActive, 'WebGL was not active in this Electron environment') await writeStableTerminalContent(orcaPage) expect(await patchAtlasCounter(orcaPage)).toBe(true) @@ -317,25 +317,16 @@ test.describe('terminal document visibility WebGL recovery @headful', () => { console.log( `[visibility-webgl] browserWindowVisibilityWorked=${browserWindowVisibilityWorked}` ) - if (browserWindowVisibilityWorked) { - await expect - .poll(() => readAtlasResetCount(orcaPage), { - timeout: 2_000, - message: 'BrowserWindow visibility resume did not clear the WebGL atlas' - }) - .toBeGreaterThan(0) - } else { + if (!browserWindowVisibilityWorked) { await resetAtlasResetCount(orcaPage) await dispatchDocumentVisibilityCycle(orcaPage) - await expect - .poll(() => readAtlasResetCount(orcaPage), { - timeout: 2_000, - message: 'document visibility resume did not clear the WebGL atlas' - }) - .toBeGreaterThan(0) } await waitForTerminalPaint(orcaPage) + expect( + await readAtlasResetCount(orcaPage), + 'ordinary document visibility resume cleared the shared WebGL atlas' + ).toBe(0) const afterResume = await terminalScreenshots(orcaPage) for (const [index, baselineShot] of baseline.entries()) {