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