chromium/tools/perf/page_sets/system_health/system_health_story.py

# Copyright 2016 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import six

from page_sets.system_health import platforms
from page_sets.system_health import story_tags

from telemetry.page import page as page_module
from telemetry.page import shared_page_state
from telemetry.util import wpr_modes


# Extra wait time after the page has loaded required by the loading metric. We
# use it in all benchmarks to avoid divergence between benchmarks.
# TODO(petrcermak): Switch the memory benchmarks to use it as well.
_WAIT_TIME_AFTER_LOAD = 10


class _SystemHealthSharedState(shared_page_state.SharedPageState):
  """Shared state which enables disabling stories on individual platforms.
     This should be used only to disable the stories permanently. For
     disabling stories temporarily use story expectations in ./expectations.py.
  """

  def CanRunOnBrowser(self, browser_info, page):
    if (browser_info.browser_type.startswith('android-webview')
        and page.WEBVIEW_NOT_SUPPORTED):
      return False

    if page.TAGS and story_tags.WEBGL in page.TAGS:
      return browser_info.HasWebGLSupport()
    return True


class _MetaSystemHealthStory(type):
  """Metaclass for SystemHealthStory."""

  @property
  def ABSTRACT_STORY(cls):
    """Class field marking whether the class is abstract.

    If true, the story will NOT be instantiated and added to a System Health
    story set. This field is NOT inherited by subclasses (that's why it's
    defined on the metaclass).
    """
    return cls.__dict__.get('ABSTRACT_STORY', False)


class SystemHealthStory(
    six.with_metaclass(_MetaSystemHealthStory, page_module.Page)):
  """Abstract base class for System Health user stories."""

  # The full name of a single page story has the form CASE:GROUP:PAGE:[VERSION]
  # (e.g. 'load:search:google' or 'load:search:google:2018').
  NAME = NotImplemented
  URL = NotImplemented
  ABSTRACT_STORY = True
  # Skip the login flow in replay mode
  # If you want to replay the login flow in your story, set SKIP_LOGIN to False
  SKIP_LOGIN = True
  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
  TAGS = []
  PLATFORM_SPECIFIC = False
  WEBVIEW_NOT_SUPPORTED = False

  def __init__(self, story_set, take_memory_measurement,
      extra_browser_args=None):
    case, group, _ = self.NAME.split(':', 2)
    tags = []
    found_year_tag = False
    for t in self.TAGS:  # pylint: disable=not-an-iterable
      assert t in story_tags.ALL_TAGS
      tags.append(t.name)
      if t in story_tags.YEAR_TAGS:
        # Assert that this is the first year tag.
        assert not found_year_tag, (
            "%s has more than one year tag found." % self.__class__.__name__)
        found_year_tag = True
    # Assert that there is one year tag.
    assert found_year_tag, (
        "%s needs exactly one year tag." % self.__class__.__name__)
    super(SystemHealthStory, self).__init__(
        shared_page_state_class=_SystemHealthSharedState,
        page_set=story_set, name=self.NAME, url=self.URL, tags=tags,
        grouping_keys={'case': case, 'group': group},
        platform_specific=self.PLATFORM_SPECIFIC,
        extra_browser_args=extra_browser_args)
    self._take_memory_measurement = take_memory_measurement

  @classmethod
  def GetStoryDescription(cls):
    if cls.__doc__:
      return cls.__doc__
    return cls.GenerateStoryDescription()

  @classmethod
  def GenerateStoryDescription(cls):
    """ Subclasses of SystemHealthStory can override this to auto generate
    their story description.
    However, it's recommended to use the Python docstring to describe the user
    stories instead and this should only be used for very repetitive cases.
    """
    return None

  def _Measure(self, action_runner):
    if self._take_memory_measurement:
      action_runner.MeasureMemory(deterministic_mode=True)
    else:
      action_runner.Wait(_WAIT_TIME_AFTER_LOAD)

  def _Login(self, action_runner):
    pass

  def _DidLoadDocument(self, action_runner):
    pass

  def RunNavigateSteps(self, action_runner):
    if not (self.SKIP_LOGIN and self.wpr_mode == wpr_modes.WPR_REPLAY):
      self._Login(action_runner)
    super(SystemHealthStory, self).RunNavigateSteps(action_runner)

  def RunPageInteractions(self, action_runner):
    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
    self._DidLoadDocument(action_runner)
    self._Measure(action_runner)