Files
SubMiner/src/window-trackers/windows-tracker.test.ts
Kyle 7698258f61 Fix Windows overlay tracking, z-order, and startup visibility
- switch Windows overlay tracking to native win32 polling with native owner and z-order helpers
- keep the visible overlay and stats overlay aligned across focus handoff, transient tracker misses, and minimize/restore cycles
- start the visible overlay click-through and hide the initial opaque startup frame until the tracked transparent state settles
- add a backlog task for the inconsistent mpv y-t overlay toggle after menu toggles
2026-04-10 01:00:53 -07:00

247 lines
8.0 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { WindowsWindowTracker } from './windows-tracker';
import type { MpvPollResult } from './win32';
function mpvVisible(
overrides: Partial<MpvPollResult & { x?: number; y?: number; width?: number; height?: number; focused?: boolean }> = {},
): MpvPollResult {
return {
matches: [
{
hwnd: 12345,
bounds: {
x: overrides.x ?? 0,
y: overrides.y ?? 0,
width: overrides.width ?? 1280,
height: overrides.height ?? 720,
},
area: (overrides.width ?? 1280) * (overrides.height ?? 720),
isForeground: overrides.focused ?? true,
},
],
focusState: overrides.focused ?? true,
windowState: 'visible',
};
}
const mpvNotFound: MpvPollResult = {
matches: [],
focusState: false,
windowState: 'not-found',
};
const mpvMinimized: MpvPollResult = {
matches: [],
focusState: false,
windowState: 'minimized',
};
test('WindowsWindowTracker skips overlapping polls while poll is in flight', () => {
let pollCalls = 0;
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => {
pollCalls += 1;
return mpvVisible();
},
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(pollCalls, 2);
});
test('WindowsWindowTracker updates geometry from poll output', () => {
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.deepEqual(tracker.getGeometry(), {
x: 10,
y: 20,
width: 1280,
height: 720,
});
assert.equal(tracker.isTargetWindowFocused(), true);
});
test('WindowsWindowTracker clears geometry for poll misses', () => {
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => mpvNotFound,
trackingLossGraceMs: 0,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.getGeometry(), null);
assert.equal(tracker.isTargetWindowFocused(), false);
});
test('WindowsWindowTracker keeps the last geometry through a single poll miss', () => {
let callIndex = 0;
const outputs = [
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
mpvNotFound,
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
];
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => outputs[callIndex++] ?? outputs.at(-1)!,
trackingLossGraceMs: 0,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
});
test('WindowsWindowTracker drops tracking after grace window expires', () => {
let callIndex = 0;
let now = 1_000;
const outputs = [
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
mpvNotFound,
mpvNotFound,
mpvNotFound,
mpvNotFound,
];
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => outputs[callIndex++] ?? outputs.at(-1)!,
now: () => now,
trackingLossGraceMs: 500,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), false);
assert.equal(tracker.getGeometry(), null);
});
test('WindowsWindowTracker keeps tracking through repeated poll misses inside grace window', () => {
let callIndex = 0;
let now = 1_000;
const outputs = [
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
mpvNotFound,
mpvNotFound,
mpvNotFound,
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
];
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => outputs[callIndex++] ?? outputs.at(-1)!,
now: () => now,
trackingLossGraceMs: 1_500,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
});
test('WindowsWindowTracker keeps tracking through a transient minimized report inside minimized grace window', () => {
let callIndex = 0;
let now = 1_000;
const outputs: MpvPollResult[] = [
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
mpvMinimized,
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
];
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => outputs[callIndex++] ?? outputs.at(-1)!,
now: () => now,
minimizedTrackingLossGraceMs: 200,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 100;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
now += 100;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
});
test('WindowsWindowTracker keeps tracking through repeated transient minimized reports inside minimized grace window', () => {
let callIndex = 0;
let now = 1_000;
const outputs: MpvPollResult[] = [
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
mpvMinimized,
mpvMinimized,
mpvVisible({ x: 10, y: 20, width: 1280, height: 720 }),
];
const tracker = new WindowsWindowTracker(undefined, {
pollMpvWindows: () => outputs[callIndex++] ?? outputs.at(-1)!,
now: () => now,
minimizedTrackingLossGraceMs: 500,
});
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowMinimized(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowMinimized(), true);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowMinimized(), false);
assert.deepEqual(tracker.getGeometry(), { x: 10, y: 20, width: 1280, height: 720 });
});