@mark1russell7/harness 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -208,6 +208,7 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
208
208
  - Each wait goes from a `reconnecting` line, where the program starts its timer, to its next `dial` line, where it makes the socket. A wait outside its band, widened by δ_abs, is a finding. A try that did not come is a finding too.
209
209
  - The kernel gives the time from the detection to the first SYN of a new socket (`syn`). It also gives the time to the first new socket in ESTABLISHED (`established`).
210
210
  - It also gives the time from each `dial` line to its SYN (`connects`): the work of the program before connect(2).
211
+ - A measurement-only prediction can give `reconnects` too. The end of its connection has no band, but each wait has one. A wait outside its band is a finding of its run, and the verdict names that run. The decision stays `measured`.
211
212
  - **A call in flight of a WebSocket client** (`callOf`). The run gives its error or its reply, and the time from the detection to its end.
212
213
  - **A stream.** In a stream (`probe.mode` is `stream`), the client sends `count` requests. The run keeps the time from each send to the last byte of its reply (`samples`). A record without `replied` lines gives the time to the first data.
213
214
  - **Reordering.** A measurement-only run also gives what reordering, duplicates and losses did (`reordering`). At the client, it counts the arrivals beyond a hole and the arrivals of data that it had already, against `rcv_nxt`. From the sender, it gives the most SACKed and lost segments, the DSACKs, the fast retransmissions, and the reordering degree and count of the kernel.
package/build/index.d.ts CHANGED
@@ -426,7 +426,8 @@ interface Reconnects {
426
426
  * A cell whose outcome is a distribution, for example under netem loss, has no band. Its
427
427
  * prediction is `detects: null` with the reason, and its runs are measurements only. A
428
428
  * measurement-only cell that names a reference (`from`) also records the time from it to the end
429
- * of the connection, or to the last data.
429
+ * of the connection, or to the last data. It can also give the waits of the reconnect after that
430
+ * end (`reconnects`).
430
431
  *
431
432
  * For a group cell, the band is the first write that a primary accepts after the fault, and its
432
433
  * errno is `OK`. `group` tells which primary, and the stages of the group on the way.
@@ -491,6 +492,11 @@ type Prediction = {
491
492
  readonly ends?: "own-close";
492
493
  readonly group?: GroupPrediction;
493
494
  readonly request?: RequestPrediction;
495
+ /**
496
+ * These are the waits of the reconnect after the end of the connection, from the document of
497
+ * the client. The end has no band, but each wait has one. A wait outside its band is a finding of its run.
498
+ */
499
+ readonly reconnects?: Reconnects;
494
500
  };
495
501
  /** A target is the server that a cell connects to: a stock image. */
496
502
  interface Target {
package/build/index.js CHANGED
@@ -329,13 +329,15 @@ const verdictOfBand = (prediction, runs, delta = NOISE) => {
329
329
  }
330
330
  const outcomes = [...kinds].map(([kind, n]) => `${n} ${kind}`).join(", ");
331
331
  const timed = times.length === 0 ? "no time" : `median ${s(middle(times)).value} s from ${prediction.from}, range [${s(times[0]).value}, ${s(times.at(-1)).value}] s`;
332
+ const missed = outside.filter((each) => counted.some((run) => name(run) === each.run && run.reconnect !== void 0));
333
+ const late = missed.length === 0 ? "" : ` ${missed.length} runs had a wait of the reconnect outside its band.`;
332
334
  return {
333
335
  ...empty,
334
336
  decision: "measured",
335
337
  band: null,
336
- outside: [],
338
+ outside: missed,
337
339
  median: times.length === 0 ? null : s(middle(times)),
338
- why: `${counted.length} runs measured: ${outcomes}; ${timed}. ${prediction.why}`
340
+ why: `${counted.length} runs measured: ${outcomes}; ${timed}.${late} ${prediction.why}`
339
341
  };
340
342
  }
341
343
  const samples = counted.flatMap((run) => (run.samples ?? []).map((sample) => valueIn(sample, "s"))).sort((a, b) => a - b);
@@ -1674,10 +1676,14 @@ const measureSocket = (cell, run) => {
1674
1676
  toward: "target"
1675
1677
  }
1676
1678
  }, run)) };
1679
+ const reconnect = prediction.reconnects === void 0 || ended === void 0 ? void 0 : reconnectOf(run, ended, cookie, drop?.line.t ?? ended);
1680
+ const late = reconnect === void 0 || prediction.reconnects === void 0 ? null : reconnectFinding(prediction.reconnects, reconnect, noiseOf(cell));
1677
1681
  return {
1678
1682
  ...base,
1679
1683
  ...nothing,
1680
1684
  ...chain,
1685
+ ...reconnect === void 0 ? {} : { reconnect },
1686
+ finding: late,
1681
1687
  outcome: ended !== void 0 ? outcome : { kind: "answered" },
1682
1688
  ...from === void 0 || until === void 0 ? {} : { elapsed: between(from, until) },
1683
1689
  noticed: prediction.from === void 0 ? null : noticed,
@@ -4154,7 +4160,7 @@ const probeArgs = (cell, lab) => {
4154
4160
  ];
4155
4161
  if (probe.ws !== void 0) {
4156
4162
  const { ws } = probe;
4157
- const waits = (prediction.detects === true ? prediction.reconnects?.waits ?? [] : []).reduce((sum, wait) => sum + valueIn(wait.high, "s"), 0);
4163
+ const waits = (prediction.detects !== false ? prediction.reconnects?.waits ?? [] : []).reduce((sum, wait) => sum + valueIn(wait.high, "s"), 0);
4158
4164
  args.push("--websocket", ws.socket);
4159
4165
  args.push("--heartbeat", ws.heartbeat === null ? "off" : "on");
4160
4166
  if (ws.heartbeat !== null) args.push("--heartbeat-interval-ms", String(valueIn(ws.heartbeat.interval, "ms")), "--heartbeat-timeout-ms", String(valueIn(ws.heartbeat.timeout, "ms")), "--heartbeat-misses", String(ws.heartbeat.misses));
@@ -4531,7 +4537,7 @@ const once = async (lab, cell, environment, index, dry = false) => {
4531
4537
  if (toxicFirst) await lab.toxic(null);
4532
4538
  }
4533
4539
  if (cell.fault === "freeze-process" && !server && cell.probe.mode !== "idle") await probe.until(["data"], 5e3).catch(() => void 0);
4534
- const expected = cell.prediction.detects === true ? cell.prediction.reconnects : void 0;
4540
+ const expected = cell.prediction.detects !== false ? cell.prediction.reconnects : void 0;
4535
4541
  if (expected !== void 0) {
4536
4542
  const waits = expected.waits.reduce((sum, wait) => sum + valueIn(wait.high, "s"), 0);
4537
4543
  await dials(probe, 1 + expected.waits.length, waits * 2e3 + 5e3);