File size: 2,354 Bytes
3144483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import assert from "node:assert/strict";
import { setImmediate } from "node:timers/promises";
import { createDeferred } from "../../test/helpers/promise.js";
import { runUtf8CommandWithTimeout } from "./exec-runner.js";

const gc = globalThis.gc;
assert.ok(gc, "The retention child requires --expose-gc");
const emittedBytes = 128 * 1024;
const cap = 16;
const observed = [];
for (const mode of ["head", "combined-head", "tail"] as const) {
  let clipped: WeakRef<ArrayBufferLike> | undefined;
  let bytes = 0;
  const output = createDeferred();
  const control = new WeakRef(new ArrayBuffer(1024));
  const abort = new AbortController();
  const command = runUtf8CommandWithTimeout(
    [
      process.execPath,
      "-e",
      `process.stdout.write(Buffer.alloc(${emittedBytes}, 88)); setInterval(() => {}, 1000);`,
    ],
    {
      baseEnv: {},
      timeoutMs: 10_000,
      signal: abort.signal,
      outputCapture: mode === "tail" ? "tail" : "head",
      maxOutputBytes: mode === "combined-head" ? emittedBytes : cap,
      ...(mode === "combined-head" ? { maxCombinedOutputBytes: cap } : {}),
      onOutputChunk(chunk, stream) {
        assert.equal(stream, "stdout");
        if (bytes < cap && bytes + chunk.byteLength > cap) {
          clipped = new WeakRef(chunk.buffer);
        }
        bytes += chunk.byteLength;
        if (bytes === emittedBytes) {
          output.resolve();
        }
      },
    },
  );
  try {
    await Promise.race([
      output.promise,
      command.then(() => {
        throw new Error("Command finished before the retention checkpoint");
      }),
    ]);
    assert.ok(clipped, "The child must emit a buffer crossing the capture boundary");
    // Dereferencing during collection would keep the target alive for that job.
    for (let pass = 0; pass < 8; pass += 1) {
      await setImmediate();
      gc();
    }
    assert.equal(control.deref(), undefined, "The uncached GC control must be collected");
    observed.push({ mode, retained: clipped.deref() !== undefined });
  } finally {
    abort.abort();
    const result = await command;
    assert.equal(result.stdout, "X".repeat(cap));
    assert.equal(result.stderr, "");
    assert.equal(result.stdoutTruncatedBytes, emittedBytes - cap);
    assert.equal(result.termination, "signal");
  }
}
process.stdout.write(JSON.stringify(observed));