import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { createRequire } from "node:module"; import { pathToFileURL } from "node:url"; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); const build = path.resolve(process.argv[2] ?? path.join(root, "freecad-source/build-web/configure/bin")); const docs = path.join(root, "docs/p6"); globalThis.__dirname = `${build}${path.sep}`; globalThis.require = createRequire(import.meta.url); const loader = await import(pathToFileURL(path.join(build, "FreeCADWebPlotRuntimeProbe.js"))); const kernel = await loader.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebPlotRuntimeProbe.wasm")), print: () => {}, printErr: () => {} }); const call = (name, type = "number", argTypes = [], args = []) => kernel.ccall(name, type, argTypes, args); const run = () => { const initialize = call("fc_web_plot_initialize"); const api = call("fc_web_plot_python_api"); return { initialize, api, moduleAvailable: call("fc_web_plot_module_available"), apiCount: call("fc_web_plot_api_count"), seriesCount: call("fc_web_plot_series_count"), svgSize: call("fc_web_plot_svg_size"), svgValid: call("fc_web_plot_svg_valid"), resourcesReleased: call("fc_web_plot_resources_released"), error: call("fc_web_plot_api_error", "string") }; }; const first = run(); const second = run(); const semantic = value => ({ api: value.api, moduleAvailable: value.moduleAvailable, apiCount: value.apiCount, seriesCount: value.seriesCount, svgPositive: value.svgSize > 0, svgValid: value.svgValid, resourcesReleased: value.resourcesReleased, error: value.error }); const invalid = call("fc_web_plot_python_api_validate", "number", ["string"], ["src/Mod/Plot/Missing.py"]); const invalidError = call("fc_web_plot_api_error", "string"); const result = { first, second, deterministic: JSON.stringify(semantic(first)) === JSON.stringify(semantic(second)), invalid, invalidError, pass: first.api === 1 && second.api === 1 && first.moduleAvailable === 1 && first.apiCount === 15 && first.seriesCount === 1 && first.svgSize > 0 && first.svgValid === 1 && first.resourcesReleased === 1 && invalid === 0 && invalidError === "revision_mismatch" }; const fixture = { module: "Plot", action: "python-api", sourceEntry: "src/Mod/Plot/Plot.py", apiCount: 15, backend: "FreeCAD Web headless SVG" }; const hash = value => crypto.createHash("sha256").update(value).digest("hex"); const report = { schema: "freecad-web-p6-plot-runtime/v2", taskId: "FW-P6-PLT-B", module: "Plot", result: result.pass && result.deterministic ? "pass" : "fail", sourceRevision: "0a2e7ce7f52ba71d54c62708c9ac5c76ca142231", fixture: { ...fixture, inputSha256: hash(JSON.stringify(fixture)) }, details: result, resultSha256: hash(JSON.stringify(result)), runtime: { initializationCalls: call("fc_web_plot_initialization_calls"), initializationRuns: call("fc_web_plot_initialization_runs"), moduleAvailable: first.moduleAvailable, nativeTypeRegistration: false, pythonModule: "Plot", backend: "headless-svg" }, artifacts: { wasm: "freecad-source/build-web/configure/bin/FreeCADWebPlotRuntimeProbe.wasm" }, boundaries: ["Plot has no C++ DocumentObject type; runtime registration is a real Python module loaded from the official Plot.py source.", "Web backend provides the Plot public data API and SVG save without PySide, QtAgg, or matplotlib."] }; fs.writeFileSync(path.join(docs, "plot-runtime-web.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(root, "docs/freecad-p6-plot-b-runtime.log"), `${JSON.stringify({ result: report.result, apiCount: first.apiCount, initializationRuns: report.runtime.initializationRuns, invalidError })}\n`); console.log(JSON.stringify({ result: report.result, apiCount: first.apiCount, initializationRuns: report.runtime.initializationRuns, invalidError })); if (report.result !== "pass") process.exitCode = 1;