<reponame>metux/chromium-deb<filename>tools/perf/benchmarks/blink_perf_unittest.py
# Copyright 2017 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 os
import unittest

from telemetry import story
from telemetry.page import page as page_module
from telemetry.testing import options_for_unittests
from telemetry.testing import page_test_test_case
from telemetry.timeline import async_slice
from telemetry.timeline import model as model_module


from benchmarks import blink_perf


class BlinkPerfTest(page_test_test_case.PageTestTestCase):
  _BLINK_PERF_TEST_DATA_DIR = os.path.join(os.path.dirname(__file__),
      '..', '..', '..', 'third_party', 'WebKit', 'PerformanceTests',
      'TestData')

  _BLINK_PERF_RESOURCES_DIR = os.path.join(os.path.dirname(__file__),
      '..', '..', '..', 'third_party', 'WebKit', 'PerformanceTests',
      'resources')
  def setUp(self):
    self._options = options_for_unittests.GetCopy()
    # pylint: disable=protected-access
    self._measurement = blink_perf._BlinkPerfMeasurement()
    # pylint: enable=protected-access

  def _CreateStorySetForTestFile(self, test_file_name):
    story_set = story.StorySet(base_dir=self._BLINK_PERF_TEST_DATA_DIR,
        serving_dirs={self._BLINK_PERF_TEST_DATA_DIR,
                      self._BLINK_PERF_RESOURCES_DIR})
    page = page_module.Page('file://' + test_file_name, story_set,
        base_dir=story_set.base_dir, name=test_file_name)
    story_set.AddStory(page)
    return story_set

  def testBlinkPerfTracingMetricsForMeasureTime(self):
    results = self.RunMeasurement(measurement=self._measurement,
        ps=self._CreateStorySetForTestFile('append-child-measure-time.html'),
        options=self._options)
    self.assertFalse(results.failures)
    self.assertEquals(len(results.FindAllTraceValues()), 1)

    frame_view_layouts = results.FindAllPageSpecificValuesNamed(
        'LocalFrameView::layout')
    self.assertEquals(len(frame_view_layouts), 1)
    # append-child-measure-time.html specifies 5 iterationCount.
    self.assertEquals(len(frame_view_layouts[0].values), 5)
    self.assertGreater(frame_view_layouts[0].GetRepresentativeNumber(), 0.001)

    update_layout_trees = results.FindAllPageSpecificValuesNamed(
        'UpdateLayoutTree')
    self.assertEquals(len(update_layout_trees), 1)
    # append-child-measure-time.html specifies 5 iterationCount.
    self.assertEquals(len(update_layout_trees[0].values), 5)

    self.assertGreater(update_layout_trees[0].GetRepresentativeNumber(), 0.001)

  def testBlinkPerfTracingMetricsForMeasureFrameTime(self):
    results = self.RunMeasurement(measurement=self._measurement,
        ps=self._CreateStorySetForTestFile(
            'color-changes-measure-frame-time.html'),
        options=self._options)
    self.assertFalse(results.failures)
    self.assertEquals(len(results.FindAllTraceValues()), 1)

    frame_view_prepaints = results.FindAllPageSpecificValuesNamed(
        'LocalFrameView::prePaint')

    self.assertEquals(len(frame_view_prepaints), 1)
    # color-changes-measure-frame-time.html specifies 9 iterationCount.
    self.assertEquals(len(frame_view_prepaints[0].values), 9)
    self.assertGreater(frame_view_prepaints[0].GetRepresentativeNumber(), 0.001)

    frame_view_painttrees = results.FindAllPageSpecificValuesNamed(
        'LocalFrameView::paintTree')
    self.assertEquals(len(frame_view_painttrees), 1)
    # color-changes-measure-frame-time.html specifies 9 iterationCount.
    self.assertEquals(len(frame_view_painttrees[0].values), 9)
    self.assertGreater(frame_view_painttrees[0].GetRepresentativeNumber(),
        0.001)

  def testBlinkPerfTracingMetricsForMeasurePageLoadTime(self):
    results = self.RunMeasurement(measurement=self._measurement,
        ps=self._CreateStorySetForTestFile(
            'simple-html-measure-page-load-time.html'),
        options=self._options)
    self.assertFalse(results.failures)
    self.assertEquals(len(results.FindAllTraceValues()), 1)

    create_child_frame = results.FindAllPageSpecificValuesNamed(
        'WebLocalFrameImpl::createChildframe')
    self.assertEquals(len(create_child_frame), 1)
    # color-changes-measure-frame-time.html specifies 7 iterationCount.
    self.assertEquals(len(create_child_frame[0].values), 7)
    self.assertGreater(create_child_frame[0].GetRepresentativeNumber(), 0.001)

    post_layout_task = results.FindAllPageSpecificValuesNamed(
        'LocalFrameView::performPostLayoutTasks')
    self.assertEquals(len(post_layout_task), 1)
    # color-changes-measure-frame-time.html specifies 7 iterationCount.
    self.assertEquals(len(post_layout_task[0].values), 7)
    self.assertGreater(post_layout_task[0].GetRepresentativeNumber(), 0.001)


  def testBlinkPerfTracingMetricsForMeasureAsync(self):
    results = self.RunMeasurement(measurement=self._measurement,
        ps=self._CreateStorySetForTestFile(
            'simple-blob-measure-async.html'),
        options=self._options)
    self.assertFalse(results.failures)
    self.assertEquals(len(results.FindAllTraceValues()), 1)

    blob_requests = results.FindAllPageSpecificValuesNamed(
        'BlobRequest')
    self.assertEquals(len(blob_requests), 1)
    # simple-blob-measure-async.html specifies 6 iterationCount.
    self.assertEquals(len(blob_requests[0].values), 6)
    self.assertGreater(blob_requests[0].GetRepresentativeNumber(), 0.001)

    blob_request_read_raw_data = results.FindAllPageSpecificValuesNamed(
        'BlobRequest::ReadRawData')
    self.assertEquals(len(blob_request_read_raw_data), 1)
    # simple-blob-measure-async.html specifies 6 iterationCount.
    self.assertEquals(len(blob_request_read_raw_data[0].values), 6)
    self.assertGreater(
        blob_request_read_raw_data[0].GetRepresentativeNumber(), 0.001)


