• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2012 The Chromium 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 copy
6import logging
7import optparse
8import os
9import shlex
10import sys
11
12from telemetry.core import browser_finder
13from telemetry.core import profile_types
14from telemetry.core import util
15from telemetry.core import wpr_modes
16from telemetry.core.platform.profiler import profiler_finder
17
18util.AddDirToPythonPath(
19    util.GetChromiumSrcDir(), 'third_party', 'webpagereplay')
20import net_configs  # pylint: disable=F0401
21
22
23class BrowserFinderOptions(optparse.Values):
24  """Options to be used for discovering a browser."""
25
26  def __init__(self, browser_type=None):
27    optparse.Values.__init__(self)
28
29    self.browser_type = browser_type
30    self.browser_executable = None
31    self.chrome_root = None
32    self.android_device = None
33    self.cros_ssh_identity = None
34
35    self.extensions_to_load = []
36
37    # If set, copy the generated profile to this path on exit.
38    self.output_profile_path = None
39
40    self.cros_remote = None
41
42    self.profiler = None
43    self.verbosity = 0
44
45    self.report_root_metrics = False
46
47    self.browser_options = BrowserOptions()
48    self.output_file = None
49    self.skip_navigate_on_repeat = False
50
51    self.android_rndis = False
52
53  def __repr__(self):
54    return str(sorted(self.__dict__.items()))
55
56  def Copy(self):
57    return copy.deepcopy(self)
58
59  def CreateParser(self, *args, **kwargs):
60    parser = optparse.OptionParser(*args, **kwargs)
61
62    # Selection group
63    group = optparse.OptionGroup(parser, 'Which browser to use')
64    group.add_option('--browser',
65        dest='browser_type',
66        default=None,
67        help='Browser type to run, '
68             'in order of priority. Supported values: list,%s' %
69             ','.join(browser_finder.ALL_BROWSER_TYPES))
70    group.add_option('--browser-executable',
71        dest='browser_executable',
72        help='The exact browser to run.')
73    group.add_option('--chrome-root',
74        dest='chrome_root',
75        help='Where to look for chrome builds.'
76             'Defaults to searching parent dirs by default.')
77    group.add_option('--device',
78        dest='android_device',
79        help='The android device ID to use'
80             'If not specified, only 0 or 1 connected devcies are supported.')
81    group.add_option(
82        '--remote',
83        dest='cros_remote',
84        help='The IP address of a remote ChromeOS device to use.')
85    identity = None
86    testing_rsa = os.path.join(
87        util.GetChromiumSrcDir(),
88        'third_party', 'chromite', 'ssh_keys', 'testing_rsa')
89    if os.path.exists(testing_rsa):
90      identity = testing_rsa
91    group.add_option('--identity',
92        dest='cros_ssh_identity',
93        default=identity,
94        help='The identity file to use when ssh\'ing into the ChromeOS device')
95    parser.add_option_group(group)
96
97    # Debugging options
98    group = optparse.OptionGroup(parser, 'When things go wrong')
99    profiler_choices = profiler_finder.GetAllAvailableProfilers()
100    group.add_option(
101        '--profiler', default=None, type='choice',
102        choices=profiler_choices,
103        help='Record profiling data using this tool. Supported values: ' +
104             ', '.join(profiler_choices))
105    group.add_option(
106        '--interactive', dest='interactive', action='store_true',
107        help='Let the user interact with the page; the actions specified for '
108             'the page are not run.')
109    group.add_option(
110        '-v', '--verbose', action='count', dest='verbosity',
111        help='Increase verbosity level (repeat as needed)')
112    group.add_option('--print-bootstrap-deps',
113                     action='store_true',
114                     help='Output bootstrap deps list.')
115    parser.add_option_group(group)
116
117    # Platform options
118    group = optparse.OptionGroup(parser, 'Platform options')
119    group.add_option('--no-performance-mode', action='store_true',
120        help='Some platforms run on "full performance mode" where the '
121        'test is executed at maximum CPU speed in order to minimize noise '
122        '(specially important for dashboards / continuous builds). '
123        'This option prevents Telemetry from tweaking such platform settings.')
124    group.add_option(
125        '--report-root-metrics', action='store_true',dest='report_root_metrics',
126        help='Enable metrics that require root access to record.')
127    group.add_option('--android-rndis', dest='android_rndis', default=False,
128        action='store_true', help='Use RNDIS forwarding on Android.')
129    group.add_option('--no-android-rndis', dest='android_rndis',
130        action='store_false', help='Do not use RNDIS forwarding on Android.'
131        ' [default]')
132    parser.add_option_group(group)
133
134    # Browser options.
135    self.browser_options.AddCommandLineArgs(parser)
136
137    real_parse = parser.parse_args
138    def ParseArgs(args=None):
139      defaults = parser.get_default_values()
140      for k, v in defaults.__dict__.items():
141        if k in self.__dict__ and self.__dict__[k] != None:
142          continue
143        self.__dict__[k] = v
144      ret = real_parse(args, self) # pylint: disable=E1121
145
146      if self.verbosity >= 2:
147        logging.getLogger().setLevel(logging.DEBUG)
148      elif self.verbosity:
149        logging.getLogger().setLevel(logging.INFO)
150      else:
151        logging.getLogger().setLevel(logging.WARNING)
152
153      if self.browser_executable and not self.browser_type:
154        self.browser_type = 'exact'
155      if self.browser_type == 'list':
156        try:
157          types = browser_finder.GetAllAvailableBrowserTypes(self)
158        except browser_finder.BrowserFinderException, ex:
159          sys.stderr.write('ERROR: ' + str(ex))
160          sys.exit(1)
161        sys.stdout.write('Available browsers:\n')
162        sys.stdout.write('  %s\n' % '\n  '.join(types))
163        sys.exit(0)
164
165      # Parse browser options.
166      self.browser_options.UpdateFromParseResults(self)
167
168      return ret
169    parser.parse_args = ParseArgs
170    return parser
171
172  def AppendExtraBrowserArgs(self, args):
173    self.browser_options.AppendExtraBrowserArgs(args)
174
175  def MergeDefaultValues(self, defaults):
176    for k, v in defaults.__dict__.items():
177      self.ensure_value(k, v)
178
179class BrowserOptions(object):
180  """Options to be used for launching a browser."""
181  def __init__(self):
182    self.browser_type = None
183    self.show_stdout = False
184
185    # When set to True, the browser will use the default profile.  Telemetry
186    # will not provide an alternate profile directory.
187    self.dont_override_profile = False
188    self.profile_dir = None
189    self.profile_type = None
190    self._extra_browser_args = set()
191    self.extra_wpr_args = []
192    self.wpr_mode = wpr_modes.WPR_OFF
193    self.netsim = None
194
195    self.no_proxy_server = False
196    self.browser_user_agent_type = None
197
198    self.clear_sytem_cache_for_browser_and_profile_on_start = False
199    self.startup_url = None
200
201    # Background pages of built-in component extensions can interfere with
202    # performance measurements.
203    self.disable_component_extensions_with_background_pages = True
204
205    self.platform = None
206
207    # Whether to use the new code path for choosing an ephemeral port for
208    # DevTools. The bots set this to true. When Chrome 37 reaches stable,
209    # remove this setting and the old code path. http://crbug.com/379980
210    self.use_devtools_active_port = False
211
212  def __repr__(self):
213    return str(sorted(self.__dict__.items()))
214
215  @classmethod
216  def AddCommandLineArgs(cls, parser):
217
218    ############################################################################
219    # Please do not add any more options here without first discussing with    #
220    # a telemetry owner. This is not the right place for platform-specific     #
221    # options.                                                                 #
222    ############################################################################
223
224    group = optparse.OptionGroup(parser, 'Browser options')
225    profile_choices = profile_types.GetProfileTypes()
226    group.add_option('--profile-type',
227        dest='profile_type',
228        type='choice',
229        default='clean',
230        choices=profile_choices,
231        help=('The user profile to use. A clean profile is used by default. '
232              'Supported values: ' + ', '.join(profile_choices)))
233    group.add_option('--profile-dir',
234        dest='profile_dir',
235        help='Profile directory to launch the browser with. '
236             'A clean profile is used by default')
237    group.add_option('--extra-browser-args',
238        dest='extra_browser_args_as_string',
239        help='Additional arguments to pass to the browser when it starts')
240    group.add_option('--extra-wpr-args',
241        dest='extra_wpr_args_as_string',
242        help=('Additional arguments to pass to Web Page Replay. '
243              'See third_party/webpagereplay/replay.py for usage.'))
244    group.add_option('--netsim', default=None, type='choice',
245        choices=net_configs.NET_CONFIG_NAMES,
246        help=('Run benchmark under simulated network conditions. '
247              'Will prompt for sudo. Supported values: ' +
248              ', '.join(net_configs.NET_CONFIG_NAMES)))
249    group.add_option('--show-stdout',
250        action='store_true',
251        help='When possible, will display the stdout of the process')
252    # This hidden option is to be removed, and the older code path deleted,
253    # once Chrome 37 reaches Stable. http://crbug.com/379980
254    group.add_option('--use-devtools-active-port',
255        action='store_true',
256        help=optparse.SUPPRESS_HELP)
257    parser.add_option_group(group)
258
259    group = optparse.OptionGroup(parser, 'Compatibility options')
260    group.add_option('--gtest_output',
261        help='Ignored argument for compatibility with runtest.py harness')
262    parser.add_option_group(group)
263
264    group = optparse.OptionGroup(parser, 'Synthetic gesture options')
265    synthetic_gesture_source_type_choices = [ 'default', 'mouse', 'touch' ]
266    group.add_option('--synthetic-gesture-source-type',
267        dest='synthetic_gesture_source_type',
268        default='default', type='choice',
269        choices=synthetic_gesture_source_type_choices,
270        help='Specify the source type for synthetic gestures. Note that some ' +
271             'actions only support a specific source type. ' +
272             'Supported values: ' +
273             ', '.join(synthetic_gesture_source_type_choices))
274    parser.add_option_group(group)
275
276
277  def UpdateFromParseResults(self, finder_options):
278    """Copies our options from finder_options"""
279    browser_options_list = [
280        'extra_browser_args_as_string',
281        'extra_wpr_args_as_string',
282        'netsim',
283        'profile_dir',
284        'profile_type',
285        'show_stdout',
286        'synthetic_gesture_source_type',
287        'use_devtools_active_port',
288        ]
289    for o in browser_options_list:
290      a = getattr(finder_options, o, None)
291      if a is not None:
292        setattr(self, o, a)
293        delattr(finder_options, o)
294
295    self.browser_type = finder_options.browser_type
296
297    if hasattr(self, 'extra_browser_args_as_string'): # pylint: disable=E1101
298      tmp = shlex.split(
299        self.extra_browser_args_as_string) # pylint: disable=E1101
300      self.AppendExtraBrowserArgs(tmp)
301      delattr(self, 'extra_browser_args_as_string')
302    if hasattr(self, 'extra_wpr_args_as_string'): # pylint: disable=E1101
303      tmp = shlex.split(
304        self.extra_wpr_args_as_string) # pylint: disable=E1101
305      self.extra_wpr_args.extend(tmp)
306      delattr(self, 'extra_wpr_args_as_string')
307    if self.profile_type == 'default':
308      self.dont_override_profile = True
309
310    if self.profile_dir and self.profile_type != 'clean':
311      logging.critical(
312          "It's illegal to specify both --profile-type and --profile-dir.\n"
313          "For more information see: http://goo.gl/ngdGD5")
314      sys.exit(1)
315
316    if self.profile_dir and not os.path.isdir(self.profile_dir):
317      logging.critical(
318          "Directory specified by --profile-dir (%s) doesn't exist "
319          "or isn't a directory.\n"
320          "For more information see: http://goo.gl/ngdGD5" % self.profile_dir)
321      sys.exit(1)
322
323    if not self.profile_dir:
324      self.profile_dir = profile_types.GetProfileDir(self.profile_type)
325
326    # This deferred import is necessary because browser_options is imported in
327    # telemetry/telemetry/__init__.py.
328    from telemetry.core.backends.chrome import chrome_browser_options
329    finder_options.browser_options = (
330        chrome_browser_options.CreateChromeBrowserOptions(self))
331
332  @property
333  def extra_browser_args(self):
334    return self._extra_browser_args
335
336  def AppendExtraBrowserArgs(self, args):
337    if isinstance(args, list):
338      self._extra_browser_args.update(args)
339    else:
340      self._extra_browser_args.add(args)
341