feat(core): ARP after Java, measured with Java FILIUS
An ARP lookup asks, waits a round trip, asks once more and gives up after another round trip; then host unreachable, observed at the sender itself or sent back by the router (for ICMP only for an echo request). A waiting sender wakes after what else happens at that time. A ping then waits a round trip for its answer, counted from when it left. Swift asks twice at once while events run and lets the packet wait without end when the address exists anywhere in the network; the option swiftArp keeps that for the Swift comparison, and three scenarios are compared with Swift until the trace where ARP fails or a ping goes unanswered. arpJava.test.ts compares ArpRun: both pings match; the two UDP stories wait for Java's transport thread (next step). DNS without a server now matches Java too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,7 +73,7 @@ function observationJson(item: IcmpObservation): Record<string, unknown> {
|
||||
|
||||
function run(scenario: Scenario): Record<string, unknown> {
|
||||
// Swift's cables carry all frames at once; everything else is compared as it is.
|
||||
const engine = new NetworkRuntimeEngine(scenario.seed, undefined, { swiftCables: true });
|
||||
const engine = new NetworkRuntimeEngine(scenario.seed, undefined, { swiftCables: true, swiftArp: true });
|
||||
if (scenario.speedPercent !== undefined) engine.setSimulationSpeed(scenario.speedPercent);
|
||||
engine.handle({ type: 'start', snapshot: snapshotOf(scenario), seed: scenario.seed, initialTimeMilliseconds: 0 });
|
||||
|
||||
@@ -301,6 +301,16 @@ const JAVA_INSTEAD_OF_SWIFT: Record<string, string> = {
|
||||
'tcp-closing': 'TCP: TIME_WAIT lasts 3 round trips and ends with the next segment; the other end closes by itself',
|
||||
};
|
||||
|
||||
/**
|
||||
* Scenarios that follow Java from a trace on: the traces before it must match
|
||||
* Swift, the rest must differ (the Java comparisons check that behaviour).
|
||||
*/
|
||||
const SWIFT_UNTIL_TRACE: Record<string, { readonly trace: number; readonly reason: string }> = {
|
||||
'firewall-and-loss': { trace: 36, reason: "a ping without an answer waits a round trip (Java's sendProbe), Swift returns at once" },
|
||||
'routed-two-networks': { trace: 156, reason: 'the router answers host unreachable when ARP has given up after two round trips, Swift at once' },
|
||||
'switch-lan': { trace: 155, reason: 'ARP for an address no card has asks again after a round trip, then host unreachable' },
|
||||
};
|
||||
|
||||
describe('same result as the Swift engine', () => {
|
||||
it('corrects Swift only where it errs', () => {
|
||||
for (const [name, correct] of Object.entries(SWIFT_ERRORS)) {
|
||||
@@ -321,7 +331,21 @@ describe('same result as the Swift engine', () => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const name of scenarioNames.filter((each) => !(each in JAVA_INSTEAD_OF_SWIFT))) {
|
||||
for (const [name, { trace, reason }] of Object.entries(SWIFT_UNTIL_TRACE)) {
|
||||
it(`${name} matches Swift until trace #${trace}, then follows Java: ${reason}`, () => {
|
||||
const scenario = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.scenario.json`), 'utf8')) as Scenario;
|
||||
const swift = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.swift.json`), 'utf8')) as Record<string, unknown>;
|
||||
const actual = run(scenario);
|
||||
const expectedTraces = swift.traces as unknown[];
|
||||
const actualTraces = actual.traces as unknown[];
|
||||
for (let index = 0; index < trace - 1; index += 1) {
|
||||
expect(actualTraces[index], `trace event #${index + 1}`).toEqual(expectedTraces[index]);
|
||||
}
|
||||
expect(actualTraces[trace - 1]).not.toEqual(expectedTraces[trace - 1]);
|
||||
});
|
||||
}
|
||||
|
||||
for (const name of scenarioNames.filter((each) => !(each in JAVA_INSTEAD_OF_SWIFT) && !(each in SWIFT_UNTIL_TRACE))) {
|
||||
it(name, () => {
|
||||
const scenario = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.scenario.json`), 'utf8')) as Scenario;
|
||||
const swift = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.swift.json`), 'utf8')) as Record<string, unknown>;
|
||||
|
||||
Reference in New Issue
Block a user