# Copyright 2016 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Interactive feedback layer abstraction.""" from autotest_lib.client.common_lib import error # All known queries. # # Audio playback and recording testing. QUERY_AUDIO_PLAYBACK_SILENT = 0 QUERY_AUDIO_PLAYBACK_AUDIBLE = 1 QUERY_AUDIO_RECORDING = 2 # Motion sensor testing. QUERY_MOTION_RESTING = 10 QUERY_MOTION_MOVING = 11 # USB keyboard plugging and typing. QUERY_KEYBOARD_PLUG = 20 QUERY_KEYBOARD_TYPE = 21 # GPIO write/read testing. QUERY_GPIO_WRITE = 30 QUERY_GPIO_READ = 31 # On-board light testing. QUERY_LIGHT_ON = 40 # TODO(garnold) Camera controls testing. #QUERY_CAMERA_??? # Power management testing. QUERY_POWER_WAKEUP = 60 INPUT_QUERIES = set(( QUERY_AUDIO_RECORDING, QUERY_MOTION_RESTING, QUERY_MOTION_MOVING, QUERY_KEYBOARD_PLUG, QUERY_KEYBOARD_TYPE, QUERY_GPIO_READ, QUERY_POWER_WAKEUP, )) OUTPUT_QUERIES = set(( QUERY_AUDIO_PLAYBACK_SILENT, QUERY_AUDIO_PLAYBACK_AUDIBLE, QUERY_GPIO_WRITE, QUERY_LIGHT_ON, )) ALL_QUERIES = INPUT_QUERIES.union(OUTPUT_QUERIES) # Feedback client definition. # class Client(object): """Interface for an interactive feedback layer.""" def __init__(self): self._initialized = False self._finalized = False def _check_active(self): """Ensure that the client was initialized and not finalized.""" if not self._initialized: raise error.TestError('Client was not initialized') if self._finalized: raise error.TestError('Client was already finalized') def __enter__(self): self._check_active() return self def __exit__(self, ex_type, ex_val, ex_tb): self.finalize() def initialize(self, test, host=None): """Initializes the feedback object. This method should be called once prior to any other call. @param test: An object representing the test case. @param host: An object representing the DUT; required for server-side tests. @raise TestError: There was an error during initialization. """ if self._initialized: raise error.TestError('Client was already initialized') if self._finalized: raise error.TestError('Client was already finalized') self._initialize_impl(test, host) self._initialized = True return self def _initialize_impl(self, test, host): """Implementation of feedback client initialization. This should be implemented in concrete subclasses. """ raise NotImplementedError def new_query(self, query_id): """Instantiates a new query. @param query_id: A query identifier (see QUERY_ constants above). @return A query object. @raise TestError: Query is invalid or not supported. """ self._check_active() return self._new_query_impl(query_id) def _new_query_impl(self, query_id): """Implementation of new query instantiation. This should be implemented in concrete subclasses. """ raise NotImplementedError def finalize(self): """Finalizes the feedback object. This method should be called once when done using the client. @raise TestError: There was an error while finalizing the client. """ self._check_active() self._finalize_impl() self._finalized = True def _finalize_impl(self): """Implementation of feedback client finalization. This should be implemented in concrete subclasses. """ raise NotImplementedError # Feedback query definitions. # class _Query(object): """Interactive feedback query base class. This class is further derived and should not be inherited directly. """ def __init__(self): self._prepare_called = False self._validate_called = False def prepare(self, **kwargs): """Prepares the tester for providing or capturing feedback. @raise TestError: Query preparation failed. """ if self._prepare_called: raise error.TestError('Prepare was already called') self._prepare_impl(**kwargs) self._prepare_called = True def _prepare_impl(self, **kwargs): """Implementation of query preparation logic. This should be implemented in concrete subclasses. """ raise NotImplementedError def validate(self, **kwargs): """Validates the interactive input/output result. This enforces that the method is called at most once, then delegates to an underlying implementation method. @raise TestError: An error occurred during validation. @raise TestFail: Query validation failed. """ if self._validate_called: raise error.TestError('Validate was already called') self._validate_impl(**kwargs) self._validate_called = True def _validate_impl(self, **kwargs): """Implementation of query validation logic. This should be implemented in concrete subclasses. """ raise NotImplementedError class OutputQuery(_Query): """Interface for an output interactive feedback query. This class mandates that prepare() is called prior to validate(). Subclasses should override implementations of _prepare_impl() and _validate_impl(). """ def __init__(self): super(OutputQuery, self).__init__() def validate(self, **kwargs): """Validates the interactive input/output result. This enforces the precondition and delegates to the base method. @raise TestError: An error occurred during validation. @raise TestFail: Query validation failed. """ if not self._prepare_called: raise error.TestError('Prepare was not called') super(OutputQuery, self).validate(**kwargs) class InputQuery(_Query): """Interface for an input interactive feedback query. This class mandates that prepare() is called first, then emit(), and finally validate(). Subclasses should override implementations of _prepare_impl(), _emit_impl() and _validate_impl(). """ def __init__(self): super(InputQuery, self).__init__() self._emit_called = False def validate(self, **kwargs): """Validates the interactive input/output result. This enforces the precondition and delegates to the base method. @raise TestError: An error occurred during validation. @raise TestFail: Query validation failed. """ if not self._emit_called: raise error.TestError('Emit was not called') super(InputQuery, self).validate(**kwargs) def emit(self): """Instructs the tester to emit a feedback to be captured by the test. This enforces the precondition and ensures the method is called at most once, then delegates to an underlying implementation method. @raise TestError: An error occurred during emission. """ if not self._prepare_called: raise error.TestError('Prepare was not called') if self._emit_called: raise error.TestError('Emit was already called') self._emit_impl() self._emit_called = True def _emit_impl(self): """Implementation of query emission logic. This should be implemented in concrete subclasses. """ raise NotImplementedError