Files
cnc_wams/wasm-port/tests/wasm/node/verify_interp_wasm.mjs
wangdequan ec3d784f2b 按规划继续工作
结论:新增解释器初始化和同步的 WASM/浏览器验证,继续保持核心行为来自 vendored LinuxCNC。
2026-06-08 11:21:05 +08:00

392 lines
11 KiB
JavaScript

import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import assert from "node:assert/strict";
import { createLinuxCncInterpSdk } from "../../../runtime/sdk/src/index.js";
import {
INTERP_ERROR_FIXTURES,
INTERP_FILE_FIXTURES,
INTERP_INI_FIXTURE,
INTERP_MDI_FIXTURES,
INTERP_POSITION_PARAMS_FILE_FIXTURE,
} from "../../fixtures/interp-fixture-matrix.mjs";
function verifyExpectedOutput(fixtureName, output, expectedText) {
const expectedLines = expectedText.split("\n").filter(Boolean);
for (const expectedLine of expectedLines) {
if (expectedLine.startsWith("absent=")) {
const forbidden = expectedLine.slice("absent=".length);
assert.equal(
output.includes(forbidden),
false,
`${fixtureName}: unexpected ${forbidden}`,
);
continue;
}
assert.equal(
output.includes(expectedLine),
true,
`${fixtureName}: ${expectedLine}`,
);
}
}
function verifyExpectedFileError(fixtureName, output, expectedText) {
assert.equal(
output.includes("file_open=0"),
true,
`${fixtureName}: file open`,
);
const expectedLines = expectedText.split("\n").filter(Boolean);
for (const expectedLine of expectedLines) {
if (expectedLine.startsWith("absent=")) {
const forbidden = expectedLine.slice("absent=".length);
assert.equal(
output.includes(forbidden),
false,
`${fixtureName}: unexpected ${forbidden}`,
);
continue;
}
if (expectedLine.startsWith("error_text=")) {
const message = expectedLine.slice("error_text=".length);
assert.equal(
output.includes(`file_error_text=${message}`),
true,
`${fixtureName}: ${message}`,
);
continue;
}
if (expectedLine.startsWith("canon_event=")) {
assert.equal(
output.includes(expectedLine),
true,
`${fixtureName}: ${expectedLine}`,
);
}
}
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = resolve(__dirname, "../../..");
const wasmPath = resolve(rootDir, "build/wasm/core/linuxcnc_interp.wasm");
const interp = await createLinuxCncInterpSdk({
wasmBinary: readFileSync(wasmPath),
print() {},
printErr(message) {
console.error(message);
},
});
verifyExpectedOutput(
"probe_init_and_synch",
interp.probeInitAndSynch(),
[
"init=0",
"setup.length_units=2",
"setup.origin_index=1",
"setup.distance_mode=0",
"setup.feed_mode=0",
"setup.motion_mode=800",
"canon_event=INIT_CANON",
"canon_event=USE_LENGTH_UNITS units=2",
"canon_event=SET_G5X_OFFSET index=1",
"canon_event=SET_G92_OFFSET x=0 y=0 z=0",
"canon_event=SET_XY_ROTATION rotation=0",
"canon_event=SET_FEED_REFERENCE reference=2",
"inch_init=0",
"inch_setup.length_units=1",
"inch_external_length_units=0.0393701",
"inch_canon_event=USE_LENGTH_UNITS units=1",
"synch=0",
"synch.current_pocket=2",
"synch.selected_pocket=2",
"synch.tool_0=2",
"synch.tool_2=2",
].join("\n"),
);
for (const fixtureName of INTERP_MDI_FIXTURES) {
const programText = readFileSync(
resolve(rootDir, `tests/fixtures/gcode/${fixtureName}.ngc`),
"utf8",
);
const expectedEvents = readFileSync(
resolve(rootDir, `tests/fixtures/canon/${fixtureName}.events`),
"utf8",
).trimEnd();
verifyExpectedOutput(fixtureName, interp.runProgram(programText), expectedEvents);
}
const namedParamIniPath = "/work/namedparams.ini";
interp.writeTextFile(
namedParamIniPath,
readFileSync(resolve(rootDir, "tests/fixtures/ini/namedparams.ini"), "utf8"),
);
const namedParamProgramText = readFileSync(
resolve(rootDir, `tests/fixtures/gcode/${INTERP_INI_FIXTURE}.ngc`),
"utf8",
);
const namedParamExpected = readFileSync(
resolve(rootDir, `tests/fixtures/canon/${INTERP_INI_FIXTURE}.events`),
"utf8",
).trimEnd();
verifyExpectedOutput(
INTERP_INI_FIXTURE,
interp.runProgramWithIni(namedParamProgramText, namedParamIniPath),
namedParamExpected,
);
for (const fixtureName of INTERP_ERROR_FIXTURES) {
const programText = readFileSync(
resolve(rootDir, `tests/fixtures/gcode_errors/${fixtureName}.ngc`),
"utf8",
);
const expectedOutput = readFileSync(
resolve(rootDir, `tests/fixtures/canon_errors/${fixtureName}.expected`),
"utf8",
).trimEnd();
verifyExpectedOutput(fixtureName, interp.runProgram(programText), expectedOutput);
}
for (const fixtureName of INTERP_ERROR_FIXTURES) {
const programPath = `/work/${fixtureName}-error.ngc`;
const programText = readFileSync(
resolve(rootDir, `tests/fixtures/gcode_errors/${fixtureName}.ngc`),
"utf8",
);
const expectedOutput = readFileSync(
resolve(rootDir, `tests/fixtures/canon_errors/${fixtureName}.expected`),
"utf8",
).trimEnd();
interp.writeTextFile(programPath, programText);
verifyExpectedFileError(fixtureName, interp.runFile(programPath), expectedOutput);
}
for (const fixtureName of INTERP_FILE_FIXTURES) {
const programPath = `/work/${fixtureName}.ngc`;
const programText = readFileSync(
resolve(rootDir, `tests/fixtures/gcode/${fixtureName}.ngc`),
"utf8",
);
const expectedEvents = readFileSync(
resolve(rootDir, `tests/fixtures/canon/${fixtureName}.events`),
"utf8",
).trimEnd();
interp.writeTextFile(programPath, programText);
verifyExpectedOutput(fixtureName, interp.runFile(programPath), expectedEvents);
}
const namedParamFilePath = `/work/${INTERP_INI_FIXTURE}.ngc`;
interp.writeTextFile(namedParamFilePath, namedParamProgramText);
verifyExpectedOutput(
"namedparam_semantics_file",
interp.runFileWithIni(namedParamFilePath, namedParamIniPath),
namedParamExpected,
);
const positionParamsFilePath = "/work/position_params.ngc";
interp.writeTextFile(
positionParamsFilePath,
readFileSync(
resolve(rootDir, `tests/fixtures/gcode/${INTERP_POSITION_PARAMS_FILE_FIXTURE}.ngc`),
"utf8",
),
);
verifyExpectedOutput(
`${INTERP_POSITION_PARAMS_FILE_FIXTURE}_file`,
interp.runFile(positionParamsFilePath),
readFileSync(
resolve(
rootDir,
`tests/fixtures/canon_file/${INTERP_POSITION_PARAMS_FILE_FIXTURE}.events`,
),
"utf8",
).trimEnd(),
);
const parameterFilePath = "/work/rs274ngc.var";
interp.writeTextFile(
parameterFilePath,
[
"5161 10.5",
"5162 20.25",
"5220 1",
"5221 2.25",
"5399 44",
"<_named_param> 123",
"",
].join("\n"),
);
verifyExpectedOutput(
"restore_parameters",
interp.restoreParameters(parameterFilePath),
[
"restore_parameters=0",
"parameter_5161=10.5",
"parameter_5162=20.25",
"parameter_5220=1",
"parameter_5221=2.25",
"parameter_5399=44",
].join("\n"),
);
verifyExpectedOutput(
"restore_parameters_missing_file",
interp.restoreParameters("/work/missing.var"),
"restore_parameters=0",
);
const outOfOrderParameterFilePath = "/work/out-of-order.var";
interp.writeTextFile(outOfOrderParameterFilePath, "5220 1\n5161 2\n");
verifyExpectedOutput(
"restore_parameters_out_of_order",
interp.restoreParameters(outOfOrderParameterFilePath),
[
"restore_parameters=5",
"restore_error_text=Parameter file out of order",
].join("\n"),
);
verifyExpectedOutput(
"save_parameters",
interp.saveParameters(parameterFilePath, {
5161: 12.34,
5162: 56.78,
5220: 1.0,
5221: 9.87,
5399: 66.6,
}),
[
"save_parameters=0",
"parameter_5161=12.34",
"parameter_5162=56.78",
"parameter_5220=1",
"parameter_5221=9.87",
"parameter_5399=66.6",
].join("\n"),
);
const savedParameterFile = interp.readTextFile(parameterFilePath);
const backupParameterFile = interp.readTextFile(`${parameterFilePath}.bak`);
assert.equal(savedParameterFile.includes("5161\t12.340000"), true);
assert.equal(savedParameterFile.includes("5162\t56.780000"), true);
assert.equal(savedParameterFile.includes("5221\t9.870000"), true);
assert.equal(savedParameterFile.includes("5399\t66.600000"), true);
assert.equal(savedParameterFile.includes("_named_param"), false);
assert.equal(backupParameterFile.includes("5161 10.5"), true);
const toolTablePath = "/work/tool.tbl";
interp.writeTextFile(
toolTablePath,
[
"T2 P7 Z3.125 D1.5 I12 J34 Q4 ;finish tool",
"T5 P9 X1 Y2 Z3 ;rough tool",
"",
].join("\n"),
);
verifyExpectedOutput(
"load_tool_table",
interp.loadToolTable(toolTablePath),
[
"tooldata_load=0",
"tooldata_last_index=2",
"tool_0.toolno=-1",
"tool_1.toolno=2",
"tool_1.pocketno=7",
"tool_1.z=3.125",
"tool_1.diameter=1.5",
"tool_1.frontangle=12",
"tool_1.backangle=34",
"tool_1.orientation=4",
"tool_1.comment=finish tool",
"tool_2.toolno=5",
"tool_2.pocketno=9",
"tool_2.z=3",
"tool_2.comment=rough tool",
"tool_index_for_tool_2=1",
].join("\n"),
);
verifyExpectedOutput(
"save_tool_table",
interp.saveToolTable(toolTablePath),
[
"tooldata_save=0",
"tool_1.toolno=2",
"tool_2.toolno=5",
].join("\n"),
);
const savedToolTable = interp.readTextFile(toolTablePath);
assert.equal(savedToolTable.includes("T2"), true);
assert.equal(savedToolTable.includes("P7"), true);
assert.equal(savedToolTable.includes("D+1.500000"), true);
assert.equal(savedToolTable.includes("Z+3.125000"), true);
assert.equal(savedToolTable.includes("I+12.000000"), true);
assert.equal(savedToolTable.includes("J+34.000000"), true);
assert.equal(savedToolTable.includes("Q4"), true);
assert.equal(savedToolTable.includes(";finish tool"), true);
assert.equal(savedToolTable.includes("T5"), true);
assert.equal(savedToolTable.includes("P9"), true);
assert.equal(savedToolTable.includes(";rough tool"), true);
const randomToolTablePath = "/work/random-tool.tbl";
interp.writeTextFile(
randomToolTablePath,
[
"T2 P7 Z3.125 D1.5 I12 J34 Q4 ;random finish tool",
"T5 P9 X1 Y2 Z3 ;random rough tool",
"",
].join("\n"),
);
verifyExpectedOutput(
"load_tool_table_random",
interp.loadToolTable(randomToolTablePath, { randomToolChanger: true }),
[
"tooldata_random_toolchanger=1",
"tooldata_load=0",
"tooldata_last_index=9",
"tool_1.toolno=-1",
"tool_2.toolno=-1",
"tool_pocket_7.toolno=2",
"tool_pocket_7.pocketno=7",
"tool_pocket_7.z=3.125",
"tool_pocket_7.diameter=1.5",
"tool_pocket_7.frontangle=12",
"tool_pocket_7.backangle=34",
"tool_pocket_7.orientation=4",
"tool_pocket_7.comment=random finish tool",
"tool_pocket_9.toolno=5",
"tool_pocket_9.pocketno=9",
"tool_pocket_9.z=3",
"tool_pocket_9.comment=random rough tool",
"tool_index_for_tool_2=7",
].join("\n"),
);
verifyExpectedOutput(
"save_tool_table_random",
interp.saveToolTable(randomToolTablePath),
[
"tooldata_save=0",
"tool_pocket_7.toolno=2",
"tool_pocket_9.toolno=5",
].join("\n"),
);
const savedRandomToolTable = interp.readTextFile(randomToolTablePath);
assert.equal(savedRandomToolTable.includes("T2"), true);
assert.equal(savedRandomToolTable.includes("P7"), true);
assert.equal(savedRandomToolTable.includes(";random finish tool"), true);
assert.equal(savedRandomToolTable.includes("T5"), true);
assert.equal(savedRandomToolTable.includes("P9"), true);
assert.equal(savedRandomToolTable.includes(";random rough tool"), true);
console.log("interp_wasm_node_smoke=ok");