Measurements
Create measurements to capture and validate data within phases.
Overview
Hardware tests are more complex than simple pass/fail checks like in software testing. They often require measuring physical values and comparing them to limits. OpenHTF simplifies logging and validating numeric, string, and boolean values, either individually or in arrays, using built-in decorators.
With the TofuPilot integration, you get control charts and histograms to compare measurements across test runs.
Numeric
You can define and validate numeric measurements.
main.py
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("temperature") # Declares the measurement name
.in_range(0, 100) # Defines the validator
.with_units(units.DEGREE_CELSIUS) # Specifies the unit
.with_precision(1) # Rounds to 1 decimal place
)
def phase_temperature(test):
test.measurements.temperature = 25 # Set the temperature value to 25°C
def main():
test = htf.Test(phase_temperature)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Validators
- Name
.in_range(minimum, maximum)
- Type
- number, number
- Description
Ensure the measurement is within the given range.
- Name
.within_percent(value, percent)
- Type
- number, number
- Description
Ensure the measurement is within the given percentage range.
- Name
.equals(value)
- Type
- number
- Description
Ensure the measurement exactly matches the specified value.
- Name
.with_validator(lambda)
- Type
- function → bool
- Description
Apply a custom validator function to the measurement.
Options
- Name
.with_units(units)
- Type
- UnitDescriptor
- Description
Define unit to the measurement (e.g. AMPERE, VOLT, full list here).
- Name
.with_precision(precision)
- Type
- int
- Description
Round the value to the specified precision before validation.
main.py
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("voltage")
.in_range(maximum=10)
.with_units(units.VOLT)
)
def phase_voltage(test):
test.measurements.voltage = 5.3
@htf.measures(
htf.Measurement("memory")
.equals(8)
.with_units(units.GIGABYTE)
)
def phase_memory(test):
test.measurements.memory = 8
def main():
test = htf.Test(phase_voltage, phase_memory)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
String
You can define and validate string measurements.
main.py
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("firmware_version")
.equals("1.2.4")
)
def phase_firmware(test):
test.measurements.firmware_version = "1.2.4"
def main():
test = htf.Test(phase_firmware)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Validators
- Name
.equals(value)
- Type
- str
- Description
Ensure the measurement exactly matches the specified value.
- Name
.matches_regex(pattern)
- Type
- str
- Description
Ensure the string matches the specified regex pattern.
- Name
.with_validator(lambda)
- Type
- function → bool
- Description
Apply a custom validator function to the measurement.
Boolean
You can define and validate boolean measurements.
main.py
import openhtf as htf
@htf.measures(
htf.Measurement("is_led_switch_on")
.equals(True)
)
def phase_led(test):
test.measurements.is_led_switch_on = True
def main():
test = htf.Test(phase_led)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Validators
- Name
.equals(value)
- Type
- bool
- Description
Ensure the measurement exactly matches the specified value.
- Name
.with_validator(lambda)
- Type
- function → bool
- Description
Apply a custom validator function to the measurement.
Multiple measurements
You can also use multiple measurements in a single phase.
main.py
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("is_connected").equals(True),
htf.Measurement("firmware_version").equals("1.2.7"),
htf.Measurement("temperature").in_range(0, 100).with_units(units.DEGREE_CELSIUS),
)
def phase_multi_measurements(test):
test.measurements.is_connected = True
test.measurements.firmware_version = "1.2.7" if test.measurements.is_connected else "N/A"
test.measurements.temperature = 22.5
def main():
test = htf.Test(phase_multi_measurements)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
TofuPilot integration
The TofuPilot integration is natively compatible with OpenHTF phases and measurements.
To integrate TofuPilot, install the open-source client:
pip install tofupilot
Add it to your test like this:
main.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
def phase_one(test):
return htf.PhaseResult.CONTINUE
@htf.measures(
htf.Measurement("temperature")
.in_range(0, 100)
.with_units(units.DEGREE_CELSIUS)
)
def phase_temperature(test):
test.measurements.temperature = 25
def main():
test = htf.Test(phase_one, phase_temperature)
with TofuPilot(test): # just works™️
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
You can visualize the test measurements (referred to as steps) on each run page.
You can display control charts and histograms for a measurement to track its evolution across test runs.
You can access more phase performance indicators on TofuPilot and filter test runs by date, revision, and other criteria.
Learn more in the TofuPilot DocsAdvanced use cases
You can leverage advanced OpenHTF options to handle complex measurement use cases.
Multidimensional
You can capture data in arrays, like time-series or across categories, with multidimensional measurements.
main.py
import openhtf as htf
from openhtf.util import units
import random
import time
@htf.measures(
htf.Measurement("voltage_over_time")
.with_dimensions(units.SECOND, units.VOLT)
)
def phase_voltage_measurement(test):
for t in range(10):
timestamp = t
voltage = round(random.uniform(3.3, 3.5), 2)
test.measurements.voltage_over_time[timestamp, voltage] = 0
time.sleep(0.1)
def main():
test = htf.Test(phase_voltage_measurement)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Options
- Name
.with_dimensions(*dims)
- Type
- UnitDescriptor
- Description
Set multiple units for multidimensional measurements.
Marginal
You can mark a measure as marginal to show it’s close to failing, even if it passes.
main.py
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement('resistance')
.with_units('ohm')
.in_range(minimum=5, maximum=17, marginal_minimum=9, marginal_maximum=11)
)
def phase_marginal(test):
test.measurements.resistance = 13
def main():
test = htf.Test(phase_marginal)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Terminal
================= test: openhtf_test outcome: PASS (MARGINAL) =================
Documentation
You can add a description to your measurements.
main.py
import openhtf as htf
@htf.measures(
htf.Measurement("temperature")
.in_range(0, 100)
.doc("This measurement tracks the ambient temperature during the test.")
)
def phase_temperature(test):
test.measurements.temperature = 25
def main():
test = htf.Test(phase_temperature)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Dynamic naming
You can customize measurement names dynamically at execution.
main.py
import openhtf as htf
@htf.measures(
htf.Measurement("test_result_{level}")
.with_args(level="high")
.equals(True)
)
def phase_test(test):
test.measurements.test_result_high = True
def main():
test = htf.Test(phase_test)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()
Transformation function
You can apply measurement transformation functions before validation.
main.py
import openhtf as htf
@htf.measures(
htf.Measurement("voltage")
.in_range(0, 10)
.with_transform(lambda x: x * 1.1)
.with_units("V")
)
def phase_voltage(test):
test.measurements.voltage = 5 # Value will be transformed to 5.5 before validation
def main():
test = htf.Test(phase_voltage)
test.execute(lambda: "PCB001")
if __name__ == "__main__":
main()