# Copyright 2018 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.
"""Quest for running a Telemetry benchmark in Swarming."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

import re

from dashboard.pinpoint.models.quest import run_performance_test

_DEFAULT_EXTRA_ARGS = [
    '-v', '--upload-results', '--output-format', 'histograms'
]

_STORY_REGEX = re.compile(r'[^a-zA-Z0-9]')


def _StoryToRegex(story_name):
  # Telemetry's --story-filter argument takes in a regex, not a
  # plain string. Stories can have all sorts of special characters
  # in their names (see crbug.com/983993) which would confuse a
  # regex. We thus keep only a small set of "safe chars"
  # and replace all others with match-any-character regex dots.
  return '^%s$' % _STORY_REGEX.sub('.', story_name)


def ChangeDependentArgs(args, change):
  # For results2 to differentiate between runs, we need to add the
  # Telemetry parameter `--results-label <change>` to the runs.
  extra_args = list(args)
  extra_args += ('--results-label', str(change))
  if '--story-filter' in extra_args:
    extra_args.append('--run-full-story-set')
  if change.change_args:
    extra_args.extend(change.change_args)
  return extra_args


class RunTelemetryTest(run_performance_test.RunPerformanceTest):

  @classmethod
  def _ComputeCommand(cls, arguments):
    # We're moving the definition of which command to run here, instead of
    # relying on what's in the isolate because the 'command' feature is
    # deprecated and will be removed soon (EOY 2020).
    # TODO(dberris): Move this out to a configuration elsewhere.
    command = [
        'luci-auth', 'context', '--', 'vpython', '../../testing/test_env.py',
        '../../testing/scripts/run_performance_tests.py',
        '../../tools/perf/run_benchmark'
    ]
    relative_cwd = arguments.get('relative_cwd', 'out/Release')
    return relative_cwd, command

  def Start(self, change, isolate_server, isolate_hash):
    extra_swarming_tags = {'change': str(change)}
    return self._Start(
        change,
        isolate_server,
        isolate_hash,
        ChangeDependentArgs(self._extra_args, change),
        extra_swarming_tags,
        execution_timeout_secs=None)

  @classmethod
  def _ExtraTestArgs(cls, arguments):
    extra_test_args = []

    benchmark = arguments.get('benchmark')
    if not benchmark:
      raise TypeError('Missing "benchmark" argument.')
    extra_test_args += ('--benchmarks', benchmark)

    story = arguments.get('story')
    if story:
      # TODO(crbug.com/982027): Note that usage of "--run-full-story-set"
      # and "--story-filter"
      # may be replaced with --story=<story> (no regex needed). Support
      # for --story flag landed in
      # https://chromium-review.googlesource.com/c/catapult/+/1869800
      # (Oct 22, 2019)
      # so we cannot turn this on by default until we no longer need to
      # be able to run revisions older than that. In the meantime, the
      # following argument plus the --run-full-story-set argument added in
      # Start() accomplish the same thing.
      extra_test_args += ('--story-filter', _StoryToRegex(story))

    story_tags = arguments.get('story_tags')
    if story_tags:
      extra_test_args += ('--story-tag-filter', story_tags)

    # TODO: Workaround for crbug.com/677843.
    if (benchmark.startswith('startup.warm')
        or benchmark.startswith('start_with_url.warm')):
      extra_test_args += ('--pageset-repeat', '2')
    else:
      extra_test_args += ('--pageset-repeat', '1')

    browser = arguments.get('browser')
    if not browser:
      raise TypeError('Missing "browser" argument.')
    extra_test_args += ('--browser', browser)
    extra_test_args += _DEFAULT_EXTRA_ARGS
    extra_test_args += super(RunTelemetryTest, cls)._ExtraTestArgs(arguments)
    return extra_test_args

  @classmethod
  def _GetSwarmingTags(cls, arguments):
    tags = {}
    benchmark = arguments.get('benchmark')
    if not benchmark:
      raise TypeError('Missing "benchmark" argument.')
    tags['benchmark'] = benchmark
    story_filter = arguments.get('story')
    tag_filter = arguments.get('story_tags')
    tags['hasfilter'] = '1' if story_filter or tag_filter else '0'
    if story_filter:
      tags['storyfilter'] = story_filter
    if tag_filter:
      tags['tagfilter'] = tag_filter
    return tags
