chromium/content/test/gpu/PRESUBMIT.py

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

"""Top-level presubmit script for content/test/gpu.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""


PRESUBMIT_VERSION = '2.0.0'

EXTRA_PATHS_COMPONENTS = [
    ('build', ),
    ('build', 'fuchsia', 'test'),
    ('build', 'util'),
    ('testing', ),
    ('third_party', 'catapult', 'common', 'py_utils'),
    ('third_party', 'catapult', 'devil'),
    ('third_party', 'catapult', 'telemetry'),
    ('third_party', 'catapult', 'third_party', 'typ'),
    ('tools', 'perf'),
]


def _GetChromiumSrcPath(input_api):
  """Returns the path to the Chromium src directory."""
  return input_api.os_path.realpath(
      input_api.os_path.join(input_api.PresubmitLocalPath(), '..', '..', '..'))


def _GetGpuEnv(input_api):
  """Gets the common environment for running GPU tests."""
  gpu_env = dict(input_api.environ)
  current_path = input_api.PresubmitLocalPath()
  chromium_src_path = _GetChromiumSrcPath(input_api)
  testing_path = input_api.os_path.join(chromium_src_path, 'testing')
  gpu_env.update({
      'PYTHONPATH':
      input_api.os_path.pathsep.join([testing_path, current_path]),
      'PYTHONDONTWRITEBYTECODE':
      '1',
  })
  return gpu_env


def CheckGpuTestsUnittests(input_api, output_api):
  """Runs the unittests for the gpu_tests directory."""
  gpu_env = _GetGpuEnv(input_api)
  command = input_api.Command(
      name='run_content_test_gpu_unittests',
      cmd=[input_api.python3_executable, 'run_unittests.py', 'gpu_tests'],
      kwargs={'env': gpu_env},
      message=output_api.PresubmitError,
      python3=True)
  return input_api.RunTests([command])


def CheckMachineTimesUnittests(input_api, output_api):
  """Runs the unittests for the machine_times directory."""
  return input_api.canned_checks.RunUnitTestsInDirectory(
      input_api,
      output_api,
      input_api.os_path.join(input_api.PresubmitLocalPath(),
                             'machine_times'), [r'^.+_unittest\.py$'],
      env=_GetGpuEnv(input_api),
      run_on_python2=False,
      run_on_python3=True,
      skip_shebang_check=True)


def CheckUnexpectedPassesUnittests(input_api, output_api):
  """Runs the unittests for the unexpected_passes directory."""
  return input_api.canned_checks.RunUnitTestsInDirectory(
      input_api,
      output_api,
      input_api.os_path.join(input_api.PresubmitLocalPath(),
                             'unexpected_passes'), [r'^.+_unittest\.py$'],
      env=_GetGpuEnv(input_api))


def CheckFlakeSuppressorUnittests(input_api, output_api):
  """Runs the unittests for the flake_suppressor directory."""
  return input_api.canned_checks.RunUnitTestsInDirectory(
      input_api,
      output_api,
      input_api.os_path.join(input_api.PresubmitLocalPath(),
                             'flake_suppressor'), [r'^.+_unittest\.py$'],
      env=_GetGpuEnv(input_api))


def CheckValidateTagConsistency(input_api, output_api):
  """Checks that GPU expectation tags are consistent across all files."""
  command = input_api.Command(name='validate_tag_consistency',
                              cmd=[
                                  input_api.python3_executable,
                                  'validate_tag_consistency.py', 'validate'
                              ],
                              kwargs={},
                              message=output_api.PresubmitError)
  return input_api.RunTests([command])


def CheckForNewSkipExpectations(input_api, output_api):
  """Checks for and dissuades the addition of new Skip expectations."""
  new_skips = []
  expectation_file_dir = input_api.os_path.join(input_api.PresubmitLocalPath(),
                                                'gpu_tests',
                                                'test_expectations')
  file_filter = lambda f: f.AbsoluteLocalPath().startswith(expectation_file_dir)
  for affected_file in input_api.AffectedFiles(file_filter=file_filter):
    for _, line in affected_file.ChangedContents():
      if input_api.re.search(r'\[\s*Skip\s*\]', line):
        new_skips.append((affected_file, line))
  result = []
  if new_skips:
    warnings = []
    for affected_file, line in new_skips:
      warnings.append('  Line "%s" in file %s' %
                      (line, affected_file.LocalPath()))
    result.append(
        output_api.PresubmitPromptWarning(
            'Suspected new Skip expectations found:\n%s\nPlease only use such '
            'expectations when they are strictly necessary, e.g. the test is '
            'impacting other tests. Otherwise, opt for a '
            'Failure/RetryOnFailure expectation.' % '\n'.join(warnings)))
  return result


def CheckPylint(input_api, output_api):
  """Runs pylint on all directory content and subdirectories."""
  chromium_src_path = _GetChromiumSrcPath(input_api)
  pylint_extra_paths = [
      input_api.os_path.join(chromium_src_path, *component)
      for component in EXTRA_PATHS_COMPONENTS
  ]
  pylint_checks = input_api.canned_checks.GetPylint(
      input_api,
      output_api,
      extra_paths_list=pylint_extra_paths,
      pylintrc='pylintrc',
      version='2.7')
  return input_api.RunTests(pylint_checks)


def CheckPytypePathsInSync(input_api, output_api):
  """Checks that run_pytype.py's paths are in sync with PRESUBMIT.py's"""
  filepath = input_api.os_path.join(input_api.PresubmitLocalPath(),
                                    'run_pytype.py')
  with open(filepath, encoding='utf-8') as infile:
    contents = infile.read()
  # Grab the EXTRA_PATHS_COMPONENTS = [...] portion as a string.
  match = input_api.re.search(r'(EXTRA_PATHS_COMPONENTS\s*=\s*[^=]*\]\n)',
                              contents, input_api.re.DOTALL)
  if not match:
    return [
        output_api.PresubmitError(
            'Unable to find EXTRA_PATHS_COMPONENTS in run_pytype.py. Maybe '
            'the code in PRESUBMIT.py needs to be updated?')
    ]
  expression = match.group(0)
  expression = expression.split('=', 1)[1]
  expression = expression.lstrip()
  pytype_path_components = input_api.ast.literal_eval(expression)
  if EXTRA_PATHS_COMPONENTS != pytype_path_components:
    return [
        output_api.PresubmitError(
            'EXTRA_PATHS_COMPONENTS is not synced between PRESUBMIT.py and '
            'run_pytype.py, please ensure they are identical.')
    ]
  return []