Live demo — data resets daily at 03:00 UTC. Nothing you enter is saved. Server UI →

product: maestro audience: test-developer authority: normative

Runtime Control Fields

enabled — static disable

false → step IS immediately SKIPPED without executing.

- name: "Legacy step"
  type: mock
  enabled: false

precondition — conditional execution

A DynamicExpresso boolean expression evaluated at runtime. false → SKIPPED. Exception during evaluation → ABORTED.

- name: "Calibrate"
  type: mock
  precondition: "runCalibration == true"

Supported syntax:

  • variable == true / variable == false
  • temperature > 25
  • a == true && b < 3
  • skipCal == true || forceRun == true

cfg.*, exec.*, and mes.* MUST NOT be used in precondition: expressions. These namespaces use dot notation which DynamicExpresso interprets as C# member access. To branch on a cfg.* value, read it into a bare variable in a preceding step.

force — verdict override

- name: "Noisy measurement"
  type: mock
  force: "pass"
Value Effect
pass Force PASS (VerdictForced = true)
fail Force FAIL
skip Do not execute; SKIPPED
run Execute even if precondition is false

retry — retry on failure

- name: "Flaky instrument read"
  runner: dotnet
  runner_type: net10.0
  assembly: "Tests.dll"
  class: "Tests.Instruments"
  method: "ReadDMM"
  retry:
    count: 3        # 3 retries → 4 total attempts
    delay_ms: 500

repeat — condition-based loop

Two built-in variables are set before each iteration:

Variable Type Description
__iteration__ int Zero-based counter (0, 1, 2, …)
__cycle__ int One-based counter (1, 2, 3, …) — use this for human-readable names

max_iterations IS required as a safety cap.

- name: "Sweep 5 points"
  runner: python
  runner_type: python3.11
  module: "sweep"
  function: "measure_point"
  parameters:
    step: "{{__iteration__}}"
  repeat:
    condition: "__iteration__ < 4"   # iterations 0–4 = 5 total
    max_iterations: 10

Looping a sub-sequence

repeat works on type: sequence steps. __iteration__ and __cycle__ are available inside the child YAML through the shared variable scope. Use __cycle__ to produce readable measurement names like VOUT_CYCLE1, VOUT_CYCLE2.

Every sub-sequence file MUST have a test: root element with name: and version:. The format is identical to a top-level test file; the engine ignores setup: and teardown: blocks inside sub-sequences. Only the steps: list is executed inline.

This is intentional — see Standalone / Composite Test Pattern for how to use this behaviour to write sub-sequences that work both as independent tests and as reusable modules in a full suite.

# poe.yaml — top-level test
steps:
  - name: "Power cycle"
    type: sequence
    sequence: "tests/poe-cycle.yaml"
    repeat:
      condition: "__iteration__ < 2"   # 2 cycles
      max_iterations: 5
    post_execution_action: terminate-on-fail

# tests/poe-cycle.yaml — the reusable sub-sequence
test:
  name: "PoE power cycle (sub-sequence)"
  version: "1.0.0"
  steps:
    - name: "[Cycle {{__cycle__}}] Enable detection"
      runner: dotnet
      runner_type: net10.0
      assembly: "regressiontests.dll"
      class: "regressiontests.Poe"
      method: "EnableDetectionAndClassification"
      post_execution_action: terminate-on-fail

    - name: "[Cycle {{__cycle__}}] Read registers"
      runner: dotnet
      runner_type: net10.0
      assembly: "regressiontests.dll"
      class: "regressiontests.Poe"
      method: "DumpRegisters"
      outputs:
        opmode: "{{opmode_ch1}}"
        vin:    "{{vin}}"
      measurements:
        - { name: "OPMODE_CYCLE{{__cycle__}}",  type: string, value: "{{opmode_ch1}}" }
        - { name: "VIN_CYCLE{{__cycle__}}",     value: "{{vin}}", unit: "V", operator: log }

timeout_ms — per-attempt wall-clock timeout

Hardware steps MUST always carry timeout_ms. An instrument that stops responding will otherwise block the runner indefinitely.

- name: "Flash firmware"
  runner: dotnet
  runner_type: net10.0
  assembly: "FlashTools.dll"
  class: "FlashTools.Programmer"
  method: "Flash"
  timeout_ms: 30000

run_on_abort — guaranteed cleanup

true → the step IS executed even when the test is aborting. MUST be set on all power-down, disconnect, and instrument-release steps.

- name: "Power down (always runs)"
  runner: dotnet
  runner_type: net10.0
  assembly: "Tests.dll"
  class: "Tests.Cleanup"
  method: "PowerDown"
  run_on_abort: true

post_execution_action

Value Effect
continue Default. Proceed regardless of verdict
terminate-on-fail Stop the test if this step FAILs or ABORTs
terminate-always Stop the test after this step regardless of verdict

MUST be set to terminate-on-fail on all power-on, connect, and init steps.

requires — shared-instrument locking (parallel stations)

A list of named shared resources the step needs exclusive access to. When the station runs more than one test at a time (MaxParallelExecutions > 1), concurrent tests share the bench instruments; requires: tells the engine to serialise access to each named resource. The whole set is acquired atomically before the step runs and released after.

- name: "Measure 3V3 Rail"
  runner: dotnet
  runner_type: net10.0
  assembly: "PowerTests.dll"
  class: "MyTests.PowerTests"
  method: "MeasureVoltage"
  requires: [dmm, mux]        # exclusive DMM + mux for the duration of this step
  measurement:
    name: "RAIL_3V3"
    value: "{{result}}"
    low_limit: 3.135
    high_limit: 3.465
    unit: "V"

Rules:

  • Names are free-form — use one consistent name per physical instrument across the whole station (e.g. dmm, psu, mux, scope).
  • The set is acquired in a fixed order, so declaring requires: can never deadlock.
  • On a single-execution station (MaxParallelExecutions = 1, the default) this has no effect — but it is always safe to declare, and makes the package correct on a parallel station.
  • Any step that touches a shared instrument MUST declare it. Omitting requires: is the common cause of corrupted measurements when two tests run at once.
An unhandled error has occurred. Reload 🗙

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.