import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import path from 'node:path' import { performance } from 'node:perf_hooks' import { build } from 'esbuild' // Pipe the baseline client module on stdin. Native process/filesystem operations are forbidden here. const entry = path.resolve('src/main/computer/macos-native-provider-client.ts') const sources = [readFileSync(0, 'utf8'), readFileSync(entry, 'utf8')] assert(sources.every((source) => source.includes('export class MacOSNativeProviderClient'))) async function load(source) { const result = await build({ entryPoints: [entry], bundle: true, platform: 'node', format: 'esm', write: false, plugins: [ { name: 'native-client-receive-fixture', setup(builder) { builder.onLoad({ filter: /macos-native-provider-client\.ts$/ }, () => ({ contents: `${source}\nexport { NativeProviderLineBuffer, consumeNativeProviderLines } from './macos-native-provider-transport';`, loader: 'ts', resolveDir: path.dirname(entry) })) builder.onResolve({ filter: /^node:(fs|child_process)$/ }, (args) => ({ path: args.path, namespace: 'forbidden-native-operation' })) builder.onLoad({ filter: /.*/, namespace: 'forbidden-native-operation' }, (args) => ({ contents: `function forbidden() { throw new Error('Native operations are forbidden in this benchmark'); } export { forbidden as ${ args.path === 'node:fs' ? 'chmodSync, forbidden as mkdtempSync, forbidden as rmSync, forbidden as writeFileSync, forbidden as existsSync' : 'spawn' } };`, loader: 'js' })) } } ] }) const bundled = `${result.outputFiles[0].text}\n//# sourceURL=native-provider-line-gate-benchmark-bundle.js` return import(`data:text/javascript;base64,${Buffer.from(bundled).toString('base64')}`) } const modules = await Promise.all(sources.map(load)) class FixtureSocket { destroyed = false writes = [] write(line) { this.writes.push(line) } end() { this.destroyed = true } destroy() { this.destroyed = true } } function clientFixture(module) { const client = new module.MacOSNativeProviderClient() const socket = new FixtureSocket() client.socket = socket return { client, socket, stale: new FixtureSocket(), events: [] } } function state(fixture) { const { client, socket, events } = fixture return { buffered: typeof client.socketBuffer === 'string' ? client.socketBuffer : client.socketBuffer.pending, pending: [...client.pending.keys()], active: client.socket === socket, generation: client.socketStartGeneration, destroyed: socket.destroyed, writes: socket.writes, events } } function register(fixture, id, throwCallback) { fixture.client.pending.set(id, { timer: undefined, resolve(value) { fixture.events.push(['resolve', id, value]) if (throwCallback) { throw new Error('fixture callback failure') } }, reject(error) { fixture.events.push(['reject', id, error.code, error.message]) if (throwCallback) { throw new Error('fixture callback failure') } } }) } let seed = 0x18c0ffee function random(max) { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 return (seed >>> 8) % max } for (let trace = 0; trace < 2000; trace++) { const fixtures = modules.map(clientFixture) let remainder = '' for (let step = 0; step < 40; step++) { const op = random(20) const id = random(8) const throwCallback = random(12) === 0 if (!remainder) { remainder = [ `${JSON.stringify({ id, ok: true, result: { text: 'Unicode 界😀', value: step } })}\n`, `${JSON.stringify({ id, ok: false, error: { code: 'fixture', message: 'failed' } })}\r\n`, `${JSON.stringify({ id, ok: false })}\n`, ' \t\r\n', 'invalid json\n', 'null\n', '\ud83d\udc00\n' ][random(7)] } const length = random(remainder.length + 1) const chunk = remainder.slice(0, length) if (op > 5) { remainder = remainder.slice(length) } for (const fixture of fixtures) { const { client, socket } = fixture try { if (op === 0) { client.shutdown() } else if (op === 1) { client.handleSocketClose(socket) } else if (op === 2) { client.handleTransportError(socket, new Error('fixture transport error')) } else if (op === 3) { client.invalidateActiveSocketAfterWriteFailure(socket, new Error('fixture write error')) } else if (op === 4) { fixture.stale = socket fixture.socket = new FixtureSocket() client.socket = fixture.socket } else if (op === 5) { register(fixture, id, throwCallback) } else { client.handleSocketData(op === 6 ? fixture.stale : socket, chunk) } } catch (error) { fixture.events.push(['throw', error.name, error.message]) } } assert.deepEqual(state(fixtures[1]), state(fixtures[0])) } } console.log('2,000 actual client receive/lifecycle traces / 80,000 commands match') for (let trace = 0; trace < 1000; trace++) { const fixtures = modules.map(clientFixture) const failThird = random(2) === 0 for (const fixture of fixtures) { for (let id = 1; id <= 4; id++) { register(fixture, id, id === 3 && failThird) } } const input = [ JSON.stringify({ id: 1, ok: true, result: { text: `界😀 ${trace}` } }), JSON.stringify({ id: 2, ok: false, error: { code: 'fixture', message: 'failed' } }), JSON.stringify({ id: 3, ok: true, result: trace }), JSON.stringify({ id: 4, ok: true, result: 'final reply' }), '' ].join('\n') let offset = 0 while (offset < input.length) { const length = 1 + random(80) const chunk = input.slice(offset, offset + length) offset += length for (const fixture of fixtures) { try { fixture.client.handleSocketData(fixture.socket, chunk) } catch (error) { fixture.events.push(['throw', error.name, error.message]) } } assert.deepEqual(state(fixtures[1]), state(fixtures[0])) } for (const fixture of fixtures) { fixture.client.handleSocketData(fixture.socket, '') assert.equal(fixture.client.pending.size, 0) assert.deepEqual(fixture.events.at(-1), ['resolve', 4, 'final reply']) } assert.deepEqual(state(fixtures[1]), state(fixtures[0])) } console.log('1,000 fragmented multi-reply client journeys / 4,000 request settlements match') class BaselineBuffer { pending = '' push(chunk, onLine) { this.pending += chunk this.pending = modules[0].consumeNativeProviderLines(this.pending, onLine) } clear() { this.pending = '' } } for (let trace = 0; trace < 3000; trace++) { const buffers = [new BaselineBuffer(), new modules[1].NativeProviderLineBuffer()] const events = [[], []] for (let step = 0; step < 30; step++) { const clear = random(25) === 0 const fail = random(10) === 0 const chunk = ['abc', '\n', '\r\n', '\ud83d', '\udc00', '\n\n', '界', '', 'ok\nfault\npartial'][ random(9) ] buffers.forEach((buffer, index) => { if (clear) { buffer.clear() } try { buffer.push(chunk, (line) => { events[index].push(line) if (fail) { throw new Error('fixture callback failure') } }) } catch (error) { events[index].push({ error: error.message }) } }) assert.deepEqual(events[1], events[0]) assert.equal(buffers[1].pending, buffers[0].pending) } } console.log('3,000 actual line-buffer traces / 90,000 feeds match') function receiveArm(module) { const fixture = clientFixture(module) return (chunks) => { let result fixture.client.pending.set(1, { timer: undefined, resolve: (value) => { result = value }, reject: (error) => { throw error } }) for (const chunk of chunks) { fixture.client.handleSocketData(fixture.socket, chunk) } return result } } function sample(arm, input, repeats) { const start = performance.now() let result for (let i = 0; i < repeats; i++) { result = arm(input) } return { elapsed: (performance.now() - start) / repeats, result } } console.log( JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch, unit: 'ms', pairs: 8 }) ) for (const [size, chunkBytes] of [ [64, 65536], [120000, 65536], [1200000, 65536], [1200000, 4096], [4800000, 65536], [1200000, Number.POSITIVE_INFINITY] ]) { const expected = { screenshot: { data: 'A'.repeat(size) }, text: 'fixture' } const input = `${JSON.stringify({ id: 1, ok: true, result: expected })}\n` const chunks = [] for (let offset = 0; offset < input.length; offset += chunkBytes) { chunks.push(input.slice(offset, offset + chunkBytes)) } const arms = modules.map(receiveArm) for (const arm of arms) { assert.deepEqual(arm(chunks), expected) const until = performance.now() + 150 while (performance.now() < until) { sample(arm, chunks, 1) } } const repeats = Math.max(3, Math.min(100000, Math.ceil(50 / sample(arms[0], chunks, 1).elapsed))) /** @type {number[][]} */ const times = [[], []] for (let pair = 0; pair < 8; pair++) { for (const index of pair % 2 ? [1, 0] : [0, 1]) { const result = sample(arms[index], chunks, repeats) assert.deepEqual(result.result, expected) times[index].push(result.elapsed) } } const median = times.map((values) => { values.sort((a, b) => a - b) return (values[3] + values[4]) / 2 }) console.log( JSON.stringify({ size, chunkBytes: Number.isFinite(chunkBytes) ? chunkBytes : 'whole frame', chunks: chunks.length, repeats, median, times }) ) }