Home | History | Annotate | Download | only in cros
      1 # Copyright (c) 2014 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 import logging
      6 import os
      7 import time
      8 
      9 from autotest_lib.client.bin import test
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import error
     12 from autotest_lib.client.cros.input_playback import input_playback
     13 
     14 
     15 class touch_playback_test_base(test.test):
     16     """Base class for touch tests involving playback."""
     17     version = 1
     18 
     19     _INPUTCONTROL = '/opt/google/input/inputcontrol'
     20     _DEFAULT_SCROLL = 5000
     21 
     22 
     23     @property
     24     def _has_touchpad(self):
     25         """True if device under test has a touchpad; else False."""
     26         return self.player.has('touchpad')
     27 
     28 
     29     @property
     30     def _has_touchscreen(self):
     31         """True if device under test has a touchscreen; else False."""
     32         return self.player.has('touchscreen')
     33 
     34 
     35     @property
     36     def _has_mouse(self):
     37         """True if device under test has or emulates a USB mouse; else False."""
     38         return self.player.has('mouse')
     39 
     40 
     41     def warmup(self, mouse_props=None):
     42         """Test setup.
     43 
     44         Instantiate player object to find touch devices, if any.
     45         These devices can be used for playback later.
     46         Emulate a USB mouse if a property file is provided.
     47         Check if the inputcontrol script is avaiable on the disk.
     48 
     49         @param mouse_props: optional property file for a mouse to emulate.
     50                             Created using 'evemu-describe /dev/input/X'.
     51 
     52         """
     53         self.player = input_playback.InputPlayback()
     54         if mouse_props:
     55             self.player.emulate(input_type='mouse', property_file=mouse_props)
     56         self.player.find_connected_inputs()
     57 
     58         self._autotest_ext = None
     59         self._has_inputcontrol = os.path.isfile(self._INPUTCONTROL)
     60         self._platform = utils.get_board()
     61 
     62 
     63     def _find_test_files(self, input_type, gestures):
     64         """Determine where the test files are.
     65 
     66         Expected file format is: <boardname>_<input type>_<hwid>_<gesture name>
     67             e.g. samus_touchpad_164.17_scroll_down
     68 
     69         @param input_type: device type, e.g. 'touchpad'
     70         @param gestures: list of gesture name strings used in filename
     71 
     72         @returns: None if not all files are found.  Dictionary of filepaths if
     73                   they are found, indexed by gesture names as given.
     74         @raises: error.TestError if no hw_id is found.
     75 
     76         """
     77         hw_id = self.player.devices[input_type].hw_id
     78         if not hw_id:
     79             raise error.TestError('No valid hw_id for this %s!' % input_type)
     80 
     81         filepaths = {}
     82         gesture_dir = os.path.join(self.bindir, 'gestures')
     83         for gesture in gestures:
     84             filename = '%s_%s_%s_%s' % (self._platform, input_type, hw_id,
     85                                         gesture)
     86             filepath = os.path.join(gesture_dir, filename)
     87             if not os.path.exists(filepath):
     88                 logging.info('Did not find %s!', filepath)
     89                 return None
     90             filepaths[gesture] = filepath
     91 
     92         return filepaths
     93 
     94 
     95     def _find_test_files_from_directions(self, input_type, fmt_str, directions):
     96         """Find test files given a list of directions and gesture name format.
     97 
     98         @param input_type: device type, e.g. 'touchpad'
     99         @param fmt_str: format string for filename, e.g. 'scroll-%s'
    100         @param directions: list of directions for fmt_string
    101 
    102         @returns: None if not all files are found.  Dictionary of filepaths if
    103                   they are found, indexed by directions as given.
    104         @raises: error.TestError if no hw_id is found.
    105 
    106         """
    107         gestures = [fmt_str % d for d in directions]
    108         temp_filepaths = self._find_test_files(input_type, gestures)
    109 
    110         filepaths = {}
    111         if temp_filepaths:
    112             filepaths = {d: temp_filepaths[fmt_str % d] for d in directions}
    113 
    114         return filepaths
    115 
    116 
    117     def _emulate_mouse(self, property_file=None):
    118         """Emulate a mouse with the given property file.
    119 
    120         player will use default mouse if no file is provided.
    121 
    122         """
    123         self.player.emulate(input_type='mouse', property_file=property_file)
    124         self.player.find_connected_inputs()
    125         if not self._has_mouse:
    126             raise error.TestError('Mouse emulation failed!')
    127 
    128 
    129     def _playback(self, filepath, touch_type='touchpad'):
    130         """Playback a given input file on the given input."""
    131         self.player.playback(filepath, touch_type)
    132 
    133 
    134     def _blocking_playback(self, filepath, touch_type='touchpad'):
    135         """Playback a given input file on the given input; block until done."""
    136         self.player.blocking_playback(filepath, touch_type)
    137 
    138 
    139     def _set_touch_setting_by_inputcontrol(self, setting, value):
    140         """Set a given touch setting the given value by inputcontrol.
    141 
    142         @param setting: Name of touch setting, e.g. 'tapclick'.
    143         @param value: True for enabled, False for disabled.
    144 
    145         """
    146         cmd_value = 1 if value else 0
    147         utils.run('%s --%s %d' % (self._INPUTCONTROL, setting, cmd_value))
    148         logging.info('%s turned %s.', setting, 'on' if value else 'off')
    149 
    150 
    151     def _set_touch_setting(self, inputcontrol_setting, autotest_ext_setting,
    152                            value):
    153         """Set a given touch setting the given value.
    154 
    155         @param inputcontrol_setting: Name of touch setting for the inputcontrol
    156                                      script, e.g. 'tapclick'.
    157         @param autotest_ext_setting: Name of touch setting for the autotest
    158                                      extension, e.g. 'TapToClick'.
    159         @param value: True for enabled, False for disabled.
    160 
    161         """
    162         if self._has_inputcontrol:
    163             self._set_touch_setting_by_inputcontrol(inputcontrol_setting, value)
    164         elif self._autotest_ext is not None:
    165             self._autotest_ext.EvaluateJavaScript(
    166                     'chrome.autotestPrivate.set%s(%s);'
    167                     % (autotest_ext_setting, ("%s" % value).lower()))
    168             # TODO: remove this sleep once checking for value is available.
    169             time.sleep(1)
    170         else:
    171             raise error.TestFail('Both inputcontrol and the autotest '
    172                                  'extension are not availble.')
    173 
    174 
    175     def _set_australian_scrolling(self, value):
    176         """Set australian scrolling to the given value.
    177 
    178         @param value: True for enabled, False for disabled.
    179 
    180         """
    181         self._set_touch_setting('australian_scrolling', 'NaturalScroll', value)
    182 
    183 
    184     def _set_tap_to_click(self, value):
    185         """Set tap-to-click to the given value.
    186 
    187         @param value: True for enabled, False for disabled.
    188 
    189         """
    190         self._set_touch_setting('tapclick', 'TapToClick', value)
    191 
    192 
    193     def _set_tap_dragging(self, value):
    194         """Set tap dragging to the given value.
    195 
    196         @param value: True for enabled, False for disabled.
    197 
    198         """
    199         self._set_touch_setting('tapdrag', 'TapDragging', value)
    200 
    201 
    202     def _reload_page(self):
    203         """Reloads test page.  Presuposes self._tab.
    204 
    205         @raise: TestError if page is not reset.
    206 
    207         """
    208         self._tab.Navigate(self._tab.url)
    209         self._wait_for_page_ready()
    210 
    211 
    212     def _set_autotest_ext(self, ext):
    213         """Set the autotest extension.
    214 
    215         @ext: the autotest extension object.
    216 
    217         """
    218         self._autotest_ext = ext
    219 
    220 
    221     def _open_test_page(self, cr, filename='test_page.html'):
    222         """Prepare test page for testing.  Set self._tab with page.
    223 
    224         @param cr: chrome.Chrome() object
    225         @param filename: name of file in self.bindir to open
    226 
    227         """
    228         cr.browser.platform.SetHTTPServerDirectories(self.bindir)
    229         self._tab = cr.browser.tabs[0]
    230         self._tab.Navigate(cr.browser.platform.http_server.UrlOf(
    231                 os.path.join(self.bindir, filename)))
    232         self._wait_for_page_ready()
    233 
    234 
    235     def _wait_for_page_ready(self):
    236         """Wait for a variable pageReady on the test page to be true.
    237 
    238         Presuposes self._tab and a pageReady variable.
    239 
    240         @raises error.TestError if page is not ready after timeout.
    241 
    242         """
    243         self._tab.WaitForDocumentReadyStateToBeComplete()
    244         utils.poll_for_condition(
    245                 lambda: self._tab.EvaluateJavaScript('pageReady'),
    246                 exception=error.TestError('Test page is not ready!'))
    247 
    248 
    249     def _center_cursor(self):
    250         """Playback mouse movement to center cursor.
    251 
    252         Requres that self._emulate_mouse() has been called.
    253 
    254         """
    255         self.player.blocking_playback_of_default_file(
    256                 'mouse_center_cursor_gesture', input_type='mouse')
    257 
    258 
    259     def _set_scroll(self, value, scroll_vertical=True):
    260         """Set scroll position to given value.  Presuposes self._tab.
    261 
    262         @param scroll_vertical: True for vertical scroll,
    263                                 False for horizontal Scroll.
    264         @param value: True for enabled, False for disabled.
    265 
    266          """
    267         if scroll_vertical:
    268             self._tab.ExecuteJavaScript(
    269                 'document.body.scrollTop=%s' % value)
    270         else:
    271             self._tab.ExecuteJavaScript(
    272                 'document.body.scrollLeft=%s' % value)
    273 
    274 
    275     def _set_default_scroll_position(self, scroll_vertical=True):
    276         """Set scroll position of page to default.  Presuposes self._tab.
    277 
    278         @param scroll_vertical: True for vertical scroll,
    279                                 False for horizontal Scroll.
    280         @raise: TestError if page is not set to default scroll position
    281 
    282         """
    283         total_tries = 2
    284         for i in xrange(total_tries):
    285             try:
    286                 self._set_scroll(self._DEFAULT_SCROLL, scroll_vertical)
    287                 self._wait_for_default_scroll_position(scroll_vertical)
    288             except error.TestError as e:
    289                 if i == total_tries - 1:
    290                    pos = self._get_scroll_position(scroll_vertical)
    291                    logging.error('SCROLL POSITION: %s', pos)
    292                    raise e
    293             else:
    294                  break
    295 
    296 
    297     def _get_scroll_position(self, scroll_vertical=True):
    298         """Return current scroll position of page.  Presuposes self._tab.
    299 
    300         @param scroll_vertical: True for vertical scroll,
    301                                 False for horizontal Scroll.
    302 
    303         """
    304         if scroll_vertical:
    305             return int(self._tab.EvaluateJavaScript('document.body.scrollTop'))
    306         else:
    307             return int(self._tab.EvaluateJavaScript('document.body.scrollLeft'))
    308 
    309 
    310     def _wait_for_default_scroll_position(self, scroll_vertical=True):
    311         """Wait for page to be at the default scroll position.
    312 
    313         @param scroll_vertical: True for vertical scroll,
    314                                 False for horizontal scroll.
    315 
    316         @raise: TestError if page either does not move or does not stop moving.
    317 
    318         """
    319         utils.poll_for_condition(
    320                 lambda: self._get_scroll_position(
    321                         scroll_vertical) == self._DEFAULT_SCROLL,
    322                 exception=error.TestError('Page not set to default scroll!'))
    323 
    324 
    325     def _wait_for_scroll_position_to_settle(self, scroll_vertical=True):
    326         """Wait for page to move and then stop moving.
    327 
    328         @param scroll_vertical: True for Vertical scroll and
    329                                 False for horizontal scroll.
    330 
    331         @raise: TestError if page either does not move or does not stop moving.
    332 
    333         """
    334         # Wait until page starts moving.
    335         utils.poll_for_condition(
    336                 lambda: self._get_scroll_position(
    337                         scroll_vertical) != self._DEFAULT_SCROLL,
    338                 exception=error.TestError('No scrolling occurred!'), timeout=30)
    339 
    340         # Wait until page has stopped moving.
    341         self._previous = self._DEFAULT_SCROLL
    342         def _movement_stopped():
    343             current = self._get_scroll_position()
    344             result = current == self._previous
    345             self._previous = current
    346             return result
    347 
    348         utils.poll_for_condition(
    349                 lambda: _movement_stopped(), sleep_interval=1,
    350                 exception=error.TestError('Page did not stop moving!'),
    351                 timeout=30)
    352 
    353 
    354     def cleanup(self):
    355         self.player.close()
    356