Upload project files
This commit is contained in:
127
kdl-wasm/web/tests/controller/virtualController.test.ts
Normal file
127
kdl-wasm/web/tests/controller/virtualController.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { SemanticProgramIr } from "../../src/grl/ir/index.js";
|
||||
import { IrExecutionRuntime, VirtualControllerStateMachine } from "../../src/controller/index.js";
|
||||
|
||||
function program(): SemanticProgramIr {
|
||||
return {
|
||||
moduleName: "Main",
|
||||
symbols: [],
|
||||
semanticChecks: [],
|
||||
paths: [],
|
||||
operations: [],
|
||||
diagnostics: [],
|
||||
sourceMap: [
|
||||
{
|
||||
kind: "CALL",
|
||||
id: "call_helper",
|
||||
procedureId: "main",
|
||||
sourceMap: { file: "main.grl", line: 2, column: 5 }
|
||||
},
|
||||
{
|
||||
kind: "ALARM",
|
||||
id: "helper_alarm",
|
||||
procedureId: "helper",
|
||||
sourceMap: { file: "main.grl", line: 6, column: 5 }
|
||||
}
|
||||
],
|
||||
procedures: [
|
||||
{
|
||||
name: "main",
|
||||
sourceMap: { file: "main.grl", line: 1, column: 1 },
|
||||
instructions: [
|
||||
{
|
||||
kind: "RAW_STATEMENT",
|
||||
text: "ready = true",
|
||||
sourceMap: { file: "main.grl", line: 1, column: 5 }
|
||||
},
|
||||
{
|
||||
kind: "CALL",
|
||||
target: "helper",
|
||||
args: [],
|
||||
sourceMap: { file: "main.grl", line: 2, column: 5 }
|
||||
},
|
||||
{
|
||||
kind: "RETURN",
|
||||
sourceMap: { file: "main.grl", line: 3, column: 5 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "helper",
|
||||
sourceMap: { file: "main.grl", line: 5, column: 1 },
|
||||
instructions: [
|
||||
{
|
||||
kind: "ALARM",
|
||||
alarmId: "A1",
|
||||
message: "helper alarm",
|
||||
severity: "warning",
|
||||
sourceMap: { file: "main.grl", line: 6, column: 5 }
|
||||
},
|
||||
{
|
||||
kind: "RETURN",
|
||||
sourceMap: { file: "main.grl", line: 7, column: 5 }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
kdlBridge: {
|
||||
motionRequests: [],
|
||||
pathRequests: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe("virtual controller state machine and IR execution runtime", () => {
|
||||
it("reports legal and illegal controller command transitions", () => {
|
||||
const machine = new VirtualControllerStateMachine();
|
||||
|
||||
expect(machine.dispatch("run")).toMatchObject({
|
||||
ok: false,
|
||||
state: "unloaded",
|
||||
diagnostic: { code: "VC_INVALID_STATE_TRANSITION" }
|
||||
});
|
||||
expect(machine.dispatch("load")).toMatchObject({ ok: true, previous: "unloaded", state: "stopped" });
|
||||
expect(machine.dispatch("run")).toMatchObject({ ok: true, previous: "stopped", state: "running" });
|
||||
expect(machine.dispatch("pause")).toMatchObject({ ok: true, previous: "running", state: "paused" });
|
||||
expect(machine.dispatch("step")).toMatchObject({ ok: true, previous: "paused", state: "paused" });
|
||||
expect(machine.dispatch("stop")).toMatchObject({ ok: true, previous: "paused", state: "stopped" });
|
||||
expect(machine.snapshot().diagnostics).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("executes PC, calls, scopes, alarms, trace, source map, and breakpoints", () => {
|
||||
const runtime = new IrExecutionRuntime();
|
||||
runtime.load(program());
|
||||
|
||||
expect(runtime.snapshot()).toMatchObject({
|
||||
procedure: "main",
|
||||
pc: 0,
|
||||
scopeStack: [{ id: "global" }, { id: "main" }]
|
||||
});
|
||||
|
||||
runtime.setBreakpoint({ id: "helper-alarm", procedure: "helper", source: { line: 6 }, enabled: true });
|
||||
expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "RAW_STATEMENT" } });
|
||||
expect(runtime.getVariable("ready")).toBe(true);
|
||||
expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "CALL" } });
|
||||
expect(runtime.snapshot()).toMatchObject({
|
||||
procedure: "helper",
|
||||
pc: 0,
|
||||
callStack: [expect.objectContaining({ procedure: "helper", returnTo: { procedure: "main", pc: 2 } })]
|
||||
});
|
||||
|
||||
expect(runtime.step()).toMatchObject({ status: "breakpoint", instruction: { kind: "ALARM" } });
|
||||
expect(runtime.removeBreakpoint("helper-alarm")).toBe(true);
|
||||
expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "ALARM" } });
|
||||
expect(runtime.snapshot().alarmQueue).toEqual([
|
||||
expect.objectContaining({ id: "A1", message: "helper alarm", severity: "warning" })
|
||||
]);
|
||||
expect(runtime.currentSource()).toMatchObject({ sourceMap: { line: 7 } });
|
||||
expect(runtime.run()).toMatchObject({ status: "completed" });
|
||||
expect(runtime.snapshot().trace).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ kind: "load" }),
|
||||
expect.objectContaining({ kind: "breakpoint", data: { breakpointId: "helper-alarm" } }),
|
||||
expect.objectContaining({ kind: "alarm", data: { alarmId: "A1", severity: "warning" } })
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user