@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/dist/coverage.d.ts +90 -153
- package/dist/coverage.js +339 -419
- package/dist/daemon.js +11 -24
- package/dist/harness/coverage.d.ts +10 -4
- package/dist/harness/coverage.js +15 -6
- package/package.json +1 -1
- package/src/coverage.test.ts +175 -358
- package/src/coverage.ts +350 -449
- package/src/daemon.ts +11 -19
- package/src/harness/coverage.ts +16 -6
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
|
-
|
|
978
|
-
|
|
979
|
-
//
|
|
980
|
-
|
|
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
|
|
package/src/harness/coverage.ts
CHANGED
|
@@ -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,
|
|
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
|
|
111
|
-
* `
|
|
112
|
-
*
|
|
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
|
|
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
|