Home | History | Annotate | Download | only in feedback
      1 # Copyright 2016 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Interactive feedback layer abstraction."""
      6 
      7 from autotest_lib.client.common_lib import error
      8 
      9 
     10 # All known queries.
     11 #
     12 # Audio playback and recording testing.
     13 QUERY_AUDIO_PLAYBACK_SILENT = 0
     14 QUERY_AUDIO_PLAYBACK_AUDIBLE = 1
     15 QUERY_AUDIO_RECORDING = 2
     16 # Motion sensor testing.
     17 QUERY_MOTION_RESTING = 10
     18 QUERY_MOTION_MOVING = 11
     19 # USB keyboard plugging and typing.
     20 QUERY_KEYBOARD_PLUG = 20
     21 QUERY_KEYBOARD_TYPE = 21
     22 # GPIO write/read testing.
     23 QUERY_GPIO_WRITE = 30
     24 QUERY_GPIO_READ = 31
     25 # On-board light testing.
     26 QUERY_LIGHT_ON = 40
     27 # TODO(garnold) Camera controls testing.
     28 #QUERY_CAMERA_???
     29 # Power management testing.
     30 QUERY_POWER_WAKEUP = 60
     31 
     32 INPUT_QUERIES = set((
     33         QUERY_AUDIO_RECORDING,
     34         QUERY_MOTION_RESTING,
     35         QUERY_MOTION_MOVING,
     36         QUERY_KEYBOARD_PLUG,
     37         QUERY_KEYBOARD_TYPE,
     38         QUERY_GPIO_READ,
     39         QUERY_POWER_WAKEUP,
     40 ))
     41 
     42 OUTPUT_QUERIES = set((
     43         QUERY_AUDIO_PLAYBACK_SILENT,
     44         QUERY_AUDIO_PLAYBACK_AUDIBLE,
     45         QUERY_GPIO_WRITE,
     46         QUERY_LIGHT_ON,
     47 ))
     48 
     49 ALL_QUERIES = INPUT_QUERIES.union(OUTPUT_QUERIES)
     50 
     51 
     52 # Feedback client definition.
     53 #
     54 class Client(object):
     55     """Interface for an interactive feedback layer."""
     56 
     57     def __init__(self):
     58         self._initialized = False
     59         self._finalized = False
     60 
     61 
     62     def _check_active(self):
     63         """Ensure that the client was initialized and not finalized."""
     64         if not self._initialized:
     65             raise error.TestError('Client was not initialized')
     66         if self._finalized:
     67             raise error.TestError('Client was already finalized')
     68 
     69 
     70     def __enter__(self):
     71         self._check_active()
     72         return self
     73 
     74 
     75     def __exit__(self, ex_type, ex_val, ex_tb):
     76         self.finalize()
     77 
     78 
     79     def initialize(self, test, host=None):
     80         """Initializes the feedback object.
     81 
     82         This method should be called once prior to any other call.
     83 
     84         @param test: An object representing the test case.
     85         @param host: An object representing the DUT; required for server-side
     86                      tests.
     87 
     88         @raise TestError: There was an error during initialization.
     89         """
     90         if self._initialized:
     91             raise error.TestError('Client was already initialized')
     92         if self._finalized:
     93             raise error.TestError('Client was already finalized')
     94         self._initialize_impl(test, host)
     95         self._initialized = True
     96         return self
     97 
     98 
     99     def _initialize_impl(self, test, host):
    100         """Implementation of feedback client initialization.
    101 
    102         This should be implemented in concrete subclasses.
    103         """
    104         raise NotImplementedError
    105 
    106 
    107     def new_query(self, query_id):
    108         """Instantiates a new query.
    109 
    110         @param query_id: A query identifier (see QUERY_ constants above).
    111 
    112         @return A query object.
    113 
    114         @raise TestError: Query is invalid or not supported.
    115         """
    116         self._check_active()
    117         return self._new_query_impl(query_id)
    118 
    119 
    120     def _new_query_impl(self, query_id):
    121         """Implementation of new query instantiation.
    122 
    123         This should be implemented in concrete subclasses.
    124         """
    125         raise NotImplementedError
    126 
    127 
    128     def finalize(self):
    129         """Finalizes the feedback object.
    130 
    131         This method should be called once when done using the client.
    132 
    133         @raise TestError: There was an error while finalizing the client.
    134         """
    135         self._check_active()
    136         self._finalize_impl()
    137         self._finalized = True
    138 
    139 
    140     def _finalize_impl(self):
    141         """Implementation of feedback client finalization.
    142 
    143         This should be implemented in concrete subclasses.
    144         """
    145         raise NotImplementedError
    146 
    147 
    148 # Feedback query definitions.
    149 #
    150 class _Query(object):
    151     """Interactive feedback query base class.
    152 
    153     This class is further derived and should not be inherited directly.
    154     """
    155 
    156     def __init__(self):
    157         self._prepare_called = False
    158         self._validate_called = False
    159 
    160 
    161     def prepare(self, **kwargs):
    162         """Prepares the tester for providing or capturing feedback.
    163 
    164         @raise TestError: Query preparation failed.
    165         """
    166         if self._prepare_called:
    167             raise error.TestError('Prepare was already called')
    168         self._prepare_impl(**kwargs)
    169         self._prepare_called = True
    170 
    171 
    172     def _prepare_impl(self, **kwargs):
    173         """Implementation of query preparation logic.
    174 
    175         This should be implemented in concrete subclasses.
    176         """
    177         raise NotImplementedError
    178 
    179 
    180     def validate(self, **kwargs):
    181         """Validates the interactive input/output result.
    182 
    183         This enforces that the method is called at most once, then delegates
    184         to an underlying implementation method.
    185 
    186         @raise TestError: An error occurred during validation.
    187         @raise TestFail: Query validation failed.
    188         """
    189         if self._validate_called:
    190             raise error.TestError('Validate was already called')
    191         self._validate_impl(**kwargs)
    192         self._validate_called = True
    193 
    194 
    195     def _validate_impl(self, **kwargs):
    196         """Implementation of query validation logic.
    197 
    198         This should be implemented in concrete subclasses.
    199         """
    200         raise NotImplementedError
    201 
    202 
    203 class OutputQuery(_Query):
    204     """Interface for an output interactive feedback query.
    205 
    206     This class mandates that prepare() is called prior to validate().
    207     Subclasses should override implementations of _prepare_impl() and
    208     _validate_impl().
    209     """
    210 
    211     def __init__(self):
    212         super(OutputQuery, self).__init__()
    213 
    214 
    215     def validate(self, **kwargs):
    216         """Validates the interactive input/output result.
    217 
    218         This enforces the precondition and delegates to the base method.
    219 
    220         @raise TestError: An error occurred during validation.
    221         @raise TestFail: Query validation failed.
    222         """
    223         if not self._prepare_called:
    224             raise error.TestError('Prepare was not called')
    225         super(OutputQuery, self).validate(**kwargs)
    226 
    227 
    228 class InputQuery(_Query):
    229     """Interface for an input interactive feedback query.
    230 
    231     This class mandates that prepare() is called first, then emit(), and
    232     finally validate(). Subclasses should override implementations of
    233     _prepare_impl(), _emit_impl() and _validate_impl().
    234     """
    235 
    236     def __init__(self):
    237         super(InputQuery, self).__init__()
    238         self._emit_called = False
    239 
    240 
    241     def validate(self, **kwargs):
    242         """Validates the interactive input/output result.
    243 
    244         This enforces the precondition and delegates to the base method.
    245 
    246         @raise TestError: An error occurred during validation.
    247         @raise TestFail: Query validation failed.
    248         """
    249         if not self._emit_called:
    250             raise error.TestError('Emit was not called')
    251         super(InputQuery, self).validate(**kwargs)
    252 
    253 
    254     def emit(self):
    255         """Instructs the tester to emit a feedback to be captured by the test.
    256 
    257         This enforces the precondition and ensures the method is called at most
    258         once, then delegates to an underlying implementation method.
    259 
    260         @raise TestError: An error occurred during emission.
    261         """
    262         if not self._prepare_called:
    263             raise error.TestError('Prepare was not called')
    264         if self._emit_called:
    265             raise error.TestError('Emit was already called')
    266         self._emit_impl()
    267         self._emit_called = True
    268 
    269 
    270     def _emit_impl(self):
    271         """Implementation of query emission logic.
    272 
    273         This should be implemented in concrete subclasses.
    274         """
    275         raise NotImplementedError
    276