Skip to main content

AOI Unit Tests

Rungs uses YAML-based test cases to validate your AOI logic. Tests define inputs, advance the simulation, and check outputs — verifying behavior across single scans, multi-step sequences, and timed progressions.

YAML Format

Each test file is a YAML array of test cases. Every test case has a name and one or more steps.

- name: motor starts on command
steps:
- in:
StartButton: 1
advance:
scans: 1
expect:
MotorRun: 1

Step Fields

FieldRequiredDescription
inNoSets input tag values before execution
advanceYesHow far to advance the simulation
expectYesOutput tag values to verify after execution

in — Set Inputs

Assigns values to input tags before the step executes. Only tags with usage: input are valid. Values must be numbers (0, 1, 3.14).

in:
StartButton: 1
Temperature: 72.5

Omit in when testing default behavior:

- name: green light when idle
steps:
- advance:
scans: 1
expect:
GreenLight: 1

advance — Progress the Simulation

Every step must specify how to advance. Two modes are available:

Scan-based — run a specific number of PLC scan cycles (each scan = 100ms):

advance:
scans: 1

Time-based — specify a duration in milliseconds or seconds:

advance:
time: 500ms
advance:
time: 3.1s

Time values are converted to scans: floor(ms / 100), minimum 1 scan.

DurationScans
100ms1
500ms5
1s10
3.1s31

expect — Verify Outputs

Checks output tag values after the step finishes. Only tags with usage: output are valid. Structured types (TIMER, COUNTER) and array tags cannot be checked directly.

expect:
MotorRun: 1
FaultActive: 0

Test Patterns

Single-Step Tests

Test combinatorial logic that resolves in one scan:

- name: both inputs required for series
steps:
- in:
InputA: 1
InputB: 1
advance:
scans: 1
expect:
SeriesResult: 1

Multi-Step Tests

Verify stateful behavior by chaining steps. State persists across all steps in a test case — local variables, latches, and accumulators carry forward.

Latch persistence:

- name: latch persists after command released
steps:
- in:
LatchCmd: 1
advance:
scans: 1
expect:
LatchedOut: 1
- in:
LatchCmd: 0
advance:
scans: 1
expect:
LatchedOut: 1

Fault seal-in and reset:

- name: clears fault on reset
steps:
- in:
OverloadTrip: 1
advance:
scans: 1
expect:
FaultActive: 1
- in:
OverloadTrip: 0
ResetFault: 1
advance:
scans: 1
expect:
FaultActive: 0

Input Persistence

Input values set via in persist into subsequent steps until explicitly changed. If step 1 sets StartButton: 1, step 2 will still see StartButton: 1 unless overridden.

- name: motor stays running after start released
steps:
- in:
StartButton: 1
advance:
scans: 1
expect:
MotorRun: 1
- in:
StartButton: 0
advance:
scans: 1
expect:
MotorRun: 1

Step 2 explicitly sets StartButton: 0 — without this, it would remain 1 from step 1.

Timer Tests

Use advance: time: to progress timers through their phases:

- name: timer completes after preset elapses
steps:
- in:
Enable: 1
advance:
time: 3.1s
expect:
TimerDone: 1
TimerTiming: 0

Timer reset on disable (TON behavior):

- name: timer resets when enable drops
steps:
- in:
Enable: 1
advance:
time: 1s
expect:
TimerTiming: 1
- in:
Enable: 0
advance:
scans: 1
expect:
TimerTiming: 0
TimerAcc: 0

Multi-Scan Accumulation

Advance multiple scans in a single step to test accumulating behavior:

- name: triggers high alarm above 80
steps:
- in:
FillCmd: 1
advance:
scans: 24
expect:
HighAlarm: 1

Edge-Triggered Counters

LD counters (CTU/CTD) count on rising edges. Toggle inputs between steps to generate multiple counts:

- name: multiple pulses increment counter
steps:
- in:
CountUpPulse: 1
advance:
scans: 1
expect:
CountValue: 1
- in:
CountUpPulse: 0
advance:
scans: 1
expect:
CountValue: 1
- in:
CountUpPulse: 1
advance:
scans: 1
expect:
CountValue: 2

Phased Sequences

Combine time and scan advances to test state machines with multiple phases:

- name: transitions to red after yellow
steps:
- in:
WalkRequest: 1
advance:
scans: 1
expect:
YellowLight: 1
- in:
WalkRequest: 0
advance:
time: 1s
expect:
RedLight: 1
WalkSign: 1

Validation Rules

The editor validates tests in real-time and highlights errors inline.

Structural Rules

  • Top level must be a YAML array
  • Each item needs name (string) and steps (array)
  • Each step needs advance and expect
  • Only valid step keys: in, advance, expect

Tag Rules

  • in values must reference tags with usage: input
  • expect values must reference tags with usage: output
  • Structured types (TIMER, COUNTER, FBD_TIMER, FBD_COUNTER) cannot appear in expect
  • Array tags cannot appear in in or expect
  • All values must be finite numbers (no booleans, strings, or NaN)

Time Format

Time strings must match the pattern <number>ms or <number>s:

✓  100ms  2.5s  0.5s  1500ms
✗ 2 seconds 100 1m

Editor Features

Autocomplete

Press Ctrl+Space or type : to trigger context-aware suggestions:

  • Top level: test case snippets
  • Inside a step: in, advance, expect keys
  • Inside in: input tag names from your AOI
  • Inside expect: output tag names from your AOI
  • Inside advance: scans or time

Snippets

Full test case and step templates are available as snippets with tab stops for quick authoring.

Execution

Tests run via the Run Tests button in the toolbar. Each test case:

  1. Initializes tag state from default values
  2. For each step:
    • Applies in values to the current state
    • Executes the AOI logic routine for the specified number of scans
    • Compares output values against expect
  3. Reports pass/fail with duration and failure details

A safety limit of 10,000 total scan cycles prevents infinite loops.

Results

Results appear in the status panel as a log stream. Each test prints its name, status, and duration.

All tests passing:

● [TEST] Running tests for MotorControl_ST...
✓ [TEST] latches run on start (21ms)
✓ [TEST] motor stays running after start released (3ms)
✓ [TEST] stop has priority over start (1ms)
✓ [TEST] stop drops sealed-in motor (1ms)
✓ [TEST] fault latches and blocks run (1ms)
✓ [TEST] fault persists after trip removed (1ms)
✓ [TEST] clears fault on reset (1ms)
✓ [TEST] cannot start while fault active (1ms)
✓ [TEST] ── Summary ── 8 passed, 0 failed (8 total)

With failures:

● [TEST] Running tests for FBD_COUNTER...
✓ [TEST] increments accumulator when CountUp is asserted (21ms)
✕ [TEST] reaches preset and asserts done (10ms)
step 1: CounterAcc — expected 5, got 1
step 1: CountDone — expected 1, got 0
✓ [TEST] clears accumulator when Reset is asserted (0ms)
✕ [TEST] ── Summary ── 2 passed, 1 failed (3 total)

With validation errors:

● [TEST] Running tests for MyAOI...
✕ [TEST] (Line 4): "advance" is required in every step
✕ [TEST] (Line 8): "BadTag" is not a valid input tag

Result Types

SymbolMeaning
Info — test run started
Pass — all expectations met
Fail — expectation mismatch, validation error, or compilation failure

Failure Details

When a step fails, each mismatch is listed with the step number, tag name, expected value, and actual value:

step 1: TagName — expected X, got Y

If the AOI fails to compile, the error message replaces individual test results.