Home | History | Annotate | Download | only in cros
      1 # Copyright (c) 2013 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, os, re
      6 
      7 from autotest_lib.client.common_lib.cros import arc_util
      8 from autotest_lib.client.cros import constants
      9 from autotest_lib.client.bin import utils
     10 from telemetry.core import cros_interface, exceptions, util
     11 from telemetry.internal.browser import browser_finder, browser_options
     12 from telemetry.internal.browser import extension_to_load
     13 
     14 CAP_USERNAME = 'crosautotest (at] gmail.com'
     15 CAP_URL = ('https://sites.google.com/a/chromium.org/dev/chromium-os'
     16            '/testing/cros-autotest/cap')
     17 
     18 Error = exceptions.Error
     19 
     20 def NormalizeEmail(username):
     21     """Remove dots from username. Add @gmail.com if necessary.
     22 
     23     TODO(achuith): Get rid of this when crbug.com/358427 is fixed.
     24 
     25     @param username: username/email to be scrubbed.
     26     """
     27     parts = re.split('@', username)
     28     parts[0] = re.sub('\.', '', parts[0])
     29 
     30     if len(parts) == 1:
     31         parts.append('gmail.com')
     32     return '@'.join(parts)
     33 
     34 
     35 class Chrome(object):
     36     """Wrapper for creating a telemetry browser instance with extensions.
     37 
     38     The recommended way to use this class is to create the instance using the
     39     with statement:
     40 
     41     >>> with chrome.Chrome(...) as cr:
     42     >>>     # Do whatever you need with cr.
     43     >>>     pass
     44 
     45     This will make sure all the clean-up functions are called.  If you really
     46     need to use this class without the with statement, make sure to call the
     47     close() method once you're done with the Chrome instance.
     48     """
     49 
     50 
     51     BROWSER_TYPE_LOGIN = 'system'
     52     BROWSER_TYPE_GUEST = 'system-guest'
     53 
     54 
     55     def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False,
     56                  num_tries=3, extra_browser_args=None,
     57                  clear_enterprise_policy=True, dont_override_profile=False,
     58                  disable_gaia_services=True, disable_default_apps = True,
     59                  auto_login=True, gaia_login=False,
     60                  username=None, password=None, gaia_id=None,
     61                  arc_mode=None, disable_arc_opt_in=True,
     62                  init_network_controller=False):
     63         """
     64         Constructor of telemetry wrapper.
     65 
     66         @param logged_in: Regular user (True) or guest user (False).
     67         @param extension_paths: path of unpacked extension to install.
     68         @param autotest_ext: Load a component extension with privileges to
     69                              invoke chrome.autotestPrivate.
     70         @param num_tries: Number of attempts to log in.
     71         @param extra_browser_args: Additional argument(s) to pass to the
     72                                    browser. It can be a string or a list.
     73         @param clear_enterprise_policy: Clear enterprise policy before
     74                                         logging in.
     75         @param dont_override_profile: Don't delete cryptohome before login.
     76                                       Telemetry will output a warning with this
     77                                       option.
     78         @param disable_gaia_services: For enterprise autotests, this option may
     79                                       be used to enable policy fetch.
     80         @param disable_default_apps: For tests that exercise default apps.
     81         @param auto_login: Does not login automatically if this is False.
     82                            Useful if you need to examine oobe.
     83         @param gaia_login: Logs in to real gaia.
     84         @param username: Log in using this username instead of the default.
     85         @param password: Log in using this password instead of the default.
     86         @param gaia_id: Log in using this gaia_id instead of the default.
     87         @param arc_mode: How ARC instance should be started.  Default is to not
     88                          start.
     89         @param disable_arc_opt_in: For opt in flow autotest. This option is used
     90                                    to disable the arc opt in flow.
     91         """
     92         self._autotest_ext_path = None
     93 
     94         # Force autotest extension if we need enable Play Store.
     95         if (utils.is_arc_available() and (arc_util.should_start_arc(arc_mode)
     96             or not disable_arc_opt_in)):
     97             autotest_ext = True
     98 
     99         if autotest_ext:
    100             self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
    101                                                    'autotest_private_ext')
    102             extension_paths.append(self._autotest_ext_path)
    103 
    104         finder_options = browser_options.BrowserFinderOptions()
    105         if utils.is_arc_available() and arc_util.should_start_arc(arc_mode):
    106             if disable_arc_opt_in:
    107                 finder_options.browser_options.AppendExtraBrowserArgs(
    108                         arc_util.get_extra_chrome_flags())
    109             logged_in = True
    110 
    111         self._browser_type = (self.BROWSER_TYPE_LOGIN
    112                 if logged_in else self.BROWSER_TYPE_GUEST)
    113         finder_options.browser_type = self.browser_type
    114         if extra_browser_args:
    115             finder_options.browser_options.AppendExtraBrowserArgs(
    116                     extra_browser_args)
    117 
    118         # finder options must be set before parse_args(), browser options must
    119         # be set before Create().
    120         # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
    121         # autotest debug logs
    122         finder_options.verbosity = 2
    123         finder_options.CreateParser().parse_args(args=[])
    124         b_options = finder_options.browser_options
    125         b_options.disable_component_extensions_with_background_pages = False
    126         b_options.create_browser_with_oobe = True
    127         b_options.clear_enterprise_policy = clear_enterprise_policy
    128         b_options.dont_override_profile = dont_override_profile
    129         b_options.disable_gaia_services = disable_gaia_services
    130         b_options.disable_default_apps = disable_default_apps
    131         b_options.disable_component_extensions_with_background_pages = disable_default_apps
    132 
    133         b_options.auto_login = auto_login
    134         b_options.gaia_login = gaia_login
    135 
    136         if utils.is_arc_available() and not disable_arc_opt_in:
    137             arc_util.set_browser_options_for_opt_in(b_options)
    138 
    139         self.username = b_options.username if username is None else username
    140         self.password = b_options.password if password is None else password
    141         self.username = NormalizeEmail(self.username)
    142         b_options.username = self.username
    143         b_options.password = self.password
    144         self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
    145         b_options.gaia_id = self.gaia_id
    146 
    147         self.arc_mode = arc_mode
    148 
    149         if logged_in:
    150             extensions_to_load = b_options.extensions_to_load
    151             for path in extension_paths:
    152                 extension = extension_to_load.ExtensionToLoad(
    153                         path, self.browser_type)
    154                 extensions_to_load.append(extension)
    155             self._extensions_to_load = extensions_to_load
    156 
    157         # Turn on collection of Chrome coredumps via creation of a magic file.
    158         # (Without this, Chrome coredumps are trashed.)
    159         open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
    160 
    161         for i in range(num_tries):
    162             try:
    163                 browser_to_create = browser_finder.FindBrowser(finder_options)
    164                 self._browser = browser_to_create.Create(finder_options)
    165                 if utils.is_arc_available():
    166                     if disable_arc_opt_in:
    167                         if arc_util.should_start_arc(arc_mode):
    168                             arc_util.enable_play_store(self.autotest_ext, True)
    169                     else:
    170                         arc_util.opt_in(self.browser, self.autotest_ext)
    171                     arc_util.post_processing_after_browser(self)
    172                 break
    173             except exceptions.LoginException as e:
    174                 logging.error('Timed out logging in, tries=%d, error=%s',
    175                               i, repr(e))
    176                 if i == num_tries-1:
    177                     raise
    178         if init_network_controller:
    179           self._browser.platform.network_controller.InitializeIfNeeded()
    180 
    181     def __enter__(self):
    182         return self
    183 
    184 
    185     def __exit__(self, *args):
    186         self.close()
    187 
    188 
    189     @property
    190     def browser(self):
    191         """Returns a telemetry browser instance."""
    192         return self._browser
    193 
    194 
    195     def get_extension(self, extension_path):
    196         """Fetches a telemetry extension instance given the extension path."""
    197         for ext in self._extensions_to_load:
    198             if extension_path == ext.path:
    199                 return self.browser.extensions[ext]
    200         return None
    201 
    202 
    203     @property
    204     def autotest_ext(self):
    205         """Returns the autotest extension."""
    206         return self.get_extension(self._autotest_ext_path)
    207 
    208 
    209     @property
    210     def login_status(self):
    211         """Returns login status."""
    212         ext = self.autotest_ext
    213         if not ext:
    214             return None
    215 
    216         ext.ExecuteJavaScript('''
    217             window.__login_status = null;
    218             chrome.autotestPrivate.loginStatus(function(s) {
    219               window.__login_status = s;
    220             });
    221         ''')
    222         return ext.EvaluateJavaScript('window.__login_status')
    223 
    224 
    225     def get_visible_notifications(self):
    226         """Returns an array of visible notifications of Chrome.
    227 
    228         For specific type of each notification, please refer to Chromium's
    229         chrome/common/extensions/api/autotest_private.idl.
    230         """
    231         ext = self.autotest_ext
    232         if not ext:
    233             return None
    234 
    235         ext.ExecuteJavaScript('''
    236             window.__items = null;
    237             chrome.autotestPrivate.getVisibleNotifications(function(items) {
    238               window.__items  = items;
    239             });
    240         ''')
    241         if ext.EvaluateJavaScript('window.__items') is None:
    242             return None
    243         return ext.EvaluateJavaScript('window.__items')
    244 
    245 
    246     @property
    247     def browser_type(self):
    248         """Returns the browser_type."""
    249         return self._browser_type
    250 
    251 
    252     @staticmethod
    253     def did_browser_crash(func):
    254         """Runs func, returns True if the browser crashed, False otherwise.
    255 
    256         @param func: function to run.
    257 
    258         """
    259         try:
    260             func()
    261         except Error:
    262             return True
    263         return False
    264 
    265 
    266     @staticmethod
    267     def wait_for_browser_restart(func):
    268         """Runs func, and waits for a browser restart.
    269 
    270         @param func: function to run.
    271 
    272         """
    273         _cri = cros_interface.CrOSInterface()
    274         pid = _cri.GetChromePid()
    275         Chrome.did_browser_crash(func)
    276         utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
    277 
    278 
    279     def wait_for_browser_to_come_up(self):
    280         """Waits for the browser to come up. This should only be called after a
    281         browser crash.
    282         """
    283         def _BrowserReady(cr):
    284             tabs = []  # Wrapper for pass by reference.
    285             if self.did_browser_crash(
    286                     lambda: tabs.append(cr.browser.tabs.New())):
    287                 return False
    288             try:
    289                 tabs[0].Close()
    290             except:
    291                 # crbug.com/350941
    292                 logging.error('Timed out closing tab')
    293             return True
    294         util.WaitFor(lambda: _BrowserReady(self), timeout=10)
    295 
    296 
    297     def close(self):
    298         """Closes the browser.
    299         """
    300         try:
    301             if utils.is_arc_available():
    302                 arc_util.pre_processing_before_close(self)
    303         finally:
    304             # Calling platform.StopAllLocalServers() to tear down the telemetry
    305             # server processes such as the one started by
    306             # platform.SetHTTPServerDirectories().  Not calling this function
    307             # will leak the process and may affect test results.
    308             # (crbug.com/663387)
    309             self._browser.platform.StopAllLocalServers()
    310             self._browser.Close()
    311             self._browser.platform.network_controller.Close()
    312