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