Skip to content

Commit 94bebbc

Browse files
authored
Branches (#960)
* Drop py2.7 tests from travis config and setup.py. * diagnoses_lib: Serialize diagnoses result values, not names Diagnosis results should values should be serialized out rather than the enum constant names. PiperOrigin-RevId: 318469885 * Attachments: Handle case where NamedTemporaryFile object stops working. We have noticed times where we get bad file descriptor exceptions trying to reload data from the temporary files. For now, just return empty byte strings while we investigate more. Also, adding a callback to close the Attachments' temporary files. PiperOrigin-RevId: 319879865 * test_record: Fix lint issues. PiperOrigin-RevId: 320025482 * output callbacks: Fix CloseAttachments Attachments are stored in a dictionary under the phase record, not a list. PiperOrigin-RevId: 321406529 * OpenHTF: Fix lint issues and run autoformatter. PiperOrigin-RevId: 323661267 * Initial pytype-based type annotations. PiperOrigin-RevId: 324720299 * conf: Use inspect.getfullargspec. PiperOrigin-RevId: 324754078 * Internal change PiperOrigin-RevId: 325271171 * Only have the attachment's temporary file open while reading/writing. If there are a lot of attachments, the program can exceed Linux's max allowed open files per process. PiperOrigin-RevId: 325490340 * Add core annotations and replace mutablerecords and namedtuple with attr. PhaseDescriptor will be in the next commit. PiperOrigin-RevId: 327075946 * Rearrange and add comments for the Measurement fields. PiperOrigin-RevId: 327140593 * Internal change PiperOrigin-RevId: 327735936 * Convert PhaseOptions and PhaseDescriptor to attr. PiperOrigin-RevId: 328778682 * callbacks: Add type annotations Add type annotations to the callbacks library and break apart some complex types. Also breaking out the JSON conversion logic to an independent function for easier use by other modules. PiperOrigin-RevId: 328972148 * Internal change PiperOrigin-RevId: 329550447 * Internal change PiperOrigin-RevId: 331590164 * Internal change PiperOrigin-RevId: 331890539 * Remove TestPhase alias. The TestPhase alias for PhaseOptions has long been deprecated. Removing it. PiperOrigin-RevId: 332291802 * Add more type annotations. PiperOrigin-RevId: 332519023 * Remove plugs-phase_descriptor circular dependency Remove the plugs to phase_descriptor circular dependency by moving the pieces phase_descriptor depends on to a new core/base_plugs.py file. PiperOrigin-RevId: 332527874 * PhaseDescriptor: with_plugs and with_args now ignore unknowns Change with_plugs and with_args to use their with_known_plugs and with_known_args implementations instead. PiperOrigin-RevId: 332546916 * Add type checking to unit tests to verify things are working. PiperOrigin-RevId: 332551203 * test_descriptor: Remove Test Teardown. Test teardown was deprecated in favor of PhaseGroup teardowns. Fully removing them. PiperOrigin-RevId: 333112589 * Add PhaseNode and implement PhaseSequence. Phase nodes are now the basic building block of the OpenHTF execution engine. Phase sequence is a phase node that constains a sequence of phase nodes. Phase groups now use phase sequences to contain its setup, main, and teardown phases. PiperOrigin-RevId: 334461205 * Implement Phase Branches Phase branches run phases conditionally based on triggered diagnosis results. PiperOrigin-RevId: 335460872 * PhaseExecutor: Raise on invalid phase result. PiperOrigin-RevId: 335469722 * Implement Subtests. Subtests are a collection of phases that can indepenently fail and skip the rest of the phases while still working with PhaseGroup teardowns. PiperOrigin-RevId: 335472411 * util/test: Allow customizing the test_start_function. The OpenHTF TestCase can now customize the test start function by setting the `test_start_function` attribute. This change will now force unit tests to call super().setUp() in all cases. PiperOrigin-RevId: 335509542 * Implement Phase Checkpoints Phase checkpoints are nodes that check if a diagnosis result has been triggered or a simple set of phases has failed. In those cases, they will either be resolved as FAIL_SUBTEST or STOP. PiperOrigin-RevId: 335513243 * Refactor TestApi and expose diagnoses store. The TestApi object should just be a proxy to the functions on the PhaseState and TestState instances. Add the diagnoses store to the API for simpler access during phases. PiperOrigin-RevId: 336213159 * Subtest skip phases when they fail. Subtests should skip the later phases with special handling for other nodes as documented in event_sequence.md. PiperOrigin-RevId: 336386553 * Fix typo in TestApi.diagnoses_store PiperOrigin-RevId: 336408351 * Internal change PiperOrigin-RevId: 336691169 * Add DimensionPivot. DimensionPivot is a validator that runs a subvalidator on each value independently for a dimensioned measurement. If any value fails, the measurement is a failure. PiperOrigin-RevId: 336730422 * Drop PY2 support from OpenHTF. PiperOrigin-RevId: 337139629
1 parent 310b64d commit 94bebbc

File tree

102 files changed

+7697
-3741
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+7697
-3741
lines changed

.travis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
language: python
22
matrix:
33
include:
4-
- python: 2.7
5-
env: TOXENV=py27
64
- python: 3.6
75
env: TOXENV=py36
86
addons:

CHANGELOG

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Changes for 2.0.
2+
3+
* Dropped Python 2.x support.
4+
* Added type annotations.
5+
* Replaced mutablerecords with attrs.
6+
* PhaseOptions:
7+
* The openhtf.TestPhase alias for PhaseOptions has been deprecated for a
8+
long time. Removing it.
9+
* PhaseDescriptor:
10+
* with_known_plugs and with_known_args are being rolled into with_plugs and
11+
with_args, respectively. They will no longer raise exceptions when
12+
the names are not found.
13+
* If the options name field is a callable, the name property of
14+
PhaseDescriptors will only return the name of the function rather than
15+
the callable. This ensures that the name property is always Text.
16+
* Test:
17+
* The test teardown has been removed in favor of using a PhaseGroup.
18+
* Unit testing:
19+
* Unit tests using openhtf.util.test.TestCase can customize the test start
20+
function when yielding openhtf.Test instances by setting the
21+
`test_start_function` attribute. This can be set to None to remove the
22+
function.

bin/units_from_xls.py

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
15-
1614
"""Read in a .xls file and generate a units module for OpenHTF.
1715
1816
UNECE, the United Nations Economic Commission for Europe, publishes a set of
@@ -39,23 +37,19 @@
3937
spaces into underscores, and converting to uppercase.
4038
"""
4139

42-
4340
import argparse
4441
import os
45-
import shutil
4642
import re
43+
import shutil
4744
import sys
48-
from tempfile import mkstemp
45+
import tempfile
4946

5047
import six
5148
import xlrd
5249

53-
5450
# Column names for the columns we care about. This list must be populated in
5551
# the expected order: [<name label>, <code label>, <suffix label>].
56-
COLUMN_NAMES = ['Name',
57-
'Common\nCode',
58-
'Symbol']
52+
COLUMN_NAMES = ['Name', 'Common\nCode', 'Symbol']
5953

6054
PRE = '''# coding: utf-8
6155
# THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
@@ -100,7 +94,14 @@
10094
import collections
10195
10296
103-
UnitDescriptor = collections.namedtuple('UnitDescriptor', 'name code suffix')
97+
class UnitDescriptor(
98+
collections.namedtuple('UnitDescriptor', [
99+
'name',
100+
'code',
101+
'suffix',
102+
])):
103+
pass
104+
104105
105106
ALL_UNITS = []
106107
@@ -120,8 +121,10 @@
120121
121122
# pylint: enable=line-too-long
122123
124+
123125
class UnitLookup(object):
124126
"""Facilitates user-friendly access to units."""
127+
125128
def __init__(self, lookup):
126129
self._lookup = lookup
127130
@@ -144,41 +147,42 @@ def __call__(self, name_or_suffix):
144147
'''
145148

146149
SHEET_NAME = 'Annex II & Annex III'
147-
UNIT_KEY_REPLACEMENTS = {' ': '_',
148-
',' : '_',
149-
'.': '_',
150-
'-': '_',
151-
'/': '_PER_',
152-
'%': 'PERCENT',
153-
'[': '',
154-
']': '',
155-
'(': '',
156-
')': '',
157-
"'": '',
158-
'8': 'EIGHT',
159-
'15': 'FIFTEEN',
160-
'30': 'THIRTY',
161-
'\\': '_',
162-
six.unichr(160): '_',
163-
six.unichr(176): 'DEG_',
164-
six.unichr(186): 'DEG_',
165-
six.unichr(8211): '_',
166-
}
150+
UNIT_KEY_REPLACEMENTS = {
151+
' ': '_',
152+
',': '_',
153+
'.': '_',
154+
'-': '_',
155+
'/': '_PER_',
156+
'%': 'PERCENT',
157+
'[': '',
158+
']': '',
159+
'(': '',
160+
')': '',
161+
"'": '',
162+
'8': 'EIGHT',
163+
'15': 'FIFTEEN',
164+
'30': 'THIRTY',
165+
'\\': '_',
166+
six.unichr(160): '_', # NO-BREAK SPACE
167+
six.unichr(176): 'DEG_', # DEGREE SIGN
168+
six.unichr(186): 'DEG_', # MASCULINE ORDINAL INDICATOR
169+
six.unichr(8211): '_', # EN DASH
170+
}
167171

168172

169173
def main():
170174
"""Main entry point for UNECE code .xls parsing."""
171175
parser = argparse.ArgumentParser(
172176
description='Reads in a .xls file and generates a units module for '
173-
'OpenHTF.',
177+
'OpenHTF.',
174178
prog='python units_from_xls.py')
175-
parser.add_argument('xlsfile', type=str,
176-
help='the .xls file to parse')
179+
parser.add_argument('xlsfile', type=str, help='the .xls file to parse')
177180
parser.add_argument(
178181
'--outfile',
179182
type=str,
180-
default=os.path.join(os.path.dirname(__file__), os.path.pardir,
181-
'openhtf','util', 'units.py'),
183+
default=os.path.join(
184+
os.path.dirname(__file__), os.path.pardir, 'openhtf', 'util',
185+
'units.py'),
182186
help='where to put the generated .py file.')
183187
args = parser.parse_args()
184188

@@ -188,14 +192,12 @@ def main():
188192
sys.exit()
189193

190194
unit_defs = unit_defs_from_sheet(
191-
xlrd.open_workbook(args.xlsfile).sheet_by_name(SHEET_NAME),
192-
COLUMN_NAMES)
195+
xlrd.open_workbook(args.xlsfile).sheet_by_name(SHEET_NAME), COLUMN_NAMES)
193196

