cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/drop-hero-notarization-note
goq / portal / input-runtime.test.mjs
input-runtime.test.mjs 9.2K
1import assert from 'node:assert/strict';2import { readFile } from 'node:fs/promises';3import test from 'node:test';45import {6 INPUT_RETRY_MS,7 REGULAR_RELIABLE_INPUT_LIMIT,8 RELIABLE_INPUT_ENQUEUE_LIMIT,9 createInputRuntime,10} from './input-runtime.mjs';1112const main = await readFile(new URL('./main.js', import.meta.url), 'utf8');1314const fullCapabilities = Object.freeze({15 control: true,16 absolutePointer: true,17 relativePointer: true,18 keyboard: true,19 text: true,20 gamepad: true,21});2223function newHarness({24 capabilities = fullCapabilities,25 invokeCommand = async () => true,26 connected = true,27 scheduleTimeout,28 scheduleMicrotask,29} = {}) {30 const fatal = [];31 const errors = [];32 const warnings = [];33 let activationResets = 0;34 const runtime = createInputRuntime({35 invokeCommand,36 getCapabilities: () => capabilities,37 isConnected: () => connected,38 onFatal: (detail) => fatal.push(detail),39 resetControllerActivation: () => { activationResets += 1; },40 scheduleTimeout,41 scheduleMicrotask,42 logger: {43 error: (...args) => errors.push(args),44 warn: (...args) => warnings.push(args),45 },46 });47 return {48 runtime,49 fatal,50 errors,51 warnings,52 get activationResets() { return activationResets; },53 };54}5556test('capability filtering preserves the current event routing contract', () => {57 const capabilities = {58 control: true,59 absolutePointer: false,60 relativePointer: true,61 keyboard: false,62 text: true,63 gamepad: false,64 };65 const { runtime } = newHarness({66 capabilities,67 scheduleTimeout: () => 0,68 });6970 assert.equal(runtime.pointerAvailable(), true);71 assert.equal(runtime.inputEventAvailable({ t: 'mm' }), false);72 assert.equal(runtime.inputEventAvailable({ t: 'mr' }), true);73 assert.equal(runtime.inputEventAvailable({ t: 'mp' }), true);74 assert.equal(runtime.inputEventAvailable({ t: 'md' }), true);75 assert.equal(runtime.inputEventAvailable({ t: 'kd' }), false);76 assert.equal(runtime.inputEventAvailable({ t: 'tx' }), true);77 assert.equal(runtime.inputEventAvailable({ t: 'gp' }), false);78 capabilities.control = false;79 assert.equal(runtime.inputEventAvailable({ t: 'tx' }), false);80});8182test('reliable transitions run before motion and only the latest gamepad state survives', async () => {83 const delivered = [];84 const { runtime } = newHarness({85 invokeCommand: async (_command, { event }) => {86 delivered.push(event);87 return true;88 },89 });9091 runtime.send({ t: 'gp', state: { a: true } });92 runtime.send({ t: 'gp', state: { a: false } });93 runtime.send({ t: 'mr', dx: 4, dy: -2 });94 runtime.send({ t: 'mr', dx: 3, dy: 1 });95 runtime.send({ t: 'kd', k: 'a' });96 await runtime.drain(200);9798 assert.deepEqual(delivered, [99 { t: 'kd', k: 'a' },100 { t: 'mr', dx: 7, dy: -1 },101 { t: 'gp', state: { a: false } },102 ]);103});104105test('absolute pointer state is latest-value-wins', async () => {106 const delivered = [];107 const { runtime } = newHarness({108 invokeCommand: async (_command, { event }) => {109 delivered.push(event);110 return true;111 },112 });113114 runtime.send({ t: 'mm', x: 10, y: 20 });115 runtime.send({ t: 'mm', x: 30, y: 40 });116 await runtime.drain(200);117118 assert.deepEqual(delivered, [{ t: 'mm', x: 30, y: 40 }]);119});120121test('pointer transition barriers drain every motion chunk and adjacent scroll coalesces', async () => {122 const delivered = [];123 const { runtime } = newHarness({124 invokeCommand: async (_command, { event }) => {125 delivered.push(event);126 return true;127 },128 });129130 runtime.send({ t: 'mr', dx: 50_000, dy: -50_000 });131 runtime.send({ t: 'md', b: 1 });132 runtime.send({ t: 'ms', dx: 900_000, dy: -900_000 });133 runtime.send({ t: 'ms', dx: 900_000, dy: -900_000 });134 await runtime.drain(200);135136 assert.deepEqual(delivered, [137 { t: 'mr', dx: 32_767, dy: -32_767 },138 { t: 'mr', dx: 17_233, dy: -17_233 },139 { t: 'md', b: 1 },140 { t: 'ms', dx: 1_000_000, dy: -1_000_000 },141 ]);142});143144test('rejected in-flight motion is restored before a transition queued behind it', async () => {145 const delivered = [];146 let startFirst;147 const firstStarted = new Promise((resolve) => { startFirst = resolve; });148 let rejectFirst;149 const firstResult = new Promise((resolve) => { rejectFirst = resolve; });150 let attempts = 0;151 const { runtime } = newHarness({152 invokeCommand: async (_command, { event }) => {153 delivered.push(event);154 attempts += 1;155 if (attempts === 1) {156 startFirst();157 return firstResult;158 }159 return true;160 },161 });162163 runtime.send({ t: 'mr', dx: 50_000, dy: 0 });164 await firstStarted;165 runtime.send({ t: 'md', b: 1 });166 rejectFirst(false);167 await runtime.drain(300);168169 assert.deepEqual(delivered, [170 { t: 'mr', dx: 32_767, dy: 0 },171 { t: 'mr', dx: 32_767, dy: 0 },172 { t: 'mr', dx: 17_233, dy: 0 },173 { t: 'md', b: 1 },174 ]);175});176177test('a rejected gamepad sample cannot replace newer controller state', async () => {178 const delivered = [];179 let startFirst;180 const firstStarted = new Promise((resolve) => { startFirst = resolve; });181 let rejectFirst;182 const firstResult = new Promise((resolve) => { rejectFirst = resolve; });183 let attempts = 0;184 const { runtime } = newHarness({185 invokeCommand: async (_command, { event }) => {186 delivered.push(event);187 attempts += 1;188 if (attempts === 1) {189 startFirst();190 return firstResult;191 }192 return true;193 },194 });195196 runtime.send({ t: 'gp', state: { a: true } });197 await firstStarted;198 runtime.send({ t: 'gp', state: { a: false } });199 rejectFirst(false);200 await runtime.drain(300);201202 assert.deepEqual(delivered, [203 { t: 'gp', state: { a: true } },204 { t: 'gp', state: { a: false } },205 ]);206});207208test('regular and release queue limits retain their reserved capacity and fail asynchronously', async () => {209 const scheduled = [];210 const microtasks = [];211 const harness = newHarness({212 scheduleTimeout: (callback, delay) => {213 scheduled.push({ callback, delay });214 return scheduled.length;215 },216 scheduleMicrotask: (callback) => microtasks.push(callback),217 });218 const { runtime } = harness;219220 for (let index = 0; index < REGULAR_RELIABLE_INPUT_LIMIT; index += 1) {221 assert.equal(runtime.send({ t: 'kd', k: `key-${index}` }), true);222 }223 assert.equal(runtime.send({ t: 'kd', k: 'ordinary-overflow' }), false);224 assert.equal(harness.fatal.length, 0);225 microtasks.shift()();226 assert.equal(harness.fatal[0].reason, 'reliable input queue full');227228 for (229 let index = REGULAR_RELIABLE_INPUT_LIMIT;230 index < RELIABLE_INPUT_ENQUEUE_LIMIT;231 index += 1232 ) {233 assert.equal(runtime.send({ t: 'ku', k: `release-${index}` }, { release: true }), true);234 }235 assert.equal(runtime.send({ t: 'ku', k: 'release-overflow' }, { release: true }), false);236 microtasks.shift()();237 assert.equal(harness.fatal[1].reason, 'input release reserve exhausted');238 assert.equal(scheduled.length, 1, 'one pending pump owns the queue');239});240241test('held transitions release once in key-then-button insertion order', async () => {242 const delivered = [];243 const { runtime } = newHarness({244 invokeCommand: async (_command, { event }) => {245 delivered.push(event);246 return true;247 },248 });249250 assert.equal(runtime.trackKey('KeyA', 'a'), 'tracked');251 assert.equal(runtime.trackKey('KeyA', 'a'), 'repeat');252 assert.equal(runtime.trackKey('KeyB', 'b'), 'tracked');253 assert.equal(runtime.trackMouseButton(2), true);254 assert.equal(runtime.trackMouseButton(1), true);255 runtime.releaseHeld();256 runtime.releaseHeld();257 await runtime.drain(200);258259 assert.deepEqual(delivered, [260 { t: 'ku', k: 'a' },261 { t: 'ku', k: 'b' },262 { t: 'mu', b: 2 },263 { t: 'mu', b: 1 },264 ]);265});266267test('transport exceptions clear all pending state without escalating disconnect', async () => {268 let calls = 0;269 const harness = newHarness({270 invokeCommand: async () => {271 calls += 1;272 throw new Error('transport closed');273 },274 });275 const { runtime } = harness;276277 runtime.trackKey('KeyA', 'a');278 runtime.send({ t: 'kd', k: 'a' });279 runtime.send({ t: 'gp', state: { a: true } });280 await runtime.drain(200);281282 assert.equal(calls, 1);283 assert.equal(runtime.hasPending(), false);284 assert.equal(runtime.takeKeyRelease('KeyA'), null);285 assert.equal(harness.activationResets, 1);286 assert.equal(harness.fatal.length, 0);287 assert.equal(harness.warnings[0][0], 'input send failed:');288});289290test('retry cadence remains eight milliseconds', () => {291 assert.equal(INPUT_RETRY_MS, 8);292});293294test('Portal delegates queue ownership while retaining DOM input wiring', () => {295 assert.match(main, /const inputRuntime = createInputRuntime\(\{/);296 assert.match(297 main,298 /resetControllerActivation: \(\) => controlRuntime\?\.resetControllerActivation\(\)/,299 );300 assert.match(main, /window\.addEventListener\('mousedown', handleMouseDown/);301 assert.match(main, /inputRuntime\.trackMouseButton\(btn\)/);302 assert.match(main, /keyboardInputForEvent\(e, connectionState\.inputCapabilities\)/);303 assert.match(main, /inputRuntime\.trackKey\(keyId, input\.key\)/);304 assert.doesNotMatch(main, /const printableText =/);305 assert.doesNotMatch(main, /const reliableInputQueue =/);306 assert.doesNotMatch(main, /function sendInput\(/);307 assert.doesNotMatch(main, /new HeldInputState\(/);308});