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=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