Phase Groups Example
The upstream phase_groups.py example — six runs showing exactly when OpenHTF executes teardown phases after setup errors, main errors, nested groups and errors inside nested groups.
Last updated · Verified with OpenHTF 1.6.1
Six short tests that answer one question each: does teardown run? Based on examples/phase_groups.py.
import openhtf as htf
def setup_phase(test):
test.logger.info('Setup in a group.')
def main_phase(test):
test.logger.info('This is a main phase.')
def teardown_phase(test):
test.logger.info('Teardown phase.')
def inner_main_phase(test):
test.logger.info('Inner main phase.')
def inner_teardown_phase(test):
test.logger.info('Inner teardown phase.')
def error_setup_phase(test):
test.logger.info('Error in setup phase.')
return htf.PhaseResult.STOP
def error_main_phase(test):
test.logger.info('Error in main phase.')
return htf.PhaseResult.STOP
def run_basic_group():
"""No terminal phase: setup, main, teardown all run."""
htf.Test(htf.PhaseGroup(
setup=[setup_phase], main=[main_phase], teardown=[teardown_phase],
)).execute()
def run_setup_error_group():
"""Setup fails: main AND teardown are skipped — the group was never entered."""
htf.Test(htf.PhaseGroup(
setup=[error_setup_phase], main=[main_phase], teardown=[teardown_phase],
)).execute()
def run_main_error_group():
"""Main fails: remaining main phases skipped, teardown still runs."""
htf.Test(htf.PhaseGroup(
setup=[setup_phase], main=[error_main_phase, main_phase], teardown=[teardown_phase],
)).execute()
def run_nested_groups():
"""Nested group, no error: main, inner main, inner teardown, teardown."""
htf.Test(htf.PhaseGroup(
main=[
main_phase,
htf.PhaseGroup.with_teardown(inner_teardown_phase)(inner_main_phase),
],
teardown=[teardown_phase],
)).execute()
def run_nested_error_groups():
"""Error inside the nested group: inner teardown, then outer teardown."""
htf.Test(htf.PhaseGroup(
main=[
htf.PhaseGroup.with_teardown(inner_teardown_phase)(error_main_phase, main_phase),
main_phase,
],
teardown=[teardown_phase],
)).execute()
def run_nested_error_skip_unentered_groups():
"""Error before the nested group: it is never entered, only outer teardown runs."""
htf.Test(htf.PhaseGroup(
main=[
error_main_phase,
htf.PhaseGroup.with_teardown(inner_teardown_phase)(main_phase),
main_phase,
],
teardown=[teardown_phase],
)).execute()
def main():
run_basic_group()
run_setup_error_group()
run_main_error_group()
run_nested_groups()
run_nested_error_groups()
run_nested_error_skip_unentered_groups()
if __name__ == '__main__':
main()Run with -v to see which phases execute:
$ python phase_groups.py -v
I ... <phase: setup_phase> - Setup in a group.
I ... <phase: main_phase> - This is a main phase.
I ... <phase: teardown_phase> - Teardown phase.
======================= test: openhtf_test outcome: PASS ======================
I ... <phase: error_setup_phase> - Error in setup phase.
======================= test: openhtf_test outcome: FAIL ======================
I ... <phase: setup_phase> - Setup in a group.
I ... <phase: error_main_phase> - Error in main phase.
I ... <phase: teardown_phase> - Teardown phase.
======================= test: openhtf_test outcome: FAIL ======================
...The six outcomes
| Scenario | Phases that run | Teardown? |
|---|---|---|
| No error | setup → main → teardown | Yes |
Setup returns STOP | setup only | No — group never entered |
Main returns STOP | setup → failing main → teardown | Yes; other main phases skipped |
| Nested, no error | main → inner main → inner teardown → teardown | Both |
| Error in nested main | inner error → inner teardown → outer teardown | Both; remaining outer main skipped |
| Error before nested group | outer error → outer teardown | Outer only — inner never entered |
Rule of thumb: a group's teardown runs if and only if its setup completed. "Entered" is the word upstream uses.
What it shows
PhaseGroup(setup=, main=, teardown=)The explicit form. Each argument is a list of phases or nested nodes. Phase Groups →
PhaseGroup.with_teardown(td)(*main)Shorthand factory: a group with only main and teardown. with_setup and with_context(setup, teardown) exist too.
test.execute() with no DUT IDAllowed; the record gets UNKNOWN_DUT. Fine for a demo, not for production — see Device Under Test.
Next
With plugs
The upstream with_plugs.py example — write one phase against a plug placeholder, then generate several phases with phase.with_plugs() and with_args(), each with its own name and measurement.
Phase branches
The upstream phase_branches.py example — an operator picks a device stage, a PhaseDiagnoser turns the answer into a Diagnosis, and BranchSequence runs only the matching phases, including a nested branch.