Skip to content

Commit c31f895

Browse files
OpenHTF Ownerscopybara-github
authored andcommitted
Add failure outcome details when Multidim measurements fail so that the user can check them (e.g. on the console).
PiperOrigin-RevId: 509680299
1 parent dc22c0c commit c31f895

File tree

1 file changed

+58
-7
lines changed

1 file changed

+58
-7
lines changed

openhtf/core/test_state.py

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
import copy
2828
import enum
2929
import functools
30+
import itertools
3031
import logging
3132
import mimetypes
3233
import os
3334
import socket
3435
import sys
35-
from typing import Any, Dict, Iterator, List, Optional, Set, Text, TYPE_CHECKING, Union
36+
from typing import Any, Dict, Iterator, List, Optional, Set, Text, Tuple, TYPE_CHECKING, Union
3637

3738
import attr
3839

@@ -79,6 +80,9 @@ class _Infer(enum.Enum):
7980
INFER_MIMETYPE: Literal[_Infer.INFER] = _Infer.INFER
8081
MimetypeT = Union[None, Literal[INFER_MIMETYPE], Text]
8182

83+
# MultiDim measurement failure code.
84+
MULTIDIM_FAIL = 'Multidim Measurement Failure'
85+
8286

8387
class BlankDutIdError(Exception):
8488
"""DUT serial cannot be blank at the end of a test."""
@@ -447,6 +451,45 @@ def finalize_from_phase_outcome(
447451
)
448452
self._finalize(test_record.Outcome.FAIL)
449453

454+
def _is_failed_multidim_measurement(self, meas: measurements.Measurement
455+
) -> bool:
456+
"""Returns whether the given value is a failed multidim measurement."""
457+
return bool(meas.outcome != measurements.Outcome.PASS and meas.dimensions)
458+
459+
def _get_failed_multidim_measurements(
460+
self,
461+
) -> List[Tuple[str, openhtf.core.measurements.Measurement]]:
462+
"""Gets all the failed phase multidim measurements in the test record.
463+
464+
Returns:
465+
a flat list containing tuples of (measurement_name, measurement) values.
466+
"""
467+
failed_phases = (
468+
phase
469+
for phase in self.test_record.phases
470+
if phase.outcome == test_record.PhaseOutcome.FAIL
471+
)
472+
phases_meas_items = (phase.measurements.items() for phase in failed_phases)
473+
flat_meas_items = itertools.chain.from_iterable(phases_meas_items)
474+
failed_multidim_meas = [
475+
meas_item
476+
for meas_item in flat_meas_items
477+
if self._is_failed_multidim_measurement(meas_item[1])
478+
]
479+
return failed_multidim_meas
480+
481+
def _add_multidim_outcome_details(self):
482+
"""Adds additional outcome details for failed multidim measurements."""
483+
failed_multidim_meas = self._get_failed_multidim_measurements()
484+
485+
for name, measurement in failed_multidim_meas:
486+
message = [f' failed_item: {name} ({measurement.outcome})']
487+
message.append(f' measured_value: {measurement.measured_value}')
488+
message.append(' validators:')
489+
for validator in measurement.validators:
490+
message.append(f' validator: {str(validator)}')
491+
self.test_record.add_outcome_details(MULTIDIM_FAIL, '\n'.join(message))
492+
450493
def finalize_normally(self) -> None:
451494
"""Mark the state as finished.
452495
@@ -461,21 +504,28 @@ def finalize_normally(self) -> None:
461504
# Vacuously PASS a TestRecord with no phases.
462505
self._finalize(test_record.Outcome.PASS)
463506
elif any(
464-
phase.outcome == test_record.PhaseOutcome.FAIL for phase in phases):
507+
phase.outcome == test_record.PhaseOutcome.FAIL for phase in phases
508+
):
509+
# Look for multidim failures to add to outcome details.
510+
self._add_multidim_outcome_details()
465511
# Any FAIL phase results in a test failure.
466512
self._finalize(test_record.Outcome.FAIL)
467513
elif all(
468-
phase.outcome == test_record.PhaseOutcome.SKIP for phase in phases):
514+
phase.outcome == test_record.PhaseOutcome.SKIP for phase in phases
515+
):
469516
# Error when all phases are skipped; otherwise, it could lead to
470517
# unintentional passes.
471518
self.state_logger.error('All phases were skipped, outcome ERROR.')
472519
self.test_record.add_outcome_details(
473-
'ALL_SKIPPED', 'All phases were unexpectedly skipped.')
520+
'ALL_SKIPPED', 'All phases were unexpectedly skipped.'
521+
)
474522
self._finalize(test_record.Outcome.ERROR)
475523
elif any(d.is_failure for d in self.test_record.diagnoses):
476524
self._finalize(test_record.Outcome.FAIL)
477-
elif any(s.outcome == test_record.SubtestOutcome.FAIL
478-
for s in self.test_record.subtests):
525+
elif any(
526+
s.outcome == test_record.SubtestOutcome.FAIL
527+
for s in self.test_record.subtests
528+
):
479529
self._finalize(test_record.Outcome.FAIL)
480530
else:
481531
# Otherwise, the test run was successful.
@@ -484,7 +534,8 @@ def finalize_normally(self) -> None:
484534

485535
self.state_logger.debug(
486536
'Finishing test execution normally with outcome %s.',
487-
self.test_record.outcome.name)
537+
self.test_record.outcome.name,
538+
)
488539

489540
def abort(self) -> None:
490541
if self._is_aborted():

0 commit comments

Comments
 (0)