mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 16:03:49 +00:00
* fix(app): bound the wake/quit paths implicated in the phone-session-ended freeze (#9447) - relay-transport: waitForClose now times out (5s) so a half-open post-sleep socket can't wedge runtimeRpc.stop() - will-quit: race teardown against a 20s deadline so app.quit() always runs (Force Quit was the only escape when any teardown member never settled) - terminal-fit-restore: local restoreTerminalFit invoke gets the same 15s bound as the remote path so the held-fit modal buttons can't pin disabled * fix(app): close wake recovery timeout gaps * fix(relay): drop late frames after forced teardown * fix(relay): fence detached socket callbacks * fix(app): close timeout resource gaps * fix(relay): detach retired mobile transports * fix(types): exclude absent stat overloads * fix(runtime): expire wedged terminal restore dedupe * fix(runtime): keep restore retries on one reclaim * chore(skills): refresh bundled skill manifests * fix(window): fence quit acknowledgements by request * fix(relay): bound revoked device socket cleanup
This commit is contained in:
@@ -60,7 +60,11 @@ vi.mock('../browser/browser-manager', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
import { createMainWindow, loadMainWindow } from './createMainWindow'
|
||||
import {
|
||||
createMainWindow,
|
||||
loadMainWindow,
|
||||
WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS
|
||||
} from './createMainWindow'
|
||||
import { ipcMain } from 'electron'
|
||||
import { shouldRecoverRendererAfterProcessGone } from '../crash-reporting/process-gone-classification'
|
||||
|
||||
@@ -1480,7 +1484,10 @@ describe('createMainWindow', () => {
|
||||
const preventDefault = vi.fn()
|
||||
windowHandlers.close({ preventDefault } as never)
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1)
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', { isQuitting: true })
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: true,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
|
||||
windowHandlers['will-prevent-unload']()
|
||||
expect(onQuitAborted).toHaveBeenCalledTimes(1)
|
||||
@@ -1533,9 +1540,10 @@ describe('createMainWindow', () => {
|
||||
windowHandlers.close({ preventDefault } as never)
|
||||
|
||||
expect(preventDefault).not.toHaveBeenCalled()
|
||||
expect(webContents.send).not.toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: true
|
||||
})
|
||||
expect(webContents.send).not.toHaveBeenCalledWith(
|
||||
'window:close-requested',
|
||||
expect.objectContaining({ isQuitting: true })
|
||||
)
|
||||
|
||||
consoleError.mockRestore()
|
||||
})
|
||||
@@ -1707,7 +1715,8 @@ describe('createMainWindow', () => {
|
||||
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1)
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: true
|
||||
isQuitting: true,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
|
||||
consoleError.mockRestore()
|
||||
@@ -1751,9 +1760,10 @@ describe('createMainWindow', () => {
|
||||
windowHandlers.close({ preventDefault } as never)
|
||||
|
||||
expect(preventDefault).not.toHaveBeenCalled()
|
||||
expect(webContents.send).not.toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: true
|
||||
})
|
||||
expect(webContents.send).not.toHaveBeenCalledWith(
|
||||
'window:close-requested',
|
||||
expect.objectContaining({ isQuitting: true })
|
||||
)
|
||||
})
|
||||
|
||||
// Why (#5787): a hung-but-ALIVE renderer (never gone, never crashed) must NOT
|
||||
@@ -1800,10 +1810,114 @@ describe('createMainWindow', () => {
|
||||
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1)
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: false
|
||||
isQuitting: false,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
})
|
||||
|
||||
it('destroys an already-unresponsive renderer after an app-wide quit deadline', async () => {
|
||||
vi.useFakeTimers()
|
||||
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
||||
const webContents = {
|
||||
id: 42,
|
||||
on: vi.fn((event, handler) => {
|
||||
windowHandlers[event] = handler
|
||||
}),
|
||||
setZoomLevel: vi.fn(),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
invalidate: vi.fn(),
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
send: vi.fn(),
|
||||
isCrashed: vi.fn(() => false)
|
||||
}
|
||||
const destroy = vi.fn()
|
||||
browserWindowMock.mockImplementation(function () {
|
||||
return {
|
||||
webContents,
|
||||
on: vi.fn((event, handler) => {
|
||||
windowHandlers[event] = handler
|
||||
}),
|
||||
isDestroyed: vi.fn(() => false),
|
||||
isMaximized: vi.fn(() => true),
|
||||
isFullScreen: vi.fn(() => false),
|
||||
getSize: vi.fn(() => [1200, 800]),
|
||||
setSize: vi.fn(),
|
||||
maximize: vi.fn(),
|
||||
show: vi.fn(),
|
||||
destroy,
|
||||
loadFile: vi.fn(),
|
||||
loadURL: vi.fn()
|
||||
}
|
||||
})
|
||||
createMainWindow(null, { getIsQuitting: () => true })
|
||||
|
||||
windowHandlers.close({ preventDefault: vi.fn() } as never)
|
||||
await vi.advanceTimersByTimeAsync(WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS - 1)
|
||||
expect(destroy).not.toHaveBeenCalled()
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
|
||||
expect(destroy).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps the renderer-owned close flow after the quit request is acknowledged', async () => {
|
||||
vi.useFakeTimers()
|
||||
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
||||
const ipcHandlers: Record<string, (...args: any[]) => void> = {}
|
||||
vi.mocked(ipcMain.on).mockImplementation((channel, handler) => {
|
||||
ipcHandlers[channel] = handler as (...args: any[]) => void
|
||||
return ipcMain
|
||||
})
|
||||
const webContents = {
|
||||
id: 42,
|
||||
on: vi.fn((event, handler) => {
|
||||
windowHandlers[event] = handler
|
||||
}),
|
||||
setZoomLevel: vi.fn(),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
invalidate: vi.fn(),
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
send: vi.fn(),
|
||||
isCrashed: vi.fn(() => false)
|
||||
}
|
||||
const destroy = vi.fn()
|
||||
browserWindowMock.mockImplementation(function () {
|
||||
return {
|
||||
webContents,
|
||||
on: vi.fn((event, handler) => {
|
||||
windowHandlers[event] = handler
|
||||
}),
|
||||
isDestroyed: vi.fn(() => false),
|
||||
isMaximized: vi.fn(() => true),
|
||||
isFullScreen: vi.fn(() => false),
|
||||
getSize: vi.fn(() => [1200, 800]),
|
||||
setSize: vi.fn(),
|
||||
maximize: vi.fn(),
|
||||
show: vi.fn(),
|
||||
destroy,
|
||||
loadFile: vi.fn(),
|
||||
loadURL: vi.fn()
|
||||
}
|
||||
})
|
||||
createMainWindow(null, { getIsQuitting: () => true })
|
||||
|
||||
windowHandlers.close({ preventDefault: vi.fn() } as never)
|
||||
windowHandlers.close({ preventDefault: vi.fn() } as never)
|
||||
const closeRequests = vi
|
||||
.mocked(webContents.send)
|
||||
.mock.calls.filter(([channel]) => channel === 'window:close-requested')
|
||||
.map(([, request]) => request as { requestId: number })
|
||||
expect(closeRequests).toHaveLength(2)
|
||||
const [staleRequest, currentRequest] = closeRequests
|
||||
ipcHandlers['window:close-request-received']?.({ sender: { id: 99 } }, currentRequest.requestId)
|
||||
ipcHandlers['window:close-request-received']?.({ sender: { id: 42 } }, staleRequest.requestId)
|
||||
await vi.advanceTimersByTimeAsync(WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS - 1)
|
||||
expect(destroy).not.toHaveBeenCalled()
|
||||
ipcHandlers['window:close-request-received']?.({ sender: { id: 42 } }, currentRequest.requestId)
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
|
||||
expect(destroy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('ignores traffic light sync IPC on non-macOS', () => {
|
||||
const windowHandlers: Record<string, (...args: any[]) => void> = {}
|
||||
const webContents = {
|
||||
@@ -3240,7 +3354,8 @@ describe('createMainWindow', () => {
|
||||
|
||||
expect(instance.hide).not.toHaveBeenCalled()
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: false
|
||||
isQuitting: false,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3254,7 +3369,8 @@ describe('createMainWindow', () => {
|
||||
|
||||
expect(instance.hide).not.toHaveBeenCalled()
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: true
|
||||
isQuitting: true,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3300,7 +3416,8 @@ describe('createMainWindow', () => {
|
||||
|
||||
expect(instance.hide).not.toHaveBeenCalled()
|
||||
expect(webContents.send).toHaveBeenCalledWith('window:close-requested', {
|
||||
isQuitting: false
|
||||
isQuitting: false,
|
||||
requestId: expect.any(Number)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user