import { mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { GPU_CRASH_THRESHOLD, GPU_CRASH_WINDOW_MS, readGpuFallbackState, recordGpuCrash, shouldUseSoftwareRendering, } from '../../electron/main/gpu-fallback'; const temporaryDirectories: string[] = []; afterEach(() => { for (const directory of temporaryDirectories.splice(0)) { rmSync(directory, { recursive: true, force: true }); } }); describe('GPU fallback policy', () => { it('enables software rendering only after repeated recent GPU failures', () => { const now = 1_000_000; expect(shouldUseSoftwareRendering({ state: null, now })).toBe(false); expect(shouldUseSoftwareRendering({ state: { crashCount: GPU_CRASH_THRESHOLD, lastCrashAt: now, reason: 'crashed:1' }, now, })).toBe(true); expect(shouldUseSoftwareRendering({ state: { crashCount: GPU_CRASH_THRESHOLD, lastCrashAt: now - GPU_CRASH_WINDOW_MS - 1, reason: 'crashed:1', }, now, })).toBe(false); expect(shouldUseSoftwareRendering({ cliDisabled: true, now })).toBe(true); }); it('persists an atomic crash counter and ignores malformed state', () => { const directory = mkdtempSync(join(tmpdir(), 'makelore-gpu-')); temporaryDirectories.push(directory); const filePath = join(directory, 'nested', 'gpu-fallback.json'); const first = recordGpuCrash(filePath, 'crashed:9', 10); const second = recordGpuCrash(filePath, 'oom:137', 20); expect(first.crashCount).toBe(1); expect(second.crashCount).toBe(2); expect(readGpuFallbackState(filePath, 20)).toEqual(second); expect(JSON.parse(readFileSync(filePath, 'utf8'))).toEqual(second); mkdirSync(join(directory, 'bad'), { recursive: true }); const malformedPath = join(directory, 'bad', 'gpu-fallback.json'); expect(readGpuFallbackState(malformedPath)).toBeNull(); }); });