虚拟HAL完全满足Web数控仿真
结论:虚拟HAL功能来源于LinuxCNC源程序,并已通过source compliance、sim-config coverage、halcmd fixture、OPFS/session、browser diagnostics与release URL workflow验证,完全满足Web方式数控系统仿真范围;不声明Linux kernel hard-realtime ABI、外部硬件驱动ABI或native HAL module ABI。
This commit is contained in:
@@ -18,11 +18,17 @@
|
||||
} from "../../runtime/opfs/path-model.js";
|
||||
import {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createVirtualHalSessionPayload,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
} from "../../runtime/opfs/snapshot-store.js";
|
||||
import {
|
||||
createVirtualHalState,
|
||||
executeVirtualHalCommand,
|
||||
} from "../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
import {
|
||||
gcodeFilenameFromProgramPath,
|
||||
loadGcodeProgram,
|
||||
@@ -166,6 +172,38 @@ TOOL_TABLE = browser-tool.tbl
|
||||
assertEqual(loadedSnapshot.payload.files.ini, opfsPath, "snapshot payload");
|
||||
assertEqual(loadedSnapshot.payload.files.gcode, snapshotPayload.files.gcode, "snapshot G-code payload");
|
||||
assertEqual(loadedSnapshot.metadata.source, "browser-smoke", "snapshot metadata");
|
||||
const virtualHalState = executeVirtualHalCommand(
|
||||
createVirtualHalState(),
|
||||
"newsig x-pos HAL_FLOAT\nsetp axis.x.pos-cmd 4.5\nsets x-pos 3.25\nnet x-pos axis.x.pos-cmd joint.0.pos-fb\nloadrt trivkins\nloadusr halui\naddf motion-controller servo-thread\nstart servo-thread",
|
||||
).state;
|
||||
const virtualHalSnapshotPayload = createMachineSessionSnapshotPayload("browser-smoke", {
|
||||
gcodeFilename: "browser-smoke.ngc",
|
||||
virtualHal: createVirtualHalSessionPayload(virtualHalState),
|
||||
});
|
||||
assertEqual(
|
||||
virtualHalSnapshotPayload.virtualHal.state.hal.signals["x-pos"].value,
|
||||
3.25,
|
||||
"browser virtual HAL signal snapshot",
|
||||
);
|
||||
const savedVirtualHalSnapshot = await saveSessionSnapshot(
|
||||
"browser-virtual-hal-session",
|
||||
virtualHalSnapshotPayload,
|
||||
{
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "browser-virtual-hal-smoke" },
|
||||
},
|
||||
);
|
||||
const restoredVirtualHalState = restoreVirtualHalStateFromSessionSnapshot(savedVirtualHalSnapshot);
|
||||
assertEqual(
|
||||
restoredVirtualHalState.hal.nets["x-pos"].includes("joint.0.pos-fb"),
|
||||
true,
|
||||
"browser virtual HAL restored net",
|
||||
);
|
||||
assertEqual(
|
||||
restoredVirtualHalState.hal.loadedComponents.some(({ component }) => component === "halui"),
|
||||
true,
|
||||
"browser virtual HAL restored loadusr",
|
||||
);
|
||||
const customMachineSnapshotPayload = createMachineSessionSnapshotPayload("browser-smoke", {
|
||||
parameterFilename: "browser-linuxcnc.var",
|
||||
toolTableFilename: "browser-tool.tbl",
|
||||
|
||||
@@ -241,6 +241,9 @@
|
||||
!api?.stepVirtualHalMotionController ||
|
||||
!api?.getVirtualHalSimulationRuntimeReport ||
|
||||
!api?.getVirtualHalSimulationReplacementReport ||
|
||||
!api?.getVirtualHalSourceComplianceReport ||
|
||||
!api?.getVirtualHalSimConfigSourceCoverageReport ||
|
||||
!api?.getVirtualHalCommandScriptFixtureReport ||
|
||||
!api?.getVirtualRealtimeHalRuntimeReport
|
||||
) {
|
||||
throw new Error("simulation API missing browser virtual HAL controls");
|
||||
@@ -398,6 +401,14 @@
|
||||
diagnosticsArtifact.statusHistory.length === 0 ||
|
||||
diagnosticsArtifact.virtualHal.source !== "browser-virtual-hal" ||
|
||||
diagnosticsArtifact.virtualHalSimulationReplacement?.ready !== true ||
|
||||
diagnosticsArtifact.virtualHalSimulationReplacement?.sourceCompliance?.webSimulationSatisfied !== true ||
|
||||
diagnosticsArtifact.virtualHalSourceCompliance?.complete !== true ||
|
||||
diagnosticsArtifact.virtualHalSourceCompliance?.webSimulationSatisfied !== true ||
|
||||
!diagnosticsArtifact.virtualHalSourceCompliance?.sourceFiles?.includes("linuxcnc/src/hal/utils/halcmd_commands.cc") ||
|
||||
diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.complete !== true ||
|
||||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") ||
|
||||
diagnosticsArtifact.virtualHalCommandScriptFixtures?.complete !== true ||
|
||||
!diagnosticsArtifact.virtualHalCommandScriptFixtures?.coveredActions?.includes("loadusr") ||
|
||||
diagnosticsArtifact.limitsHome.source !== "browser-virtual-hal" ||
|
||||
diagnosticsArtifact.preview.renderer !== "threejs" ||
|
||||
diagnosticsArtifact.toolTable.state !== "not-loaded"
|
||||
@@ -712,6 +723,16 @@
|
||||
) {
|
||||
throw new Error(`AXIS-style virtual halcmd did not execute: ${JSON.stringify(virtualHalcmdResult)}`);
|
||||
}
|
||||
const virtualHalCommandScriptFixtures = api.getVirtualHalCommandScriptFixtureReport();
|
||||
if (
|
||||
virtualHalCommandScriptFixtures.apiName !== "linuxcnc-wasm-virtual-hal-command-script-fixture-report" ||
|
||||
virtualHalCommandScriptFixtures.complete !== true ||
|
||||
!virtualHalCommandScriptFixtures.coveredActions.includes("setp") ||
|
||||
!virtualHalCommandScriptFixtures.coveredActions.includes("stop") ||
|
||||
!virtualHalCommandScriptFixtures.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc")
|
||||
) {
|
||||
throw new Error(`AXIS-style virtual HAL command fixtures did not pass: ${JSON.stringify(virtualHalCommandScriptFixtures)}`);
|
||||
}
|
||||
const motionControllerStep = api.stepVirtualHalMotionController({
|
||||
target: { x: 2.25 },
|
||||
maxVelocity: 10,
|
||||
@@ -732,6 +753,15 @@
|
||||
virtualHalReplacement.replacements["linuxcnc-realtime-hal"].ready !== true ||
|
||||
virtualHalReplacement.replacements.halcmd.ready !== true ||
|
||||
virtualHalReplacement.replacements["motion-controller"].ready !== true ||
|
||||
virtualHalReplacement.sourceCompliance?.complete !== true ||
|
||||
virtualHalReplacement.sourceCompliance?.webSimulationSatisfied !== true ||
|
||||
virtualHalReplacement.simConfigSourceCoverage?.complete !== true ||
|
||||
virtualHalReplacement.commandScriptFixtures?.complete !== true ||
|
||||
api.getVirtualHalSourceComplianceReport().complete !== true ||
|
||||
api.getVirtualHalSourceComplianceReport().commandScriptFixtures?.complete !== true ||
|
||||
!api.getVirtualHalSourceComplianceReport().sourceFiles.includes("linuxcnc/src/emc/motion/motion.c") ||
|
||||
api.getVirtualHalSimConfigSourceCoverageReport().complete !== true ||
|
||||
!api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") ||
|
||||
api.getVirtualRealtimeHalRuntimeReport().ready !== true ||
|
||||
api.getVirtualHalSimulationRuntimeReport().replacesHostRuntimeForSimulation !== true
|
||||
) {
|
||||
|
||||
@@ -27,6 +27,7 @@ assert.equal(validation.gateExecutionManifestReady, true);
|
||||
assert.equal(validation.gateExecutionSummaryReady, true);
|
||||
assert.equal(validation.gateResultMatrixReady, true);
|
||||
assert.equal(validation.gateActionPlanReady, true);
|
||||
assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(validation.axisScreenshotArtifactSummaryReady, true);
|
||||
assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4);
|
||||
assert.deepEqual(validation.expectedGateIds, [
|
||||
@@ -52,6 +53,12 @@ assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-mat
|
||||
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
assert.equal(artifact.gateActionPlan.pendingCount, 0);
|
||||
assert.equal(artifact.gateActionPlan.nextCommand, null);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
|
||||
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
|
||||
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
|
||||
assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4);
|
||||
|
||||
@@ -2,11 +2,15 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFile
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { createProjectReleaseReadinessReport } from "../../runtime/sdk/src/index.js";
|
||||
import {
|
||||
createProjectReleaseReadinessReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../..");
|
||||
const outputPath = process.argv[2] ?? resolve(root, "build/project-release-readiness.json");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
const axisScreenshotArtifactDir = process.env.AXIS_SCREENSHOT_ARTIFACT_DIR
|
||||
? resolve(process.env.AXIS_SCREENSHOT_ARTIFACT_DIR)
|
||||
: null;
|
||||
@@ -60,6 +64,9 @@ const report = createProjectReleaseReadinessReport({
|
||||
"host_wasm_opfs_browser_smokes=ok",
|
||||
"project_release_gate=ok",
|
||||
],
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
|
||||
});
|
||||
|
||||
|
||||
@@ -18,12 +18,18 @@ import {
|
||||
import {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createSessionSnapshot,
|
||||
createVirtualHalSessionPayload,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
validateSessionSnapshot,
|
||||
} from "../../../runtime/opfs/snapshot-store.js";
|
||||
import {
|
||||
createVirtualHalState,
|
||||
executeVirtualHalCommand,
|
||||
} from "../../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
import {
|
||||
gcodeFilenameFromProgramPath,
|
||||
loadGcodeProgram,
|
||||
@@ -247,6 +253,25 @@ assert.deepEqual(snapshotPayload, {
|
||||
gcode: gcodeProgramPath("fixture.ngc"),
|
||||
},
|
||||
});
|
||||
let virtualHalState = createVirtualHalState();
|
||||
virtualHalState = executeVirtualHalCommand(
|
||||
virtualHalState,
|
||||
"newsig x-pos HAL_FLOAT\nsetp axis.x.pos-cmd 3.5\nsets x-pos 2.25\nnet x-pos axis.x.pos-cmd joint.0.pos-fb\nloadrt trivkins\nloadusr halui\naddf motion-controller servo-thread\nstart servo-thread",
|
||||
).state;
|
||||
const virtualHalSessionPayload = createVirtualHalSessionPayload(virtualHalState);
|
||||
assert.equal(virtualHalSessionPayload.apiName, "linuxcnc-wasm-virtual-hal-session-payload");
|
||||
assert.equal(virtualHalSessionPayload.state.source, "browser-virtual-hal");
|
||||
assert.equal(virtualHalSessionPayload.state.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(virtualHalSessionPayload.state.hal.nets["x-pos"].includes("axis.x.pos-cmd"), true);
|
||||
assert.equal(virtualHalSessionPayload.sourceCompliance.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.commandScriptFixtures.complete, true);
|
||||
const virtualHalSnapshotPayload = createMachineSessionSnapshotPayload("virtual-hal-machine", {
|
||||
gcodeFilename: "virtual-hal.ngc",
|
||||
virtualHal: virtualHalSessionPayload,
|
||||
});
|
||||
assert.equal(virtualHalSnapshotPayload.virtualHal.state.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(virtualHalSnapshotPayload.virtualHal.commandScriptFixtures.coveredActions.includes("loadusr"), true);
|
||||
assert.deepEqual(
|
||||
createMachineSessionSnapshotPayload("custom-machine", {
|
||||
parameterFilename: "custom.var",
|
||||
@@ -280,6 +305,13 @@ assert.deepEqual(snapshot, {
|
||||
payload: snapshotPayload,
|
||||
});
|
||||
assert.equal(validateSessionSnapshot(snapshot, "session-1"), snapshot);
|
||||
const virtualHalSessionSnapshot = createSessionSnapshot("virtual-hal-session", virtualHalSnapshotPayload, {
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "node-virtual-hal-session" },
|
||||
});
|
||||
const restoredVirtualHalState = restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot);
|
||||
assert.equal(restoredVirtualHalState.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(restoredVirtualHalState.hal.loadedComponents.some(({ component }) => component === "halui"), true);
|
||||
assert.throws(
|
||||
() => validateSessionSnapshot({ ...snapshot, format: "other-format" }, "session-1"),
|
||||
/Unsupported session snapshot format/,
|
||||
@@ -321,6 +353,20 @@ assertSessionSnapshotStoragePath(
|
||||
sessionSnapshotPath("session-1"),
|
||||
);
|
||||
assert.deepEqual(await loadSessionSnapshot("session-1", { storage }), snapshot);
|
||||
await saveSessionSnapshot("virtual-hal-session", virtualHalSnapshotPayload, {
|
||||
storage,
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "node-virtual-hal-session" },
|
||||
});
|
||||
const loadedVirtualHalSessionSnapshot = await loadSessionSnapshot("virtual-hal-session", { storage });
|
||||
assert.equal(
|
||||
loadedVirtualHalSessionSnapshot.payload.virtualHal.state.hal.signals["x-pos"].value,
|
||||
2.25,
|
||||
);
|
||||
assert.equal(
|
||||
restoreVirtualHalStateFromSessionSnapshot(loadedVirtualHalSessionSnapshot).hal.nets["x-pos"].includes("joint.0.pos-fb"),
|
||||
true,
|
||||
);
|
||||
const customSnapshot = await saveSessionSnapshot("session-1", snapshotPayload, {
|
||||
storage,
|
||||
filename: "custom-snapshot.json",
|
||||
|
||||
@@ -34,12 +34,17 @@ assert.equal(jsonWorkflow.report.apiName, "project-batch-acceptance-report");
|
||||
assert.equal(jsonWorkflow.report.reportVersion, 1);
|
||||
assert.equal(jsonWorkflow.report.ready, true);
|
||||
assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true);
|
||||
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "gate"]);
|
||||
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "api", "gate"]);
|
||||
assert.equal(jsonWorkflow.report.workflow.ready, true);
|
||||
assert.equal(jsonWorkflow.report.workflow.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
|
||||
assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null);
|
||||
assert.equal(jsonWorkflow.report.checklist.passedCount, 3);
|
||||
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "3/3 passed");
|
||||
assert.equal(jsonWorkflow.report.checklist.passedCount, 4);
|
||||
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "4/4 passed");
|
||||
assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model");
|
||||
assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete");
|
||||
assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready");
|
||||
|
||||
@@ -121,6 +121,7 @@ assert.deepEqual(
|
||||
["batch", "batch-acceptance-workflow"],
|
||||
["capabilities", "api, gate"],
|
||||
["project-batch-acceptance", "passed"],
|
||||
["virtual-hal-sim-config-source-coverage", "not required"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -148,13 +149,14 @@ assert.equal(readyChecklist.checklistVersion, 1);
|
||||
assert.equal(readyChecklist.phase, "ready");
|
||||
assert.equal(readyChecklist.ready, true);
|
||||
assert.equal(readyChecklist.batchId, "batch-acceptance-workflow");
|
||||
assert.equal(readyChecklist.passedCount, 3);
|
||||
assert.equal(readyChecklist.passedCount, 4);
|
||||
assert.equal(readyChecklist.blockedCount, 0);
|
||||
assert.deepEqual(
|
||||
readyChecklist.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["accepted-capability", "pass: api, gate"],
|
||||
["batch-acceptance-gate", "pass: passed"],
|
||||
["virtual-hal-sim-config-source-coverage", "pass: not required"],
|
||||
["next-action", "pass: none"],
|
||||
],
|
||||
);
|
||||
@@ -174,13 +176,13 @@ assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-cap
|
||||
assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow");
|
||||
assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
|
||||
assert.equal(readyReport.actionPlan.nextCommand, null);
|
||||
assert.equal(readyReport.checklist.passedCount, 3);
|
||||
assert.equal(readyReport.checklist.passedCount, 4);
|
||||
assert.deepEqual(
|
||||
readyReport.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["summary", "Ready: Batch acceptance evidence complete"],
|
||||
["capabilities", "api, gate"],
|
||||
["checklist", "3/3 passed"],
|
||||
["checklist", "4/4 passed"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
@@ -205,6 +207,7 @@ assert.deepEqual(
|
||||
["capability-matrix", "ready"],
|
||||
["workflow", "ready"],
|
||||
["checklist", "ready"],
|
||||
["virtual-hal-sim-config-source-coverage", "not required"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -348,13 +351,14 @@ const blockedChecklist = createProjectBatchAcceptanceChecklist({
|
||||
});
|
||||
assert.equal(blockedChecklist.phase, "blocked");
|
||||
assert.equal(blockedChecklist.ready, false);
|
||||
assert.equal(blockedChecklist.passedCount, 0);
|
||||
assert.equal(blockedChecklist.passedCount, 1);
|
||||
assert.equal(blockedChecklist.blockedCount, 3);
|
||||
assert.deepEqual(
|
||||
blockedChecklist.items.map(({ id, status }) => [id, status]),
|
||||
[
|
||||
["accepted-capability", "blocked"],
|
||||
["batch-acceptance-gate", "blocked"],
|
||||
["virtual-hal-sim-config-source-coverage", "pass"],
|
||||
["next-action", "blocked"],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,23 +1,50 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
|
||||
const virtualHalState = createVirtualHalState();
|
||||
const browserDiagnosticsArtifact = {
|
||||
apiName: "real-browser-simulation-diagnostics-artifact",
|
||||
artifactVersion: 1,
|
||||
virtualHal: virtualHalState,
|
||||
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(virtualHalState),
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
|
||||
};
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact).ready, true);
|
||||
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
}));
|
||||
|
||||
let fetchedUrl = null;
|
||||
const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
diagnosticsUrl: "/real-browser-simulation-diagnostics.json",
|
||||
fetchRef: async (url) => {
|
||||
fetchedUrl = url;
|
||||
if (url === "/real-browser-simulation-diagnostics.json") {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(browserDiagnosticsArtifact),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
@@ -28,7 +55,7 @@ const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
const readySummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(readyWorkflow);
|
||||
const readyActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(readyWorkflow);
|
||||
|
||||
assert.equal(fetchedUrl, "/project-release-readiness.json");
|
||||
assert.equal(fetchedUrl, "/real-browser-simulation-diagnostics.json");
|
||||
assert.equal(readyWorkflow.apiName, "project-release-readiness-artifact-url-workflow");
|
||||
assert.equal(readyWorkflow.workflowVersion, 1);
|
||||
assert.equal(readyWorkflow.ready, true);
|
||||
@@ -37,6 +64,9 @@ assert.equal(readyWorkflow.httpStatus, 200);
|
||||
assert.equal(readyWorkflow.fetchError, null);
|
||||
assert.equal(readyWorkflow.jsonWorkflow.ready, true);
|
||||
assert.equal(readyWorkflow.validation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsFetched, true);
|
||||
assert.equal(readyWorkflow.diagnosticsHttpStatus, 200);
|
||||
assert.equal(readyWorkflow.actionPlan.ready, true);
|
||||
assert.equal(readySummary.apiName, "project-release-readiness-artifact-url-workflow-summary-view-model");
|
||||
assert.equal(readySummary.statusLine, "Ready: Artifact URL workflow complete");
|
||||
@@ -47,6 +77,7 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["browser-diagnostics", "ready"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
|
||||
|
||||
assert.equal(manifest.apiName, "project-release-gate-manifest");
|
||||
assert.equal(manifest.manifestVersion, 1);
|
||||
@@ -201,6 +203,7 @@ assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_proj
|
||||
|
||||
const report = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
});
|
||||
assert.deepEqual(report.gateManifest, manifest);
|
||||
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||
@@ -243,6 +246,11 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
label: "Sim config inventory",
|
||||
value: "unexpected_fail=0",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
@@ -262,11 +270,12 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
});
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
|
||||
"Waiting: project-release-gate",
|
||||
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
"promoted: L4-TOOL-DB",
|
||||
@@ -308,6 +317,7 @@ assert.deepEqual(
|
||||
["gate-execution-summary", "ready"],
|
||||
["gate-result-matrix", "ready"],
|
||||
["gate-action-plan", "ready"],
|
||||
["virtual-hal-sim-config-source-coverage", "ready"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -376,6 +386,7 @@ assert.deepEqual(
|
||||
[
|
||||
["fetch", "ready"],
|
||||
["json-workflow", "ready"],
|
||||
["diagnostics-workflow", "not requested"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
@@ -393,6 +404,7 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["browser-diagnostics", "not requested"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
|
||||
@@ -32,8 +32,11 @@ import {
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES,
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
|
||||
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
|
||||
VIRTUAL_HAL_SOURCE_FILES,
|
||||
@@ -48,6 +51,7 @@ import {
|
||||
createLinuxCncVirtualHalRuntime,
|
||||
createVirtualHalIntegrityReport,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createVirtualHalSessionPayload,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
@@ -63,6 +67,7 @@ import {
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -78,11 +83,14 @@ import {
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
createVirtualHalBridgeActionPlan,
|
||||
createVirtualHalBridgeReadiness,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalPinInventory,
|
||||
createVirtualHalPinRegistry,
|
||||
createVirtualHalProjectReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalState,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalSystemCoverageReport,
|
||||
createVirtualHalWasmBridgeSnapshot,
|
||||
executeVirtualHalCommand,
|
||||
@@ -146,6 +154,7 @@ import {
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
restoreMachineParametersFromOpfs,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveMachineTextFiles,
|
||||
saveSessionSnapshot,
|
||||
@@ -160,6 +169,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "utf8");
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
const requiredExports = [
|
||||
@@ -189,6 +199,7 @@ const requiredExports = [
|
||||
["createProjectBatchAcceptanceReportValidation", createProjectBatchAcceptanceReportValidation],
|
||||
["createProjectBatchAcceptanceSummaryViewModel", createProjectBatchAcceptanceSummaryViewModel],
|
||||
["createProjectBatchAcceptanceWorkflow", createProjectBatchAcceptanceWorkflow],
|
||||
["createProjectReleaseBrowserDiagnosticsArtifactValidation", createProjectReleaseBrowserDiagnosticsArtifactValidation],
|
||||
["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan],
|
||||
["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest],
|
||||
["createProjectReleaseGateExecutionSummaryViewModel", createProjectReleaseGateExecutionSummaryViewModel],
|
||||
@@ -220,12 +231,15 @@ const requiredExports = [
|
||||
["createLinuxCncVirtualHalRuntime", createLinuxCncVirtualHalRuntime],
|
||||
["createVirtualHalBridgeActionPlan", createVirtualHalBridgeActionPlan],
|
||||
["createVirtualHalBridgeReadiness", createVirtualHalBridgeReadiness],
|
||||
["createVirtualHalCommandScriptFixtureReport", createVirtualHalCommandScriptFixtureReport],
|
||||
["createVirtualHalIntegrityReport", createVirtualHalIntegrityReport],
|
||||
["createVirtualHalPinInventory", createVirtualHalPinInventory],
|
||||
["createVirtualHalPinRegistry", createVirtualHalPinRegistry],
|
||||
["createVirtualHalProjectReport", createVirtualHalProjectReport],
|
||||
["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport],
|
||||
["createVirtualHalSimulationReplacementReport", createVirtualHalSimulationReplacementReport],
|
||||
["createVirtualHalSimulationRuntimeReport", createVirtualHalSimulationRuntimeReport],
|
||||
["createVirtualHalSourceComplianceReport", createVirtualHalSourceComplianceReport],
|
||||
["createVirtualHalState", createVirtualHalState],
|
||||
["createVirtualHalSystemCoverageReport", createVirtualHalSystemCoverageReport],
|
||||
["createVirtualHalWasmBridgeSnapshot", createVirtualHalWasmBridgeSnapshot],
|
||||
@@ -359,10 +373,12 @@ const requiredExports = [
|
||||
["defaultMachinePaths", defaultMachinePaths],
|
||||
["normalizeOpfsPath", normalizeOpfsPath],
|
||||
["createMachineSessionSnapshotPayload", createMachineSessionSnapshotPayload],
|
||||
["createVirtualHalSessionPayload", createVirtualHalSessionPayload],
|
||||
["createSessionSnapshot", createSessionSnapshot],
|
||||
["saveSessionSnapshot", saveSessionSnapshot],
|
||||
["saveMachineSessionSnapshot", saveMachineSessionSnapshot],
|
||||
["loadMachineSessionSnapshot", loadMachineSessionSnapshot],
|
||||
["restoreVirtualHalStateFromSessionSnapshot", restoreVirtualHalStateFromSessionSnapshot],
|
||||
["validateSessionSnapshot", validateSessionSnapshot],
|
||||
["machineFilePaths", machineFilePaths],
|
||||
["saveMachineTextFiles", saveMachineTextFiles],
|
||||
@@ -468,14 +484,29 @@ assert.equal(VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES.includes("motion-contro
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("linuxcnc-realtime-hal"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("halcmd"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("motion-controller"), true);
|
||||
assert.equal(VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.some(({ id }) => id === "pin-signal-net-show"), true);
|
||||
assert.equal(VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.some(({ id }) => id === "load-thread-start-stop"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some(({ id }) => id === "axis-foam"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some(({ id }) => id === "qtdragon-on-abort"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS
|
||||
.find(({ id }) => id === "external-offsets")
|
||||
.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/M111"), true);
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES["halcmd-simulation-replacement"].sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES["motion-controller-simulation-replacement"].sourceFiles.includes("linuxcnc/src/emc/motion/motion.c"), true);
|
||||
assert.equal(VIRTUAL_HAL_COVERAGE_STATES.runtimeBoundary, "runtime-boundary");
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_FILES.halui, "linuxcnc/src/emc/usr_intf/halui.cc");
|
||||
assert.equal(VIRTUAL_HAL_SYSTEM_PIN_FAMILIES.some(({ id }) => id === "iocontrol"), true);
|
||||
assert.equal(VIRTUAL_HAL_SYSTEM_PIN_FAMILIES.some(({ id }) => id === "motion-core"), true);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_PROJECT_PIN_GROUPS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_PROJECT_PIN_GROUPS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS\b/);
|
||||
@@ -486,6 +517,8 @@ assert.match(sdkIndexText, /\bexecuteVirtualHalCommand\b/);
|
||||
assert.match(sdkReadmeText, /\bexecuteVirtualHalCommand\b/);
|
||||
assert.match(sdkIndexText, /\bexecuteVirtualHalcmd\b/);
|
||||
assert.match(sdkReadmeText, /\bexecuteVirtualHalcmd\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalCommandScriptFixtureReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalCommandScriptFixtureReport\b/);
|
||||
assert.match(sdkIndexText, /\bstepVirtualHalMotion\b/);
|
||||
assert.match(sdkReadmeText, /\bstepVirtualHalMotion\b/);
|
||||
assert.match(sdkIndexText, /\bstepVirtualHalMotionController\b/);
|
||||
@@ -494,6 +527,10 @@ assert.match(sdkIndexText, /\bcreateVirtualHalSimulationRuntimeReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationRuntimeReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSimulationReplacementReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationReplacementReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSourceComplianceReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSystemCoverageReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSystemCoverageReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalPinRegistry\b/);
|
||||
@@ -596,6 +633,17 @@ const directHalcmdAliasResult = executeVirtualHalcmd(virtualHalRuntime.getState(
|
||||
assert.equal(directHalcmdAliasResult.ok, true);
|
||||
assert.equal(directHalcmdAliasResult.apiName, "linuxcnc-wasm-virtual-halcmd-result");
|
||||
assert.match(directHalcmdAliasResult.output, /halui\.machine\.is-on/);
|
||||
const commandScriptFixtureReport = createVirtualHalCommandScriptFixtureReport({ halState: virtualHalRuntime.getState() });
|
||||
assert.equal(commandScriptFixtureReport.apiName, "linuxcnc-wasm-virtual-hal-command-script-fixture-report");
|
||||
assert.equal(commandScriptFixtureReport.complete, true);
|
||||
assert.equal(commandScriptFixtureReport.webSimulationSatisfied, true);
|
||||
assert.equal(commandScriptFixtureReport.missingActions.length, 0);
|
||||
assert.equal(commandScriptFixtureReport.missingFixtures.length, 0);
|
||||
assert.equal(commandScriptFixtureReport.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
for (const action of ["setp", "sets", "net", "show", "getp", "gets", "loadrt", "loadusr", "addf", "start", "stop"]) {
|
||||
assert.equal(commandScriptFixtureReport.coveredActions.includes(action), true);
|
||||
}
|
||||
assert.equal(virtualHalRuntime.getCommandScriptFixtureReport().complete, true);
|
||||
const steppedVirtualHal = stepVirtualHalMotion(virtualHalRuntime.getState(), {
|
||||
target: { x: 3 },
|
||||
maxVelocity: 10,
|
||||
@@ -636,7 +684,73 @@ assert.equal(replacementReport.replacements["linuxcnc-realtime-hal"].ready, true
|
||||
assert.equal(replacementReport.replacements.halcmd.ready, true);
|
||||
assert.equal(replacementReport.replacements["motion-controller"].ready, true);
|
||||
assert.equal(replacementReport.hardRealtime, false);
|
||||
assert.equal(replacementReport.sourceCompliance.complete, true);
|
||||
assert.equal(replacementReport.sourceCompliance.webSimulationSatisfied, true);
|
||||
assert.equal(replacementReport.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(replacementReport.commandScriptFixtures.complete, true);
|
||||
assert.equal(virtualHalRuntime.getSimulationReplacementReport().ready, true);
|
||||
const simConfigSourceCoverageReport = createVirtualHalSimConfigSourceCoverageReport({ manifestText: sourceManifestText });
|
||||
assert.equal(simConfigSourceCoverageReport.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(simConfigSourceCoverageReport.complete, true);
|
||||
assert.equal(simConfigSourceCoverageReport.webSimulationSatisfied, true);
|
||||
assert.equal(simConfigSourceCoverageReport.manifestChecked, true);
|
||||
assert.equal(simConfigSourceCoverageReport.missingTargets.length, 0);
|
||||
assert.equal(simConfigSourceCoverageReport.missingCapabilities.length, 0);
|
||||
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
|
||||
assert.equal(simConfigSourceCoverageReport.rows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(virtualHalRuntime.getSimConfigSourceCoverageReport({ manifestText: sourceManifestText }).complete, true);
|
||||
const sourceComplianceReport = createVirtualHalSourceComplianceReport({ halState: virtualHalRuntime.getState() });
|
||||
assert.equal(sourceComplianceReport.apiName, "linuxcnc-wasm-virtual-hal-source-compliance-report");
|
||||
assert.equal(sourceComplianceReport.complete, true);
|
||||
assert.equal(sourceComplianceReport.webSimulationSatisfied, true);
|
||||
assert.equal(sourceComplianceReport.missingCapabilitySources.length, 0);
|
||||
assert.equal(sourceComplianceReport.missingFamilySources.length, 0);
|
||||
assert.equal(sourceComplianceReport.targetRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.capabilityRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.familyRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/bin/axis"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/src/emc/motion/motion.c"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(sourceComplianceReport.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(sourceComplianceReport.commandScriptFixtures.complete, true);
|
||||
assert.equal(virtualHalRuntime.getSourceComplianceReport().complete, true);
|
||||
const virtualHalSessionPayload = createVirtualHalSessionPayload(virtualHalRuntime.getState(), {
|
||||
simConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
commandScriptFixtures: commandScriptFixtureReport,
|
||||
});
|
||||
assert.equal(virtualHalSessionPayload.apiName, "linuxcnc-wasm-virtual-hal-session-payload");
|
||||
assert.equal(virtualHalSessionPayload.state.hal.signals["x-pos"].value, 1.25);
|
||||
assert.equal(virtualHalSessionPayload.sourceCompliance.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.commandScriptFixtures.complete, true);
|
||||
const virtualHalSessionSnapshot = createSessionSnapshot(
|
||||
"sdk-virtual-hal-session",
|
||||
createMachineSessionSnapshotPayload("sdk-mill", {
|
||||
gcodeFilename: "demo.ngc",
|
||||
virtualHal: virtualHalSessionPayload,
|
||||
}),
|
||||
{ createdAt: "2026-06-17T00:00:00.000Z" },
|
||||
);
|
||||
assert.equal(
|
||||
restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot).hal.nets["x-pos"].includes("axis.x.pos-cmd"),
|
||||
true,
|
||||
);
|
||||
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
apiName: "real-browser-simulation-diagnostics-artifact",
|
||||
virtualHal: virtualHalRuntime.getState(),
|
||||
virtualHalSimulationReplacement: replacementReport,
|
||||
virtualHalSourceCompliance: sourceComplianceReport,
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
|
||||
});
|
||||
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
|
||||
assert.equal(browserDiagnosticsArtifactValidation.ready, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS });
|
||||
assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true);
|
||||
assert.equal(runtimeProjectReport.systemCoverage.unclassifiedSnapshotPins.length, 0);
|
||||
@@ -705,14 +819,17 @@ assert.equal(
|
||||
batchId: "sdk-surface-batch",
|
||||
capabilities: batchAcceptanceWorkflow.capabilities,
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
}).rows.find(({ id }) => id === "checklist")?.value,
|
||||
"3/3 passed",
|
||||
"4/4 passed",
|
||||
);
|
||||
const batchAcceptanceReport = createProjectBatchAcceptanceReport({
|
||||
batchId: "sdk-surface-batch",
|
||||
capabilities: batchAcceptanceWorkflow.capabilities,
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
});
|
||||
assert.equal(batchAcceptanceReport.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true);
|
||||
assert.equal(
|
||||
createProjectBatchAcceptanceReportValidationSummaryViewModel(
|
||||
@@ -766,7 +883,7 @@ assert.equal(
|
||||
);
|
||||
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
|
||||
assert.equal(releaseReadinessWaiting.ready, false);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate"]);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate", "virtual-hal-sim-config-source-coverage"]);
|
||||
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
|
||||
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
|
||||
assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
@@ -778,9 +895,11 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
|
||||
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
|
||||
assert.equal(createProjectReleaseGateActionPlan({
|
||||
@@ -796,6 +915,7 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
|
||||
apiName: "real-browser-simulation-axis-screenshot-artifact",
|
||||
artifactVersion: 1,
|
||||
@@ -829,6 +949,7 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
})));
|
||||
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
|
||||
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
|
||||
@@ -848,6 +969,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
gateExecutionSummaryReady: true,
|
||||
gateResultMatrixReady: true,
|
||||
gateActionPlanReady: true,
|
||||
virtualHalSimConfigSourceCoverageReady: true,
|
||||
axisScreenshotArtifactSummaryReady: true,
|
||||
axisScreenshotArtifactCount: 0,
|
||||
blockedRuntimeFamilies: [
|
||||
@@ -891,6 +1013,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
label: "Gate action plan",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "axis-screenshot-artifacts",
|
||||
label: "AXIS screenshot artifacts",
|
||||
@@ -987,6 +1114,7 @@ assert.deepEqual(
|
||||
"simConfigInventory.passed",
|
||||
"simConfigInventory.skipped",
|
||||
"simConfigInventory.unexpectedFail",
|
||||
"virtualHalSimConfigSourceCoverage",
|
||||
"blockedRuntimeFamilies",
|
||||
"gateManifest",
|
||||
"gateExecutionManifest",
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { createProjectBatchAcceptanceReport } from "../../../runtime/sdk/src/index.js";
|
||||
import {
|
||||
createProjectBatchAcceptanceReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
const outputPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
|
||||
const report = createProjectBatchAcceptanceReport({
|
||||
batchId: "project-batch-acceptance-artifact",
|
||||
@@ -18,6 +22,13 @@ const report = createProjectBatchAcceptanceReport({
|
||||
evidence: "project_batch_acceptance_artifact_node_smoke=ok",
|
||||
command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
type: "api",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
evidence: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
|
||||
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
|
||||
},
|
||||
{
|
||||
id: "project-batch-acceptance-workflow-gate",
|
||||
type: "gate",
|
||||
@@ -27,6 +38,9 @@ const report = createProjectBatchAcceptanceReport({
|
||||
},
|
||||
],
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
});
|
||||
|
||||
mkdirSync(dirname(outputPath), { recursive: true });
|
||||
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseReadinessReport,
|
||||
} from "../../../runtime/sdk/src/project-release-readiness.js";
|
||||
import {
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
@@ -1350,6 +1353,7 @@ assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow
|
||||
assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract);
|
||||
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
}));
|
||||
const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(
|
||||
releaseReadinessArtifactJson,
|
||||
@@ -1655,7 +1659,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
|
||||
rowsNode: releaseArtifactRowsNode,
|
||||
});
|
||||
assert.equal(releaseArtifactRenderResult.rendered, true);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 10);
|
||||
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
|
||||
@@ -1673,8 +1677,12 @@ assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("axis-screenshot-artifacts"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("virtual-hal-sim-config-source-coverage"),
|
||||
true,
|
||||
);
|
||||
assert.equal(releaseArtifactMountResult.ready, true);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 10);
|
||||
assert.deepEqual(
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
|
||||
["artifact-json"],
|
||||
@@ -1715,7 +1723,7 @@ assert.equal(
|
||||
);
|
||||
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 10);
|
||||
const releaseArtifactUrlWorkflowSummary =
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
releaseArtifactUrlWorkflow,
|
||||
|
||||
Reference in New Issue
Block a user