test: F3 acceptance — the classroom classics on FILIUS' examples against Java
A web page by its DNS name (internetwork_komplett), an e-mail between two networks through NAT and MX relay (Internet_Komplett_..._DE), a file found and fetched with Gnutella across a router (zwei_rechnernetze_komplett): ExampleRun records frames, tables, program lines and end states; the core plays the same calls and matches card by card. Equal RIP ties take Java's choice, RIP beacons are left out, the search's two connections compared flow by flow. Also: a core test that an equal RIP route from another router neither replaces nor refreshes the route, and nine stale type errors in core tests fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -207,6 +207,16 @@ export function expectSameCaptures(
|
||||
tables?: JavaTables;
|
||||
/** Placeholders in a table cell, beyond those of tableCell. */
|
||||
cell?: (text: string) => string;
|
||||
/**
|
||||
* A frame's flow, by its text (after `text`). Where frames of different
|
||||
* flows may come in either order (Java's threads, a few ten
|
||||
* milliseconds apart), a card's frames are compared flow by flow, the
|
||||
* flows by their names: each flow's frames in order, at about the same
|
||||
* time, with their rows.
|
||||
*/
|
||||
flow?: ((text: string) => string) | undefined;
|
||||
/** Which frames are compared, by their text (after `text`); all where not given. */
|
||||
compared?: ((text: string) => boolean) | undefined;
|
||||
} = {},
|
||||
): void {
|
||||
const text = options.text ?? ((each: string) => each);
|
||||
@@ -225,14 +235,31 @@ export function expectSameCaptures(
|
||||
const from = (options.from ?? Number.NEGATIVE_INFINITY) - javaStart;
|
||||
const until = (options.until ?? Number.POSITIVE_INFINITY) - javaStart;
|
||||
const normalized = options.normalized ?? ((frames: SeenFrame[], _card: string) => frames);
|
||||
const compared = options.compared ?? (() => true);
|
||||
const window = (frames: SeenFrame[], start: number) =>
|
||||
frames.filter((frame) => frame.time - start >= from && frame.time - start < until).map((frame) => ({ time: frame.time - start, text: frame.text }));
|
||||
frames
|
||||
.filter((frame) => frame.time - start >= from && frame.time - start < until && compared(frame.text))
|
||||
.map((frame) => ({ time: frame.time - start, text: frame.text }));
|
||||
const inWindow = (time: number, start: number) => time - start >= from && time - start < until;
|
||||
// Stable: a flow's frames keep their order.
|
||||
const inFlows = <T>(items: T[], textOf: (item: T) => string): T[] =>
|
||||
options.flow === undefined
|
||||
? items
|
||||
: items
|
||||
.map((item, index) => ({ item, index, flow: options.flow!(textOf(item)) }))
|
||||
.sort((lhs, rhs) => (lhs.flow < rhs.flow ? -1 : lhs.flow > rhs.flow ? 1 : lhs.index - rhs.index))
|
||||
.map(({ item }) => item);
|
||||
for (const [card, javaList] of javaByCard) {
|
||||
if (options.tables !== undefined) {
|
||||
// The rows of the frames in the window, frame by frame.
|
||||
const javaNumbers = javaList.flatMap((frame, index) => (inWindow(frame.time, javaStart) ? [index + 1] : []));
|
||||
const engineFrames = capturedByCard.get(card)!.filter((each) => inWindow(each.timeMilliseconds, engineStart));
|
||||
const javaNumbers = inFlows(
|
||||
javaList.flatMap((frame, index) => (inWindow(frame.time, javaStart) && compared(frame.text) ? [index + 1] : [])),
|
||||
(number) => javaList[number - 1]!.text,
|
||||
);
|
||||
const engineFrames = inFlows(
|
||||
capturedByCard.get(card)!.filter((each) => inWindow(each.timeMilliseconds, engineStart) && compared(text(engineText(each.frame)))),
|
||||
(each) => text(engineText(each.frame)),
|
||||
);
|
||||
for (const [view, withAddresses] of [['rows', true], ['layeredRows', false]] as const) {
|
||||
const javaRows = options.tables[view]?.[card];
|
||||
if (javaRows === undefined) continue;
|
||||
@@ -250,8 +277,8 @@ export function expectSameCaptures(
|
||||
}
|
||||
}
|
||||
}
|
||||
const expected = normalized(window(javaList, javaStart), card);
|
||||
const actual = normalized(window(engineByCard.get(card)!, engineStart), card);
|
||||
const expected = inFlows(normalized(window(javaList, javaStart), card), (frame) => frame.text);
|
||||
const actual = inFlows(normalized(window(engineByCard.get(card)!, engineStart), card), (frame) => frame.text);
|
||||
for (let index = 0; index < Math.max(expected.length, actual.length); index += 1) {
|
||||
expect(actual[index]?.text, `${card} frame #${index + 1}`).toBe(expected[index]?.text);
|
||||
expect(Math.abs(actual[index]!.time - expected[index]!.time), `${card} time of frame #${index + 1}`).toBeLessThanOrEqual(
|
||||
@@ -268,8 +295,8 @@ export function withClientPortText(text: string): string {
|
||||
|
||||
/** Java's capture tables per card: number, source, destination, protocol, layer, comment (ScenarioNetwork.writeRows). */
|
||||
export interface JavaTables {
|
||||
rows?: Record<string, (string | null)[][]>;
|
||||
layeredRows?: Record<string, (string | null)[][]>;
|
||||
rows?: Record<string, (string | null)[][]> | undefined;
|
||||
layeredRows?: Record<string, (string | null)[][]> | undefined;
|
||||
}
|
||||
|
||||
/** Java's layer names and comment words: its German message bundle, the locale it runs in (Locale.GERMANY). */
|
||||
|
||||
Reference in New Issue
Block a user