packages/protocol holds the one list of /sim messages and checks every field, naming what is wrong. packages/sim has the simulation room for F and the web worker: the document's snapshot at start, the engine's clock on the wall clock with Java's speed as cable delay, subscriptions to cable activity, a card's new frames and a program's new window lines, capture and clock kept over a stop, the command line as first action, a core failure told to every client. LocalSimHost mirrors the document into the worker and back without echo. M9 in Chromium: 17 examples give the same capture as in Node, character for character; the room runs in a real worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
3.0 KiB
TypeScript
58 lines
3.0 KiB
TypeScript
// M9 and the real worker, in Chromium (Playwright). The packages are bundled
|
|
// with esbuild as an editor would bundle them.
|
|
//
|
|
// - M9: every FILIUS example of the corpus (but the one with modems, which
|
|
// the core has not got yet), run 30 s with a few lines typed, gives the
|
|
// same capture and windows in Chromium as in Node.
|
|
// - LocalSimHost in a real Worker: started, pinged, the frames come back.
|
|
|
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { expect, test } from '@playwright/test';
|
|
import { build } from 'esbuild';
|
|
import { traceText } from './traceDigest.js';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const examples = join(here, '../../../corpus/java-examples');
|
|
/** Remote links (Modem) are not in the core yet: this example's run stops at its first frame to the modem. */
|
|
const NOT_YET = new Set(['modem_beispiel.fls']);
|
|
|
|
async function bundle(entry: string): Promise<string> {
|
|
const result = await build({ entryPoints: [join(here, entry)], bundle: true, format: 'iife', platform: 'browser', write: false, logLevel: 'silent' });
|
|
return result.outputFiles[0]!.text;
|
|
}
|
|
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test('M9: the same trace in Node and in Chromium', async ({ page }) => {
|
|
test.setTimeout(240_000);
|
|
await page.setContent('<!doctype html><title>M9</title>');
|
|
await page.addScriptTag({ content: await bundle('m9Entry.ts') });
|
|
for (const name of readdirSync(examples).filter((file) => file.endsWith('.fls') && !NOT_YET.has(file))) {
|
|
const archive = new Uint8Array(readFileSync(join(examples, name)));
|
|
const inNode = traceText(archive);
|
|
// Every run has traffic: frames on the cards and lines in a window.
|
|
expect(inNode.includes('"protocol":"ARP"') && inNode.includes('"kind":"text"'), name).toBe(true);
|
|
const inBrowser = await page.evaluate((bytes) => (globalThis as unknown as { filiusM9: { traceText: (archive: Uint8Array) => string } }).filiusM9.traceText(new Uint8Array(bytes)), [...archive]);
|
|
expect(inBrowser.length, name).toBe(inNode.length);
|
|
expect(inBrowser === inNode, name).toBe(true);
|
|
}
|
|
});
|
|
|
|
test('LocalSimHost runs the room in a real worker', async ({ page }) => {
|
|
await page.setContent('<!doctype html><title>LocalSimHost</title>');
|
|
await page.addScriptTag({ content: await bundle('localSimHostEntry.ts') });
|
|
const archive = [...new Uint8Array(readFileSync(join(examples, 'internetwork_komplett.fls')))];
|
|
const workerCode = await bundle('../src/worker.ts');
|
|
const result = await page.evaluate(
|
|
async ({ bytes, code }) => {
|
|
const api = (globalThis as unknown as { filiusLocal: { run: (archive: Uint8Array, workerUrl: string) => Promise<{ icmpRows: number; mode: string }> } }).filiusLocal;
|
|
return api.run(new Uint8Array(bytes), URL.createObjectURL(new Blob([code], { type: 'text/javascript' })));
|
|
},
|
|
{ bytes: archive, code: workerCode },
|
|
);
|
|
expect(result.mode).toBe('simulation');
|
|
expect(result.icmpRows).toBeGreaterThanOrEqual(8);
|
|
});
|