# Copyright 2013 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

from core import perf_benchmark

from telemetry import benchmark
from telemetry.core import util
from telemetry import page as page_module
from telemetry.page import page_test
from telemetry.page import shared_page_state
from telemetry import story
from telemetry.value import list_of_scalar_values

from page_sets import webgl_supported_shared_state


BLINK_PERF_BASE_DIR = os.path.join(util.GetChromiumSrcDir(),
                                   'third_party', 'WebKit', 'PerformanceTests')
SKIPPED_FILE = os.path.join(BLINK_PERF_BASE_DIR, 'Skipped')


def CreateStorySetFromPath(path, skipped_file,
                          shared_page_state_class=(
                            shared_page_state.SharedPageState)):
  assert os.path.exists(path)

  page_urls = []
  serving_dirs = set()

  def _AddPage(path):
    if not path.endswith('.html'):
      return
    if '../' in open(path, 'r').read():
      # If the page looks like it references its parent dir, include it.
      serving_dirs.add(os.path.dirname(os.path.dirname(path)))
    page_urls.append('file://' + path.replace('\\', '/'))

  def _AddDir(dir_path, skipped):
    for candidate_path in os.listdir(dir_path):
      if candidate_path == 'resources':
        continue
      candidate_path = os.path.join(dir_path, candidate_path)
      if candidate_path.startswith(skipped):
        continue
      if os.path.isdir(candidate_path):
        _AddDir(candidate_path, skipped)
      else:
        _AddPage(candidate_path)

  if os.path.isdir(path):
    skipped = []
    if os.path.exists(skipped_file):
      for line in open(skipped_file, 'r').readlines():
        line = line.strip()
        if line and not line.startswith('#'):
          skipped_path = os.path.join(os.path.dirname(skipped_file), line)
          skipped.append(skipped_path.replace('/', os.sep))
    _AddDir(path, tuple(skipped))
  else:
    _AddPage(path)
  ps = story.StorySet(base_dir=os.getcwd()+os.sep,
                        serving_dirs=serving_dirs)
  for url in page_urls:
    ps.AddStory(page_module.Page(
      url, ps, ps.base_dir,
      shared_page_state_class=shared_page_state_class))
  return ps


class _BlinkPerfMeasurement(page_test.PageTest):
  """Tuns a blink performance test and reports the results."""
  def __init__(self):
    super(_BlinkPerfMeasurement, self).__init__()
    with open(os.path.join(os.path.dirname(__file__),
                           'blink_perf.js'), 'r') as f:
      self._blink_perf_js = f.read()

  def WillNavigateToPage(self, page, tab):
    page.script_to_evaluate_on_commit = self._blink_perf_js

  def CustomizeBrowserOptions(self, options):
    options.AppendExtraBrowserArgs([
        '--js-flags=--expose_gc',
        '--enable-experimental-web-platform-features',
        '--disable-gesture-requirement-for-media-playback'
    ])
    if 'content-shell' in options.browser_type:
      options.AppendExtraBrowserArgs('--expose-internals-for-testing')

  def ValidateAndMeasurePage(self, page, tab, results):
    tab.WaitForJavaScriptExpression('testRunner.isDone', 600)

    log = tab.EvaluateJavaScript('document.getElementById("log").innerHTML')

    for line in log.splitlines():
      if not line.startswith('values '):
        continue
      parts = line.split()
      values = [float(v.replace(',', '')) for v in parts[1:-1]]
      units = parts[-1]
      metric = page.display_name.split('.')[0].replace('/', '_')
      results.AddValue(list_of_scalar_values.ListOfScalarValues(
          results.current_page, metric, units, values))

      break

    print log


class _BlinkPerfFullFrameMeasurement(_BlinkPerfMeasurement):
  def __init__(self):
    super(_BlinkPerfFullFrameMeasurement, self).__init__()
    self._blink_perf_js += '\nwindow.fullFrameMeasurement = true;'

  def CustomizeBrowserOptions(self, options):
    super(_BlinkPerfFullFrameMeasurement, self).CustomizeBrowserOptions(
        options)
    # Full layout measurement needs content_shell with internals testing API.
    assert 'content-shell' in options.browser_type
    options.AppendExtraBrowserArgs(['--expose-internals-for-testing'])

@benchmark.Disabled  # http://crbug.com/500958
class BlinkPerfBindings(perf_benchmark.PerfBenchmark):
  tag = 'bindings'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.bindings'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Bindings')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Enabled('content-shell')
class BlinkPerfBlinkGC(perf_benchmark.PerfBenchmark):
  tag = 'blink_gc'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.blink_gc'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'BlinkGC')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


class BlinkPerfCSS(perf_benchmark.PerfBenchmark):
  tag = 'css'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.css'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'CSS')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Disabled('xp',  # http://crbug.com/488059
                    'android')  # http://crbug.com/496707
class BlinkPerfCanvas(perf_benchmark.PerfBenchmark):
  tag = 'canvas'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.canvas'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Canvas')
    story_set = CreateStorySetFromPath(
      path, SKIPPED_FILE,
      shared_page_state_class=(
        webgl_supported_shared_state.WebGLSupportedSharedState))
    # WebGLSupportedSharedState requires the skipped_gpus property to
    # be set on each page.
    for page in story_set:
      page.skipped_gpus = []
    return story_set


class BlinkPerfDOM(perf_benchmark.PerfBenchmark):
  tag = 'dom'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.dom'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'DOM')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Disabled('release_x64')  # http://crbug.com/480999
class BlinkPerfEvents(perf_benchmark.PerfBenchmark):
  tag = 'events'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.events'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Events')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Disabled('win8')  # http://crbug.com/462350
class BlinkPerfLayout(perf_benchmark.PerfBenchmark):
  tag = 'layout'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.layout'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Layout')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Enabled('content-shell')
class BlinkPerfLayoutFullLayout(BlinkPerfLayout):
  tag = 'layout_full_frame'
  test = _BlinkPerfFullFrameMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.layout_full_frame'


class BlinkPerfMutation(perf_benchmark.PerfBenchmark):
  tag = 'mutation'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.mutation'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Mutation')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Disabled('win')  # crbug.com/488493
class BlinkPerfParser(perf_benchmark.PerfBenchmark):
  tag = 'parser'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.parser'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'Parser')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


class BlinkPerfSVG(perf_benchmark.PerfBenchmark):
  tag = 'svg'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.svg'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'SVG')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


@benchmark.Enabled('content-shell')
class BlinkPerfSVGFullLayout(BlinkPerfSVG):
  tag = 'svg_full_frame'
  test = _BlinkPerfFullFrameMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.svg_full_frame'


class BlinkPerfShadowDOM(perf_benchmark.PerfBenchmark):
  tag = 'shadow_dom'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.shadow_dom'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'ShadowDOM')
    return CreateStorySetFromPath(path, SKIPPED_FILE)


# This benchmark is for local testing, doesn't need to run on bots.
@benchmark.Disabled()
class BlinkPerfXMLHttpRequest(perf_benchmark.PerfBenchmark):
  tag = 'xml_http_request'
  test = _BlinkPerfMeasurement

  @classmethod
  def Name(cls):
    return 'blink_perf.xml_http_request'

  def CreateStorySet(self, options):
    path = os.path.join(BLINK_PERF_BASE_DIR, 'XMLHttpRequest')
    return CreateStorySetFromPath(path, SKIPPED_FILE)