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