import { describe, expect, it } from "vitest"; import { IoImageRuntime, ioKey } from "../../src/runtime/index.js"; import type { IoReference, PulseInstruction, WaitInstruction } from "../../src/grl/ir/index.js"; const DO1: IoReference = { domain: "do", index: 1, raw: "io.do[1]" }; const DI1: IoReference = { domain: "di", index: 1, raw: "io.di[1]" }; const DI2: IoReference = { domain: "di", index: 2, raw: "io.di[2]" }; const AI1: IoReference = { domain: "ai", index: 1, raw: "io.ai[1]" }; describe("virtual IO image, waits, pulses, edges, and scripts", () => { it("enforces write permissions and records IO events", () => { const io = new IoImageRuntime({ permissions: [ { writer: "program", domains: ["do"], access: "write" }, { writer: "script", domains: ["di", "ai"], access: "write" } ] }); expect(io.write(DO1, true, "program")).toMatchObject({ kind: "write", value: true }); expect(io.write(DI1, true, "program")).toBeUndefined(); expect(io.snapshot()).toMatchObject({ image: { [ioKey(DO1)]: true }, diagnostics: [expect.objectContaining({ code: "VC_IO_PERMISSION_DENIED" })] }); }); it("evaluates waits, timeouts, on_timeout hold-stop, and edge conditions", () => { const io = new IoImageRuntime({ permissions: [{ writer: "script", domains: ["di", "ai"], access: "write" }] }); const wait: WaitInstruction = { kind: "WAIT", condition: "all ( io . di [ 1 ] == true , io . di [ 2 ] == false )", timeout: 1, onTimeout: { kind: "call", value: "recover" } }; expect(io.evaluateWait(wait, 0)).toMatchObject({ status: "waiting" }); expect(io.evaluateWait(wait, 1)).toMatchObject({ status: "hold-stop", onTimeout: { kind: "call", value: "recover" }, diagnostic: { code: "VC_WAIT_TIMEOUT" } }); io.write(DI1, true, "script"); io.write(DI2, false, "script"); expect(io.evaluateWait(wait, 0)).toMatchObject({ status: "satisfied" }); expect(io.matchesCondition("rising ( io . di [ 1 ] )")).toBe(true); io.write(DI1, false, "script"); expect(io.matchesCondition("falling ( io . di [ 1 ] )")).toBe(true); io.write(AI1, 5, "script"); expect(io.matchesCondition("changed ( io . ai [ 1 ] )")).toBe(true); }); it("auto-resets pulses and executes delayed IO scripts", () => { const io = new IoImageRuntime({ permissions: [ { writer: "program", domains: ["do"], access: "write" }, { writer: "script", domains: ["di"], access: "write" } ] }); const pulse: PulseInstruction = { kind: "PULSE", target: DO1, duration: 0.2, trace: [ { time: 0, action: "set", target: DO1, value: true }, { time: 0.2, action: "reset", target: DO1, value: false } ] }; io.executePulse(pulse); expect(io.read(DO1)).toBe(true); io.advance(0.2); expect(io.read(DO1)).toBe(false); io.addScript("part-arrival", [{ delay: 0.5, target: DI1, value: true }]); io.advance(0.49); expect(io.read(DI1)).toBeUndefined(); io.advance(0.01); expect(io.read(DI1)).toBe(true); expect(io.snapshot().events.map((event) => event.kind)).toEqual([ "pulse_set", "pulse_reset", "script" ]); }); });