Files
linuxcncWebWASM/scripts/test_axis_source_ui_oracle.mjs
wangdequan bade560372 Align AXIS control flow and fix pause and program rendering
Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
2026-09-08 10:46:27 +08:00

71 lines
3.9 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { spawnSync } from "node:child_process";
import { axisSourceUi } from "../web/worker/axis-source-ui.mjs";
const source = await readFile(new URL("../linuxcnc/share/axis/tcl/axis.tcl", import.meta.url), "utf8");
const start = source.indexOf("proc update_state {args} {");
const end = source.indexOf("\nproc set_mode_from_tab", start);
assert.ok(start >= 0 && end > start);
const formulas = source.slice(start, end);
const scenarios = [];
for (const task_state of ["STATE_ESTOP", "STATE_ESTOP_RESET", "STATE_ON"])
for (const interp_state of ["INTERP_IDLE", "INTERP_READING", "INTERP_PAUSED", "INTERP_WAITING"])
for (const taskfile of [false, true])
for (const queued_mdi_commands of [0, 10])
for (const motion_mode of ["FREE", "TELEOP"])
for (const kinematics_type of ["BOTH", "IDENTITY"])
for (const spindle_direction of [0, -1, 1]) scenarios.push({ task_state, interp_state, taskfile, queued_mdi_commands, motion_mode, kinematics_type, spindle_direction, tool: taskfile ? 1 : 0, paused: interp_state === "INTERP_PAUSED" });
const widgetMap = { power: ".toolbar.machine_power", load: ".toolbar.file_open", reload: ".toolbar.reload", run: ".toolbar.program_run", step: ".toolbar.program_step", pauseResume: ".toolbar.program_pause", abort: ".toolbar.program_stop", manual: "manualgroup", mdi: "mdigroup", spindleAdjust: ".manual.spindlef.spindleminus", touchOff: ".manual.jogf.zerohome.zero", toolTouchOff: ".manual.jogf.zerohome.tooltouch", overrideLimits: ".manual.jogf.override" };
const prelude = `
proc _ {x} { return $x }
proc relief {condition args} {}
proc state {condition args} { foreach widget $args { set ::result($widget) [uplevel #0 [list expr $condition]] } }
proc enable_group {group} { set ::result($group) 1 }
proc disable_group {group} { set ::result($group) 0 }
proc set_mode_from_tab {} {}
proc unknown {name args} { if {[lindex $args 0] eq "configure"} { set ::result($name) [expr {[lindex $args 2] eq "normal"}] }; return "" }
foreach name {STATE_ESTOP STATE_ESTOP_RESET STATE_ON INTERP_IDLE INTERP_READING INTERP_PAUSED INTERP_WAITING TRAJ_MODE_FREE TRAJ_MODE_TELEOP KINEMATICS_IDENTITY KINEMATICS_BOTH TASK_MODE_AUTO} { set ::$name $name }
set ::_tabs_manual .manual
set ::manualgroup manualgroup
set ::mdigroup mdigroup
set ::has_editor 1
set ::has_ladder 0
set ::highlight_line -1
set ::task_mode MANUAL
set ::coord_type 0
set ::display_type 0
set ::block_delete 0
set ::optional_stop 0
set ::last_interp_state INTERP_IDLE
set ::last_task_state STATE_ON
set ::on_any_limit 0
set ::max_queued_mdi_commands 10
${formulas}
`;
const script = prelude + scenarios.map((s) => `
set ::task_state ${s.task_state}
set ::interp_state ${s.interp_state}
set ::taskfile ${s.taskfile ? "program.ngc" : '""'}
set ::queued_mdi_commands ${s.queued_mdi_commands}
set ::motion_mode TRAJ_MODE_${s.motion_mode}
set ::kinematics_type KINEMATICS_${s.kinematics_type}
set ::spindledir ${s.spindle_direction}
set ::tool ${s.tool || '"No tool"'}
set ::interp_pause ${Number(s.paused)}
update_state
puts [list ${Object.values(widgetMap).map((key) => `$::result(${key})`).join(" ")}]
`).join("\n");
const result = spawnSync("tclsh", [], { input: script, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
assert.equal(result.status, 0, result.stderr);
assert.equal(result.stderr, "");
const rows = result.stdout.trim().split("\n");
assert.equal(rows.length, scenarios.length);
for (const [index, row] of rows.entries()) {
const expected = row.split(" ").map(Number);
const actual = axisSourceUi(scenarios[index]).gates;
Object.keys(widgetMap).forEach((name, column) => assert.equal(Number(actual[name].enabled), expected[column], `${name}: ${JSON.stringify(scenarios[index])}`));
}
console.log(JSON.stringify({ schema: "axis-source-ui-oracle.v1", status: "verified", source: "axis.tcl::update_state", scenarios: scenarios.length, predicates: Object.keys(widgetMap).length, assertions: scenarios.length * Object.keys(widgetMap).length }));