Phases
Organize your test flow by breaking it down into multiple phases.
 
Overview
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 validation function, which we’ll cover on the next page.
main.py
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.
- Name
- PhaseResult.CONTINUE
- Description
- Set the phase outcome to - PASSand execute the next phase.
 
- Name
- PhaseResult.STOP
- Description
- Set the phase outcome to - FAILand stop executing the test.
 
- Name
- PhaseResult.REPEAT
- Description
- Repeat the phase, ignoring current measurement outcomes. If exceeded the - repeat_limit, it triggers a- PhaseResult.STOP.
 
- Name
- PhaseResult.FAIL_AND_CONTINUE
- Description
- Set the phase outcome to - FAILand execute the next phase.
 
- Name
- PhaseResult.SKIP
- Description
- Set the phase outcome to - SKIP, ignore current measurement outcomes, and execute the next phase.
 
main.py
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()
Terminal
======================= test: openhtf_test  outcome: FAIL ======================
Options
You can use the @openhtf.PhaseOptions decorator to modify phase execution behavior.
- Name
- timeout_s
- Type
- float
- Description
- Timeout for the phase, in seconds. 
 
- Name
- repeat_limit
- Type
- int or None
- Description
- Maximum number of repeats. Set to - Nonefor infinite repeats, as long as- PhaseResult.REPEATis returned.
 
main.py
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:
- Name
- name
- Type
- str
- Description
- Override for the name of the phase. 
 
- Name
- phase_name_case
- Type
- PhaseNameCase
- Description
- Change phase name case to CamelCase using - CAMEL.
 
main.py
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:
- Name
- repeat_limit
- Type
- int
- Description
- Define a maximum number of repeats. None indicates that a phase will be repeated infinitely as long as - PhaseResult.REPEATis returned.
 
- Name
- force_repeat
- Type
- bool
- Description
- Force the phase to repeat up to - repeat_limittimes.
 
- Name
- repeat_on_timeout
- Type
- bool
- Description
- Repeat phase on timeout. 
 
- Name
- stop_on_measurement_fail
- Type
- bool
- Description
- Stop the test if any measurements fail. 
 
main.py
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.
main.py
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 determine whether to execute the phase; if skipped, it won't be logged.
main.py
import openhtf as htf
import random
def first_phase(test):
    return htf.PhaseResult.CONTINUE
# Will always fail, but only runs if a condition is met
@htf.PhaseOptions(run_if=lambda: random.choice([True, False]))
def conditional_fail_phase(test):
    return htf.PhaseResult.STOP
# Will run if the conditional_fail_phase is not executed
def last_phase(test):
    return htf.PhaseResult.CONTINUE
def main():
    test = htf.Test(first_phase, conditional_fail_phase, last_phase)
    test.execute(lambda: "SN1234")
if __name__ == "__main__":
    main()
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.
main.py
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.
main.py
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()
