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