194-
_, tmp_path = mkstemp()
197+
_, tmp_path = tempfile.mkstemp()
195198
with open(tmp_path, 'w') as new_file:
196199
new_file.write(PRE)
197-
new_file.writelines(
198-
[line.encode('utf8', 'replace') for line in unit_defs])
200+
new_file.writelines([line.encode('utf8', 'replace') for line in unit_defs])
199201
new_file.write(POST)
200202
new_file.flush()
201203

@@ -209,14 +211,16 @@ def unit_defs_from_sheet(sheet, column_names):
209211
Args:
210212
sheet: An xldr.sheet object representing a UNECE code worksheet.
211213
column_names: A list/tuple with the expected column names corresponding to
212-
the unit name, code and suffix in that order.
213-
Yields: Lines of Python source code that define OpenHTF Unit objects.
214+
the unit name, code and suffix in that order.
215+
216+
Yields:
217+
Lines of Python source code that define OpenHTF Unit objects.
214218
"""
215219
seen = set()
216220
try:
217221
col_indices = {}
218222
rows = sheet.get_rows()
219-
223+
220224
# Find the indices for the columns we care about.
221225
for idx, cell in enumerate(six.next(rows)):
222226
if cell.value in column_names:
@@ -234,9 +238,9 @@ def unit_defs_from_sheet(sheet, column_names):
234238

235239
# Split on ' or ' to support the units like '% or pct'
236240
for suffix in suffix.split(' or '):
237-
yield "%s = UnitDescriptor('%s', '%s', '''%s''')\n" % (
238-
key, name, code, suffix)
239-
yield "ALL_UNITS.append(%s)\n" % key
241+
yield "%s = UnitDescriptor('%s', '%s', '''%s''')\n" % (key, name, code,
242+
suffix)
243+
yield 'ALL_UNITS.append(%s)\n' % key
240244

241245
except xlrd.XLRDError:
242246
sys.stdout.write('Unable to process the .xls file.')

docs/event_sequence.md

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,58 @@ further:
66
1. `test_start`'s plugs are instantiated
77
1. `test_start` is run in a new thread
88
1. All plugs for the test are instantiated
9-
1. Each phase is run
10-
1. The teardown phase is run
9+
1. Each phase node is run
10+
1. If the node is a subtest, each node is run until a FAIL_SUBTEST is
11+
returned by a phase.
12+
1. If the node is a branch, each node is run if the condition is met
13+
1. If the node is a sequence, each node is run
14+
1. If the node is a group, each of the groups sequences is run
15+
1. If the node is a phase descriptor, that phase is run
1116
1. All plugs' `tearDown` function is called
1217
1. All plugs are deleted
1318
1. Test outcome is calculated as PASS or FAIL
1419
1. Output callbacks are called
1520

21+
## Phase node execution
22+
23+
```
24+
[PhaseNode]
25+
|
26+
+--[PhaseDescriptor]
27+
|
28+
+--[Checkpoint]
29+
|
30+
\--[PhaseCollection]
31+
|
32+
+--[PhaseSequence]
33+
| |
34+
| +--[PhaseBranch]
35+
| |
36+
| \--[Subtest]
37+
|
38+
\--[PhaseGroup]
39+
```
40+
41+
`PhaseNode`s are the basic building block for OpenHTF's phase execution. They
42+
are a base class that defines a few basic operations that can get recursively
43+
applied. The `PhaseDescriptor` is the primary executable unit that wraps the
44+
phase functions. `PhaseCollection` is a base class for a node that contains
45+
multiple nodes. The primary one of these is the `PhaseSequence`, which is a
46+
tuple of phase nodes; each of those nodes is executed in order with nested
47+
execution if those nodes are other collections. `PhaseBranch`s are phase
48+
sequences that are only run when the Diagnosis Result-based conditions are met.
49+
`Checkpoint` nodes check conditions, like phase failure or a triggered
50+
diagnosis; if that condition is met, they act as a failed phase. `PhaseGroup`s
51+
are phase collections that have three sequences as described below.
52+
53+
### Recursive nesting
54+
55+
Phase collections allow for nested nodes where each nested level is handled with
56+
recursion.
57+
58+
OpenHTF does not check for or handle the situation where a node is nested inside
59+
itself. The current collection types are frozen to prevent this from happening.
60+
1661
## Test error short-circuiting
1762

1863
A phase raising an exception won't kill the test, but will initiate a
@@ -23,27 +68,52 @@ If a `test_start` phase is terminal, then the executor will skip to Plug
2368
Teardown, where only the plugs initialized for `test_start` have their
2469
`teardown` functions called.
2570

71+
In all cases with terminal phases, the Test outcome is ERROR for output
72+
callbacks.
73+
74+
### PhaseGroups
75+
2676
`PhaseGroup` collections behave like contexts. They are entered if their
2777
`setup` phases are all non-terminal; if this happens, the `teardown` phases are
2878
guarenteed to run. `PhaseGroup` collections can contain additional `PhaseGroup`
2979
instances. If a nested group has a terminal phase, the outer groups will trigger
3080
the same shortcut logic.
3181

32-
For terminal phases in a `PhaseGroup`,
33-
* If the phase was a `PhaseGroup.setup` phase, then we skip the rest of the
34-
`PhaseGroup`.
35-
* If the phase was a `PhaseGroup.main` phase, then we skip to the
36-
`PhaseGroup.teardown` phases of that `PhaseGroup`.
37-
* If the phase was a `PhaseGroup.teardown` phase, the rest of the `teardown`
38-
phases are run, but outer groups will trigger the shortcut logic.
39-
40-
In all cases with terminal phases, the Test outcome is ERROR for output
41-
callbacks.
82+
For terminal phases (or phases that return `FAIL_SUBTEST`) in a `PhaseGroup`,
83+
* If the phase was in the `setup` sequence, then we do not run the rest of
84+
the `PhaseGroup`.
85+
* If the phase was in the `main` sequence, then we do not run the rest of the
86+
`main` sequence and proceed to the `teardown` sequence of that `PhaseGroup`.
87+
* If the phase was in the `teardown` sequence, the rest of the `teardown`
88+
sequence ndoes are run, but outer groups will trigger the shortcut logic.
89+
This also applies to all nested phase nodes.
4290

4391
NOTE: If a phase calls `os.abort()` or an equivalent to the C++
4492
`die()` function, then the process dies and you cannot recover the results from
4593
this, so try to avoid such behavior in any Python or C++ libraries you use.
4694

95+
### Subtests
96+
97+
`Subtest`s are Phase Sequences that allow phases to exit early, but continue on
98+
with other phases. A phase can indicate this by returning
99+
`htf.PhaseResult.FAIL_SUBTEST` or with a checkpoint with that result as its
100+
action. The details of subtests are included in the output test record.
101+
102+
The rest of the phases in a subtest after the failing node will be processed as:
103+
104+
* Phase descriptors are all skipped.
105+
* Branches are not run at all, as though their condition was evaluated as false.
106+
* Groups entered after the failing node are entirely skipped, including their
107+
`teardown` sequences.
108+
* Groups with the failing node in its `main` sequence will skip the rest of the
109+
`main` sequence, but will run the teardown phases.
110+
* Groups with the failing node in its `setup` sequence will skip the rest of the
111+
setup phases and will record skips for the `main` and `teardown` sequences.
112+
* Groups with the failing node in its `teardown` sequence will still run the
113+
rest of the `teardown` sequence.
114+
* Sequences are recursively processed by these same rules.
115+
116+
Phase group teardowns are run properly when nested in a subtest.
47117

48118
## Test abortion short-circuiting
49119

0 commit comments

Comments
 (0)