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 5import logging, os, re 6 7from autotest_lib.client.common_lib.cros import arc_util 8from autotest_lib.client.cros import constants 9from autotest_lib.client.bin import utils 10from telemetry.core import cros_interface, exceptions, util 11from telemetry.internal.browser import browser_finder, browser_options 12from telemetry.internal.browser import extension_to_load 13 14CAP_USERNAME = 'crosautotest@gmail.com' 15CAP_URL = ('https://sites.google.com/a/chromium.org/dev/chromium-os' 16 '/testing/cros-autotest/cap') 17 18Error = exceptions.Error 19 20def 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 35class 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