• 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_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