• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_common
8from autotest_lib.client.common_lib.cros import arc_util
9from autotest_lib.client.common_lib.cros import assistant_util
10from autotest_lib.client.cros import constants
11from autotest_lib.client.bin import utils
12from telemetry.core import cros_interface, exceptions, util
13from telemetry.internal.browser import browser_finder, browser_options
14from telemetry.internal.browser import extension_to_load
15
16Error = exceptions.Error
17
18def NormalizeEmail(username):
19    """Remove dots from username. Add @gmail.com if necessary.
20
21    TODO(achuith): Get rid of this when crbug.com/358427 is fixed.
22
23    @param username: username/email to be scrubbed.
24    """
25    parts = re.split('@', username)
26    parts[0] = re.sub('\.', '', parts[0])
27
28    if len(parts) == 1:
29        parts.append('gmail.com')
30    return '@'.join(parts)
31
32
33class Chrome(object):
34    """Wrapper for creating a telemetry browser instance with extensions.
35
36    The recommended way to use this class is to create the instance using the
37    with statement:
38
39    >>> with chrome.Chrome(...) as cr:
40    >>>     # Do whatever you need with cr.
41    >>>     pass
42
43    This will make sure all the clean-up functions are called.  If you really
44    need to use this class without the with statement, make sure to call the
45    close() method once you're done with the Chrome instance.
46    """
47
48
49    BROWSER_TYPE_LOGIN = 'system'
50    BROWSER_TYPE_GUEST = 'system-guest'
51
52
53    def __init__(self, logged_in=True, extension_paths=None, autotest_ext=False,
54                 num_tries=3, extra_browser_args=None,
55                 clear_enterprise_policy=True, expect_policy_fetch=False,
56                 dont_override_profile=False, disable_gaia_services=True,
57                 disable_default_apps=True, auto_login=True, gaia_login=False,
58                 username=None, password=None, gaia_id=None,
59                 arc_mode=None, disable_arc_opt_in=True,
60                 disable_arc_opt_in_verification=True,
61                 disable_app_sync=False,
62                 disable_play_auto_install=False,
63                 disable_locale_sync=True,
64                 disable_play_store_auto_update=True,
65                 enable_assistant=False,
66                 enterprise_arc_test=False,
67                 init_network_controller=False,
68                 login_delay=0):
69        """
70        Constructor of telemetry wrapper.
71
72        @param logged_in: Regular user (True) or guest user (False).
73        @param extension_paths: path of unpacked extension to install.
74        @param autotest_ext: Load a component extension with privileges to
75                             invoke chrome.autotestPrivate.
76        @param num_tries: Number of attempts to log in.
77        @param extra_browser_args: Additional argument(s) to pass to the
78                                   browser. It can be a string or a list.
79        @param clear_enterprise_policy: Clear enterprise policy before
80                                        logging in.
81        @param expect_policy_fetch: Expect that chrome can reach the device
82                                    management server and download policy.
83        @param dont_override_profile: Don't delete cryptohome before login.
84                                      Telemetry will output a warning with this
85                                      option.
86        @param disable_gaia_services: For enterprise autotests, this option may
87                                      be used to enable policy fetch.
88        @param disable_default_apps: For tests that exercise default apps.
89        @param auto_login: Does not login automatically if this is False.
90                           Useful if you need to examine oobe.
91        @param gaia_login: Logs in to real gaia.
92        @param username: Log in using this username instead of the default.
93        @param password: Log in using this password instead of the default.
94        @param gaia_id: Log in using this gaia_id instead of the default.
95        @param arc_mode: How ARC instance should be started.  Default is to not
96                         start.
97        @param disable_arc_opt_in: For opt in flow autotest. This option is used
98                                   to disable the arc opt in flow.
99        @param disable_arc_opt_in_verification:
100             Adds --disable-arc-opt-in-verification to browser args. This should
101             generally be enabled when disable_arc_opt_in is enabled. However,
102             for data migration tests where user's home data is already set up
103             with opted-in state before login, this option needs to be set to
104             False with disable_arc_opt_in=True to make ARC container work.
105        @param disable_app_sync:
106            Adds --arc-disable-app-sync to browser args and this disables ARC
107            app sync flow. By default it is enabled.
108        @param disable_play_auto_install:
109            Adds --arc-disable-play-auto-install to browser args and this
110            disables ARC Play Auto Install flow. By default it is enabled.
111        @param enterprise_arc_test: Skips opt_in causing enterprise tests to fail
112        @param disable_locale_sync:
113            Adds --arc-disable-locale-sync to browser args and this
114            disables locale sync between Chrome and Android container. In case
115            of disabling sync, Android container is started with language and
116            preference language list as it was set on the moment of starting
117            full instance. Used to prevent random app restarts caused by racy
118            locale change, coming from profile sync. By default locale sync is
119            disabled.
120        @param disable_play_store_auto_update:
121            Adds --arc-play-store-auto-update=off to browser args and this
122            disables Play Store, GMS Core and third-party apps auto-update.
123            By default auto-update is off to have stable autotest environment.
124        @param login_delay: Time for idle in login screen to simulate the time
125                            required for password typing.
126        @param enable_assistant: For tests that require to enable Google
127                                  Assistant service. Default is False.
128        """
129        self._autotest_ext_path = None
130
131        # Force autotest extension if we need enable Play Store.
132        if (utils.is_arc_available() and (arc_util.should_start_arc(arc_mode)
133            or not disable_arc_opt_in)):
134            autotest_ext = True
135
136        if extension_paths is None:
137            extension_paths = []
138
139        if autotest_ext:
140            self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
141                                                   'autotest_private_ext')
142            extension_paths.append(self._autotest_ext_path)
143
144        finder_options = browser_options.BrowserFinderOptions()
145        if utils.is_arc_available() and arc_util.should_start_arc(arc_mode):
146            if disable_arc_opt_in and disable_arc_opt_in_verification:
147                finder_options.browser_options.AppendExtraBrowserArgs(
148                        ['--disable-arc-opt-in-verification'])
149            if disable_app_sync:
150                finder_options.browser_options.AppendExtraBrowserArgs(
151                        ['--arc-disable-app-sync'])
152            if disable_play_auto_install:
153                finder_options.browser_options.AppendExtraBrowserArgs(
154                        ['--arc-disable-play-auto-install'])
155            if disable_locale_sync:
156                finder_options.browser_options.AppendExtraBrowserArgs(
157                        ['--arc-disable-locale-sync'])
158            if disable_play_store_auto_update:
159                finder_options.browser_options.AppendExtraBrowserArgs(
160                        ['--arc-play-store-auto-update=off'])
161            logged_in = True
162
163        self._browser_type = (self.BROWSER_TYPE_LOGIN
164                if logged_in else self.BROWSER_TYPE_GUEST)
165        finder_options.browser_type = self.browser_type
166        if extra_browser_args:
167            finder_options.browser_options.AppendExtraBrowserArgs(
168                    extra_browser_args)
169
170        # finder options must be set before parse_args(), browser options must
171        # be set before Create().
172        # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
173        # autotest debug logs
174        finder_options.verbosity = 2
175        finder_options.CreateParser().parse_args(args=[])
176        b_options = finder_options.browser_options
177        b_options.disable_component_extensions_with_background_pages = False
178        b_options.create_browser_with_oobe = True
179        b_options.clear_enterprise_policy = clear_enterprise_policy
180        b_options.dont_override_profile = dont_override_profile
181        b_options.disable_gaia_services = disable_gaia_services
182        b_options.disable_default_apps = disable_default_apps
183        b_options.disable_component_extensions_with_background_pages = disable_default_apps
184        b_options.disable_background_networking = False
185        b_options.expect_policy_fetch = expect_policy_fetch
186
187        b_options.auto_login = auto_login
188        b_options.gaia_login = gaia_login
189        b_options.login_delay = login_delay
190
191        if utils.is_arc_available() and not disable_arc_opt_in:
192            arc_util.set_browser_options_for_opt_in(b_options)
193
194        self.username = b_options.username if username is None else username
195        self.password = b_options.password if password is None else password
196        self.username = NormalizeEmail(self.username)
197        b_options.username = self.username
198        b_options.password = self.password
199        self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
200        b_options.gaia_id = self.gaia_id
201
202        self.arc_mode = arc_mode
203
204        if logged_in:
205            extensions_to_load = b_options.extensions_to_load
206            for path in extension_paths:
207                extension = extension_to_load.ExtensionToLoad(
208                        path, self.browser_type)
209                extensions_to_load.append(extension)
210            self._extensions_to_load = extensions_to_load
211
212        # Turn on collection of Chrome coredumps via creation of a magic file.
213        # (Without this, Chrome coredumps are trashed.)
214        open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
215
216        self._browser_to_create = browser_finder.FindBrowser(
217            finder_options)
218        self._browser_to_create.SetUpEnvironment(b_options)
219        for i in range(num_tries):
220            try:
221                self._browser = self._browser_to_create.Create()
222                self._browser_pid = \
223                    cros_interface.CrOSInterface().GetChromePid()
224                if utils.is_arc_available():
225                    if disable_arc_opt_in:
226                        if arc_util.should_start_arc(arc_mode):
227                            arc_util.enable_play_store(self.autotest_ext, True)
228                    else:
229                        if not enterprise_arc_test:
230                            wait_for_provisioning = \
231                                    arc_mode != arc_common.ARC_MODE_ENABLED_ASYNC
232                            arc_util.opt_in(
233                                    browser = self.browser,
234                                    autotest_ext = self.autotest_ext,
235                                    wait_for_provisioning = wait_for_provisioning)
236                    arc_util.post_processing_after_browser(self)
237                if enable_assistant:
238                    assistant_util.enable_assistant(self.autotest_ext)
239                break
240            except exceptions.LoginException as e:
241                logging.error('Timed out logging in, tries=%d, error=%s',
242                              i, repr(e))
243                if i == num_tries-1:
244                    raise
245        if init_network_controller:
246          self._browser.platform.network_controller.Open()
247
248    def __enter__(self):
249        return self
250
251
252    def __exit__(self, *args):
253        self.close()
254
255
256    @property
257    def browser(self):
258        """Returns a telemetry browser instance."""
259        return self._browser
260
261
262    def get_extension(self, extension_path):
263        """Fetches a telemetry extension instance given the extension path."""
264        for ext in self._extensions_to_load:
265            if extension_path == ext.path:
266                return self.browser.extensions[ext]
267        return None
268
269
270    @property
271    def autotest_ext(self):
272        """Returns the autotest extension."""
273        return self.get_extension(self._autotest_ext_path)
274
275
276    @property
277    def login_status(self):
278        """Returns login status."""
279        ext = self.autotest_ext
280        if not ext:
281            return None
282
283        ext.ExecuteJavaScript('''
284            window.__login_status = null;
285            chrome.autotestPrivate.loginStatus(function(s) {
286              window.__login_status = s;
287            });
288        ''')
289        return utils.poll_for_condition(
290                lambda: ext.EvaluateJavaScript('window.__login_status'),
291                timeout=10)
292
293
294    def get_visible_notifications(self):
295        """Returns an array of visible notifications of Chrome.
296
297        For specific type of each notification, please refer to Chromium's
298        chrome/common/extensions/api/autotest_private.idl.
299        """
300        ext = self.autotest_ext
301        if not ext:
302            return None
303
304        ext.ExecuteJavaScript('''
305            window.__items = null;
306            chrome.autotestPrivate.getVisibleNotifications(function(items) {
307              window.__items  = items;
308            });
309        ''')
310        if ext.EvaluateJavaScript('window.__items') is None:
311            return None
312        return ext.EvaluateJavaScript('window.__items')
313
314
315    @property
316    def browser_type(self):
317        """Returns the browser_type."""
318        return self._browser_type
319
320
321    @staticmethod
322    def did_browser_crash(func):
323        """Runs func, returns True if the browser crashed, False otherwise.
324
325        @param func: function to run.
326
327        """
328        try:
329            func()
330        except Error:
331            return True
332        return False
333
334
335    @staticmethod
336    def wait_for_browser_restart(func, browser):
337        """Runs func, and waits for a browser restart.
338
339        @param func: function to run.
340
341        """
342        _cri = cros_interface.CrOSInterface()
343        pid = _cri.GetChromePid()
344        Chrome.did_browser_crash(func)
345        utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
346        browser.WaitForBrowserToComeUp()
347
348
349    def wait_for_browser_to_come_up(self):
350        """Waits for the browser to come up. This should only be called after a
351        browser crash.
352        """
353        def _BrowserReady(cr):
354            tabs = []  # Wrapper for pass by reference.
355            if self.did_browser_crash(
356                    lambda: tabs.append(cr.browser.tabs.New())):
357                return False
358            try:
359                tabs[0].Close()
360            except:
361                # crbug.com/350941
362                logging.error('Timed out closing tab')
363            return True
364        util.WaitFor(lambda: _BrowserReady(self), timeout=10)
365
366
367    def close(self):
368        """Closes the browser.
369        """
370        try:
371            if utils.is_arc_available():
372                arc_util.pre_processing_before_close(self)
373        finally:
374            # Calling platform.StopAllLocalServers() to tear down the telemetry
375            # server processes such as the one started by
376            # platform.SetHTTPServerDirectories().  Not calling this function
377            # will leak the process and may affect test results.
378            # (crbug.com/663387)
379            self._browser.platform.StopAllLocalServers()
380            self._browser.Close()
381            self._browser_to_create.CleanUpEnvironment()
382            self._browser.platform.network_controller.Close()
383