Phases
Discover how to define and execute test phases using OpenHTF, including phase creation, phase results, and phase options with detailed examples.
Last updated · Verified with OpenHTF 1.6.1
Organize your test flow by breaking it down into multiple phases.
A test executes its phases in order. Each phase returns a PhaseResult: CONTINUE sets the outcome to PASS and moves on, REPEAT runs the phase again, SKIP marks it skipped, and STOP sets the outcome to FAIL and ends the test.
- Test start
- phase_boot
- phase_calibrate
- phase_optional
- phase_voltage
- Test end
A hardware test typically consists of several steps that perform measurements and validation. OpenHTF refers to these steps as phases and allows for precise management of their execution based on the results obtained.
Syntax
Phases are Python functions that take the test object as an argument and must be added to the Test object to be executed.
The phase outcome is set either manually with a PhaseResult or automatically through a measurement validator, covered on the Measurements page.
import openhtf as htf
def phase_one(test):
return htf.PhaseResult.CONTINUE
def phase_two(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(phase_one, phase_two)
test.execute(lambda: "SN1234")
if __name__ == '__main__':
main()Results
You can choose the next phase's execution by setting the PhaseResult from the following options.
PhaseResult.CONTINUESet the phase outcome to PASS and execute the next phase.
PhaseResult.STOPSet the phase outcome to FAIL and stop executing the test.
PhaseResult.REPEATRepeat the phase, ignoring current measurement outcomes. If exceeded the
repeat_limit, it triggers a PhaseResult.STOP.
PhaseResult.FAIL_AND_CONTINUESet the phase outcome to FAIL and execute the next phase.
PhaseResult.SKIPSet the phase outcome to SKIP, ignore current measurement outcomes, and
execute the next phase.
PhaseResult.FAIL_SUBTESTFail the enclosing subtest and continue with the next one. Outside a subtest this is an ERROR.
A phase that returns None (no explicit return) is treated as CONTINUE.
import openhtf as htf
import random
# Always pass
def phase_pass(test):
return htf.PhaseResult.CONTINUE
# Retries on failure
def phase_retry(test):
if random.choice([True, False]):
return htf.PhaseResult.CONTINUE
else:
return htf.PhaseResult.REPEAT
# Fail and stop the test
def phase_fail(test):
return htf.PhaseResult.STOP
def main():
test = htf.Test(phase_pass, phase_retry, phase_fail)
test.execute(lambda: "SN1234")
if __name__ == '__main__':
main()======================= test: openhtf_test outcome: FAIL ======================Options
You can use the @openhtf.PhaseOptions decorator to modify phase execution behavior.
timeout_sfloatTimeout for the phase, in seconds.
repeat_limitint or NoneMaximum number of repeats. Set to None for infinite repeats, as long as
PhaseResult.REPEAT is returned.
import openhtf as htf
import random
@htf.PhaseOptions(timeout_s=5)
def phase_pass(test):
return htf.PhaseResult.CONTINUE
@htf.PhaseOptions(repeat_limit=3) # Retries up to 3 times in case of failure
def phase_retry(test):
if random.choice([True, False]):
return htf.PhaseResult.CONTINUE
else:
return htf.PhaseResult.REPEAT
def main():
test = htf.Test(phase_pass, phase_retry)
test.execute(lambda: "SN1234")
if __name__ == '__main__':
main()For more options, check the advanced use cases.
Advanced use cases
You can leverage advanced OpenHTF options to handle more complex phase execution cases.
Override phase name
You can replace the default phase name or change the case formatting:
namestrOverride for the name of the phase.
phase_name_casePhaseNameCasePhaseNameCase.KEEP (default) leaves the function name as-is; PhaseNameCase.CAMEL converts measure_voltage to MeasureVoltage.
import openhtf as htf
from openhtf import PhaseNameCase
@htf.PhaseOptions(name="new_phase_name", phase_name_case=PhaseNameCase.CAMEL)
def example_phase(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(example_phase)
test.execute(lambda: "SN1234")
if __name__ == '__main__':
main()Change repeat behavior
You can repeat or stop phases under specific conditions with these following PhaseOptions:
repeat_limitintDefine a maximum number of repeats. None indicates that a phase will be
repeated infinitely as long as PhaseResult.REPEAT is returned.
force_repeatboolForce the phase to repeat up to repeat_limit times.
repeat_on_timeoutboolRepeat phase on timeout.
repeat_on_measurement_failboolRepeat the phase (up to repeat_limit) when any of its measurements fails validation, instead of failing the phase.
stop_on_measurement_failboolStop the test if any measurements fail.
import openhtf as htf
import random
@htf.PhaseOptions(force_repeat=True, repeat_limit=3)
def repeat_phase(test):
return htf.PhaseResult.CONTINUE
@htf.PhaseOptions(repeat_on_timeout=True)
def timeout_phase(test):
return htf.PhaseResult.CONTINUE
@htf.PhaseOptions(stop_on_measurement_fail=True)
def random_fail_phase(test):
if random.choice([True, False]):
return htf.PhaseResult.CONTINUE
else:
return htf.PhaseResult.STOP
# This test won't be run if random_fail_phase is FAIL.
def always_true_phase(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(repeat_phase, timeout_phase, random_fail_phase, always_true_phase)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Python Debugger
You can run the phase under the Python Debugger. When setting this option, increase the phase timeout_s as well because the timeout will still apply when under the debugger.
import openhtf as htf
@htf.PhaseOptions(run_under_pdb=True, timeout_s=20)
def first_phase(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(first_phase)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Run if
You can use a callback to decide whether a phase executes at all. The typical use is gating a subset of phases on an environment variable or a configuration flag, so the same test file serves several station setups (a full end-of-line run on one bench, a quick subset on another) without maintaining separate copies of the test.
import os
import openhtf as htf
# One flag per optional block, driven by the station's environment
RUN_BURNIN = os.environ.get("RUN_BURNIN", "0") == "1"
def measure_voltage(test):
return htf.PhaseResult.CONTINUE
# Only runs when the station sets RUN_BURNIN=1
@htf.PhaseOptions(run_if=lambda: RUN_BURNIN)
def thermal_burnin(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(measure_voltage, thermal_burnin)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Two things to keep in mind:
- The callback takes no arguments, so it cannot see the test state or earlier measurements; it can only read module globals, environment variables, or configuration values. To skip based on something measured during the run, return
PhaseResult.SKIPfrom inside the phase instead. - A phase skipped by
run_ifis not logged: it leaves no phase record in the test output, as if it were never part of the test.PhaseResult.SKIPon the other hand records the phase with a skip outcome.
Requires state
You can use this option when a phase needs to manage internal test details, such as wrapping or controlling other phases.
The complete TestState object is passed instead of default TestApi.
import openhtf as htf
@htf.PhaseOptions()
def check_condition(test):
return htf.PhaseResult.CONTINUE
@htf.PhaseOptions(requires_state=True)
def conditional_phase(test_state):
check_condition(test_state) # Manually invoke another phase
def main():
test = htf.Test(conditional_phase)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Phase Groups
You can group phases into setup, main, and teardown. If a failure occurs during the setup or main phases, the system will automatically ensure that the teardown phase is always executed. The Phase Groups page covers nesting and the shorthand decorators.
import openhtf as htf
from openhtf.util import units
def setup_phase(test):
return htf.PhaseResult.CONTINUE
def first_measurement_phase(test):
return htf.PhaseResult.CONTINUE
def second_measurement_phase(test):
return htf.PhaseResult.STOP
def teardown_phase(test):
return htf.PhaseResult.CONTINUE
def main():
test = htf.Test(
htf.PhaseGroup(
setup=[setup_phase],
main=[first_measurement_phase, second_measurement_phase],
teardown=[teardown_phase],
)
)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Related
Concepts
The OpenHTF vocabulary — DUT, station, test, phase, measurement, validator, plug, attachment, test record, output callback — each defined in one paragraph with a link to its page.
Measurements
Learn how to capture and validate data within OpenHTF phases, including boolean, string, numeric, multidimensional, and marginal measurements.