Files
julian.kiedaischandClaude Opus 5.5 c85b0e45da feat(core): modems run — the pair in the project, and a modem of another project hands its frames on (K0–K2)
Measured first: tools/java-oracle's ModemRun joins two networks by a pair
of Java modems over TCP on localhost. The modem has no card and adds no
delay of its own — a frame takes the cables on both sides, nothing more.

The core pairs the two enabled modems on a port (Swift's inProject, with
its conditions and cycle suppression) on Java's timing: no 20 ms. The
modem example of FILIUS now pings through instead of failing the core.
A modem of another project (relay) gives its frames to whoever runs the
engine and puts on its cable what comes back, with this engine's frame
and packet numbers; a frame dies after MAXIMUM_RELAY_HOPS trips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:12:27 +00:00

87 lines
3.3 KiB
TypeScript

// Test fixtures, ported from the private helpers of
// filius-ipad ios/FiliusPadTests/TopologyNetworkRuntimeEngineTests.swift.
import {
deviceConfiguration,
type EthernetFrame,
type TcpSegment,
NetworkRuntimeEngine,
portSnapshot,
topologySnapshot,
type DeviceConfiguration,
type Id,
type LinkSnapshot,
type NodeSnapshot,
type TopologySnapshotInput,
} from '../src/index.js';
let linkCounter = 0;
/** Swift uses a fresh `UUID()` per link; a counter keeps runs reproducible. */
export function link(sourceNodeId: Id, sourcePortId: Id, targetNodeId: Id, targetPortId: Id): LinkSnapshot {
linkCounter += 1;
return {
id: `ffffffff-0000-0000-0000-${linkCounter.toString(16).padStart(12, '0')}`,
sourceNodeId,
sourcePortId,
targetNodeId,
targetPortId,
};
}
export function pc(nodeId: Id, portId: Id): NodeSnapshot {
return { id: nodeId, kind: 'pc', ports: [portSnapshot(portId, 'eth0')] };
}
export function device(ipAddress: string, options: { mask?: string; gateway?: string } = {}): DeviceConfiguration {
return deviceConfiguration(ipAddress, options.mask ?? '255.255.255.0', options.gateway ?? '');
}
export function startedEngine(
input: TopologySnapshotInput,
seed: number = 14,
): NetworkRuntimeEngine {
const engine = new NetworkRuntimeEngine(seed);
engine.handle({ type: 'start', snapshot: topologySnapshot(input), seed, initialTimeMilliseconds: 0 });
return engine;
}
/** `00000000-0000-0000-0000-` followed by twelve hex digits. */
export function uuid(suffix: number): Id {
return `00000000-0000-0000-0000-${suffix.toString(16).toUpperCase().padStart(12, '0')}`;
}
const FRAME_EVENTS = new Set(['ethernetLinkDeparture', 'ethernetLinkDelivery', 'arpAnswered']);
/**
* Advances until every frame in flight has arrived — what Swift's send did
* by itself; the engine's send returns at once, as Java's.
*/
export function letFramesArrive(engine: NetworkRuntimeEngine): void {
const inFlight = () =>
engine.state.pendingEvents.filter((event) => FRAME_EVENTS.has(event.kind.kind)).map((event) => event.deadlineMilliseconds);
for (let deadlines = inFlight(); deadlines.length > 0; deadlines = inFlight()) {
engine.handle({ type: 'advance', toMilliseconds: Math.min(...deadlines) });
}
}
/**
* The first frame the node sends whose TCP segment matches is lost, as with
* TcpRun's loseFirst: it leaves (the sender's card shows it) and never arrives.
*/
export function loseFirst(engine: NetworkRuntimeEngine, nodeId: Id, which: (segment: TcpSegment) => boolean): void {
type Depart = (fromNodeId: Id, portId: Id, endpoint: unknown, frame: EthernetFrame) => void;
const target = engine as unknown as { departEthernetFrame: Depart };
const depart = target.departEthernetFrame.bind(engine);
let lost = false;
target.departEthernetFrame = (fromNodeId, portId, endpoint, frame) => {
depart(fromNodeId, portId, endpoint, frame);
const payload = frame.payload.kind === 'ipv4' ? frame.payload.packet.payload : undefined;
if (lost || fromNodeId !== nodeId || payload?.kind !== 'tcp' || !which(payload.segment)) return;
lost = true;
engine.state.pendingEvents = engine.state.pendingEvents.filter(
(event) => !(event.kind.kind === 'ethernetLinkDelivery' && event.kind.frame.identity === frame.identity),
);
};
}