Files
hermes-agent/apps/desktop/electron/windows-stack-cookie-fallback.test.ts
KoNit-K 085d9ee608 fix(desktop): pass --disable-gpu on 0xC0000409 relaunch so marker write failure cannot loop
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit ecaf213873ca00654d1b3cf543eb56e5bf3279c3)
2026-09-25 01:09:23 -05:00

327 lines
9.8 KiB
TypeScript

import assert from 'node:assert/strict'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { test } from 'vitest'
import { shouldRelaunchForRendererSandboxCrashLoop } from './windows-sandbox-fallback'
import {
alreadyHasDisableGpu,
buildDisableGpuRelaunchArgs,
decideWindowsGpuStackCookieLaunch,
gpuStackCookieFallbackMarker,
gpuStackCookieMarkerPath,
isWindowsStackCookieExit,
readGpuStackCookieMarker,
shouldRelaunchForRendererStackCookieCrashLoop,
shouldSurfaceErrorForRendererStackCookieCrashLoop,
WINDOWS_GPU_STACK_COOKIE_MARKER_FILENAME,
WINDOWS_STACK_COOKIE_EXIT,
writeGpuStackCookieMarker
} from './windows-stack-cookie-fallback'
test('isWindowsStackCookieExit recognizes the issue signed exitCode', () => {
assert.equal(isWindowsStackCookieExit(-1073740791), true)
assert.equal(isWindowsStackCookieExit(WINDOWS_STACK_COOKIE_EXIT), true)
})
test('isWindowsStackCookieExit recognizes unsigned STATUS_STACK_BUFFER_OVERRUN', () => {
assert.equal(isWindowsStackCookieExit(0xc0000409), true)
})
test('isWindowsStackCookieExit rejects STATUS_BREAKPOINT (sandbox owns that code)', () => {
assert.equal(isWindowsStackCookieExit(-2147483645), false)
})
test('isWindowsStackCookieExit rejects unrelated and non-finite values', () => {
assert.equal(isWindowsStackCookieExit(1), false)
assert.equal(isWindowsStackCookieExit(Number.NaN), false)
assert.equal(isWindowsStackCookieExit(Number.POSITIVE_INFINITY), false)
assert.equal(isWindowsStackCookieExit('nope'), false)
assert.equal(isWindowsStackCookieExit(undefined), false)
})
test('shouldRelaunchForRendererStackCookieCrashLoop fires for the issue signature', () => {
assert.equal(
shouldRelaunchForRendererStackCookieCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -1073740791,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: false
}),
true
)
})
test('shouldRelaunchForRendererStackCookieCrashLoop stays off outside Windows', () => {
const base = {
reason: 'crashed' as const,
exitCode: -1073740791,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: false
}
assert.equal(shouldRelaunchForRendererStackCookieCrashLoop({ ...base, platform: 'linux' }), false)
assert.equal(shouldRelaunchForRendererStackCookieCrashLoop({ ...base, platform: 'darwin' }), false)
})
test('shouldRelaunchForRendererStackCookieCrashLoop stays off for oom or killed', () => {
const base = {
platform: 'win32' as const,
exitCode: -1073740791,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: false
}
assert.equal(shouldRelaunchForRendererStackCookieCrashLoop({ ...base, reason: 'oom' }), false)
assert.equal(shouldRelaunchForRendererStackCookieCrashLoop({ ...base, reason: 'killed' }), false)
})
test('shouldRelaunchForRendererStackCookieCrashLoop stays off when GPU is already disabled', () => {
assert.equal(
shouldRelaunchForRendererStackCookieCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -1073740791,
alreadyGpuDisabled: true,
relaunchAttempted: false,
gpuOverrideOff: false
}),
false
)
})
test('shouldRelaunchForRendererStackCookieCrashLoop stays off after a relaunch attempt', () => {
assert.equal(
shouldRelaunchForRendererStackCookieCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -1073740791,
alreadyGpuDisabled: false,
relaunchAttempted: true,
gpuOverrideOff: false
}),
false
)
})
test('shouldRelaunchForRendererStackCookieCrashLoop stays off when GPU override is explicitly off', () => {
assert.equal(
shouldRelaunchForRendererStackCookieCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -1073740791,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: true
}),
false
)
})
test('decideWindowsGpuStackCookieLaunch enables sticky fallback, re-probes on version change, and fail-opens', () => {
const sticky = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
env: {},
appVersion: '1.2.3'
})
assert.equal(sticky.enable, true)
assert.equal(sticky.nextMarker.state, 'fallback')
const linux = decideWindowsGpuStackCookieLaunch({
platform: 'linux',
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
env: {},
appVersion: '1.2.3'
})
assert.equal(linux.enable, false)
const overrideOff = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
env: { HERMES_DESKTOP_DISABLE_GPU: '0' },
appVersion: '1.2.3'
})
assert.equal(overrideOff.enable, false)
const reprobe = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
env: {},
appVersion: '1.3.0'
})
assert.equal(reprobe.enable, false)
assert.equal(reprobe.nextMarker.state, 'booting')
assert.equal(reprobe.nextMarker.reprobe, true)
const legacy = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
marker: { state: 'fallback' },
env: {},
appVersion: '1.3.0'
})
assert.equal(legacy.enable, true)
})
test('alreadyHasDisableGpu honors argv and HERMES_DESKTOP_DISABLE_GPU', () => {
assert.equal(alreadyHasDisableGpu(['--foo', '--disable-gpu'], {}), true)
assert.equal(alreadyHasDisableGpu([], { HERMES_DESKTOP_DISABLE_GPU: '1' }), true)
assert.equal(alreadyHasDisableGpu([], { HERMES_DESKTOP_DISABLE_GPU: 'true' }), true)
assert.equal(alreadyHasDisableGpu(['--disable-gpu-compositing'], {}), false)
assert.equal(alreadyHasDisableGpu(['--no-sandbox'], {}), false)
})
test('buildDisableGpuRelaunchArgs appends a single GPU-off pair', () => {
assert.deepEqual(buildDisableGpuRelaunchArgs(['--foo', '--disable-gpu', 'hermes://x']), [
'--foo',
'hermes://x',
'--disable-gpu',
'--disable-gpu-compositing'
])
})
test('decideWindowsGpuStackCookieLaunch honors argv --disable-gpu even without a marker', () => {
const viaArgs = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
argv: ['--disable-gpu'],
env: {},
marker: null,
appVersion: '1.2.3'
})
assert.equal(viaArgs.enable, true)
assert.equal(viaArgs.reason, 'already-enabled')
assert.deepEqual(viaArgs.nextMarker, { state: 'booting' })
const viaArgsKeepsSticky = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
argv: ['--disable-gpu'],
env: {},
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
appVersion: '1.2.3'
})
assert.equal(viaArgsKeepsSticky.enable, true)
assert.equal(viaArgsKeepsSticky.nextMarker.state, 'fallback')
const overrideOffBeatsArgv = decideWindowsGpuStackCookieLaunch({
platform: 'win32',
argv: ['--disable-gpu'],
env: { HERMES_DESKTOP_DISABLE_GPU: '0' },
marker: { state: 'fallback', reason: 'renderer-crash-loop', version: '1.2.3' },
appVersion: '1.2.3'
})
assert.equal(overrideOffBeatsArgv.enable, false)
})
test('CONTROL: sandbox relaunch stays false for 0xC0000409 (do not piggyback --no-sandbox)', () => {
assert.equal(
shouldRelaunchForRendererSandboxCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -1073740791,
alreadyNoSandbox: false,
relaunchAttempted: false
}),
false
)
assert.notEqual(WINDOWS_GPU_STACK_COOKIE_MARKER_FILENAME, 'windows-sandbox-fallback.json')
})
test('shouldSurfaceErrorForRendererStackCookieCrashLoop when GPU fallback cannot run', () => {
const signature = {
platform: 'win32' as const,
reason: 'crashed' as const,
exitCode: -1073740791
}
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
...signature,
alreadyGpuDisabled: true,
relaunchAttempted: false,
gpuOverrideOff: false
}),
true
)
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
...signature,
alreadyGpuDisabled: false,
relaunchAttempted: true,
gpuOverrideOff: false
}),
true
)
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
...signature,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: true
}),
true
)
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
...signature,
alreadyGpuDisabled: false,
relaunchAttempted: false,
gpuOverrideOff: false
}),
false
)
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
...signature,
platform: 'linux',
alreadyGpuDisabled: true,
relaunchAttempted: false,
gpuOverrideOff: false
}),
false
)
assert.equal(
shouldSurfaceErrorForRendererStackCookieCrashLoop({
platform: 'win32',
reason: 'crashed',
exitCode: -2147483645,
alreadyGpuDisabled: true,
relaunchAttempted: false,
gpuOverrideOff: false
}),
false
)
})
test('GPU stack-cookie marker lives in its own file and round-trips', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-stack-cookie-marker-'))
try {
assert.equal(gpuStackCookieMarkerPath(dir), path.join(dir, WINDOWS_GPU_STACK_COOKIE_MARKER_FILENAME))
assert.equal(WINDOWS_GPU_STACK_COOKIE_MARKER_FILENAME, 'windows-gpu-stack-cookie-fallback.json')
assert.equal(readGpuStackCookieMarker(dir), null)
writeGpuStackCookieMarker(dir, gpuStackCookieFallbackMarker('renderer-crash-loop', '1.2.3'))
assert.deepEqual(readGpuStackCookieMarker(dir), {
state: 'fallback',
reason: 'renderer-crash-loop',
version: '1.2.3'
})
} finally {
fs.rmSync(dir, { recursive: true, force: true })
}
})