Home | History | Annotate | Download | only in desktopui_FlashSanityCheck
      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 
      5 import logging
      6 import os
      7 import pprint
      8 import shutil
      9 import subprocess
     10 import sys
     11 import time
     12 from autotest_lib.client.bin import test, utils
     13 from autotest_lib.client.common_lib import error
     14 from autotest_lib.client.common_lib.cros import chrome
     15 from 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 
     54 class 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