@specific.dev/spectest 0.81.1 → 0.83.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/src/daemon.ts CHANGED
@@ -49,6 +49,8 @@ import {
49
49
  } from "./harness/coverage.js";
50
50
  import { configureBrowserCoverage } from "./browser-coverage.js";
51
51
  import {
52
+ NODE_COVERAGE_MARKERS_SUBDIR,
53
+ NODE_COVERAGE_SCRIPTS_SUBDIR,
52
54
  applyCoverageAdapters,
53
55
  coverageAdapters,
54
56
  coverageReportsMode,
@@ -974,10 +976,15 @@ async function ensureCoverage(svc: NamedService): Promise<string[]> {
974
976
  if (svc.coverage === undefined) return [];
975
977
  validateCoverage(svc.name, svc.coverage);
976
978
  const host = coverageHostDir(WORKSPACE, svc.name);
977
- await fs.mkdir(host, { recursive: true });
978
- await fs.chmod(host, 0o777);
979
- // Nothing else is mounted: no conversion runs in the container (the
980
- // c8 tools mount went with SDK 0.80).
979
+ // World-writable, the subdirectories the node() hook writes into too:
980
+ // the container's processes run as whatever user the image says, and
981
+ // a directory the first of them created would be closed to the next.
982
+ for (const d of [host, path.join(host, NODE_COVERAGE_SCRIPTS_SUBDIR), path.join(host, NODE_COVERAGE_MARKERS_SUBDIR)]) {
983
+ await fs.mkdir(d, { recursive: true });
984
+ await fs.chmod(d, 0o777);
985
+ }
986
+ // Nothing else is mounted: nothing runs in the container for coverage
987
+ // beyond the hook in the process itself.
981
988
  return [`--volume=${host}:${COVERAGE_CONTAINER_DIR}`];
982
989
  }
983
990
 
@@ -4052,21 +4059,6 @@ function coverageCaptureContext(
4052
4059
  await fs.rename(tmp, file);
4053
4060
  written.add(path.relative(reportDir, file));
4054
4061
  },
4055
- async readContainerFile(containerPath: string) {
4056
- // `docker cp` rather than `exec cat`: a source map can be tens of
4057
- // MB, and the exec path buffers stdout as a string. The copy lands
4058
- // in the VM's tmp, never in the coverage directory (it would ship).
4059
- const tmp = path.join(tmpdir(), `spectest-cov-${svc.name}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
4060
- try {
4061
- const r = await docker(["cp", `${svc.name}:${containerPath}`, tmp], COVERAGE_CAPTURE_TIMEOUT_MS);
4062
- if (r.code !== 0) return null;
4063
- return await fs.readFile(tmp);
4064
- } catch {
4065
- return null;
4066
- } finally {
4067
- await fs.rm(tmp, { force: true, recursive: true }).catch(() => {});
4068
- }
4069
- },
4070
4062
  };
4071
4063
  }
4072
4064
 
@@ -3,7 +3,8 @@
3
3
  // A service that opts in (`coverage: true | { command }`) gets a writable
4
4
  // directory bind-mounted at `/spectest/coverage/` in its container. The
5
5
  // service's own tool, or its coverage adapters, write reports there —
6
- // lcov, or the V8 coverage JSON that `NODE_V8_COVERAGE` writes. The daemon reads the
6
+ // lcov, the V8 coverage JSON shape (what `coverage.node()`'s hook or
7
+ // `NODE_V8_COVERAGE` write), or a node() script record. The daemon reads the
7
8
  // directory from the VM side (no `docker exec` to fetch) after bring-up
8
9
  // and after each test, and ships the reports **verbatim** to the control
9
10
  // plane as a gzipped bundle. Nothing is interpreted here beyond the one
@@ -107,17 +108,26 @@ export function isLcovShaped(text: string): boolean {
107
108
 
108
109
  /**
109
110
  * True when `text` looks like a V8 coverage JSON document — what
110
- * `NODE_V8_COVERAGE` / `v8.takeCoverage()` write: an object with a
111
- * `result` array of scripts. A cheap shape check, not a parse (a report
112
- * can be megabytes); the control plane parses for real.
111
+ * `NODE_V8_COVERAGE` / `v8.takeCoverage()` write, and the shape the
112
+ * `node()` hook's dumps keep: an object with a `result` array of
113
+ * scripts. A cheap shape check, not a parse (a report can be
114
+ * megabytes); the control plane parses for real.
113
115
  */
114
116
  export function isV8CoverageJson(text: string): boolean {
115
117
  return /^\s*\{/.test(text) && /"result"\s*:\s*\[/.test(text);
116
118
  }
117
119
 
118
- /** True when `text` is a report spectest stores: lcov or V8 JSON. */
120
+ /** True when `text` is a script record the `node()` hook writes
121
+ * (`scripts/<id>.json`: `{ "spectestScript": { url, lineLengths,
122
+ * functions, sourceMap } }`), the static side of its dumps. */
123
+ export function isSpectestScriptRecord(text: string): boolean {
124
+ return /^\s*\{\s*"spectestScript"\s*:/.test(text);
125
+ }
126
+
127
+ /** True when `text` is a report spectest stores: lcov, V8 JSON, or a
128
+ * node() script record. */
119
129
  export function isWellFormedReport(text: string): boolean {
120
- return isLcovShaped(text) || isV8CoverageJson(text);
130
+ return isLcovShaped(text) || isV8CoverageJson(text) || isSpectestScriptRecord(text);
121
131
  }
122
132
 
123
133
  /** True when a well-formed report names no source file at all (an lcov