1# Copyright (c) 2012 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 5import logging 6import os 7import pprint 8import shutil 9import subprocess 10import sys 11import time 12from autotest_lib.client.bin import test, utils 13from autotest_lib.client.common_lib import error 14from autotest_lib.client.common_lib.cros import chrome 15from autotest_lib.client.cros import constants, cros_logging 16 17# The name of the Chrome OS Pepper Flash binary. 18_BINARY = 'libpepflashplayer.so' 19# The path to the system provided (read only) Flash binary. 20_SYSTEM_STORE = '/opt/google/chrome/pepper' 21# The name of the file containing metainformation for the system binary. 22_FLASH_INFO = 'pepper-flash.info' 23# The name of the component updated manifest describing version, OS, 24# architecture and required ppapi interfaces. 25_MANIFEST = 'manifest.json' 26# The tmp location Chrome downloads the bits from Omaha to. 27_DOWNLOAD_STORE = '/home/chronos/PepperFlash' 28# The location the CrOS component updater stores new images in. 29_COMPONENT_STORE = '/var/lib/imageloader/PepperFlashPlayer' 30# latest-version gets updated after the library in the store. We use it to 31# check for completion of download. 32_COMPONENT_STORE_LATEST = _COMPONENT_STORE + '/latest-version' 33# The location at which the latest component updated Flash binary is mounted 34# for execution. 35_COMPONENT_MOUNT = '/run/imageloader/PepperFlashPlayer' 36# Set of all possible paths at which Flash binary could be found. 37_FLASH_PATHS = { 38 _SYSTEM_STORE, _DOWNLOAD_STORE, _COMPONENT_STORE, _COMPONENT_MOUNT} 39 40# Run the traditional Flash sanity check (just check that any Flash works). 41_CU_ACTION_SANITY = 'sanity' 42# Clean out all component update state (in preparation to next update). 43_CU_ACTION_DELETE = 'delete-component' 44# TODO(ihf): Implement this action to simulated component on component update. 45_CU_ACTION_INSTALL_OLD = 'install-old-component' 46# Download the latest available component from Omaha. 47_CU_ACTION_DOWNLOAD = 'download-omaha-component' 48# Using current state of DUT verify the Flash in _COMPONENT_MOUNT. 49_CU_ACTION_VERIFY_COMPONENT = 'verify-component-flash' 50# Using current state of DUT verify the Flash shipping with the system image. 51_CU_ACTION_VERIFY_SYSTEM = 'verify-system-flash' 52 53 54class desktopui_FlashSanityCheck(test.test): 55 """ 56 Sanity test that ensures flash instance is launched when a swf is played. 57 """ 58 version = 4 59 60 _messages_log_reader = None 61 _ui_log_reader = None 62 _test_url = None 63 _testServer = None 64 _time_to_wait_secs = 5 65 _swf_runtime = 5 66 _retries = 10 67 _component_download_timeout_secs = 300 68 69 def verify_file(self, name): 70 """ 71 Does sanity checks on a file on disk. 72 73 @param name: filename to verify. 74 """ 75 if not os.path.exists(name): 76 raise error.TestFail('Failed: File does not exist %s' % name) 77 if not os.path.isfile(name): 78 raise error.TestFail('Failed: Not a file %s' % name) 79 if os.path.getsize(name) <= 0: 80 raise error.TestFail('Failed: File is too short %s' % name) 81 if name.endswith('libpepflashplayer.so'): 82 output = subprocess.check_output(['file %s' % name], shell=True) 83 if not 'stripped' in output: 84 logging.error(output) 85 raise error.TestFail('Failed: Flash binary not stripped.') 86 if not 'dynamically linked' in output: 87 logging.error(output) 88 raise error.TestFail('Failed: Flash not dynamically linked.') 89 arch = utils.get_arch_userspace() 90 logging.info('get_arch_userspace = %s', arch) 91 if arch == 'arm' and not 'ARM' in output: 92 logging.error(output) 93 raise error.TestFail('Failed: Flash binary not for ARM.') 94 if arch == 'x86_64' and not 'x86-64' in output: 95 logging.error(output) 96 raise error.TestFail('Failed: Flash binary not for x86_64.') 97 if arch == 'i386' and not '80386' in output: 98 logging.error(output) 99 raise error.TestFail('Failed: Flash binary not for i386.') 100 logging.info('Verified file %s', name) 101 102 def serve_swf_to_browser(self, browser): 103 """ 104 Tries to serve a sample swf to browser. 105 106 A failure of this function does not imply a problem with Flash. 107 @param browser: The Browser object to run the test with. 108 @return: True if we managed to send swf to browser, False otherwise. 109 """ 110 # Prepare index.html/Trivial.swf to be served. 111 browser.platform.SetHTTPServerDirectories(self.bindir) 112 test_url = browser.platform.http_server.UrlOf(os.path.join(self.bindir, 113 'index.html')) 114 tab = None 115 # BUG(485108): Work around a telemetry timing out after login. 116 try: 117 logging.info('Getting tab from telemetry...') 118 tab = browser.tabs[0] 119 except: 120 logging.warning('Unexpected exception getting tab: %s', 121 pprint.pformat(sys.exc_info()[0])) 122 if tab is None: 123 return False 124 125 logging.info('Initialize reading system logs.') 126 self._messages_log_reader = cros_logging.LogReader() 127 self._messages_log_reader.set_start_by_current() 128 self._ui_log_reader = cros_logging.LogReader('/var/log/ui/ui.LATEST') 129 self._ui_log_reader.set_start_by_current() 130 logging.info('Done initializing system logs.') 131 132 # Verify that the swf got pulled. 133 try: 134 tab.Navigate(test_url) 135 tab.WaitForDocumentReadyStateToBeComplete() 136 return True 137 except: 138 logging.warning('Unexpected exception waiting for document: %s', 139 pprint.pformat(sys.exc_info()[0])) 140 return False 141 142 143 def verify_flash_process(self, load_path=None): 144 """Verifies the Flash process runs and doesn't crash. 145 146 @param load_path: The expected path of the Flash binary. If set 147 function and Flash was loaded from a different path, 148 function will fail the test. 149 """ 150 logging.info('Waiting for Pepper process.') 151 # Verify that we see a ppapi process and assume it is Flash. 152 ppapi = utils.wait_for_value_changed( 153 lambda: (utils.get_process_list('chrome', '--type=ppapi')), 154 old_value=[], 155 timeout_sec=self._time_to_wait_secs) 156 logging.info('ppapi process list at start: %s', ', '.join(ppapi)) 157 if not ppapi: 158 msg = 'flash/platform/pepper/pep_' 159 if not self._ui_log_reader.can_find(msg): 160 raise error.TestFail( 161 'Failed: Flash did not start (logs) and no ppapi process ' 162 'found.' 163 ) 164 # There is a chrome bug where the command line of the ppapi and 165 # other processes is shown as "type=zygote". Bail out if we see more 166 # than 2. Notice, we already did the waiting, so there is no need to 167 # do more of it. 168 zygote = utils.get_process_list('chrome', '--type=zygote') 169 if len(zygote) > 2: 170 logging.warning('Flash probably launched by Chrome as zygote: ' 171 '<%s>.', ', '.join(zygote)) 172 173 # We have a ppapi process. Let it run for a little and see if it is 174 # still alive. 175 logging.info('Running Flash content for a little while.') 176 time.sleep(self._swf_runtime) 177 logging.info('Verifying the Pepper process is still around.') 178 ppapi = utils.wait_for_value_changed( 179 lambda: (utils.get_process_list('chrome', '--type=ppapi')), 180 old_value=[], 181 timeout_sec=self._time_to_wait_secs) 182 # Notice that we are not checking for equality of ppapi on purpose. 183 logging.info('PPapi process list found: <%s>', ', '.join(ppapi)) 184 185 # Any better pattern matching? 186 msg = ' Received crash notification for ' + constants.BROWSER 187 if self._messages_log_reader.can_find(msg): 188 raise error.TestFail('Failed: Browser crashed during test.') 189 if not ppapi: 190 raise error.TestFail( 191 'Failed: Pepper process disappeared during test.') 192 193 # At a minimum Flash identifies itself during process start. 194 msg = 'flash/platform/pepper/pep_' 195 if not self._ui_log_reader.can_find(msg): 196 raise error.TestFail( 197 'Failed: Saw ppapi process but no Flash output.') 198 199 # Check that libpepflashplayer.so was loaded from the expected path. 200 if load_path: 201 # Check all current process for Flash library. 202 output = subprocess.check_output( 203 ['grep libpepflashplayer.so /proc/*/maps'], shell=True) 204 # Verify there was no other than the expected location. 205 for dont_load_path in _FLASH_PATHS - {load_path}: 206 if dont_load_path in output: 207 logging.error('Flash incorrectly loaded from %s', 208 dont_load_path) 209 logging.info(output) 210 raise error.TestFail('Failed: Flash incorrectly loaded ' 211 'from %s' % dont_load_path) 212 logging.info('Verified Flash was indeed not loaded from %s', 213 dont_load_path) 214 # Verify at least one of the libraries came from where we expected. 215 if not load_path in output: 216 # Mystery. We saw a Flash loaded from who knows where. 217 logging.error('Flash not loaded from %s', load_path) 218 logging.info(output) 219 raise error.TestFail('Failed: Flash not loaded from %s' % 220 load_path) 221 logging.info('Saw a flash library loaded from %s.', load_path) 222 223 224 def action_delete_component(self): 225 """ 226 Deletes all components on the DUT. Notice _COMPONENT_MOUNT cannot be 227 deleted. It will remain until after reboot of the DUT. 228 """ 229 if os.path.exists(_COMPONENT_STORE): 230 shutil.rmtree(_COMPONENT_STORE) 231 if os.path.exists(_COMPONENT_STORE): 232 raise error.TestFail('Error: could not delete %s', 233 _COMPONENT_STORE) 234 if os.path.exists(_DOWNLOAD_STORE): 235 shutil.rmtree(_DOWNLOAD_STORE) 236 if os.path.exists(_DOWNLOAD_STORE): 237 raise error.TestFail('Error: could not delete %s', 238 _DOWNLOAD_STORE) 239 240 def action_download_omaha_component(self): 241 """ 242 Pretend we have no system Flash binary and tell browser to 243 accelerate the component update process. 244 TODO(ihf): Is this better than pretending the system binary is old? 245 """ 246 # TODO(ihf): Find ways to test component updates on top of component 247 # updates maybe by checking hashlib.md5(open(_COMPONENT_STORE_LATEST)). 248 if os.path.exists(_COMPONENT_STORE): 249 raise error.TestFail('Error: currently unable to test component ' 250 'update as component store not clean before ' 251 'download.') 252 # TODO(ihf): Remove --component-updater=test-request once Finch is set 253 # up to behave more like a user in the field. 254 browser_args = ['--ppapi-flash-path=', 255 '--ppapi-flash-version=0.0.0.0', 256 '--component-updater=fast-update,test-request'] 257 logging.info(browser_args) 258 # Browser will download component, but it will require a subsequent 259 # reboot by the caller to use it. (Browser restart is not enough.) 260 with chrome.Chrome(extra_browser_args=browser_args, 261 init_network_controller=True) as cr: 262 self.serve_swf_to_browser(cr.browser) 263 # Wait for the last file to be written by component updater. 264 utils.wait_for_value_changed( 265 lambda: (os.path.exists(_COMPONENT_STORE_LATEST)), 266 False, 267 timeout_sec=self._component_download_timeout_secs) 268 if not os.path.exists(_COMPONENT_STORE): 269 raise error.TestFail('Failed: after download no component at ' 270 '%s' % _COMPONENT_STORE) 271 # This may look silly but we prefer giving the system a bit more 272 # time to write files to disk before subsequent reboot. 273 os.system('sync') 274 time.sleep(10) 275 276 def action_install_old_component(self): 277 """ 278 Puts an old/mock manifest and Flash binary into _COMPONENT_STORE. 279 """ 280 # TODO(ihf): Implement. Problem is, mock component binaries need to be 281 # signed by Omaha. But if we had this we could test component updating 282 # a component update. 283 pass 284 285 def action_verify_component_flash(self): 286 """ 287 Verifies that the next use of Flash is from _COMPONENT_MOUNT. 288 """ 289 # Verify there is already a binary in the component store. 290 self.verify_file(_COMPONENT_STORE_LATEST) 291 # Verify that binary was mounted during boot. 292 self.verify_file(os.path.join(_COMPONENT_MOUNT, 'libpepflashplayer.so')) 293 self.verify_file(os.path.join(_COMPONENT_MOUNT, 'manifest.json')) 294 # Pretend we have a really old Flash revision on system to force using 295 # the downloaded component. 296 browser_args = ['--ppapi-flash-version=1.0.0.0'] 297 # Verify that Flash runs from _COMPONENT_MOUNT. 298 self.run_flash_test( 299 browser_args=browser_args, load_path=_COMPONENT_MOUNT) 300 301 def action_verify_system_flash(self): 302 """ 303 Verifies that next use of Flash is from the _SYSTEM_STORE. 304 """ 305 # Verify there is a binary in the system store. 306 self.verify_file(os.path.join(_SYSTEM_STORE, _BINARY)) 307 # Enable component updates and pretend we have a really new Flash 308 # version on the system image. 309 browser_args = ['--ppapi-flash-version=9999.0.0.0'] 310 # Verify that Flash runs from _SYSTEM_STORE. 311 self.run_flash_test(browser_args=browser_args, load_path=_SYSTEM_STORE) 312 313 def run_flash_test(self, browser_args=None, load_path=None): 314 """ 315 Verifies that directing the browser to an swf file results in a running 316 Pepper Flash process which does not immediately crash. 317 318 @param browser_args: additional browser args. 319 @param load_path: flash load path. 320 """ 321 if not browser_args: 322 browser_args = [] 323 # This is Flash. Disable html5 by default feature. 324 browser_args += ['--disable-features=PreferHtmlOverPlugins'] 325 # As this is an end to end test with nontrivial setup we can expect a 326 # certain amount of flakes which are *unrelated* to running Flash. We 327 # try to hide these unrelated flakes by selective retry. 328 for _ in range(0, self._retries): 329 logging.info(browser_args) 330 with chrome.Chrome(extra_browser_args=browser_args, 331 init_network_controller=True) as cr: 332 if self.serve_swf_to_browser(cr.browser): 333 self.verify_flash_process(load_path) 334 return 335 raise error.TestFail( 336 'Error: Unable to test Flash due to setup problems.') 337 338 def run_once(self, CU_action=_CU_ACTION_SANITY): 339 """ 340 Main entry point for desktopui_FlashSanityCheck. 341 342 Performs an action as specified by control file or 343 by the component_UpdateFlash server test. (The current need to reboot 344 after switching to/from component binary makes this test a server test.) 345 346 @param CU_action: component updater action to verify (typically called 347 from server test). 348 """ 349 logging.info('+++++ desktopui_FlashSanityCheck +++++') 350 logging.info('Performing %s', CU_action) 351 if CU_action == _CU_ACTION_DELETE: 352 self.action_delete_component() 353 elif CU_action == _CU_ACTION_DOWNLOAD: 354 self.action_download_omaha_component() 355 elif CU_action == _CU_ACTION_INSTALL_OLD: 356 self.action_install_old_component() 357 elif CU_action == _CU_ACTION_SANITY: 358 self.run_flash_test() 359 elif CU_action == _CU_ACTION_VERIFY_COMPONENT: 360 self.action_verify_component_flash() 361 elif CU_action == _CU_ACTION_VERIFY_SYSTEM: 362 self.action_verify_system_flash() 363 else: 364 raise error.TestError('Error: unknown action %s', CU_action) 365