diff --git a/packages/xod-tabtest/cpp/Arduino.cpp b/packages/xod-tabtest/cpp/Arduino.cpp index 4ed19149..e8ebaeeb 100644 --- a/packages/xod-tabtest/cpp/Arduino.cpp +++ b/packages/xod-tabtest/cpp/Arduino.cpp @@ -1,10 +1,12 @@ #include "Arduino.h" +static uint32_t g_time = 1; + uint32_t millis() { - return 0; + return ++g_time; } -void delay(uint32_t) { - // no-op +void delay(uint32_t dt) { + g_time += dt; } diff --git a/packages/xod-tabtest/src/TabData.re b/packages/xod-tabtest/src/TabData.re index 3a358466..a133a5b0 100644 --- a/packages/xod-tabtest/src/TabData.re +++ b/packages/xod-tabtest/src/TabData.re @@ -11,6 +11,7 @@ module Value = { | Boolean(bool) | String(string) | Byte(int) + | Pulse(bool) | Invalid(string); let numberRegex = [%re {|/^[+-]?(?=.)*\d*(?:\.\d+)?$/|}]; let approxNumberRegex = [%re {|/^[+-]?(?=.)*\d*(?:\.\d+)?~$/|}]; @@ -42,6 +43,8 @@ module Value = { | "" => Empty | "true" => Boolean(true) | "false" => Boolean(false) + | "pulse" => Pulse(true) + | "no-pulse" => Pulse(false) | "NaN" => NaN | "Inf" => Number(infinity) | "+Inf" => Number(infinity) @@ -79,6 +82,8 @@ type t = list(Record.t); let map = List.map; +let mapWithIndex = List.mapWithIndex; + let tabSplit = x => Js.String.split("\t", x) |. List.fromArray |. List.map(Js.String.trim); diff --git a/packages/xod-tabtest/src/TabData.rei b/packages/xod-tabtest/src/TabData.rei index 8dda3170..3b6e232f 100644 --- a/packages/xod-tabtest/src/TabData.rei +++ b/packages/xod-tabtest/src/TabData.rei @@ -18,6 +18,7 @@ module Value: { | Boolean(bool) | String(string) | Byte(int) + | Pulse(bool) | Invalid(string); }; @@ -33,6 +34,9 @@ type t = list(Record.t); /** Maps all records in data like regular lists do */ let map: (t, Record.t => 'v) => List.t('v); +/** Maps all records in data with indexes like regular lists do */ +let mapWithIndex: (t, (int, Record.t) => 'v) => List.t('v); + /** Parses a plain source (as read from file) into tabular data. Note, the parsing always succeeds leaving `Value.Invalid` for diff --git a/packages/xod-tabtest/src/Tabtest.re b/packages/xod-tabtest/src/Tabtest.re index dc7adb22..24ca5dd4 100644 --- a/packages/xod-tabtest/src/Tabtest.re +++ b/packages/xod-tabtest/src/Tabtest.re @@ -178,6 +178,8 @@ module TestCase = { switch (value) { | Boolean(true) => "true" | Boolean(false) => "false" + | Pulse(true) => "true /* pulse */" + | Pulse(false) => "false /* no-pulse */" | NaN => "NAN" | String(x) => let str = Cpp.enquote(x); @@ -193,13 +195,14 @@ module TestCase = { /* Generates a block of code corresponding to a single TSV line check. Contains setup, evaluation, and assertion validation. It might be wrapped into Catch2 SECTION, the purpose is the same. */ - let generateSection = (record, probes) : Cpp.code => { + let generateSection = (record, probes, sectionIndex) : Cpp.code => { let injectionStatements = probes |. Probes.keepInjecting |. Probes.map(probe => { let name = probe |. Probe.getTargetPin |. Pin.getLabel; switch (record |. TabData.Record.get(name)) { + | Some(noPulse) when noPulse == Pulse(false) => {j|// No pulse for $name|j} | Some(value) => let literal = valueToLiteral(value); {j|INJECT(probe_$name, $literal);|j}; @@ -225,7 +228,7 @@ module TestCase = { source([ "", source(injectionStatements), - "loop();", + sectionIndex == 0 ? "setup();" : "loop();", source(assertionsStatements), ]) ); @@ -250,7 +253,10 @@ module TestCase = { |. Map.String.toList |. List.map(((name, id)) => {j|auto& probe_$name = xod::node_$id;|j}); let sections = - tabData |. TabData.map(record => generateSection(record, probes)); + tabData + |. TabData.mapWithIndex((idx, record) => + generateSection(record, probes, idx) + ); Cpp.( source([ "#include \"catch.hpp\"", @@ -263,7 +269,7 @@ module TestCase = { " (probe).isNodeDirty = true; \\", " }", "", - catch2TestCase(name, ["setup();", source(sections)]), + catch2TestCase(name, [source(sections)]), ]) ); }; diff --git a/packages/xod-tabtest/test/TabData_jest.re b/packages/xod-tabtest/test/TabData_jest.re index 3aeea34d..5f52d1de 100644 --- a/packages/xod-tabtest/test/TabData_jest.re +++ b/packages/xod-tabtest/test/TabData_jest.re @@ -52,35 +52,39 @@ describe("TSV parser", () => { }); test("recognizes types", () => { let tsv = - "Number\tBoolean\tByte\tString\n" - ++ "+.5\ttrue\t00h\t\"Hello\"\n" - ++ "-42\ttrue\t00001101b\t\"World\"\n" - ++ "-1.243~\tfalse\t11111111b\t\"!\"\n" - ++ "1.3\tfalse\t255d\t\"Some \"quoted\" string\""; + "Number\tBoolean\tByte\tString\tPulse\n" + ++ "+.5\ttrue\t00h\t\"Hello\"\tpulse\n" + ++ "-42\ttrue\t00001101b\t\"World\"\tpulse\n" + ++ "-1.243~\tfalse\t11111111b\t\"!\"\tno-pulse\n" + ++ "1.3\tfalse\t255d\t\"Some \"quoted\" string\"\tno-pulse"; let expected: TabData.t = [ Map.String.fromArray([| ("Number", Number(0.5)), ("Boolean", Boolean(true)), ("Byte", Byte(0)), ("String", String("Hello")), + ("Pulse", Pulse(true)), |]), Map.String.fromArray([| ("Number", Number(-42.0)), ("Boolean", Boolean(true)), ("Byte", Byte(13)), ("String", String("World")), + ("Pulse", Pulse(true)), |]), Map.String.fromArray([| ("Number", ApproxNumber(-1.243, 3)), ("Boolean", Boolean(false)), ("Byte", Byte(255)), ("String", String("!")), + ("Pulse", Pulse(false)), |]), Map.String.fromArray([| ("Number", Number(1.3)), ("Boolean", Boolean(false)), ("Byte", Byte(255)), ("String", String({|Some "quoted" string|})), + ("Pulse", Pulse(false)), |]), ]; expect(TabData.parse(tsv)) |> toEqual(expected); diff --git a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.cpp b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.cpp index 1046896f..62542b7a 100644 --- a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.cpp +++ b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.cpp @@ -1,7 +1,12 @@ struct State { + bool lastValue = false; }; {{ GENERATED_CODE }} void evaluate(Context ctx) { + getState(ctx)->lastValue = isInputDirty(ctx); + // This node should check dirtieness of input port on each transaction + // so we set timeout 0 to mark this node as dirty on the next transaction + setTimeout(ctx, 0); } diff --git a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.xodp b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.xodp index 2caf7bcd..51d3572c 100644 --- a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.xodp +++ b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/capture-pulse/patch.xodp @@ -16,7 +16,7 @@ "x": 34, "y": 0 }, - "type": "xod/patch-nodes/input-number" + "type": "xod/patch-nodes/input-pulse" } ] } diff --git a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/inject-pulse/patch.cpp b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/inject-pulse/patch.cpp index 76ac39cf..a5e48461 100644 --- a/packages/xod-tabtest/workspace/__lib__/xod/tabtest/inject-pulse/patch.cpp +++ b/packages/xod-tabtest/workspace/__lib__/xod/tabtest/inject-pulse/patch.cpp @@ -3,5 +3,11 @@ struct State {}; {{ GENERATED_CODE }} void evaluate(Context ctx) { - emitValue(ctx, getValue(ctx)); + if (getValue(ctx)) { + // Any call of `emitValue` marks output port as dirty + // and it means that pulse emited. + // But the tabtest engine injects `true` or `false` into `output_VAL` + // so we have to emit pulse only when it's `true`. + emitValue(ctx, 1); + } }