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         if autotest_ext:
     94             self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
     95                                                    'autotest_private_ext')
     96             extension_paths.append(self._autotest_ext_path)
     97 
     98         finder_options = browser_options.BrowserFinderOptions()
     99         if utils.is_arc_available() and arc_util.should_start_arc(arc_mode):
    100             if disable_arc_opt_in:
    101                 finder_options.browser_options.AppendExtraBrowserArgs(
    102                         arc_util.get_extra_chrome_flags())
    103             logged_in = True
    104 
    105         self._browser_type = (self.BROWSER_TYPE_LOGIN
    106                 if logged_in else self.BROWSER_TYPE_GUEST)
    107         finder_options.browser_type = self.browser_type
    108         if extra_browser_args:
    109             finder_options.browser_options.AppendExtraBrowserArgs(
    110                     extra_browser_args)
    111 
    112         # finder options must be set before parse_args(), browser options must
    113         # be set before Create().
    114         # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
    115         # autotest debug logs
    116         finder_options.verbosity = 2
    117         finder_options.CreateParser().parse_args(args=[])
    118         b_options = finder_options.browser_options
    119         b_options.disable_component_extensions_with_background_pages = False
    120         b_options.create_browser_with_oobe = True
    121         b_options.clear_enterprise_policy = clear_enterprise_policy
    122         b_options.dont_override_profile = dont_override_profile
    123         b_options.disable_gaia_services = disable_gaia_services
    124         b_options.disable_default_apps = disable_default_apps
    125         b_options.disable_component_extensions_with_background_pages = disable_default_apps
    126 
    127         b_options.auto_login = auto_login
    128         b_options.gaia_login = gaia_login
    129 
    130         if utils.is_arc_available() and not disable_arc_opt_in:
    131             arc_util.set_browser_options_for_opt_in(b_options)
    132 
    133         self.username = b_options.username if username is None else username
    134         self.password = b_options.password if password is None else password
    135         self.username = NormalizeEmail(self.username)
    136         b_options.username = self.username
    137         b_options.password = self.password
    138         self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
    139         b_options.gaia_id = self.gaia_id
    140 
    141         self.arc_mode = arc_mode
    142 
    143         if logged_in:
    144             extensions_to_load = b_options.extensions_to_load
    145             for path in extension_paths:
    146                 extension = extension_to_load.ExtensionToLoad(
    147                         path, self.browser_type)
    148                 extensions_to_load.append(extension)
    149             self._extensions_to_load = extensions_to_load
    150 
    151         # Turn on collection of Chrome coredumps via creation of a magic file.
    152         # (Without this, Chrome coredumps are trashed.)
    153         open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
    154 
    155         for i in range(num_tries):
    156             try:
    157                 browser_to_create = browser_finder.FindBrowser(finder_options)
    158                 self._browser = browser_to_create.Create(finder_options)
    159                 if utils.is_arc_available():
    160                     if disable_arc_opt_in:
    161                         if arc_util.should_start_arc(arc_mode):
    162                             arc_util.enable_arc_setting(self.browser)
    163                     else:
    164                         arc_util.opt_in(self.browser)
    165                     arc_util.post_processing_after_browser(self)
    166                 break
    167             except exceptions.LoginException as e:
    168                 logging.error('Timed out logging in, tries=%d, error=%s',
    169                               i, repr(e))
    170                 if i == num_tries-1:
    171                     raise
    172         if init_network_controller:
    173           self._browser.platform.network_controller.InitializeIfNeeded()
    174 
    175     def __enter__(self):
    176         return self
    177 
    178 
    179     def __exit__(self, *args):
    180         self.close()
    181 
    182 
    183     @property
    184     def browser(self):
    185         """Returns a telemetry browser instance."""
    186         return self._browser
    187 
    188 
    189     def get_extension(self, extension_path):
    190         """Fetches a telemetry extension instance given the extension path."""
    191         for ext in self._extensions_to_load:
    192             if extension_path == ext.path:
    193                 return self.browser.extensions[ext]
    194         return None
    195 
    196 
    197     @property
    198     def autotest_ext(self):
    199         """Returns the autotest extension."""
    200         return self.get_extension(self._autotest_ext_path)
    201 
    202 
    203     @property
    204     def login_status(self):
    205         """Returns login status."""
    206         ext = self.autotest_ext
    207         if not ext:
    208             return None
    209 
    210         ext.ExecuteJavaScript('''
    211             window.__login_status = null;
    212             chrome.autotestPrivate.loginStatus(function(s) {
    213               window.__login_status = s;
    214             });
    215         ''')
    216         return ext.EvaluateJavaScript('window.__login_status')
    217 
    218 
    219     def get_visible_notifications(self):
    220         """Returns an array of visible notifications of Chrome.
    221 
    222         For specific type of each notification, please refer to Chromium's
    223         chrome/common/extensions/api/autotest_private.idl.
    224         """
    225         ext = self.autotest_ext
    226         if not ext:
    227             return None
    228 
    229         ext.ExecuteJavaScript('''
    230             window.__items = null;
    231             chrome.autotestPrivate.getVisibleNotifications(function(items) {
    232               window.__items  = items;
    233             });
    234         ''')
    235         if ext.EvaluateJavaScript('window.__items') is None:
    236             return None
    237         return ext.EvaluateJavaScript('window.__items')
    238 
    239 
    240     @property
    241     def browser_type(self):
    242         """Returns the browser_type."""
    243         return self._browser_type
    244 
    245 
    246     @staticmethod
    247     def did_browser_crash(func):
    248         """Runs func, returns True if the browser crashed, False otherwise.
    249 
    250         @param func: function to run.
    251 
    252         """
    253         try:
    254             func()
    255         except Error:
    256             return True
    257         return False
    258 
    259 
    260     @staticmethod
    261     def wait_for_browser_restart(func):
    262         """Runs func, and waits for a browser restart.
    263 
    264         @param func: function to run.
    265 
    266         """
    267         _cri = cros_interface.CrOSInterface()
    268         pid = _cri.GetChromePid()
    269         Chrome.did_browser_crash(func)
    270         utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
    271 
    272 
    273     def wait_for_browser_to_come_up(self):
    274         """Waits for the browser to come up. This should only be called after a
    275         browser crash.
    276         """
    277         def _BrowserReady(cr):
    278             tabs = []  # Wrapper for pass by reference.
    279             if self.did_browser_crash(
    280                     lambda: tabs.append(cr.browser.tabs.New())):
    281                 return False
    282             try:
    283                 tabs[0].Close()
    284             except:
    285                 # crbug.com/350941
    286                 logging.error('Timed out closing tab')
    287             return True
    288         util.WaitFor(lambda: _BrowserReady(self), timeout=10)
    289 
    290 
    291     def close(self):
    292         """Closes the browser.
    293         """
    294         try:
    295             if utils.is_arc_available():
    296                 arc_util.pre_processing_before_close(self)
    297         finally:
    298             # Calling platform.StopAllLocalServers() to tear down the telemetry
    299             # server processes such as the one started by
    300             # platform.SetHTTPServerDirectories().  Not calling this function
    301             # will leak the process and may affect test results.
    302             # (crbug.com/663387)
    303             self._browser.platform.StopAllLocalServers()
    304             self._browser.Close()
    305             self._browser.platform.network_controller.Close()
    306