chromium/tools/perf/page_sets/press_story.py

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

from telemetry.page import page as page_module


class PressStory(page_module.Page):
  """Base class for Press stories.

    Override ExecuteTest to execute javascript on the page and
    ParseTestResults to obtain javascript metrics from page.

    Example Implementation:

    class FooPressStory:
      URL = 'http://example.com/foo_story'
      NAME = 'FooStory'

      def ExecuteTest(self, action_runner):
        // Execute some javascript

      def ParseTestResults(self, action_runner):
        js_code = 'some_js_expression;'
        self.AddJavaScriptMeasurement(name, unit, js_code)

  """
  URL = None
  DETERMINISTIC_JS = False
  NAME = None

  def __init__(self,
               page_set,
               tags=None,
               extra_browser_args=None,
               url=None,
               name=None):
    super(PressStory,
          self).__init__(url or self.URL,
                         page_set,
                         base_dir=page_set.base_dir,
                         make_javascript_deterministic=self.DETERMINISTIC_JS,
                         name=name or self.NAME or self.URL,
                         tags=self.PreprocessTags(tags),
                         extra_browser_args=extra_browser_args)
    self._measurements = []
    self._action_runner = None

  def PreprocessTags(self, tags=None):
    if tags is None:
      tags = []
    else:
      assert 'all' not in tags
    tags.append('all')
    return tags

  def AddMeasurement(self, name, unit, samples, description=None):
    """Record an ad-hoc measurement.

    Args:
      name: A string with the name of the measurement (e.g. 'score', 'runtime',
        etc).
      unit: A string specifying the unit used for measurements (e.g. 'ms',
        'count', etc).
      samples: Either a single numeric value or a list of numeric values to
        record as part of this measurement.
      description: An optional string with a short human readable description
        of the measurement.
    """
    # TODO(crbug.com/40642835): Ideally, these should be recorded directly into
    # the results object, rather than held on this temporary list. That needs,
    # however, another slight refactor to make the results object available at
    # this point.
    self._measurements.append({'name': name, 'unit': unit, 'samples': samples,
                               'description': description})

  def AddJavaScriptMeasurement(self, name, unit, code, **kwargs):
    """Run some JavaScript to obtain and record an ad-hoc measurements.

    Args:
      name: A string with the name of the measurement (e.g. 'score', 'runtime',
        etc).
      unit: A string specifying the unit used for measurements (e.g. 'ms',
        'count', etc).
      code: A piece of JavaScript code to run on the current tab, it must
        return either a single or a list of numeric values. These are the
        values for the measurement to be recorded.
      description: An optional string with a short human readable description
        of the measurement.
      Other keyword arguments provide values to be interpolated within
          the JavaScript code. See telemetry.util.js_template for details.
    """
    description = kwargs.pop('description', None)
    samples = self._action_runner.EvaluateJavaScript(code, **kwargs)
    self.AddMeasurement(name, unit, samples, description)

  def GetMeasurements(self):
    return self._measurements

  def Run(self, shared_state):
    self._measurements = []
    super(PressStory, self).Run(shared_state)

  def ExecuteTest(self, action_runner):
    pass

  def ParseTestResults(self, action_runner):
    pass

  def RunPageInteractions(self, action_runner):
    self._action_runner = action_runner
    try:
      self.ExecuteTest(action_runner)
      self.ParseTestResults(action_runner)
    finally:
      self._action_runner = None