chromium/android_webview/tools/update_cts.py

#!/usr/bin/env vpython3
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Update CTS Tests to a new version."""

from __future__ import print_function

import argparse
import os
import re
import sys

sys.path.append(
    os.path.join(
        os.path.dirname(__file__), os.pardir, os.pardir, 'third_party',
        'catapult', 'devil'))
# pylint: disable=wrong-import-position,import-error
from devil.utils import cmd_helper
from devil.utils import logging_common

import cts_utils


def _query_git_for_cts_tags():
  cts_git_url = 'https://android.googlesource.com/platform/cts/'

  tags = cmd_helper.GetCmdOutput(['git', 'ls-remote', '--tags',
                                  cts_git_url]).splitlines()

  print('[Updating CTS versions] Retrieved the CTS git tags')

  return tags


class UpdateCTS:
  """Updates CTS archive to a new version.

  Performs the following tasks to simplify the CTS test update process:
  (TODO(crbug.com/40259004): - Update the CTS versions in fetch.py / install.sh)
  - Update the CTS versions in webview_cts_gcs_path.json

  After these steps are completed, the user can commit and upload
  the CL to Chromium Gerrit.
  """

  def __init__(self, repo_root):
    """Construct UpdateCTS instance.

    Args:
      repo_root: Repository root (e.g. /path/to/chromium/src) to base
                 all configuration files
    """
    self._repo_root = os.path.abspath(repo_root)
    helper = cts_utils.ChromiumRepoHelper(self._repo_root)
    self._repo_helper = helper
    self._cts_config_path = helper.rebase(cts_utils.TOOLS_DIR,
                                          cts_utils.CONFIG_FILE)
    self._CTSConfig = cts_utils.CTSConfig(self._cts_config_path)

  def _check_for_latest_cts_versions(self, cts_tags):
    """Query for the latest cts versions per platform

    We can retrieve the newest CTS versions by searching through the git tags
    for each CTS version and looking for the latest
    """

    prefixes = [(platform, self._CTSConfig.get_git_tag_prefix(platform))
                for platform in self._CTSConfig.iter_platforms()]
    release_versions = dict()

    tag_prefix_regexes = {
        # Do a forward lookup for the tag prefix plus an '_r'
        # Eg: 'android-cts-7.0_r2'
        # Then retrieve the digits after this
        tag_prefix: re.compile('(?<=/%s_r)\\d*' % re.escape(tag_prefix))
        for _, tag_prefix in prefixes
    }

    for tag in cts_tags:
      for platform, prefix in prefixes:
        matches = tag_prefix_regexes[prefix].search(tag)
        if matches:
          version = int(matches.group(0))
          if release_versions.get(platform, -1) < version:
            release_versions[platform] = version

    print('[Updating CTS versions] Retrieved the latest CTS versions')

    return release_versions

  def _update_cts_config_file_download_origins(self, release_versions):
    """ Update the CTS release version for each architecture
    and then save the config json
    """
    for platform, arch in self._CTSConfig.iter_platform_archs():
      self._CTSConfig.set_release_version(platform, arch,
                                          release_versions[platform])

    self._CTSConfig.save()

    print('[Updating CTS versions] Updated cts config')

  def update_cts_download_origins_cmd(self):
    """Performs the cts download origins update command"""
    tags = _query_git_for_cts_tags()
    release_versions = self._check_for_latest_cts_versions(tags)
    self._update_cts_config_file_download_origins(release_versions)


DESC = """Updates the WebView CTS tests to a new version.

See https://source.android.com/compatibility/cts/downloads for the latest
versions.

Please create a new branch, then edit the
{}
file with updated origin and file name before running this script.

After performing all steps, perform git add then commit.""".format(
    os.path.join(cts_utils.TOOLS_DIR, cts_utils.CONFIG_FILE))

UPDATE_CONFIG = 'update-config'


def main():
  parser = argparse.ArgumentParser(
      description=DESC, formatter_class=argparse.RawTextHelpFormatter)

  logging_common.AddLoggingArguments(parser)

  subparsers = parser.add_subparsers(dest='cmd')

  subparsers.add_parser(
      UPDATE_CONFIG,
      help='Update the CTS config to the newest release versions.')

  args = parser.parse_args()
  logging_common.InitializeLogging(args)

  cts_updater = UpdateCTS(repo_root=cts_utils.SRC_DIR)

  if args.cmd == UPDATE_CONFIG:
    cts_updater.update_cts_download_origins_cmd()


if __name__ == '__main__':
  main()