Skip to content
Concepts & Methodology

TofuPilot Framework vs OpenHTF

How the TofuPilot Framework and OpenHTF differ, and how each one plugs into the TofuPilot dashboard and stations for live test data.

CCharlotte Evequoz
intermediate9 min readJune 24, 2026

OpenHTF and the TofuPilot Framework are the two open-source Python frameworks purpose-built for hardware manufacturing test. OpenHTF was built by Google in 2016 and is the established, widely deployed option. The TofuPilot Framework is newer, built on a Rust execution engine for parallel execution. Both model a test as ordered phases with structured measurements, both connect instruments through reusable plugs, and both run natively on the TofuPilot CLI and stream to the same dashboard. This guide compares them side by side with real code so you can pick the right one.

Framework Overview

TofuPilot FrameworkOpenHTF
LanguagePython (Rust engine)Python
LicenseMITApache 2.0
MaintainerTofuPilotGoogle
First release20252016
Authoring modelDeclarative YAML procedure plus Python phases and plugsCode-first Python with decorators
ExecutionParallel phases and slotsSequential phases
CommunityNewer, smallerEstablished, larger install base

Feature Comparison

FeatureTofuPilot FrameworkOpenHTF
PhasesYesYes
Numeric, string, boolean measurementsYesYes
Logs and attachmentsYesYes
Unit identification by serial numberYesYes
Sub-units (child components)YesYes
Plugs (shared instrument connections)YesYes
Multi-dimensional measurements (waveforms, sweeps)YesYes
Aggregations (mean, min, max, std over arrays)YesNo
Per-axis validators (validate curve shape)YesNo
Operator promptsYesYes
Custom forms and display componentsYesNo
Retry, timeout, setup/teardownYesYes
Conditional executionYesYes
Parallel phasesYesNo
Multi-slot execution (multiple boards on one fixture)YesNo

On the shared data model the two are close to parity. The gaps fall in three areas: advanced measurement analysis, the operator UI, and execution.

The Same Test in Both Frameworks

A functional test that verifies a board's 3.3V rail and firmware version, written in each framework.

TofuPilot Framework Version

The procedure YAML declares the sequence and limits. The phases and plug are plain Python that the procedure references by name.

procedure.yaml
name: Functional Testversion: 1.0.0unit:  serial_number:    default_value: "PCBA000001"  part_number:    default_value: "PCBA-100"plugs:  - name: dut    python: plugs.dut:Dutmain:  - name: Measure Voltage    python: phases.measure_voltage    measurements:      - name: rail_3v3        unit: V        validators:          - operator: ">="            expected_value: 3.2          - operator: "<="            expected_value: 3.4  - name: Check Firmware    python: phases.check_firmware    measurements:      - name: firmware_version        validators:          - operator: "=="            expected_value: "2.1.0"
phases/measure_voltage.py
# Parameters are injected by name: measurements, plus the dut plug.def measure_voltage(measurements, dut):    measurements.rail_3v3 = dut.read_voltage()
phases/check_firmware.py
def check_firmware(measurements, dut):    measurements.firmware_version = dut.query_firmware()
plugs/dut.py
class Dut:    """Manage the connection to the unit under test."""    def read_voltage(self) -> float:        return 3.31  # Replace with an instrument read    def query_firmware(self) -> str:        return "2.1.0"  # Replace with a DUT query

Run it from the procedure directory with tofupilot run, then deploy to a station with a Git push.

OpenHTF Version

OpenHTF attaches measurements and plugs to phase functions with decorators, then runs them in sequence.

openhtf_test.py
import openhtf as htffrom openhtf.plugs import BasePlugfrom openhtf.util import unitsclass DutPlug(BasePlug):    """Manage the connection to the unit under test."""    def read_voltage(self) -> float:        return 3.31  # Replace with an instrument read    def query_firmware(self) -> str:        return "2.1.0"  # Replace with a DUT query@htf.measures(    htf.Measurement("rail_3v3").in_range(3.2, 3.4).with_units(units.VOLT))@htf.plug(dut=DutPlug)def measure_voltage(test, dut):    test.measurements.rail_3v3 = dut.read_voltage()@htf.measures(htf.Measurement("firmware_version").equals("2.1.0"))@htf.plug(dut=DutPlug)def check_firmware(test, dut):    test.measurements.firmware_version = dut.query_firmware()def main():    test = htf.Test(measure_voltage, check_firmware, part_number="PCBA-100")    test.execute(test_start=lambda: input("Scan serial number: "))if __name__ == "__main__":    main()

