Index: tools/telemetry/telemetry/page/shared_page_state.py |
diff --git a/tools/telemetry/telemetry/page/shared_page_state.py b/tools/telemetry/telemetry/page/shared_page_state.py |
deleted file mode 100644 |
index a3046968d111669c6ba7a865d3f54389e7e0267e..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/telemetry/page/shared_page_state.py |
+++ /dev/null |
@@ -1,527 +0,0 @@ |
-# Copyright 2014 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. |
- |
-import logging |
-import os |
-import shutil |
-import sys |
-import tempfile |
-import zipfile |
- |
-from catapult_base import cloud_storage # pylint: disable=import-error |
- |
-from telemetry.core import exceptions |
-from telemetry.core import util |
-from telemetry import decorators |
-from telemetry.internal.browser import browser_finder |
-from telemetry.internal.browser import browser_finder_exceptions |
-from telemetry.internal.browser import browser_info as browser_info_module |
-from telemetry.internal.platform.profiler import profiler_finder |
-from telemetry.internal.util import exception_formatter |
-from telemetry.internal.util import file_handle |
-from telemetry.page import page_test |
-from telemetry import story |
-from telemetry.util import image_util |
-from telemetry.util import wpr_modes |
-from telemetry.web_perf import timeline_based_measurement |
- |
- |
-def _PrepareFinderOptions(finder_options, test, device_type): |
- browser_options = finder_options.browser_options |
- # Set up user agent. |
- browser_options.browser_user_agent_type = device_type |
- |
- test.CustomizeBrowserOptions(finder_options.browser_options) |
- if finder_options.profiler: |
- profiler_class = profiler_finder.FindProfiler(finder_options.profiler) |
- profiler_class.CustomizeBrowserOptions(browser_options.browser_type, |
- finder_options) |
- |
- |
-class SharedPageState(story.SharedState): |
- """ |
- This class contains all specific logic necessary to run a Chrome browser |
- benchmark. |
- """ |
- |
- _device_type = None |
- |
- def __init__(self, test, finder_options, story_set): |
- super(SharedPageState, self).__init__(test, finder_options, story_set) |
- if isinstance(test, timeline_based_measurement.TimelineBasedMeasurement): |
- assert not finder_options.profiler, ( |
- 'This is a Timeline Based Measurement benchmark. You cannot run it ' |
- 'with the --profiler flag. If you need trace data, tracing is always ' |
- ' enabled in Timeline Based Measurement benchmarks and you can get ' |
- 'the trace data by using --output-format=json.') |
- # This is to avoid the cyclic-import caused by timeline_based_page_test. |
- from telemetry.web_perf import timeline_based_page_test |
- self._test = timeline_based_page_test.TimelineBasedPageTest(test) |
- else: |
- self._test = test |
- device_type = self._device_type |
- # TODO(aiolos, nednguyen): Remove this logic of pulling out user_agent_type |
- # from story_set once all page_set are converted to story_set |
- # (crbug.com/439512). |
- |
- def _IsPageSetInstance(s): |
- # This is needed to avoid importing telemetry.page.page_set which will |
- # cause cyclic import. |
- return 'PageSet' == s.__class__.__name__ or 'PageSet' in ( |
- list(c.__name__ for c in s.__class__.__bases__)) |
- if not device_type and _IsPageSetInstance(story_set): |
- device_type = story_set.user_agent_type |
- _PrepareFinderOptions(finder_options, self._test, device_type) |
- self._browser = None |
- self._finder_options = finder_options |
- self._possible_browser = self._GetPossibleBrowser( |
- self._test, finder_options) |
- |
- # TODO(slamm): Remove _append_to_existing_wpr when replay lifetime changes. |
- self._append_to_existing_wpr = False |
- self._first_browser = True |
- self._did_login_for_current_page = False |
- self._current_page = None |
- self._current_tab = None |
- self._migrated_profile = None |
- |
- self._pregenerated_profile_archive_dir = None |
- self._test.SetOptions(self._finder_options) |
- |
- @property |
- def browser(self): |
- return self._browser |
- |
- def _FindBrowser(self, finder_options): |
- possible_browser = browser_finder.FindBrowser(finder_options) |
- if not possible_browser: |
- raise browser_finder_exceptions.BrowserFinderException( |
- 'No browser found.\n\nAvailable browsers:\n%s\n' % |
- '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options))) |
- return possible_browser |
- |
- def _GetPossibleBrowser(self, test, finder_options): |
- """Return a possible_browser with the given options for |test|. """ |
- possible_browser = self._FindBrowser(finder_options) |
- finder_options.browser_options.browser_type = ( |
- possible_browser.browser_type) |
- |
- enabled, msg = decorators.IsEnabled(test, possible_browser) |
- if not enabled and not finder_options.run_disabled_tests: |
- logging.warning(msg) |
- logging.warning('You are trying to run a disabled test.') |
- logging.warning( |
- 'Pass --also-run-disabled-tests to squelch this message.') |
- sys.exit(0) |
- |
- if possible_browser.IsRemote(): |
- possible_browser.RunRemote() |
- sys.exit(0) |
- return possible_browser |
- |
- def _TryCaptureScreenShot(self, page, tab, results): |
- try: |
- # TODO(nednguyen): once all platforms support taking screenshot, |
- # remove the tab checking logic and consider moving this to story_runner. |
- # (crbug.com/369490) |
- if tab.browser.platform.CanTakeScreenshot(): |
- tf = tempfile.NamedTemporaryFile(delete=False, suffix='.png') |
- tf.close() |
- tab.browser.platform.TakeScreenshot(tf.name) |
- results.AddProfilingFile(page, file_handle.FromTempFile(tf)) |
- elif tab.IsAlive() and tab.screenshot_supported: |
- tf = tempfile.NamedTemporaryFile(delete=False, suffix='.png') |
- tf.close() |
- image = tab.Screenshot() |
- image_util.WritePngFile(image, tf.name) |
- results.AddProfilingFile(page, file_handle.FromTempFile(tf)) |
- else: |
- logging.warning( |
- 'Either tab has crashed or browser does not support taking tab ' |
- 'screenshot. Skip taking screenshot on failure.') |
- except Exception as e: |
- logging.warning('Exception when trying to capture screenshot: %s', |
- repr(e)) |
- |
- def DidRunStory(self, results): |
- if self._finder_options.profiler: |
- self._StopProfiling(results) |
- # We might hang while trying to close the connection, and need to guarantee |
- # the page will get cleaned up to avoid future tests failing in weird ways. |
- try: |
- if self._current_tab and self._current_tab.IsAlive(): |
- self._current_tab.CloseConnections() |
- except Exception: |
- if self._current_tab: |
- self._current_tab.Close() |
- finally: |
- if self._current_page.credentials and self._did_login_for_current_page: |
- self.browser.credentials.LoginNoLongerNeeded( |
- self._current_tab, self._current_page.credentials) |
- if self._test.StopBrowserAfterPage(self.browser, self._current_page): |
- self._StopBrowser() |
- self._current_page = None |
- self._current_tab = None |
- |
- @property |
- def platform(self): |
- return self._possible_browser.platform |
- |
- def _PrepareWpr(self, network_controller, archive_path, |
- make_javascript_deterministic): |
- browser_options = self._finder_options.browser_options |
- if self._finder_options.use_live_sites: |
- browser_options.wpr_mode = wpr_modes.WPR_OFF |
- elif browser_options.wpr_mode != wpr_modes.WPR_RECORD: |
- browser_options.wpr_mode = ( |
- wpr_modes.WPR_REPLAY |
- if archive_path and os.path.isfile(archive_path) |
- else wpr_modes.WPR_OFF) |
- |
- # Replay's life-cycle is tied to the browser. Start and Stop are handled by |
- # platform_backend.DidCreateBrowser and platform_backend.WillCloseBrowser, |
- # respectively. |
- # TODO(slamm): Update life-cycle comment with https://crbug.com/424777 fix. |
- wpr_mode = browser_options.wpr_mode |
- if self._append_to_existing_wpr and wpr_mode == wpr_modes.WPR_RECORD: |
- wpr_mode = wpr_modes.WPR_APPEND |
- network_controller.SetReplayArgs( |
- archive_path, wpr_mode, browser_options.netsim, |
- browser_options.extra_wpr_args, make_javascript_deterministic) |
- |
- def _StartBrowser(self, page): |
- assert self._browser is None |
- self._possible_browser.SetCredentialsPath(page.credentials_path) |
- |
- self._test.WillStartBrowser(self.platform) |
- if page.startup_url: |
- self._finder_options.browser_options.startup_url = page.startup_url |
- self._browser = self._possible_browser.Create(self._finder_options) |
- self._test.DidStartBrowser(self.browser) |
- |
- if self._first_browser: |
- self._first_browser = False |
- self.browser.credentials.WarnIfMissingCredentials(page) |
- logging.info('OS: %s %s', |
- self.platform.GetOSName(), |
- self.platform.GetOSVersionName()) |
- if self.browser.supports_system_info: |
- system_info = self.browser.GetSystemInfo() |
- if system_info.model_name: |
- logging.info('Model: %s', system_info.model_name) |
- if system_info.gpu: |
- for i, device in enumerate(system_info.gpu.devices): |
- logging.info('GPU device %d: %s', i, device) |
- if system_info.gpu.aux_attributes: |
- logging.info('GPU Attributes:') |
- for k, v in sorted(system_info.gpu.aux_attributes.iteritems()): |
- logging.info(' %-20s: %s', k, v) |
- if system_info.gpu.feature_status: |
- logging.info('Feature Status:') |
- for k, v in sorted(system_info.gpu.feature_status.iteritems()): |
- logging.info(' %-20s: %s', k, v) |
- if system_info.gpu.driver_bug_workarounds: |
- logging.info('Driver Bug Workarounds:') |
- for workaround in system_info.gpu.driver_bug_workarounds: |
- logging.info(' %s', workaround) |
- else: |
- logging.info('No GPU devices') |
- else: |
- logging.warning('System info not supported') |
- |
- def WillRunStory(self, page): |
- if self._ShouldDownloadPregeneratedProfileArchive(): |
- self._DownloadPregeneratedProfileArchive() |
- |
- if self._ShouldMigrateProfile(): |
- self._MigratePregeneratedProfile() |
- |
- page_set = page.page_set |
- self._current_page = page |
- if self._test.RestartBrowserBeforeEachPage() or page.startup_url: |
- self._StopBrowser() |
- started_browser = not self.browser |
- self._PrepareWpr(self.platform.network_controller, |
- page_set.WprFilePathForStory(page), |
- page.make_javascript_deterministic) |
- if self.browser: |
- # Set new credential path for browser. |
- self.browser.credentials.credentials_path = page.credentials_path |
- self.platform.network_controller.UpdateReplayForExistingBrowser() |
- else: |
- self._StartBrowser(page) |
- if self.browser.supports_tab_control and self._test.close_tabs_before_run: |
- # Create a tab if there's none. |
- if len(self.browser.tabs) == 0: |
- self.browser.tabs.New() |
- |
- # Ensure only one tab is open, unless the test is a multi-tab test. |
- if not self._test.is_multi_tab_test: |
- while len(self.browser.tabs) > 1: |
- self.browser.tabs[-1].Close() |
- |
- # Must wait for tab to commit otherwise it can commit after the next |
- # navigation has begun and RenderFrameHostManager::DidNavigateMainFrame() |
- # will cancel the next navigation because it's pending. This manifests as |
- # the first navigation in a PageSet freezing indefinitely because the |
- # navigation was silently canceled when |self.browser.tabs[0]| was |
- # committed. Only do this when we just started the browser, otherwise |
- # there are cases where previous pages in a PageSet never complete |
- # loading so we'll wait forever. |
- if started_browser: |
- self.browser.tabs[0].WaitForDocumentReadyStateToBeComplete() |
- |
- # Start profiling if needed. |
- if self._finder_options.profiler: |
- self._StartProfiling(self._current_page) |
- |
- def CanRunStory(self, page): |
- return self.CanRunOnBrowser(browser_info_module.BrowserInfo(self.browser), |
- page) |
- |
- def CanRunOnBrowser(self, browser_info, |
- page): # pylint: disable=unused-argument |
- """Override this to return whether the browser brought up by this state |
- instance is suitable for running the given page. |
- |
- Args: |
- browser_info: an instance of telemetry.core.browser_info.BrowserInfo |
- page: an instance of telemetry.page.Page |
- """ |
- del browser_info, page # unused |
- return True |
- |
- def _PreparePage(self): |
- self._current_tab = self._test.TabForPage(self._current_page, self.browser) |
- if self._current_page.is_file: |
- self.platform.SetHTTPServerDirectories( |
- self._current_page.page_set.serving_dirs | |
- set([self._current_page.serving_dir])) |
- |
- if self._current_page.credentials: |
- if not self.browser.credentials.LoginNeeded( |
- self._current_tab, self._current_page.credentials): |
- raise page_test.Failure( |
- 'Login as ' + self._current_page.credentials + ' failed') |
- self._did_login_for_current_page = True |
- |
- if self._test.clear_cache_before_each_run: |
- self._current_tab.ClearCache(force=True) |
- |
- @property |
- def current_page(self): |
- return self._current_page |
- |
- @property |
- def current_tab(self): |
- return self._current_tab |
- |
- @property |
- def page_test(self): |
- return self._test |
- |
- def RunStory(self, results): |
- try: |
- self._PreparePage() |
- self._current_page.Run(self) |
- self._test.ValidateAndMeasurePage( |
- self._current_page, self._current_tab, results) |
- except exceptions.Error: |
- if self._finder_options.browser_options.take_screenshot_for_failed_page: |
- self._TryCaptureScreenShot(self._current_page, self._current_tab, |
- results) |
- if self._test.is_multi_tab_test: |
- # Avoid trying to recover from an unknown multi-tab state. |
- exception_formatter.PrintFormattedException( |
- msg='Telemetry Error during multi tab test:') |
- raise page_test.MultiTabTestAppCrashError |
- raise |
- except Exception: |
- if self._finder_options.browser_options.take_screenshot_for_failed_page: |
- self._TryCaptureScreenShot(self._current_page, self._current_tab, |
- results) |
- raise |
- |
- def TearDownState(self): |
- if self._migrated_profile: |
- shutil.rmtree(self._migrated_profile) |
- self._migrated_profile = None |
- |
- self._StopBrowser() |
- self.platform.StopAllLocalServers() |
- |
- def _StopBrowser(self): |
- if self._browser: |
- self._browser.Close() |
- self._browser = None |
- |
- # Restarting the state will also restart the wpr server. If we're |
- # recording, we need to continue adding into the same wpr archive, |
- # not overwrite it. |
- self._append_to_existing_wpr = True |
- |
- def _StartProfiling(self, page): |
- output_file = os.path.join(self._finder_options.output_dir, |
- page.file_safe_name) |
- is_repeating = (self._finder_options.page_repeat != 1 or |
- self._finder_options.pageset_repeat != 1) |
- if is_repeating: |
- output_file = util.GetSequentialFileName(output_file) |
- self.browser.profiling_controller.Start( |
- self._finder_options.profiler, output_file) |
- |
- def _StopProfiling(self, results): |
- if self.browser: |
- profiler_files = self.browser.profiling_controller.Stop() |
- for f in profiler_files: |
- if os.path.isfile(f): |
- results.AddProfilingFile(self._current_page, |
- file_handle.FromFilePath(f)) |
- |
- def _ShouldMigrateProfile(self): |
- return not self._migrated_profile |
- |
- def _MigrateProfile(self, finder_options, found_browser, |
- initial_profile, final_profile): |
- """Migrates a profile to be compatible with a newer version of Chrome. |
- |
- Launching Chrome with the old profile will perform the migration. |
- """ |
- # Save the current input and output profiles. |
- saved_input_profile = finder_options.browser_options.profile_dir |
- saved_output_profile = finder_options.output_profile_path |
- |
- # Set the input and output profiles. |
- finder_options.browser_options.profile_dir = initial_profile |
- finder_options.output_profile_path = final_profile |
- |
- # Launch the browser, then close it. |
- browser = found_browser.Create(finder_options) |
- browser.Close() |
- |
- # Load the saved input and output profiles. |
- finder_options.browser_options.profile_dir = saved_input_profile |
- finder_options.output_profile_path = saved_output_profile |
- |
- def _MigratePregeneratedProfile(self): |
- """Migrates the pre-generated profile by launching Chrome with it. |
- |
- On success, updates self._migrated_profile and |
- self._finder_options.browser_options.profile_dir with the directory of the |
- migrated profile. |
- """ |
- self._migrated_profile = tempfile.mkdtemp() |
- logging.info("Starting migration of pre-generated profile to %s", |
- self._migrated_profile) |
- pregenerated_profile = self._finder_options.browser_options.profile_dir |
- |
- possible_browser = self._FindBrowser(self._finder_options) |
- self._MigrateProfile(self._finder_options, possible_browser, |
- pregenerated_profile, self._migrated_profile) |
- self._finder_options.browser_options.profile_dir = self._migrated_profile |
- logging.info("Finished migration of pre-generated profile to %s", |
- self._migrated_profile) |
- |
- def GetPregeneratedProfileArchiveDir(self): |
- return self._pregenerated_profile_archive_dir |
- |
- def SetPregeneratedProfileArchiveDir(self, archive_path): |
- """ |
- Benchmarks can set a pre-generated profile archive to indicate that when |
- Chrome is launched, it should have a --user-data-dir set to the |
- pre-generated profile, rather than to an empty profile. |
- |
- If the benchmark is invoked with the option --profile-dir=<dir>, that |
- option overrides this value. |
- """ |
- self._pregenerated_profile_archive_dir = archive_path |
- |
- def _ShouldDownloadPregeneratedProfileArchive(self): |
- """Whether to download a pre-generated profile archive.""" |
- # There is no pre-generated profile archive. |
- if not self.GetPregeneratedProfileArchiveDir(): |
- return False |
- |
- # If profile dir is specified on command line, use that instead. |
- if self._finder_options.browser_options.profile_dir: |
- logging.warning("Profile directory specified on command line: %s, this" |
- "overrides the benchmark's default profile directory.", |
- self._finder_options.browser_options.profile_dir) |
- return False |
- |
- # If the browser is remote, a local download has no effect. |
- if self._possible_browser.IsRemote(): |
- return False |
- |
- return True |
- |
- def _DownloadPregeneratedProfileArchive(self): |
- """Download and extract the profile directory archive if one exists. |
- |
- On success, updates self._finder_options.browser_options.profile_dir with |
- the directory of the extracted profile. |
- """ |
- # Download profile directory from cloud storage. |
- generated_profile_archive_path = self.GetPregeneratedProfileArchiveDir() |
- |
- try: |
- cloud_storage.GetIfChanged(generated_profile_archive_path, |
- cloud_storage.PUBLIC_BUCKET) |
- except (cloud_storage.CredentialsError, |
- cloud_storage.PermissionError) as e: |
- if os.path.exists(generated_profile_archive_path): |
- # If the profile directory archive exists, assume the user has their |
- # own local copy simply warn. |
- logging.warning('Could not download Profile archive: %s', |
- generated_profile_archive_path) |
- else: |
- # If the archive profile directory doesn't exist, this is fatal. |
- logging.error('Can not run without required profile archive: %s. ' |
- 'If you believe you have credentials, follow the ' |
- 'instructions below.', |
- generated_profile_archive_path) |
- logging.error(str(e)) |
- sys.exit(-1) |
- |
- # Check to make sure the zip file exists. |
- if not os.path.isfile(generated_profile_archive_path): |
- raise Exception("Profile directory archive not downloaded: ", |
- generated_profile_archive_path) |
- |
- # The location to extract the profile into. |
- extracted_profile_dir_path = ( |
- os.path.splitext(generated_profile_archive_path)[0]) |
- |
- # Unzip profile directory. |
- with zipfile.ZipFile(generated_profile_archive_path) as f: |
- try: |
- f.extractall(os.path.dirname(generated_profile_archive_path)) |
- except e: |
- # Cleanup any leftovers from unzipping. |
- if os.path.exists(extracted_profile_dir_path): |
- shutil.rmtree(extracted_profile_dir_path) |
- logging.error("Error extracting profile directory zip file: %s", e) |
- sys.exit(-1) |
- |
- # Run with freshly extracted profile directory. |
- logging.info("Using profile archive directory: %s", |
- extracted_profile_dir_path) |
- self._finder_options.browser_options.profile_dir = ( |
- extracted_profile_dir_path) |
- |
- |
-class SharedMobilePageState(SharedPageState): |
- _device_type = 'mobile' |
- |
- |
-class SharedDesktopPageState(SharedPageState): |
- _device_type = 'desktop' |
- |
- |
-class SharedTabletPageState(SharedPageState): |
- _device_type = 'tablet' |
- |
- |
-class Shared10InchTabletPageState(SharedPageState): |
- _device_type = 'tablet_10_inch' |