| # Copyright 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import print_function |
| import atexit |
| import copy |
| import logging |
| import optparse |
| import os |
| import shlex |
| import socket |
| import sys |
| |
| from py_utils import cloud_storage # pylint: disable=import-error |
| |
| from telemetry import compat_mode_options |
| from telemetry.core import platform |
| from telemetry.core import util |
| from telemetry.internal.browser import browser_finder |
| from telemetry.internal.browser import browser_finder_exceptions |
| from telemetry.internal.browser import profile_types |
| from telemetry.internal.platform import android_device |
| from telemetry.internal.platform import device_finder |
| from telemetry.internal.platform import remote_platform_options |
| from telemetry.internal.util import binary_manager |
| from telemetry.util import wpr_modes |
| |
| |
| def _IsWin(): |
| return sys.platform == 'win32' |
| |
| |
| class BrowserFinderOptions(optparse.Values): |
| """Options to be used for discovering a browser.""" |
| |
| emulator_environment = None |
| |
| def __init__(self, browser_type=None): |
| optparse.Values.__init__(self) |
| |
| self.browser_type = browser_type |
| self.browser_executable = None |
| # The set of possible platforms the browser should run on. |
| self.target_platforms = None |
| self.chrome_root = None # Path to src/ |
| self.chromium_output_dir = None # E.g.: out/Debug |
| self.device = None |
| self.cros_ssh_identity = None |
| |
| self.cros_remote = None |
| self.cros_remote_ssh_port = None |
| |
| self.verbosity = 0 |
| |
| self.browser_options = BrowserOptions() |
| self.output_file = None |
| |
| self.remote_platform_options = None |
| |
| self.performance_mode = None |
| |
| # TODO(crbug.com/798703): remove this |
| self.no_performance_mode = False |
| |
| self.interval_profiling_target = '' |
| self.interval_profiling_periods = [] |
| self.interval_profiling_frequency = 1000 |
| self.interval_profiler_options = '' |
| self.capture_screen_video = False |
| |
| self.experimental_system_tracing = False |
| self.experimental_system_data_sources = False |
| self.force_sideload_perfetto = False |
| |
| def __repr__(self): |
| return str(sorted(self.__dict__.items())) |
| |
| def Copy(self): |
| return copy.deepcopy(self) |
| |
| def CreateParser(self, *args, **kwargs): |
| parser = optparse.OptionParser(*args, **kwargs) |
| |
| # Options to interact with a potential external results processor. |
| parser.set_defaults( |
| external_results_processor=False, |
| intermediate_dir=None, |
| # TODO(crbug.com/928275): Remove these when Telemetry is no longer |
| # involved in any results processing. |
| output_dir=None, |
| output_formats=[], |
| legacy_output_formats=[], |
| reset_results=True, |
| results_label='telemetry_run', |
| upload_results=False, |
| upload_bucket=None) |
| |
| # Selection group |
| group = optparse.OptionGroup(parser, 'Which browser to use') |
| group.add_option( |
| '--browser', |
| dest='browser_type', |
| default=None, |
| help='Browser type to run, ' |
| 'in order of priority. Supported values: list,%s' % |
| ', '.join(browser_finder.FindAllBrowserTypes())) |
| group.add_option( |
| '--browser-executable', |
| dest='browser_executable', |
| help='The exact browser to run.') |
| group.add_option( |
| '--chrome-root', |
| dest='chrome_root', |
| help='Where to look for chrome builds. ' |
| 'Defaults to searching parent dirs by default.') |
| group.add_option( |
| '--chromium-output-directory', |
| dest='chromium_output_dir', |
| help='Where to look for build artifacts. ' |
| 'Can also be specified by setting environment variable ' |
| 'CHROMIUM_OUTPUT_DIR.') |
| group.add_option( |
| '--remote', |
| dest='cros_remote', |
| help='The hostname of a remote ChromeOS device to use.') |
| group.add_option( |
| '--remote-ssh-port', |
| type=int, |
| # This is set in ParseArgs if necessary. |
| default=-1, |
| dest='cros_remote_ssh_port', |
| help='The SSH port of the remote ChromeOS device (requires --remote).') |
| compat_mode_options_list = [ |
| compat_mode_options.NO_FIELD_TRIALS, |
| compat_mode_options.IGNORE_CERTIFICATE_ERROR, |
| compat_mode_options.LEGACY_COMMAND_LINE_PATH, |
| compat_mode_options.GPU_BENCHMARKING_FALLBACKS, |
| compat_mode_options.DONT_REQUIRE_ROOTED_DEVICE] |
| parser.add_option( |
| '--compatibility-mode', |
| action='append', |
| dest='compatibility_mode', |
| choices=compat_mode_options_list, |
| default=[], |
| help='Select the compatibility change that you want to enforce when ' |
| 'running benchmarks. The options are: %s' % ', '.join( |
| compat_mode_options_list)) |
| parser.add_option( |
| '--legacy-json-trace-format', |
| action='store_true', |
| help='Request traces from Chrome in legacy JSON format.') |
| parser.add_option( |
| '--experimental-system-tracing', |
| action='store_true', |
| help='Use system tracing from Perfetto to trace Chrome.') |
| parser.add_option( |
| '--experimental-system-data-sources', |
| action='store_true', |
| help='Use Perfetto tracing to collect power and CPU usage data.') |
| parser.add_option( |
| '--force-sideload-perfetto', |
| action='store_true', |
| help='Sideload perfetto binaries from the cloud even if the device ' |
| 'already has Perfetto installed.') |
| identity = None |
| testing_rsa = os.path.join( |
| util.GetTelemetryThirdPartyDir(), 'chromite', 'ssh_keys', 'testing_rsa') |
| if os.path.exists(testing_rsa): |
| identity = testing_rsa |
| group.add_option( |
| '--identity', |
| dest='cros_ssh_identity', |
| default=identity, |
| help='The identity file to use when ssh\'ing into the ChromeOS device') |
| parser.add_option_group(group) |
| |
| # Debugging options |
| group = optparse.OptionGroup(parser, 'When things go wrong') |
| group.add_option('--print-bootstrap-deps', |
| action='store_true', |
| help='Output bootstrap deps list.') |
| group.add_option( |
| '--extra-chrome-categories', dest='extra_chrome_categories', type=str, |
| help='Filter string to enable additional chrome tracing categories. See' |
| ' documentation here: https://cs.chromium.org/chromium/src/base/' |
| 'trace_event/trace_config.h?rcl=' |
| 'c8db6c6371ca047c24d41f3972d5819bc83d83ae&l=125') |
| group.add_option( |
| '--extra-atrace-categories', dest='extra_atrace_categories', type=str, |
| help='Comma-separated list of extra atrace categories. Use atrace' |
| ' --list_categories to get full list.') |
| group.add_option( |
| '--enable-systrace', dest='enable_systrace', action='store_true', |
| help='Enable collection of systrace. (Useful on ChromeOS where' |
| ' atrace is not supported; collects scheduling information.)') |
| group.add_option( |
| '--capture-screen-video', |
| dest='capture_screen_video', action='store_true', |
| help='Capture the screen during the test and save it to a video file ' |
| '(note that it is supported only on some platforms)') |
| group.add_option( |
| '--periodic-screenshot-frequency-ms', |
| dest='periodic_screenshot_frequency_ms', type=int, |
| help='During each story, capture a screenshot every x ms and save it to a file.') |
| parser.add_option_group(group) |
| |
| # Platform options |
| group = optparse.OptionGroup(parser, 'Platform options') |
| group.add_option( |
| '--performance-mode', |
| choices=[android_device.HIGH_PERFORMANCE_MODE, |
| android_device.NORMAL_PERFORMANCE_MODE, |
| android_device.LITTLE_ONLY_PERFORMANCE_MODE, |
| android_device.KEEP_PERFORMANCE_MODE], |
| default=android_device.HIGH_PERFORMANCE_MODE, |
| help='Some platforms run on "full performance mode" where the ' |
| 'test is executed at maximum CPU speed in order to minimize noise ' |
| '(specially important for dashboards / continuous builds). ' |
| 'This option allows to choose performance mode. ' |
| 'Available choices: ' |
| 'high (default): high performance mode; ' |
| 'normal: normal performance mode; ' |
| 'little-only: execute the benchmark on little cores only; ' |
| 'keep: don\'t touch the device performance settings.') |
| # TODO(crbug.com/1025207): Rename this to --support-apk |
| group.add_option( |
| '--webview-embedder-apk', |
| action="append", |
| default=[], |
| help='When running tests on android webview, more than one apk needs to' |
| ' be installed. The apk running the test is said to embed webview. More' |
| ' than one apk may be specified if needed.') |
| parser.add_option_group(group) |
| |
| # Remote platform options |
| group = optparse.OptionGroup(parser, 'Remote platform options') |
| group.add_option('--android-denylist-file', |
| help='Device denylist JSON file.') |
| group.add_option( |
| '--device', |
| help='The device ID to use. ' |
| 'If not specified, only 0 or 1 connected devices are supported. ' |
| 'If specified as "android", all available Android devices are ' |
| 'used.') |
| group.add_option( |
| '--install-bundle-module', |
| dest='modules_to_install', |
| action='append', |
| default=[], |
| help='Specify Android App Bundle modules to install in addition to the ' |
| 'base module. Ignored on Non-Android platforms.') |
| group.add_option( |
| '--compile-apk', |
| help='Compiles the APK under test using dex2oat in the specified mode. ' |
| 'Ignored on non-Android platforms.') |
| group.add_option( |
| '--avd-config', |
| default=None, |
| help='A path to an AVD configuration to use for starting an Android ' |
| 'emulator.' |
| ) |
| parser.add_option_group(group) |
| |
| group = optparse.OptionGroup(parser, 'Fuchsia platform options') |
| group.add_option( |
| '--fuchsia-ssh-config', |
| default='out/Release', |
| help='Specify the ssh_config file used to connect to the Fuchsia OS.') |
| group.add_option( |
| '--fuchsia-device-address', |
| help='The IP of the target Fuchsia device. Optional.') |
| group.add_option( |
| '--fuchsia-ssh-port', |
| type=int, |
| help='The port on the host to which the ssh service running on the ' |
| 'Fuchsia device was forwarded. Will skip using the device-finder tool ' |
| 'if specified.') |
| group.add_option( |
| '--fuchsia-system-log-file', |
| help='The file where Fuchsia system logs will be stored.') |
| group.add_option( |
| '--fuchsia-repo', |
| default="fuchsia.com", |
| help='The name of the Fuchsia repo used to serve required packages.') |
| parser.add_option_group(group) |
| |
| # CPU profiling on Android/Linux/ChromeOS. |
| group = optparse.OptionGroup(parser, ( |
| 'CPU profiling over intervals of interest, ' |
| 'Android, Linux, and ChromeOS only')) |
| group.add_option( |
| '--interval-profiling-target', dest='interval_profiling_target', |
| default='renderer:main', |
| metavar='PROCESS_NAME[:THREAD_NAME]|"system_wide"', |
| help='Run the CPU profiler on this process/thread (default=%default), ' |
| 'which is supported only on Linux and Android, or system-wide, which ' |
| 'is supported only on ChromeOS.') |
| group.add_option( |
| '--interval-profiling-period', dest='interval_profiling_periods', |
| type='choice', |
| choices=('navigation', 'interactions', 'story_run'), |
| action='append', default=[], metavar='PERIOD', |
| help='Run the CPU profiler during this test period. ' |
| 'May be specified multiple times except when the story_run period is ' |
| 'used; available choices are ["navigation", "interactions", ' |
| '"story_run"]. Profile data will be written to ' |
| 'artifacts/*.perf.data (Android/ChromeOS) or ' |
| 'artifacts/*.profile.pb (Linux) files in the output directory. See ' |
| 'https://developer.android.com/ndk/guides/simpleperf for more info on ' |
| 'Android profiling via simpleperf.') |
| group.add_option( |
| '--interval-profiling-frequency', default=1000, metavar='FREQUENCY', |
| type=int, |
| help='Frequency of CPU profiling samples, in samples per second ' |
| '(default=%default). This flag is used only on Android') |
| group.add_option( |
| '--interval-profiler-options', |
| dest='interval_profiler_options', type=str, |
| metavar='"--flag <options> ..."', |
| help='Addtional arguments to pass to the CPU profiler. This is used ' |
| 'only on ChromeOS. On ChromeOS, pass the linux perf\'s subcommand name ' |
| 'followed by the options to pass to the perf tool. Supported perf ' |
| 'subcommands are "record" and "stat". ' |
| 'Eg: "record -e cycles -c 4000000 -g". Note: "-a" flag is added to the ' |
| 'perf command by default. Do not pass options that are incompatible ' |
| 'with the system-wide profile collection.') |
| parser.add_option_group(group) |
| |
| # Browser options. |
| self.browser_options.AddCommandLineArgs(parser) |
| |
| real_parse = parser.parse_args |
| def ParseArgs(args=None): |
| defaults = parser.get_default_values() |
| for k, v in defaults.__dict__.items(): |
| if k in self.__dict__ and self.__dict__[k] != None: |
| continue |
| self.__dict__[k] = v |
| ret = real_parse(args, self) # pylint: disable=E1121 |
| |
| if self.chromium_output_dir: |
| os.environ['CHROMIUM_OUTPUT_DIR'] = self.chromium_output_dir |
| |
| # Set up Android emulator if necessary. |
| self.ParseAndroidEmulatorOptions() |
| |
| # Parse remote platform options. |
| self.BuildRemotePlatformOptions() |
| |
| if self.remote_platform_options.device == 'list': |
| if binary_manager.NeedsInit(): |
| binary_manager.InitDependencyManager([]) |
| devices = device_finder.GetDevicesMatchingOptions(self) |
| print('Available devices:') |
| for device in devices: |
| print(' ', device.name) |
| sys.exit(0) |
| |
| if self.browser_executable and not self.browser_type: |
| self.browser_type = 'exact' |
| if self.browser_type == 'list': |
| if binary_manager.NeedsInit(): |
| binary_manager.InitDependencyManager([]) |
| devices = device_finder.GetDevicesMatchingOptions(self) |
| if not devices: |
| sys.exit(0) |
| browser_types = {} |
| for device in devices: |
| try: |
| possible_browsers = browser_finder.GetAllAvailableBrowsers(self, |
| device) |
| browser_types[device.name] = sorted( |
| [browser.browser_type for browser in possible_browsers]) |
| except browser_finder_exceptions.BrowserFinderException as ex: |
| print('ERROR: ', ex, file=sys.stderr) |
| sys.exit(1) |
| print('Available browsers:') |
| if len(browser_types) == 0: |
| print(' No devices were found.') |
| for device_name in sorted(browser_types.keys()): |
| print(' ', device_name) |
| for browser_type in browser_types[device_name]: |
| print(' ', browser_type) |
| if len(browser_types[device_name]) == 0: |
| print(' No browsers found for this device') |
| sys.exit(0) |
| |
| if ((self.browser_type == 'cros-chrome' or |
| self.browser_type == 'lacros-chrome') and |
| self.cros_remote and (self.cros_remote_ssh_port < 0)): |
| try: |
| self.cros_remote_ssh_port = socket.getservbyname('ssh') |
| except OSError as e: |
| raise RuntimeError( |
| 'Running a CrOS test in remote mode, but failed to retrieve port ' |
| 'used by SSH service. This likely means SSH is not installed on ' |
| 'the system. Original error: %s' % e) |
| |
| # Profiling other periods along with the story_run period leads to running |
| # multiple profiling processes at the same time. The effects of performing |
| # muliple CPU profiling at the same time is unclear and may generate |
| # incorrect profiles so this will not be supported. |
| if (len(self.interval_profiling_periods) > 1 |
| and 'story_run' in self.interval_profiling_periods): |
| print('Cannot specify other periods along with the story_run period.') |
| sys.exit(1) |
| |
| self.interval_profiler_options = shlex.split( |
| self.interval_profiler_options, posix=(not _IsWin())) |
| |
| # Parse browser options. |
| self.browser_options.UpdateFromParseResults(self) |
| |
| return ret |
| parser.parse_args = ParseArgs |
| return parser |
| |
| def IsBrowserTypeRelevant(self, browser_type): |
| """Determines if the browser_type is worth initializing. |
| |
| This check is used to sidestep any unnecessary work involved with searching |
| for a browser that might not actually be needed. For example, this check |
| could be used to prevent Telemetry from searching for a Clank browser if |
| browser_type is android-weblayer. |
| """ |
| return (browser_type == self.browser_type or |
| self.browser_type in ('list', 'any',)) |
| |
| def IsBrowserTypeReference(self): |
| """Determines if the browser_type is a reference browser_type.""" |
| return self.browser_type and self.browser_type.startswith('reference-') |
| |
| def IsBrowserTypeBundle(self): |
| """Determines if the browser_type is a bundle browser_type.""" |
| return self.browser_type and self.browser_type.endswith('-bundle') |
| |
| def _NoOpFunctionForTesting(self): |
| """No-op function that can be overridden for unittests.""" |
| pass |
| |
| def ParseAndroidEmulatorOptions(self): |
| """Parses Android emulator args, and if necessary, starts an emulator. |
| |
| No-ops if --avd-config is not specified or if an emulator is already |
| started. |
| |
| Performing this setup during argument parsing isn't ideal, but we need to |
| set up the emulator sometime between arg parsing and browser finding. |
| """ |
| if not self.avd_config: |
| return |
| if BrowserFinderOptions.emulator_environment: |
| return |
| build_android_dir = os.path.join( |
| util.GetChromiumSrcDir(), 'build', 'android') |
| if not os.path.exists(build_android_dir): |
| raise RuntimeError( |
| '--avd-config specified, but Chromium //build/android directory not ' |
| 'available') |
| # Everything after this point only works if //build/android is actually |
| # available, which we can't rely on, so use this to exit early in unittests. |
| self._NoOpFunctionForTesting() |
| sys.path.append(build_android_dir) |
| # pylint: disable=import-error |
| from pylib import constants as pylib_constants |
| from pylib.local.emulator import local_emulator_environment |
| # pylint: enable=import-error |
| |
| # We need to call this so that the Chromium output directory is set if it |
| # is not explicitly specified via the command line argument/environment |
| # variable. |
| pylib_constants.CheckOutputDirectory() |
| |
| class AvdArgs(object): |
| """A class to stand in for the AVD argparse.ArgumentParser object. |
| |
| Chromium's Android emulator code expects quite a few arguments from |
| //build/android/test_runner.py, but the only one we actually care about |
| here is avd_config. So, use a stand-in class with some sane defaults. |
| """ |
| def __init__(self, avd_config): |
| self.avd_config = avd_config |
| self.emulator_count = 1 |
| self.emulator_window = False |
| self.use_webview_provider = False |
| self.replace_system_package = False |
| self.denylist_file = None |
| self.test_devices = [] |
| self.enable_concurrent_adb = False |
| self.logcat_output_dir = None |
| self.logcat_output_file = None |
| self.num_retries = 1 |
| self.recover_devices = False |
| self.skip_clear_data = True |
| self.tool = None |
| self.adb_path = None |
| self.enable_device_cache = True |
| |
| avd_args = AvdArgs(self.avd_config) |
| BrowserFinderOptions.emulator_environment =\ |
| local_emulator_environment.LocalEmulatorEnvironment( |
| avd_args, None, None) |
| BrowserFinderOptions.emulator_environment.SetUp() |
| atexit.register(BrowserFinderOptions.emulator_environment.TearDown) |
| |
| # TODO(eakuefner): Factor this out into OptionBuilder pattern |
| def BuildRemotePlatformOptions(self): |
| if self.device or self.android_denylist_file: |
| self.remote_platform_options = ( |
| remote_platform_options.AndroidPlatformOptions( |
| self.device, self.android_denylist_file)) |
| |
| # We delete these options because they should live solely in the |
| # AndroidPlatformOptions instance belonging to this class. |
| if self.device: |
| del self.device |
| if self.android_denylist_file: |
| del self.android_denylist_file |
| else: |
| self.remote_platform_options = ( |
| remote_platform_options.AndroidPlatformOptions()) |
| |
| def AppendExtraBrowserArgs(self, args): |
| self.browser_options.AppendExtraBrowserArgs(args) |
| |
| def MergeDefaultValues(self, defaults): |
| for k, v in defaults.__dict__.items(): |
| self.ensure_value(k, v) |
| |
| |
| class BrowserOptions(object): |
| """Options to be used for launching a browser.""" |
| # Allows clients to check whether they are dealing with a browser_options |
| # object, without having to import this module. This may be needed in some |
| # cases to avoid cyclic-imports. |
| IS_BROWSER_OPTIONS = True |
| |
| # Levels of browser logging. |
| NO_LOGGING = 'none' |
| NON_VERBOSE_LOGGING = 'non-verbose' |
| VERBOSE_LOGGING = 'verbose' |
| SUPER_VERBOSE_LOGGING = 'super-verbose' |
| |
| _LOGGING_LEVELS = (NO_LOGGING, NON_VERBOSE_LOGGING, VERBOSE_LOGGING, |
| SUPER_VERBOSE_LOGGING) |
| _DEFAULT_LOGGING_LEVEL = NO_LOGGING |
| |
| def __init__(self): |
| self.browser_type = None |
| self.show_stdout = False |
| |
| self.extensions_to_load = [] |
| |
| # When set to True, the browser will use the default profile. Telemetry |
| # will not provide an alternate profile directory. |
| self.dont_override_profile = False |
| self.profile_dir = None |
| self.profile_type = None |
| |
| self.assert_gpu_compositing = False |
| self._extra_browser_args = set() |
| self.extra_wpr_args = [] |
| self.wpr_mode = wpr_modes.WPR_OFF |
| |
| # The amount of time Telemetry should wait for the browser to start. |
| # This property is not exposed as a command line option. |
| self._browser_startup_timeout = 60 |
| |
| self.disable_background_networking = True |
| self.browser_user_agent_type = None |
| |
| # Some benchmarks (startup, loading, and memory related) need this to get |
| # more representative measurements. Only has an effect for page sets based |
| # on SharedPageState. New clients should probably define their own shared |
| # state and make cache clearing decisions on their own. |
| self.flush_os_page_caches_on_start = False |
| |
| # Background pages of built-in component extensions can interfere with |
| # performance measurements. |
| # pylint: disable=invalid-name |
| self.disable_component_extensions_with_background_pages = True |
| # Disable default apps. |
| self.disable_default_apps = True |
| |
| self.logging_verbosity = self._DEFAULT_LOGGING_LEVEL |
| |
| # Whether to log verbose browser details like the full commandline used to |
| # start the browser. This variable can be changed from one run to another |
| # in order to cut back on log sizes. See crbug.com/943650. |
| self.trim_logs = False |
| |
| # The cloud storage bucket & path for uploading logs data produced by the |
| # browser to. |
| # If logs_cloud_remote_path is None, a random remote path is generated every |
| # time the logs data is uploaded. |
| self.logs_cloud_bucket = cloud_storage.TELEMETRY_OUTPUT |
| self.logs_cloud_remote_path = None |
| |
| # Whether to take screen shot for failed page & put them in telemetry's |
| # profiling results. |
| self.take_screenshot_for_failed_page = False |
| |
| # A list of tuples where the first element is path to an existing file, |
| # and the second argument is a path (relative to the user-data-dir) to copy |
| # the file to. Uses recursive directory creation if directories do not |
| # already exist. |
| self.profile_files_to_copy = [] |
| |
| # The list of compatibility change that you want to enforce, mainly for |
| # earlier versions of Chrome |
| self.compatibility_mode = [] |
| |
| # If not None, a ProjectConfig object with information about the benchmark |
| # runtime environment. |
| self.environment = None |
| |
| def __repr__(self): |
| return str(sorted(self.__dict__.items())) |
| |
| def Copy(self): |
| return copy.deepcopy(self) |
| |
| def IsCrosBrowserOptions(self): |
| return False |
| |
| @classmethod |
| def AddCommandLineArgs(cls, parser): |
| |
| ############################################################################ |
| # Please do not add any more options here without first discussing with # |
| # a telemetry owner. This is not the right place for platform-specific # |
| # options. # |
| ############################################################################ |
| |
| group = optparse.OptionGroup(parser, 'Browser options') |
| profile_choices = profile_types.GetProfileTypes() |
| group.add_option( |
| '--profile-type', |
| dest='profile_type', |
| type='choice', |
| default='clean', |
| choices=profile_choices, |
| help=('The user profile to use. A clean profile is used by default. ' |
| 'Supported values: ' + ', '.join(profile_choices))) |
| group.add_option( |
| '--profile-dir', |
| dest='profile_dir', |
| help='Profile directory to launch the browser with. ' |
| 'A clean profile is used by default') |
| group.add_option( |
| '--extra-browser-args', |
| dest='extra_browser_args_as_string', |
| help='Additional arguments to pass to the browser when it starts') |
| group.add_option( |
| '--extra-wpr-args', |
| dest='extra_wpr_args_as_string', |
| help=('Additional arguments to pass to Web Page Replay. ' |
| 'See third_party/web-page-replay/replay.py for usage.')) |
| group.add_option( |
| '--show-stdout', |
| action='store_true', |
| help='When possible, will display the stdout of the process') |
| |
| group.add_option( |
| '--browser-logging-verbosity', |
| dest='logging_verbosity', |
| type='choice', |
| choices=cls._LOGGING_LEVELS, |
| help=('Browser logging verbosity. The log file is saved in temp ' |
| "directory. Note that logging affects the browser's " |
| 'performance. Supported values: %s. Defaults to %s.' % ( |
| ', '.join(cls._LOGGING_LEVELS), cls._DEFAULT_LOGGING_LEVEL))) |
| group.add_option( |
| '--assert-gpu-compositing', |
| dest='assert_gpu_compositing', action='store_true', |
| help='Assert the browser uses gpu compositing and not software path.') |
| parser.add_option_group(group) |
| |
| group = optparse.OptionGroup(parser, 'Compatibility options') |
| group.add_option( |
| '--gtest_output', |
| help='Ignored argument for compatibility with runtest.py harness') |
| parser.add_option_group(group) |
| |
| def UpdateFromParseResults(self, finder_options): |
| """Copies our options from finder_options.""" |
| browser_options_list = [ |
| 'extra_browser_args_as_string', |
| 'extra_wpr_args_as_string', |
| 'profile_dir', |
| 'profile_type', |
| 'show_stdout', |
| 'compatibility_mode' |
| ] |
| for o in browser_options_list: |
| a = getattr(finder_options, o, None) |
| if a is not None: |
| setattr(self, o, a) |
| delattr(finder_options, o) |
| |
| self.browser_type = finder_options.browser_type |
| |
| if hasattr(self, 'extra_browser_args_as_string'): |
| tmp = shlex.split(self.extra_browser_args_as_string, posix=(not _IsWin())) |
| self.AppendExtraBrowserArgs(tmp) |
| delattr(self, 'extra_browser_args_as_string') |
| if hasattr(self, 'extra_wpr_args_as_string'): |
| tmp = shlex.split(self.extra_wpr_args_as_string, posix=(not _IsWin())) |
| self.extra_wpr_args.extend(tmp) |
| delattr(self, 'extra_wpr_args_as_string') |
| if self.profile_type == 'default': |
| self.dont_override_profile = True |
| |
| if self.profile_dir: |
| if self.profile_type != 'clean': |
| logging.critical( |
| "It's illegal to specify both --profile-type and --profile-dir.\n" |
| "For more information see: http://goo.gl/ngdGD5") |
| sys.exit(1) |
| self.profile_dir = os.path.abspath(self.profile_dir) |
| |
| if self.profile_dir and not os.path.isdir(self.profile_dir): |
| logging.critical( |
| "Directory specified by --profile-dir (%s) doesn't exist " |
| "or isn't a directory.\n" |
| "For more information see: http://goo.gl/ngdGD5" % self.profile_dir) |
| sys.exit(1) |
| |
| if not self.profile_dir: |
| self.profile_dir = profile_types.GetProfileDir(self.profile_type) |
| |
| if getattr(finder_options, 'logging_verbosity'): |
| self.logging_verbosity = finder_options.logging_verbosity |
| delattr(finder_options, 'logging_verbosity') |
| |
| # This deferred import is necessary because browser_options is imported in |
| # telemetry/telemetry/__init__.py. |
| finder_options.browser_options = CreateChromeBrowserOptions(self) |
| |
| @property |
| def extra_browser_args(self): |
| return self._extra_browser_args |
| |
| @property |
| def browser_startup_timeout(self): |
| return self._browser_startup_timeout |
| |
| @browser_startup_timeout.setter |
| def browser_startup_timeout(self, value): |
| self._browser_startup_timeout = value |
| |
| def AppendExtraBrowserArgs(self, args): |
| if isinstance(args, list): |
| self._extra_browser_args.update(args) |
| else: |
| self._extra_browser_args.add(args) |
| |
| |
| def CreateChromeBrowserOptions(br_options): |
| browser_type = br_options.browser_type |
| |
| if (platform.GetHostPlatform().GetOSName() == 'chromeos' or |
| (browser_type and 'cros' in browser_type)): |
| return CrosBrowserOptions(br_options) |
| |
| return br_options |
| |
| |
| class ChromeBrowserOptions(BrowserOptions): |
| """Chrome-specific browser options.""" |
| |
| def __init__(self, br_options): |
| super(ChromeBrowserOptions, self).__init__() |
| # Copy to self. |
| self.__dict__.update(br_options.__dict__) |
| |
| |
| class CrosBrowserOptions(ChromeBrowserOptions): |
| """ChromeOS-specific browser options.""" |
| |
| def __init__(self, br_options): |
| super(CrosBrowserOptions, self).__init__(br_options) |
| # Create a browser with oobe property. |
| self.create_browser_with_oobe = False |
| # Clear enterprise policy before logging in. |
| self.clear_enterprise_policy = True |
| # By default, allow policy fetches to fail. A side effect is that the user |
| # profile may become initialized before policy is available. |
| # When this is set to True, chrome will not allow policy fetches to fail and |
| # block user profile initialization on policy initialization. |
| self.expect_policy_fetch = False |
| # Disable GAIA/enterprise services. |
| self.disable_gaia_services = True |
| # Mute audio. |
| self.mute_audio = True |
| |
| # TODO(cywang): crbug.com/760414 |
| # Add login delay for ARC container boot time measurement for now. |
| # Should actually simulate username/password typing in the login |
| # screen instead or make the wait time fixed for cros login. |
| self.login_delay = 0 |
| |
| self.auto_login = True |
| self.gaia_login = False |
| self.username = 'test@test.test' |
| self.password = '' |
| self.gaia_id = '12345' |
| # For non-accelerated QEMU VMs. |
| self.browser_startup_timeout = 240 |
| |
| def IsCrosBrowserOptions(self): |
| return True |