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