The TofuPilot CLI runs both frameworks natively and streams phases, measurements, logs, attachments, and the operator UI to the dashboard, with no extra code on either side.

Key Differences in the Code

AspectTofuPilot FrameworkOpenHTF
Sequence definitionDeclared in procedure.yaml, separate from logicFunction order in htf.Test(...), in code
Limitsvalidators in YAML, stored as data.in_range(3.2, 3.4) chained in code
Plug injectionBy parameter name (dut)By @htf.plug(dut=...) decorator
Measurement accessmeasurements.rail_3v3 = ...test.measurements.rail_3v3 = ...
Plug classPlain Python classSubclass of BasePlug

Execution and Parallelism

This is the clearest architectural difference.

OpenHTF runs phases sequentially against one unit under test. To test multiple boards or run independent steps concurrently, you orchestrate that yourself outside the framework.

The TofuPilot Framework runs on a Rust engine that executes independent phases in parallel and serializes only where you declare a depends_on. It also supports multi-slot execution, testing several boards in parallel on one fixture, which directly reduces cycle time on high-volume lines. Your phase and plug code stays plain Python; the engine handles the scheduling.

If your bottleneck is throughput on a fixture that holds multiple DUTs, this is the deciding factor. If you test one unit at a time, it matters less.

Measurements and Operator UI

Both frameworks capture typed measurements with limits, units, and validators, and both support multi-dimensional measurements for waveforms and sweeps. The TofuPilot Framework adds two things OpenHTF does not: aggregations (mean, min, max, std computed over an array) and per-axis validators that check the shape of a curve rather than only its endpoints. For RF sweeps, thermal profiles, or any test where the waveform itself is the spec, that removes glue code.

Both ship a built-in operator UI. OpenHTF provides web prompts for operator input such as scanning a serial number. The TofuPilot Framework adds declarative form and display components (text, number, switch, radio, select, multiselect, image choice, sliders, progress) that you define in the procedure without writing any frontend.

When to Use the TofuPilot Framework

The TofuPilot Framework is the better choice when:

  • Your line is throughput-bound. Parallel phases and multi-slot execution test several boards at once on one fixture and cut cycle time. OpenHTF executes sequentially.
  • You validate waveforms or sweeps. Built-in aggregations and per-axis validators check curve shape, not only endpoints, with no glue code.
  • Operators need rich guided workflows. Declarative forms and display components render on the station with no frontend work.
  • You want the test plan separate from the logic. The YAML procedure is readable and diffable independently of the Python phases.
  • You are setting up new stations. Deploy-on-push, real-time streaming, and offline queueing are built into the CLI.

When to Use OpenHTF

OpenHTF is the better choice when:

  • You already run it. Existing OpenHTF suites run natively on TofuPilot with no rewrite. The migration cost is not worth it.
  • Maturity and community matter most. Years of production use at scale and a larger body of public examples and patterns.
  • You want a single pure-Python paradigm. Everything is in Python, with no separate YAML layer to learn.
  • Apache 2.0 fits your legal review. Its explicit patent grant can matter in some procurement contexts.

Using Both Together

You do not have to choose the framework to choose the platform. A common path is to keep an existing OpenHTF suite running as is, and reach for the TofuPilot Framework on new stations that need parallel slots or richer measurements. Both stream phases, measurements, logs, attachments, and the operator UI to the same dashboard, and both are open-source, so neither choice locks the test code itself.

Decision Matrix

QuestionBetter fit
Testing multiple boards on one fixture, throughput-bound?TofuPilot Framework
Validating waveform or sweep shape, not just endpoints?TofuPilot Framework
Need rich operator forms on the station?TofuPilot Framework
Already running OpenHTF suites in production?OpenHTF
Want a single pure-Python paradigm, no YAML?OpenHTF
Maximum maturity and community matter most?OpenHTF

Both run on the TofuPilot CLI and upload to the same dashboard, so you can start with either and mix them per station.

More Guides

Put this guide into practice