blob: 45571407ee1fad6bd8643280addef94334098003 [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
32
33# Feedback client definition.
34#
35class Client(object):
36 """Interface for an interactive feedback layer."""
37
38 def __init__(self):
39 self._initialized = False
40 self._finalized = False
41
42
43 def _check_active(self):
44 """Ensure that the client was initialized and not finalized."""
45 if not self._initialized:
46 raise error.TestError('Client was not initialized')
47 if self._finalized:
48 raise error.TestError('Client was already finalized')
49
50
51 def __enter__(self):
52 self._check_active()
53 return self
54
55
56 def __exit__(self, ex_type, ex_val, ex_tb):
57 self.finalize()
58
59
Gilad Arnoldb957e6c2016-01-12 17:22:50 -080060 def initialize(self, test, host=None):
Gilad Arnolde57b7e82015-12-08 10:26:13 -080061 """Initializes the feedback object.
62
63 This method should be called once prior to any other call.
64
65 @param test: An object representing the test case.
Gilad Arnoldb957e6c2016-01-12 17:22:50 -080066 @param host: An object representing the DUT; required for server-side
67 tests.
Gilad Arnolde57b7e82015-12-08 10:26:13 -080068
69 @raise TestError: There was an error during initialization.
70 """
71 if self._initialized:
72 raise error.TestError('Client was already initialized')
73 if self._finalized:
74 raise error.TestError('Client was already finalized')
75 self._initialize_impl(test, host)
76 self._initialized = True
77 return self
78
79
80 def _initialize_impl(self, test, host):
81 """Implementation of feedback client initialization.
82
83 This should be implemented in concrete subclasses.
84 """
85 raise NotImplementedError
86
87
88 def new_query(self, query_id):
89 """Instantiates a new query.
90
91 @param query_id: A query identifier (see QUERY_ constants above).
92
93 @return A query object.
94
95 @raise TestError: Query is invalid or not supported.
96 """
97 self._check_active()
98 return self._new_query_impl(query_id)
99
100
101 def _new_query_impl(self, query_id):
102 """Implementation of new query instantiation.
103
104 This should be implemented in concrete subclasses.
105 """
106 raise NotImplementedError
107
108
109 def finalize(self):
110 """Finalizes the feedback object.
111
112 This method should be called once when done using the client.
113
114 @raise TestError: There was an error while finalizing the client.
115 """
116 self._check_active()
117 self._finalize_impl()
118 self._finalized = True
119
120
121 def _finalize_impl(self):
122 """Implementation of feedback client finalization.
123
124 This should be implemented in concrete subclasses.
125 """
126 raise NotImplementedError
127
128
129# Feedback query definitions.
130#
131class _Query(object):
132 """Interactive feedback query base class.
133
134 This class is further derived and should not be inherited directly.
135 """
136
137 def __init__(self):
138 self._prepare_called = False
139 self._validate_called = False
140
141
142 def prepare(self, **kwargs):
143 """Prepares the tester for providing or capturing feedback.
144
145 @raise TestError: Query preparation failed.
146 """
147 if self._prepare_called:
148 raise error.TestError('Prepare was already called')
149 self._prepare_impl(**kwargs)
150 self._prepare_called = True
151
152
153 def _prepare_impl(self, **kwargs):
154 """Implementation of query preparation logic.
155
156 This should be implemented in concrete subclasses.
157 """
158 raise NotImplementedError
159
160
161 def validate(self, **kwargs):
162 """Validates the interactive input/output result.
163
164 This enforces that the method is called at most once, then delegates
165 to an underlying implementation method.
166
167 @raise TestError: An error occurred during validation.
168 @raise TestFail: Query validation failed.
169 """
170 if self._validate_called:
171 raise error.TestError('Validate was already called')
172 self._validate_impl(**kwargs)
173 self._validate_called = True
174
175
176 def _validate_impl(self, **kwargs):
177 """Implementation of query validation logic.
178
179 This should be implemented in concrete subclasses.
180 """
181 raise NotImplementedError
182
183
184class OutputQuery(_Query):
185 """Interface for an output interactive feedback query.
186
187 This class mandates that prepare() is called prior to validate().
188 Subclasses should override implementations of _prepare_impl() and
189 _validate_impl().
190 """
191
192 def __init__(self):
193 super(OutputQuery, self).__init__()
194
195
196 def validate(self, **kwargs):
197 """Validates the interactive input/output result.
198
199 This enforces the precondition and delegates to the base method.
200
201 @raise TestError: An error occurred during validation.
202 @raise TestFail: Query validation failed.
203 """
204 if not self._prepare_called:
205 raise error.TestError('Prepare was not called')
206 super(OutputQuery, self).validate(**kwargs)
207
208
209class InputQuery(_Query):
210 """Interface for an input interactive feedback query.
211
212 This class mandates that prepare() is called first, then emit(), and
213 finally validate(). Subclasses should override implementations of
214 _prepare_impl(), _emit_impl() and _validate_impl().
215 """
216
217 def __init__(self):
218 super(InputQuery, self).__init__()
219 self._emit_called = False
220
221
222 def validate(self, **kwargs):
223 """Validates the interactive input/output result.
224
225 This enforces the precondition and delegates to the base method.
226
227 @raise TestError: An error occurred during validation.
228 @raise TestFail: Query validation failed.
229 """
230 if not self._emit_called:
231 raise error.TestError('Emit was not called')
232 super(InputQuery, self).validate(**kwargs)
233
234
235 def emit(self):
236 """Instructs the tester to emit a feedback to be captured by the test.
237
238 This enforces the precondition and ensures the method is called at most
239 once, then delegates to an underlying implementation method.
240
241 @raise TestError: An error occurred during emission.
242 """
243 if not self._prepare_called:
244 raise error.TestError('Prepare was not called')
245 if self._emit_called:
246 raise error.TestError('Emit was already called')
247 self._emit_impl()
248 self._emit_called = True
249
250
251 def _emit_impl(self):
252 """Implementation of query emission logic.
253
254 This should be implemented in concrete subclasses.
255 """
256 raise NotImplementedError