# pylint: disable=protected-access
# This is needed for testing _ComputeTraceEventsThreadTimeForBlinkPerf method.
class ComputeTraceEventsMetricsForBlinkPerfTest(unittest.TestCase):

  def _AddBlinkTestSlice(self, renderer_thread, start, end):
    s = async_slice.AsyncSlice(
        'blink', 'blink_perf.runTest',
        timestamp=start, duration=end - start, start_thread=renderer_thread,
        end_thread=renderer_thread)
    renderer_thread.AddAsyncSlice(s)

  def testTraceEventMetricsSingleBlinkTest(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    # Set up a main thread model that looks like:
    #   [          blink_perf.run_test                ]
    #   |     [ foo ]          [  bar ]      [        baz     ]
    #   |     |     |          |      |      |        |       |
    #   100   120   140        400    420    500      550     600
    #             |                |                  |
    # CPU dur:    15               18                 70
    #
    self._AddBlinkTestSlice(renderer_main, 100, 550)

    renderer_main.BeginSlice('blink', 'foo', 120, 122)
    renderer_main.EndSlice(140, 137)

    renderer_main.BeginSlice('blink', 'bar', 400, 402)
    renderer_main.EndSlice(420, 420)

    # Since this "baz" slice has CPU duration = 70ms, wall-time duration = 100ms
    # & its overalapped wall-time with "blink_perf.run_test" is 50 ms, its
    # overlapped CPU time with "blink_perf.run_test" is
    # 50 * 70 / 100 = 35ms.
    renderer_main.BeginSlice('blink', 'baz', 500, 520)
    renderer_main.EndSlice(600, 590)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo', 'bar', 'baz']),
        {'foo': [15], 'bar': [18], 'baz': [35]})

  def testTraceEventMetricsMultiBlinkTest(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    # Set up a main thread model that looks like:
    #   [          blink_perf.run_test    ]         [ blink_perf.run_test  ]
    #   |     [ foo ]          [  bar ]   |     [   |    foo         ]     |
    #   |     |     |          |      |   |     |   |     |          |     |
    #   100   120   140        400    420 440   500 520              600   640
    #             |                |                      |
    # CPU dur:    15               18                     40
    #
    self._AddBlinkTestSlice(renderer_main, 100, 440)
    self._AddBlinkTestSlice(renderer_main, 520, 640)

    renderer_main.BeginSlice('blink', 'foo', 120, 122)
    renderer_main.EndSlice(140, 137)

    renderer_main.BeginSlice('blink', 'bar', 400, 402)
    renderer_main.EndSlice(420, 420)

    # Since this "foo" slice has CPU duration = 40ms, wall-time duration = 100ms
    # & its overalapped wall-time with "blink_perf.run_test" is 80 ms, its
    # overlapped CPU time with "blink_perf.run_test" is
    # 80 * 40 / 100 = 32ms.
    renderer_main.BeginSlice('blink', 'foo', 500, 520)
    renderer_main.EndSlice(600, 560)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo', 'bar', 'baz']),
        {'foo': [15, 32], 'bar': [18, 0], 'baz': [0, 0]})

  def testTraceEventMetricsNoThreadTimeAvailable(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    # Set up a main thread model that looks like:
    #   [          blink_perf.run_test                ]
    #   |     [ foo ]          [  bar ]               |
    #   |     |     |          |      |               |
    #   100   120   140        400    420             550
    #             |                |
    # CPU dur:    None             None
    #
    self._AddBlinkTestSlice(renderer_main, 100, 550)

    renderer_main.BeginSlice('blink', 'foo', 120)
    renderer_main.EndSlice(140)

    renderer_main.BeginSlice('blink', 'bar', 400)
    renderer_main.EndSlice(420)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo', 'bar']),
        {'foo': [20], 'bar': [20]})

  def testTraceEventMetricsMultiBlinkTestCrossProcesses(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    foo_thread = model.GetOrCreateProcess(2).GetOrCreateThread(4)
    bar_thread =  model.GetOrCreateProcess(2).GetOrCreateThread(5)

    # Set up a main model that looks like (P1 & P2 are different processes):
    # P1  [          blink_perf.run_test    ]         [ blink_perf.run_test  ]
    #     |                                 |         |                      |
    # P2  |     [ foo ]                     |     [   |    foo         ]     |
    #     |     |  |  |          [  bar ]   |     |   |     |          |
    #     |     |  |  |          |   |  |   |     |   |     |          |     |
    #    100   120 | 140        400  | 420 440   500 520    |         600   640
    #              |                 |                      |
    # CPU dur:    15                N/A                     40
    #
    self._AddBlinkTestSlice(renderer_main, 100, 440)
    self._AddBlinkTestSlice(renderer_main, 520, 640)

    foo_thread.BeginSlice('blink', 'foo', 120, 122)
    foo_thread.EndSlice(140, 137)

    bar_thread.BeginSlice('blink', 'bar', 400)
    bar_thread.EndSlice(420)

    # Since this "foo" slice has CPU duration = 40ms, wall-time duration = 100ms
    # & its overalapped wall-time with "blink_perf.run_test" is 80 ms, its
    # overlapped CPU time with "blink_perf.run_test" is
    # 80 * 40 / 100 = 32ms.
    foo_thread.BeginSlice('blink', 'foo', 500, 520)
    foo_thread.EndSlice(600, 560)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo', 'bar', 'baz']),
        {'foo': [15, 32], 'bar': [20, 0], 'baz': [0, 0]})

  def testTraceEventMetricsNoDoubleCountingBasic(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    # Set up a main thread model that looks like:
    #   [          blink_perf.run_test                     ]
    #   |     [          foo           ]     [  foo  ]     |
    #   |     [          foo           ]     |       |     |
    #   |     |     [    foo     ]     |     |       |     |
    #   |     |     |            |     |     |       |     |
    #   100   120  140          400   420   440     510   550
    #                     |                      |
    # CPU dur of          |                      |
    # of top most event:  280                    50
    #
    self._AddBlinkTestSlice(renderer_main, 100, 550)

    renderer_main.BeginSlice('blink', 'foo', 120, 130)
    renderer_main.BeginSlice('blink', 'foo', 120, 130)
    renderer_main.BeginSlice('blink', 'foo', 140, 150)
    renderer_main.EndSlice(400, 390)
    renderer_main.EndSlice(420, 410)
    renderer_main.EndSlice(420, 410)

    renderer_main.BeginSlice('blink', 'foo', 440, 455)
    renderer_main.EndSlice(510, 505)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo']), {'foo': [330]})


  def testTraceEventMetricsNoDoubleCountingWithOtherSlidesMixedIn(self):
    model = model_module.TimelineModel()
    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
    renderer_main.name = 'CrRendererMain'

    # Set up a main thread model that looks like:
    #   [          blink_perf.run_test                                   ]
    #   |     [          foo                 ]      [      bar     ]     |
    #   |     |   [        bar         ]     |      |    [ foo  ]  |     |
    #   |     |   |    [    foo     ]  |     |      |    |      |  |     |
    #   |     |   |    |            |  |     |      |    |      |  |     |
    #   100   120 130 140     |    400 405   420    440  480 | 510 520  550
    #                         |                              |
    # CPU dur of              |                              |
    # of top most event:  280 (foo) & 270 (bar)            50 (bar) & 20 (foo)
    #
    self._AddBlinkTestSlice(renderer_main, 100, 550)

    renderer_main.BeginSlice('blink', 'foo', 120, 130)
    renderer_main.BeginSlice('blink', 'bar', 130, 135)
    renderer_main.BeginSlice('blink', 'foo', 140, 150)
    renderer_main.EndSlice(400, 390)
    renderer_main.EndSlice(405, 405)
    renderer_main.EndSlice(420, 410)

    renderer_main.BeginSlice('blink', 'bar', 440, 455)
    renderer_main.BeginSlice('blink', 'foo', 480, 490)
    renderer_main.EndSlice(510, 510)
    renderer_main.EndSlice(510, 505)

    self.assertEquals(
        blink_perf._ComputeTraceEventsThreadTimeForBlinkPerf(
            model, renderer_main, ['foo', 'bar']),
            {'foo': [300], 'bar': [320]})
