import assert from 'node:assert/strict'; import test from 'node:test'; import { FrameRenderer } from '../../YouleNexus/assets/framework/presentation/frame-renderer.ts'; test('coalesces frames and invalidates callbacks even if scheduler delivers late', () => { const callbacks: (() => void)[] = []; let cancelled = 0; let rendered = 0; const renderer = new FrameRenderer({ request(cb) { callbacks.push(cb); return () => { cancelled++; }; }, }, () => { rendered++; }, e => { throw e; }); renderer.request(); renderer.request(); assert.equal(callbacks.length, 1); renderer.flush(); assert.equal(rendered, 1); callbacks[0]!(); assert.equal(rendered, 1); renderer.request(); renderer.dispose(); renderer.dispose(); callbacks[1]!(); assert.equal(rendered, 1); assert.equal(cancelled, 2); }); test('releases the current frame before render can request the next frame', () => { const callbacks: (() => void)[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; renderer.request(); }, error => { throw error; }); renderer.request(); callbacks[0]!(); assert.equal(rendered, 1); assert.equal(callbacks.length, 2); callbacks[1]!(); assert.equal(rendered, 2); assert.equal(callbacks.length, 3); }); test('dispose during render prevents a reentrant frame request', () => { const callbacks: (() => void)[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; renderer.dispose(); renderer.request(); }, error => { throw error; }); renderer.request(); callbacks[0]!(); assert.equal(rendered, 1); assert.equal(callbacks.length, 1); renderer.flush(); assert.equal(rendered, 1); }); test('a throwing dispose cancellation still makes every callback stale', () => { const callbacks: (() => void)[] = []; const cancelError = new Error('cancel failed'); let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); let thrown: unknown; try { renderer.dispose(); } catch (error) { thrown = error; } assert.equal(thrown, cancelError); callbacks[0]!(); assert.equal(rendered, 0); renderer.request(); assert.equal(callbacks.length, 1); }); test('a throwing flush cancellation invalidates the old frame before rethrowing', () => { const callbacks: (() => void)[] = []; const cancelError = new Error('cancel failed'); let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); let thrown: unknown; try { renderer.flush(); } catch (error) { thrown = error; } assert.equal(thrown, cancelError); callbacks[0]!(); assert.equal(rendered, 0); renderer.request(); assert.equal(callbacks.length, 2); }); test('flush does not render when cancellation disposes the renderer', () => { let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request() { return () => { renderer.dispose(); }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); renderer.flush(); assert.equal(rendered, 0); }); test('a cancellation that reenters flush supersedes the outer render', () => { let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request() { return () => { renderer.flush(); }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); renderer.flush(); assert.equal(rendered, 1); }); test('reports an asynchronous render error once and terminates future frames', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const faults: unknown[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, undefined); assert.deepEqual(faults, [renderError]); callbacks[0]!(); renderer.request(); assert.equal(rendered, 1); assert.deepEqual(faults, [renderError]); assert.equal(callbacks.length, 1); }); test('aggregates an asynchronous render error with reentrant cancellation failure', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const cancelError = new Error('cancel failed'); const faults: unknown[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; renderer.request(); throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, undefined); assert.equal(faults.length, 1); const fault = faults[0]; assert.ok(fault instanceof Error); assert.ok('errors' in fault); assert.deepEqual(fault.errors, [renderError, cancelError]); callbacks[1]!(); assert.equal(rendered, 1); assert.equal(faults.length, 1); }); test('flush rethrows a synchronous render error without reporting an async fault', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const faults: unknown[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { renderer.flush(); } catch (error) { thrown = error; } assert.equal(thrown, renderError); assert.deepEqual(faults, []); callbacks[0]!(); assert.equal(rendered, 1); }); test('does not swallow an onFault callback error', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const callbackError = new Error('fault callback failed'); const reported: unknown[] = []; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { throw renderError; }, error => { reported.push(error); throw callbackError; }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, callbackError); assert.deepEqual(reported, [renderError]); renderer.request(); assert.equal(callbacks.length, 1); }); test('registration reentering flush cancels the superseded handle without blocking the next frame', () => { const callbacks: (() => void)[] = []; let cancelled = 0; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); if (callbacks.length === 1) renderer.flush(); return () => { cancelled += 1; }; } }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); assert.equal(rendered, 1); assert.equal(cancelled, 1); callbacks[0](); assert.equal(rendered, 1); renderer.request(); assert.equal(callbacks.length, 2); callbacks[1](); assert.equal(rendered, 2); }); test('registration reentering dispose immediately cancels its returned handle once', () => { const callbacks: (() => void)[] = []; let cancelled = 0; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); renderer.dispose(); return () => { cancelled += 1; }; } }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); renderer.dispose(); assert.equal(cancelled, 1); callbacks[0](); assert.equal(rendered, 0); renderer.request(); assert.equal(callbacks.length, 1); }); test('a reentrant request during registration coalesces with the acquiring frame', () => { const callbacks: (() => void)[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); if (callbacks.length === 1) renderer.request(); return () => {}; } }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); assert.equal(callbacks.length, 1); callbacks[0](); assert.equal(rendered, 1); }); test('registration failure releases the reservation and invalidates its captured callback', () => { const failure = new Error('request failed'); const callbacks: (() => void)[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); if (callbacks.length === 1) throw failure; return () => {}; } }, () => { rendered += 1; }, error => { throw error; }); assert.throws(() => renderer.request(), error => error === failure); renderer.request(); assert.equal(callbacks.length, 2); callbacks[0](); assert.equal(rendered, 0); callbacks[1](); assert.equal(rendered, 1); }); test('superseded registration preserves the successor scheduled by its synchronous flush', () => { const callbacks: (() => void)[] = []; const cancelled: number[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { const index = callbacks.length; callbacks.push(callback); if (index === 0) renderer.flush(); return () => { cancelled.push(index); }; } }, () => { rendered += 1; if (rendered === 1) renderer.request(); }, error => { throw error; }); renderer.request(); assert.deepEqual(cancelled, [0]); assert.equal(callbacks.length, 2); callbacks[0](); assert.equal(rendered, 1); renderer.flush(); assert.deepEqual(cancelled, [0, 1]); assert.equal(rendered, 2); callbacks[1](); assert.equal(rendered, 2); }); test('superseded handle cancellation errors propagate without reviving stale callbacks', () => { const failure = new Error('late handle cancellation'); const callbacks: (() => void)[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); renderer.dispose(); return () => { throw failure; }; } }, () => { rendered += 1; }, error => { throw error; }); assert.throws(() => renderer.request(), error => error === failure); callbacks[0](); renderer.dispose(); renderer.request(); assert.equal(rendered, 0); assert.equal(callbacks.length, 1); });