Files
fluxer-desktop-patched/fluxer_desktop/native/win-game-capture/index.test.mjs
T
brenden 682afacd30 Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
2026-07-01 18:22:43 -04:00

840 lines
27 KiB
JavaScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import {existsSync} from 'node:fs';
import {createRequire} from 'node:module';
import {afterEach, describe, test} from 'node:test';
const require = createRequire(import.meta.url);
const winGameCapture = require('./index.js');
const startupSupported = winGameCapture.isSupported();
const startupAvailability = winGameCapture.getAvailability();
const realBindingSkip = startupSupported
? false
: 'no native binding loaded at startup (binding-less platform / unbuilt addon)';
const hasBindingHook = typeof winGameCapture.__setBindingForTests === 'function';
const injectionSkip = hasBindingHook ? false : 'no __setBindingForTests hook exported';
if (!hasBindingHook) {
console.warn(
'[index.test.mjs] __setBindingForTests not present on index.js; ' +
'skipping binding-injection tests and running only the no-binding tests. ' +
'This should only happen when testing an older or stripped loader.',
);
}
function makeFakeBinding() {
const calls = [];
const frameSinkHandleCalls = [];
const priorityCalls = [];
const natives = [];
const diagnostics = {
state: 1,
apiType: 5,
transport: 0,
fallbackReason: 0,
captureFlags: 0,
width: 1920,
height: 1080,
dxgiFormat: 87,
frameCounter: 42,
droppedFrameCounter: 0,
lastPresentTimestampUs: 123456,
lastError: 0,
requestedInjectionMethod: 'auto',
injectionMethod: 'remote-thread',
activeStrategy: 'game-hook',
lastFallbackReason: '',
startOptions: {
colorRange: 'full',
colorSpace: 'rec709',
showCursorClicks: true,
captureRect: {x: 10, y: 20, width: 300, height: 200},
unsupportedOptions: ['showCursorClicks', 'captureRect', 'colorRange', 'colorSpace'],
},
frameSinkAccepted: 0,
frameSinkCoalesced: 0,
frameSinkRejected: 0,
mediaFramesDroppedWithoutSink: 0,
cpuFallbackFramesDropped: 0,
};
const encoderDiagnostics = {
attached: false,
width: 0,
height: 0,
capacity: 0,
framesSubmitted: 0,
framesDropped: 0,
ringFullEvents: 0,
failedBlits: 0,
};
const frameSinkDiagnostics = {
accepted: 0,
coalesced: 0,
rejected: 0,
mediaFramesDroppedWithoutSink: 0,
cpuFallbackFramesDropped: 0,
};
class FakeNative {
constructor() {
this.lifecycleCallback = undefined;
this.stopCount = 0;
natives.push(this);
}
setLifecycleCallback(callback) {
this.lifecycleCallback = callback;
}
setFrameSinkHandle(handle) {
frameSinkHandleCalls.push(handle);
}
start(
sourceId,
sourceKind,
width,
height,
frameRate,
hookDllPath,
hookDllPathX86,
injectionMethod,
captureId,
captureOptions,
) {
calls.push({
sourceId,
sourceKind,
width,
height,
frameRate,
hookDllPath,
hookDllPathX86,
injectionMethod,
captureId,
captureOptions,
});
return {
width: width || 1920,
height: height || 1080,
frameRate: frameRate || 30,
pixelFormat: 'bgra',
};
}
stop() {
this.stopCount += 1;
}
getDiagnostics() {
return diagnostics;
}
attachEncoder(width, height) {
encoderDiagnostics.attached = true;
encoderDiagnostics.width = width;
encoderDiagnostics.height = height;
encoderDiagnostics.capacity = 8;
}
detachEncoder() {
encoderDiagnostics.attached = false;
}
isEncoderAttached() {
return encoderDiagnostics.attached;
}
encoderRingFullCount() {
return encoderDiagnostics.ringFullEvents;
}
getEncoderAttachDiagnostics() {
if (!encoderDiagnostics.attached) return null;
return {...encoderDiagnostics};
}
getFrameSinkDiagnostics() {
return {...frameSinkDiagnostics};
}
}
return {
binding: {
ScreenCapture: FakeNative,
isSupported: () => true,
getAvailability: () => ({available: true, backend: 'windows-game-capture'}),
listSources: () => [
{
kind: 'screen',
id: 'screen:0:0',
name: 'Display 1',
width: 2560,
height: 1440,
},
{
kind: 'window',
id: 'window:5050:0',
name: 'Fixture',
width: 1280,
height: 720,
targetPid: 4242,
},
{
kind: 'browser',
id: 'screen:1:0',
name: 'unsupported shape',
},
],
elevateGpuSchedulingPriority: (processId, priorityClass) => {
priorityCalls.push({type: 'elevate', processId, priorityClass});
},
restoreGpuSchedulingPriority: (processId) => {
priorityCalls.push({type: 'restore', processId});
},
},
calls,
frameSinkHandleCalls,
priorityCalls,
natives,
diagnostics,
encoderDiagnostics,
frameSinkDiagnostics,
};
}
afterEach(() => {
if (hasBindingHook) {
winGameCapture.__setBindingForTests(null);
}
});
describe('win-game-capture loader wrapper -- binding-absent fallback path', () => {
function forceNoBinding() {
if (hasBindingHook) winGameCapture.__setBindingForTests(null);
}
test(
'isSupported() is false when no binding is loaded',
{skip: hasBindingHook ? false : 'no __setBindingForTests hook'},
() => {
forceNoBinding();
assert.equal(winGameCapture.isSupported(), false);
},
);
test(
'getAvailability() returns the {available:false, ...} shape when no binding is loaded',
{skip: hasBindingHook ? false : 'no __setBindingForTests hook'},
() => {
forceNoBinding();
const availability = winGameCapture.getAvailability();
assert.equal(availability.available, false);
assert.equal(availability.backend, 'windows-game-capture');
assert.equal(typeof availability.reason, 'string');
},
);
test(
'constructing ScreenCapture without a binding throws',
{skip: hasBindingHook ? false : 'no __setBindingForTests hook'},
() => {
forceNoBinding();
assert.throws(() => new winGameCapture.ScreenCapture({sourceId: '1'}));
try {
new winGameCapture.ScreenCapture({sourceId: '1'});
assert.fail('expected ScreenCapture constructor to throw without a binding');
} catch (error) {
assert.ok(error instanceof Error);
}
},
);
});
describe('win-game-capture loader wrapper -- arch path resolvers (platform-portable)', () => {
test('resolveGameHookPath() is null or the host-arch hook path', () => {
const r = winGameCapture.resolveGameHookPath();
assert.ok(
r === null || (typeof r === 'string' && /fluxer-game-hook\.win32-(x64|ia32|arm64)-msvc\.dll$/.test(r)),
`unexpected resolveGameHookPath(): ${r}`,
);
});
test('resolveGameHookPathX86() is null or the ia32 hook path', () => {
const r = winGameCapture.resolveGameHookPathX86();
assert.ok(
r === null || (typeof r === 'string' && r.endsWith('fluxer-game-hook.win32-ia32-msvc.dll')),
`unexpected resolveGameHookPathX86(): ${r}`,
);
});
test('resolveVulkanLayerManifestPath() is null or the host-arch layer manifest path', () => {
const r = winGameCapture.resolveVulkanLayerManifestPath();
assert.ok(
r === null || (typeof r === 'string' && /fluxer-vulkan-layer\.win32-(x64|ia32|arm64)-msvc\.json$/.test(r)),
`unexpected resolveVulkanLayerManifestPath(): ${r}`,
);
});
});
describe('win-game-capture loader wrapper -- real native binding (built Windows box)', () => {
test('getAvailability() reports available with the windows-game-capture backend', {skip: realBindingSkip}, () => {
assert.equal(startupAvailability.available, true);
assert.equal(startupAvailability.backend, 'windows-game-capture');
});
test('resolveGameHookPath() points at an existing host-arch hook DLL', {skip: realBindingSkip}, () => {
const r = winGameCapture.resolveGameHookPath();
assert.equal(typeof r, 'string', 'expected a hook path on a built Windows box');
assert.ok(existsSync(r), `hook DLL should exist on disk: ${r}`);
});
test('resolveVulkanLayerManifestPath() points at an existing layer manifest', {skip: realBindingSkip}, () => {
const r = winGameCapture.resolveVulkanLayerManifestPath();
assert.equal(typeof r, 'string', 'expected a Vulkan layer manifest path on a built Windows box');
assert.ok(existsSync(r), `Vulkan layer manifest should exist on disk: ${r}`);
});
});
describe('win-game-capture loader wrapper -- injected fake binding', () => {
test(
'listSources() forwards sanitized screen/window sources from the native binding',
{skip: injectionSkip},
async () => {
const {binding} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const sources = await winGameCapture.listSources();
assert.deepEqual(sources, [
{
kind: 'screen',
id: 'screen:0:0',
name: 'Display 1',
width: 2560,
height: 1440,
targetPid: undefined,
},
{
kind: 'window',
id: 'window:5050:0',
name: 'Fixture',
width: 1280,
height: 720,
targetPid: 4242,
},
]);
},
);
test(
'start() forwards sourceId/kind/dims/frameRate and BOTH hook paths (6th + 7th args)',
{skip: injectionSkip},
async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '987654',
sourceKind: 'game',
width: 1600,
height: 900,
frameRate: 60,
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
captureId: 'capture-1',
colorRange: 'full',
colorSpace: 'rec709',
showCursorClicks: true,
captureRect: {x: 10, y: 20, width: 300, height: 200},
});
capture.on('error', () => {});
const result = await capture.start();
assert.equal(calls.length, 1);
assert.deepEqual(calls[0], {
sourceId: '987654',
sourceKind: 'game',
width: 1600,
height: 900,
frameRate: 60,
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
injectionMethod: undefined,
captureId: 'capture-1',
captureOptions: {
colorRange: 'full',
colorSpace: 'rec709',
showCursorClicks: true,
captureRect: {x: 10, y: 20, width: 300, height: 200},
},
});
assert.equal(result.pixelFormat, 'bgra');
assert.equal(result.width, 1600);
assert.equal(result.height, 900);
assert.equal(result.frameRate, 60);
},
);
test(
'start() forwards the injectionMethod option as the 8th arg for game capture',
{skip: injectionSkip},
async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '555',
sourceKind: 'game',
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls.length, 1);
assert.equal(calls[0].injectionMethod, 'set-windows-hook');
},
);
test('window sourceKind does not forward the injectionMethod', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '42',
sourceKind: 'window',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls[0].injectionMethod, undefined);
});
test('window sourceKind does not forward hook paths', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '42',
sourceKind: 'window',
hookDllPath: 'C:/hooks/primary.dll',
hookDllPathX86: 'C:/hooks/x86.dll',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls[0].sourceKind, 'window');
assert.equal(calls[0].hookDllPath, undefined);
assert.equal(calls[0].hookDllPathX86, undefined);
});
test('screen sourceKind does not forward hook paths or the injectionMethod', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: 'screen:0:0',
sourceKind: 'screen',
hookDllPath: 'C:/hooks/primary.dll',
hookDllPathX86: 'C:/hooks/x86.dll',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls[0].sourceKind, 'screen');
assert.equal(calls[0].hookDllPath, undefined);
assert.equal(calls[0].hookDllPathX86, undefined);
assert.equal(calls[0].injectionMethod, undefined);
});
test('getDiagnostics() exposes native start option state', {skip: injectionSkip}, () => {
const {binding} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'});
const diagnostics = capture.getDiagnostics();
assert.deepEqual(diagnostics.startOptions, {
colorRange: 'full',
colorSpace: 'rec709',
showCursorClicks: true,
captureRect: {x: 10, y: 20, width: 300, height: 200},
unsupportedOptions: ['showCursorClicks', 'captureRect', 'colorRange', 'colorSpace'],
});
});
test('encoder attachment diagnostics are forwarded by the wrapper', {skip: injectionSkip}, () => {
const {binding, encoderDiagnostics} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'});
assert.equal(capture.isEncoderAttached(), false);
assert.equal(capture.encoderRingFullCount(), 0);
assert.equal(capture.getEncoderAttachDiagnostics(), null);
capture.attachEncoder(1280, 720);
encoderDiagnostics.framesSubmitted = 3;
encoderDiagnostics.framesDropped = 1;
encoderDiagnostics.ringFullEvents = 1;
encoderDiagnostics.failedBlits = 0;
assert.equal(capture.isEncoderAttached(), true);
assert.equal(capture.encoderRingFullCount(), 1);
assert.deepEqual(capture.getEncoderAttachDiagnostics(), {
attached: true,
width: 1280,
height: 720,
capacity: 8,
framesSubmitted: 3,
framesDropped: 1,
ringFullEvents: 1,
failedBlits: 0,
});
capture.detachEncoder();
assert.equal(capture.isEncoderAttached(), false);
});
test('frame sink diagnostics are forwarded by the wrapper', {skip: injectionSkip}, () => {
const {binding, frameSinkDiagnostics} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'});
frameSinkDiagnostics.accepted = 7;
frameSinkDiagnostics.coalesced = 2;
frameSinkDiagnostics.rejected = 1;
frameSinkDiagnostics.mediaFramesDroppedWithoutSink = 3;
frameSinkDiagnostics.cpuFallbackFramesDropped = 4;
assert.deepEqual(capture.getFrameSinkDiagnostics(), {
accepted: 7,
coalesced: 2,
rejected: 1,
mediaFramesDroppedWithoutSink: 3,
cpuFallbackFramesDropped: 4,
});
});
test('installs a native frame sink handle once before start', {skip: injectionSkip}, async () => {
const {binding, calls, frameSinkHandleCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const frameSinkHandle = {native: true};
const capture = new winGameCapture.ScreenCapture({
sourceId: '42',
sourceKind: 'window',
frameSinkHandle,
nativeFrameSinkRequired: true,
});
capture.on('error', () => {});
await capture.start();
assert.deepEqual(frameSinkHandleCalls, [frameSinkHandle]);
assert.equal(calls.length, 1);
});
test(
'fails before native start when a native frame sink is required but missing',
{skip: injectionSkip},
async () => {
const {binding, calls, frameSinkHandleCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '42',
sourceKind: 'window',
nativeFrameSinkRequired: true,
});
capture.on('error', () => {});
await assert.rejects(() => capture.start(), /native frame sink handle is required/i);
assert.deepEqual(frameSinkHandleCalls, []);
assert.deepEqual(calls, []);
},
);
test(
'game sourceKind without a hook path fails closed without WGC/browser fallback',
{skip: injectionSkip},
async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '7',
sourceKind: 'game',
hookDllPath: '',
hookDllPathX86: '',
});
capture.on('error', () => {});
await assert.rejects(() => capture.start(), /Game capture hook unavailable/);
assert.equal(calls.length, 0, 'native start must not be called when the game hook is unavailable');
},
);
test('lifecycle "error" emits an Error event', {skip: injectionSkip}, () => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const errors = [];
capture.on('error', (err) => errors.push(err));
natives[0].lifecycleCallback('error', 'DXGI device removed');
assert.equal(errors.length, 1);
assert.ok(errors[0] instanceof Error);
assert.equal(errors[0].message, 'DXGI device removed');
});
test('lifecycle "closed" stops the native capture and emits closed once', {skip: injectionSkip}, async () => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
let closed = 0;
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback('closed', '');
await Promise.resolve();
await capture.stop();
assert.equal(closed, 1);
assert.equal(natives[0].stopCount, 1);
});
test('non-fatal "stalled" lifecycle re-emits as a stalled event WITHOUT stopping', {skip: injectionSkip}, () => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const stalls = [];
let closed = 0;
capture.on('stalled', (message) => stalls.push(message));
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback('stalled', 'frame counter frozen while focused');
assert.deepEqual(stalls, ['frame counter frozen while focused']);
assert.equal(natives[0].stopCount, 0);
assert.equal(closed, 0);
});
test(
'non-fatal "diagnostic" lifecycle re-emits as a diagnostic event WITHOUT stopping',
{skip: injectionSkip},
() => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const diags = [];
let closed = 0;
capture.on('diagnostic', (message) => diags.push(message));
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback('diagnostic', 'fell back to CPU readback');
assert.deepEqual(diags, ['fell back to CPU readback']);
assert.equal(natives[0].stopCount, 0);
assert.equal(closed, 0);
},
);
test(
'lifecycle "error" in the real napi [type, message] array shape emits an Error event',
{skip: injectionSkip},
() => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const errors = [];
capture.on('error', (err) => errors.push(err));
natives[0].lifecycleCallback(['error', 'DXGI device removed']);
assert.equal(errors.length, 1);
assert.ok(errors[0] instanceof Error);
assert.equal(errors[0].message, 'DXGI device removed');
},
);
test(
'lifecycle "closed" in the napi array shape stops the native capture and emits closed once',
{skip: injectionSkip},
async () => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
let closed = 0;
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback(['closed', '']);
await Promise.resolve();
await capture.stop();
assert.equal(closed, 1);
assert.equal(natives[0].stopCount, 1);
},
);
test(
'lifecycle "stalled" in the napi array shape re-emits as a stalled event WITHOUT stopping',
{skip: injectionSkip},
() => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const stalls = [];
let closed = 0;
capture.on('stalled', (message) => stalls.push(message));
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback(['stalled', 'frame counter frozen while focused']);
assert.deepEqual(stalls, ['frame counter frozen while focused']);
assert.equal(natives[0].stopCount, 0);
assert.equal(closed, 0);
},
);
test(
'lifecycle "diagnostic" (injected via <method>) in the napi array shape re-emits WITHOUT stopping',
{skip: injectionSkip},
() => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const diags = [];
let closed = 0;
capture.on('diagnostic', (message) => diags.push(message));
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback(['diagnostic', 'game capture injected via set-windows-hook']);
assert.deepEqual(diags, ['game capture injected via set-windows-hook']);
assert.equal(natives[0].stopCount, 0);
assert.equal(closed, 0);
},
);
test('getDiagnostics() passes the native snapshot through', {skip: injectionSkip}, () => {
const {binding, diagnostics} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const snapshot = capture.getDiagnostics();
assert.deepEqual(snapshot, diagnostics);
});
test('getDiagnostics() surfaces the activeStrategy + lastFallbackReason fields', {skip: injectionSkip}, () => {
const {binding, diagnostics} = makeFakeBinding();
diagnostics.activeStrategy = 'dxgi-duplication';
diagnostics.lastFallbackReason =
'game-hook capture could not inject its capture hook; switching to dxgi-duplication capture';
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const snapshot = capture.getDiagnostics();
assert.equal(snapshot.activeStrategy, 'dxgi-duplication');
assert.match(snapshot.lastFallbackReason, /switching to dxgi-duplication/);
});
test('elevateGpuSchedulingPriority forwards the optional realtime priority class', {skip: injectionSkip}, () => {
const {binding, priorityCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'realtime'), true);
assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: 'realtime'}]);
});
test('elevateGpuSchedulingPriority canonicalizes the real-time priority alias', {skip: injectionSkip}, () => {
const {binding, priorityCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'real-time'), true);
assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: 'realtime'}]);
});
test('elevateGpuSchedulingPriority preserves the existing default priority class', {skip: injectionSkip}, () => {
const {binding, priorityCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234), true);
assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: undefined}]);
});
test(
'elevateGpuSchedulingPriority rejects unsupported priority classes before binding calls',
{skip: injectionSkip},
() => {
const {binding, priorityCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'normal'), false);
assert.deepEqual(priorityCalls, []);
},
);
test('lifecycle fallback "error" (next-strategy=...) re-emits as a fatal error event', {skip: injectionSkip}, () => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const errors = [];
capture.on('error', (err) => errors.push(err));
natives[0].lifecycleCallback([
'error',
'fallback: game-hook -> dxgi-duplication (game-hook capture could not inject its capture hook) [next-strategy=dxgi-duplication]',
]);
assert.equal(errors.length, 1);
assert.ok(errors[0] instanceof Error);
assert.equal(winGameCapture.parseFallbackRecommendation(errors[0].message), 'dxgi-duplication');
});
test(
'lifecycle fallback "diagnostic" (upgrade, next-strategy=...) re-emits WITHOUT stopping',
{skip: injectionSkip},
() => {
const {binding, natives} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const diags = [];
let closed = 0;
capture.on('diagnostic', (message) => diags.push(message));
capture.on('closed', () => {
closed += 1;
});
natives[0].lifecycleCallback([
'diagnostic',
'upgrade: window-gdi -> dxgi-duplication (window-gdi has been stable) [next-strategy=dxgi-duplication]',
]);
assert.equal(diags.length, 1);
assert.equal(winGameCapture.parseFallbackRecommendation(diags[0]), 'dxgi-duplication');
assert.equal(natives[0].stopCount, 0);
assert.equal(closed, 0);
},
);
});
describe('win-game-capture loader wrapper -- parseFallbackRecommendation', () => {
test('extracts the recommended strategy from a transition error message', () => {
assert.equal(
winGameCapture.parseFallbackRecommendation(
'fallback: game-hook -> window-gdi (reason) [next-strategy=window-gdi]',
),
'window-gdi',
);
});
test('returns "none" for an exhausted give-up message', () => {
assert.equal(
winGameCapture.parseFallbackRecommendation(
'fallback exhausted: window-gdi was the last resort [next-strategy=none]',
),
'none',
);
});
test('returns null for an ordinary error with no recommendation', () => {
assert.equal(winGameCapture.parseFallbackRecommendation('DXGI device removed'), null);
assert.equal(winGameCapture.parseFallbackRecommendation(undefined), null);
assert.equal(winGameCapture.parseFallbackRecommendation(''), null);
});
test('returns null for an unrecognised strategy token', () => {
assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=teleporter]'), null);
});
test('accepts the WGC fallback strategy token', () => {
assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=wgc]'), 'wgc');
assert.equal(
winGameCapture.parseFallbackRecommendation(
'fallback: game-hook -> wgc (game-hook capture could not inject its capture hook) [next-strategy=wgc]',
),
'wgc',
);
});
test('rejects browser fallback strategy tokens', () => {
assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=browser-display]'), null);
assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=desktop-capturer]'), null);
});
});