import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { execFileSync } from "node:child_process"; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); const sourceRoot = path.join(root, "freecad-source"); const bin = path.join(sourceRoot, "build-web/configure/bin"); const smoke = path.join(sourceRoot, "src/WebAssembly/FreeCADCoreProbe/import-runtime-smoke.js"); const dist = path.join(root, "web/dist"); const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); function replay() { const output = execFileSync(process.execPath, [smoke, bin], { encoding: "utf8" }).trim(); const result = JSON.parse(output.split("\n").at(-1)); return { handle: result.importHandle, summary: result.firstImport, release: result.release, activeAfterRelease: result.activeAfterRelease, stateSha256: result.validSnapshotHash, error: result.missingFileError, stateAfterError: result.afterMissingSnapshot, }; } const first = replay(); const reopened = replay(); const html = fs.readFileSync(path.join(dist, "import.html"), "utf8"); const entryMatch = html.match(/src="(\/assets\/import-[^"]+\.js)"/); const worker = fs.readdirSync(path.join(dist, "assets")).find((file) => /^import\.worker-[^.]+\.js$/.test(file)); if (!entryMatch || !worker) throw new Error("Import dist entry or worker asset is missing"); const artifacts = { html: { path: "web/dist/import.html", sha256: sha256(path.join(dist, "import.html")) }, entry: { path: `web/dist${entryMatch[1]}`, sha256: sha256(path.join(dist, entryMatch[1])) }, worker: { path: `web/dist/assets/${worker}`, sha256: sha256(path.join(dist, "assets", worker)) }, js: { path: "web/dist/kernel/FreeCADWebImportRuntimeProbe.js", sha256: sha256(path.join(dist, "kernel/FreeCADWebImportRuntimeProbe.js")) }, wasm: { path: "web/dist/kernel/FreeCADWebImportRuntimeProbe.wasm", sha256: sha256(path.join(dist, "kernel/FreeCADWebImportRuntimeProbe.wasm")) }, }; assert.deepEqual(reopened.summary, first.summary); assert.equal(first.handle, 1); assert.equal(reopened.handle, 1); assert.equal(first.release, 1); assert.equal(reopened.release, 1); assert.equal(first.activeAfterRelease, 0); assert.equal(reopened.activeAfterRelease, 0); assert.equal(first.stateSha256, reopened.stateSha256); assert.deepEqual(first.stateAfterError, first.summary); assert.equal(artifacts.js.sha256, sha256(path.join(bin, "FreeCADWebImportRuntimeProbe.js"))); assert.equal(artifacts.wasm.sha256, sha256(path.join(bin, "FreeCADWebImportRuntimeProbe.wasm"))); const report = { schema: "freecad-web-p6-import-persistence/v1", module: "Import", action: "import/step", result: "pass_boundary", persistence: { applicability: "not_applicable_current_action", reason: "FCStd save/reopen is a separate declared Import action and is not claimed by the import/step first slice", verification: "resource-manifest-and-fresh-runtime-replay", }, fixture: { path: "web/public/samples/freecad-sample.step", sha256: sha256(path.join(root, "web/public/samples/freecad-sample.step")), }, first, reopened, replayDeterministic: JSON.stringify(first.summary) === JSON.stringify(reopened.summary), oldStatePreservedAfterError: JSON.stringify(first.summary) === JSON.stringify(first.stateAfterError), artifacts, sourceBacked: true, }; fs.writeFileSync(path.join(root, "docs/p6/import-persistence.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(root, "docs/freecad-p6-import-g-persistence.log"), [ `result=${report.result}`, `applicability=${report.persistence.applicability}`, `fixtureSha256=${report.fixture.sha256}`, `stateSha256=${first.stateSha256}`, `replayDeterministic=${report.replayDeterministic}`, `oldStatePreservedAfterError=${report.oldStatePreservedAfterError}`, `wasmSha256=${artifacts.wasm.sha256}`, "", ].join("\n")); console.log(JSON.stringify(report));