27
27
import copy
28
28
import enum
29
29
import functools
30
+ import itertools
30
31
import logging
31
32
import mimetypes
32
33
import os
33
34
import socket
34
35
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
36
37
37
38
import attr
38
39
@@ -79,6 +80,9 @@ class _Infer(enum.Enum):
79
80
INFER_MIMETYPE : Literal [_Infer .INFER ] = _Infer .INFER
80
81
MimetypeT = Union [None , Literal [INFER_MIMETYPE ], Text ]
81
82
83
+ # MultiDim measurement failure code.
84
+ MULTIDIM_FAIL = 'Multidim Measurement Failure'
85
+
82
86
83
87
class BlankDutIdError (Exception ):
84
88
"""DUT serial cannot be blank at the end of a test."""
@@ -447,6 +451,45 @@ def finalize_from_phase_outcome(
447
451
)
448
452
self ._finalize (test_record .Outcome .FAIL )
449
453
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
+
450
493
def finalize_normally (self ) -> None :
451
494
"""Mark the state as finished.
452
495
@@ -461,21 +504,28 @@ def finalize_normally(self) -> None:
461
504
# Vacuously PASS a TestRecord with no phases.
462
505
self ._finalize (test_record .Outcome .PASS )
463
506
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 ()
465
511
# Any FAIL phase results in a test failure.
466
512
self ._finalize (test_record .Outcome .FAIL )
467
513
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
+ ):
469
516
# Error when all phases are skipped; otherwise, it could lead to
470
517
# unintentional passes.
471
518
self .state_logger .error ('All phases were skipped, outcome ERROR.' )
472
519
self .test_record .add_outcome_details (
473
- 'ALL_SKIPPED' , 'All phases were unexpectedly skipped.' )
520
+ 'ALL_SKIPPED' , 'All phases were unexpectedly skipped.'
521
+ )
474
522
self ._finalize (test_record .Outcome .ERROR )
475
523
elif any (d .is_failure for d in self .test_record .diagnoses ):
476
524
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
+ ):
479
529
self ._finalize (test_record .Outcome .FAIL )
480
530
else :
481
531
# Otherwise, the test run was successful.
@@ -484,7 +534,8 @@ def finalize_normally(self) -> None:
484
534
485
535
self .state_logger .debug (
486
536
'Finishing test execution normally with outcome %s.' ,
487
- self .test_record .outcome .name )
537
+ self .test_record .outcome .name ,
538
+ )
488
539
489
540
def abort (self ) -> None :
490
541
if self ._is_aborted ():
0 commit comments