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