xref: /aosp_15_r20/external/cronet/build/skia_gold_common/skia_gold_properties.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2020 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Class for storing Skia Gold comparison properties.
5
6Examples:
7* git revision being tested
8* Whether the test is being run locally or on a bot
9* What the continuous integration system is
10"""
11
12import argparse
13import logging
14import optparse
15import os
16import subprocess
17import sys
18from typing import Optional, Union
19
20CHROMIUM_SRC_DIR = os.path.realpath(
21    os.path.join(os.path.dirname(__file__), '..', '..'))
22
23ParsedCmdArgs = Union[argparse.Namespace, optparse.Values]
24
25
26def _IsWin() -> bool:
27  return sys.platform == 'win32'
28
29
30class SkiaGoldProperties():
31  def __init__(self, args: ParsedCmdArgs):
32    """Class to validate and store properties related to Skia Gold.
33
34    The base implementation is usable on its own, but is meant to be overridden
35    as necessary.
36
37    Args:
38      args: The parsed arguments from an argparse.ArgumentParser.
39    """
40    self._git_revision: Optional[str] = None
41    self._issue: Optional[int] = None
42    self._patchset: Optional[int] = None
43    self._job_id: Optional[str] = None
44    self._local_pixel_tests: Optional[bool] = None
45    self._no_luci_auth: Optional[bool] = None
46    self._service_account: Optional[str] = None
47    self._bypass_skia_gold_functionality: Optional[bool] = None
48    self._code_review_system: Optional[str] = None
49    self._continuous_integration_system: Optional[str] = None
50    self._local_png_directory: Optional[str] = None
51
52    self._InitializeProperties(args)
53
54  def IsTryjobRun(self) -> bool:
55    return self.issue is not None
56
57  @property
58  def continuous_integration_system(self) -> str:
59    return self._continuous_integration_system or 'buildbucket'
60
61  @property
62  def code_review_system(self) -> str:
63    return self._code_review_system or 'gerrit'
64
65  @property
66  def git_revision(self) -> str:
67    return self._GetGitRevision()
68
69  @property
70  def issue(self) -> Optional[int]:
71    return self._issue
72
73  @property
74  def job_id(self) -> Optional[str]:
75    return self._job_id
76
77  @property
78  def local_pixel_tests(self) -> bool:
79    return self._IsLocalRun()
80
81  @property
82  def local_png_directory(self) -> Optional[str]:
83    return self._local_png_directory
84
85  @property
86  def no_luci_auth(self) -> Optional[bool]:
87    return self._no_luci_auth
88
89  @property
90  def service_account(self) -> Optional[str]:
91    return self._service_account
92
93  @property
94  def patchset(self) -> Optional[int]:
95    return self._patchset
96
97  @property
98  def bypass_skia_gold_functionality(self) -> Optional[bool]:
99    return self._bypass_skia_gold_functionality
100
101  def _GetGitOriginMainHeadSha1(self) -> Optional[str]:
102    try:
103      return subprocess.check_output(
104          ['git', 'rev-parse', 'origin/main'],
105          shell=_IsWin(),
106          cwd=self._GetGitRepoDirectory()).decode('utf-8').strip()
107    except subprocess.CalledProcessError:
108      return None
109
110  def _GetGitRepoDirectory(self) -> str:
111    return CHROMIUM_SRC_DIR
112
113  def _GetGitRevision(self) -> str:
114    if not self._git_revision:
115      # Automated tests should always pass the revision, so assume we're on
116      # a workstation and try to get the local origin/master HEAD.
117      if not self._IsLocalRun():
118        raise RuntimeError(
119            '--git-revision was not passed when running on a bot')
120      revision = self._GetGitOriginMainHeadSha1()
121      if not revision or len(revision) != 40:
122        raise RuntimeError(
123            '--git-revision not passed and unable to determine from git')
124      self._git_revision = revision
125    return self._git_revision
126
127  def _IsLocalRun(self) -> bool:
128    if self._local_pixel_tests is None:
129      # Look for the presence of the SWARMING_SERVER environment variable as a
130      # heuristic to determine whether we're running on a workstation or a bot.
131      # This should always be set on swarming, but would be strange to be set on
132      # a workstation.
133      # However, since Skylab technically isn't swarming, we need to look for
134      # an alternative environment variable there.
135      in_swarming = 'SWARMING_SERVER' in os.environ
136      in_skylab = bool(int(os.environ.get('RUNNING_IN_SKYLAB', '0')))
137      self._local_pixel_tests = not (in_swarming or in_skylab)
138      if self._local_pixel_tests:
139        logging.warning(
140            'Automatically determined that test is running on a workstation')
141      else:
142        logging.warning(
143            'Automatically determined that test is running on a bot')
144    return self._local_pixel_tests
145
146  @staticmethod
147  def AddCommandLineArguments(parser: argparse.ArgumentParser) -> None:
148    """ Add command line arguments to an ArgumentParser instance
149
150    Args:
151      parser: ArgumentParser instance
152
153    Returns:
154      None
155    """
156    parser.add_argument('--git-revision', type=str, help='Git revision')
157    parser.add_argument('--gerrit-issue', type=int, help='Gerrit issue number')
158    parser.add_argument('--gerrit-patchset',
159                        type=int,
160                        help='Gerrit patchset number')
161    parser.add_argument('--buildbucket-id',
162                        type=int,
163                        help='Buildbucket ID of builder')
164    parser.add_argument('--code-review-system',
165                        type=str,
166                        help='Code review system')
167    parser.add_argument('--continuous-integration-system',
168                        type=str,
169                        help='Continuous integration system')
170
171  def _InitializeProperties(self, args: ParsedCmdArgs) -> None:
172    if hasattr(args, 'local_pixel_tests'):
173      # If not set, will be automatically determined later if needed.
174      self._local_pixel_tests = args.local_pixel_tests
175
176    if hasattr(args, 'skia_gold_local_png_write_directory'):
177      self._local_png_directory = args.skia_gold_local_png_write_directory
178
179    if hasattr(args, 'no_luci_auth'):
180      self._no_luci_auth = args.no_luci_auth
181
182    if hasattr(args, 'service_account'):
183      self._service_account = args.service_account
184      if self._service_account:
185        self._no_luci_auth = True
186
187    if hasattr(args, 'bypass_skia_gold_functionality'):
188      self._bypass_skia_gold_functionality = args.bypass_skia_gold_functionality
189
190    if hasattr(args, 'code_review_system'):
191      self._code_review_system = args.code_review_system
192
193    if hasattr(args, 'continuous_integration_system'):
194      self._continuous_integration_system = args.continuous_integration_system
195
196    # Will be automatically determined later if needed.
197    if not hasattr(args, 'git_revision') or not args.git_revision:
198      return
199    self._git_revision = args.git_revision
200
201    # Only expected on tryjob runs.
202    if not hasattr(args, 'gerrit_issue') or not args.gerrit_issue:
203      return
204    self._issue = args.gerrit_issue
205    if not hasattr(args, 'gerrit_patchset') or not args.gerrit_patchset:
206      raise RuntimeError(
207          '--gerrit-issue passed, but --gerrit-patchset not passed.')
208    self._patchset = args.gerrit_patchset
209    if not hasattr(args, 'buildbucket_id') or not args.buildbucket_id:
210      raise RuntimeError(
211          '--gerrit-issue passed, but --buildbucket-id not passed.')
212    self._job_id = args.buildbucket_id
213