blob: 9a0a4e361e74a23b1d2ede0338133cf1898e3240 [file] [log] [blame]
Gilad Arnolde57b7e82015-12-08 10:26:13 -08001# 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
7from autotest_lib.client.common_lib import error
8
9
10# All known queries.
11#
12# Audio playback and recording testing.
13QUERY_AUDIO_PLAYBACK_SILENT = 0
14QUERY_AUDIO_PLAYBACK_AUDIBLE = 1
15QUERY_AUDIO_RECORDING = 2
16# Motion sensor testing.
17QUERY_MOTION_RESTING = 10
18QUERY_MOTION_MOVING = 11
19# USB keyboard plugging and typing.
20QUERY_KEYBOARD_PLUG = 20
21QUERY_KEYBOARD_TYPE = 21
22# GPIO write/read testing.
23QUERY_GPIO_WRITE = 30
24QUERY_GPIO_READ = 31
25# On-board light testing.
26QUERY_LIGHT_ON = 40
27# TODO(garnold) Camera controls testing.
28#QUERY_CAMERA_???
29# Power management testing.
30QUERY_POWER_WAKEUP = 60
31
Gilad Arnoldf3110622016-01-15 16:14:47 -080032INPUT_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
42OUTPUT_QUERIES = set((
43 QUERY_AUDIO_PLAYBACK_SILENT,
44 QUERY_AUDIO_PLAYBACK_AUDIBLE,
45 QUERY_GPIO_WRITE,
46 QUERY_LIGHT_ON,
47))
48
49ALL_QUERIES = INPUT_QUERIES.union(OUTPUT_QUERIES)
50
Gilad Arnolde57b7e82015-12-08 10:26:13 -080051
52# Feedback client definition.
53#
54class 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
Gilad Arnoldb957e6c2016-01-12 17:22:50 -080079 def initialize(self, test, host=None):
Gilad Arnolde57b7e82015-12-08 10:26:13 -080080 """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.
Gilad Arnoldb957e6c2016-01-12 17:22:50 -080085 @param host: An object representing the DUT; required for server-side
86 tests.
Gilad Arnolde57b7e82015-12-08 10:26:13 -080087
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#
150class _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
203class 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
228class 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