Title: [279445] trunk/Tools
Revision
279445
Author
jbed...@apple.com
Date
2021-06-30 17:16:18 -0700 (Wed, 30 Jun 2021)

Log Message

[webkitscmpy] Cache identifiers in Git checkouts
https://bugs.webkit.org/show_bug.cgi?id=225616
<rdar://problem/77789230>

Reviewed by Dewei Zhu.

Computing identifiers in git can be slow, especially if you need to compute
multiple. Caching all identifiers for a branch is not much more expensive than calculating a single identifier. Additionally, caching all identifiers
lets us build a performant subversion mapping, bypassing git-svn.

* Scripts/libraries/webkitcorepy/setup.py:
* Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py:
* Scripts/libraries/webkitscmpy/webkitscmpy/local/git.py:
(Git.Cache): Class holding a cache allowing quick mapping between hashes, identifiers and revisions.
(Git.Cache.__init__): Load cache from json file on disk.
(Git.Cache.path): Path to location of cache on disk.
(Git.Cache._fill): Populate revision and hash to identifier dictionaries.
(Git.Cache.populate): Parse git-log and populate cache.
(Git.Cache.hash): Given an identifier or revision, find the hash.
(Git.Cache.revision): Given an identifier or hash, find the revision.
(Git.Cache.identifier): Given a hash or revision, determine the identifier.
(Git.__init__): Instantiate cache.
(Git.commit): Check cache before running expensive commands.
* Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py: Add `git log` mock.
* Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py:
(TestGit.test_tag): Surpress logging.
(TestGit.test_checkout): Ditto.
(TestGit.test_no_log): Ditto.
(TestGit.test_order): Ditto.
(test_cache): Verify identifier cache.
(test_revision_cache): Verify revision cache.
* Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py:
(SummarizedResultsTest.test_svn_revision_git): Surpress logging.
* Scripts/webkitpy/port/base_unittest.py:
(PortTest.test_commits_for_upload_git_svn): Surpress logging.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (279444 => 279445)


--- trunk/Tools/ChangeLog	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/ChangeLog	2021-07-01 00:16:18 UTC (rev 279445)
@@ -1,3 +1,41 @@
+2021-06-30  Jonathan Bedard  <jbed...@apple.com>
+
+        [webkitscmpy] Cache identifiers in Git checkouts
+        https://bugs.webkit.org/show_bug.cgi?id=225616
+        <rdar://problem/77789230>
+
+        Reviewed by Dewei Zhu.
+
+        Computing identifiers in git can be slow, especially if you need to compute
+        multiple. Caching all identifiers for a branch is not much more expensive than calculating a single identifier. Additionally, caching all identifiers
+        lets us build a performant subversion mapping, bypassing git-svn.
+
+        * Scripts/libraries/webkitcorepy/setup.py:
+        * Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py:
+        * Scripts/libraries/webkitscmpy/webkitscmpy/local/git.py:
+        (Git.Cache): Class holding a cache allowing quick mapping between hashes, identifiers and revisions.
+        (Git.Cache.__init__): Load cache from json file on disk.
+        (Git.Cache.path): Path to location of cache on disk.
+        (Git.Cache._fill): Populate revision and hash to identifier dictionaries.
+        (Git.Cache.populate): Parse git-log and populate cache.
+        (Git.Cache.hash): Given an identifier or revision, find the hash.
+        (Git.Cache.revision): Given an identifier or hash, find the revision.
+        (Git.Cache.identifier): Given a hash or revision, determine the identifier.
+        (Git.__init__): Instantiate cache.
+        (Git.commit): Check cache before running expensive commands.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py: Add `git log` mock.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py:
+        (TestGit.test_tag): Surpress logging.
+        (TestGit.test_checkout): Ditto.
+        (TestGit.test_no_log): Ditto.
+        (TestGit.test_order): Ditto.
+        (test_cache): Verify identifier cache.
+        (test_revision_cache): Verify revision cache.
+        * Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py:
+        (SummarizedResultsTest.test_svn_revision_git): Surpress logging.
+        * Scripts/webkitpy/port/base_unittest.py:
+        (PortTest.test_commits_for_upload_git_svn): Surpress logging.
+
 2021-06-30  Megan Gardner  <megan_gard...@apple.com>
 
         Add ID and versioning support for AppHighlights

Modified: trunk/Tools/Scripts/libraries/webkitcorepy/setup.py (279444 => 279445)


--- trunk/Tools/Scripts/libraries/webkitcorepy/setup.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/setup.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -30,7 +30,7 @@
 
 setup(
     name='webkitcorepy',
-    version='0.7.0',
+    version='0.7.1',
     description='Library containing various Python support classes and functions.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py (279444 => 279445)


--- trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -39,7 +39,7 @@
 from webkitcorepy.measure_time import MeasureTime
 from webkitcorepy.nested_fuzzy_dict import NestedFuzzyDict
 
-version = Version(0, 7, 0)
+version = Version(0, 7, 1)
 
 from webkitcorepy.autoinstall import Package, AutoInstall
 if sys.version_info > (3, 0):

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/git.py (279444 => 279445)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/git.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/local/git.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -23,6 +23,7 @@
 import calendar
 import logging
 import os
+import json
 import re
 import six
 import subprocess
@@ -31,12 +32,223 @@
 
 from datetime import datetime, timedelta
 
-from webkitcorepy import run, decorators
+from webkitcorepy import run, decorators, NestedFuzzyDict
 from webkitscmpy.local import Scm
 from webkitscmpy import Commit, Contributor, log
 
 
 class Git(Scm):
+    class Cache(object):
+        def __init__(self, repo, guranteed_for=10):
+            self.repo = repo
+            self._ordered_commits = {}
+            self._hash_to_identifiers = NestedFuzzyDict(primary_size=6)
+            self._ordered_revisions = {}
+            self._revisions_to_identifiers = {}
+            self._last_populated = {}
+            self._guranteed_for = guranteed_for
+
+            if not os.path.exists(self.path):
+                return
+
+            try:
+                with open(self.path) as file:
+                    content = json.load(file)
+                    self._ordered_commits = content['hashes']
+                    self._ordered_revisions = content['revisions']
+
+                self._fill(self.repo.default_branch)
+                for branch in self._ordered_commits.keys():
+                    if branch == self.repo.default_branch:
+                        continue
+                    self._fill(branch)
+            except BaseException:
+                pass
+
+        @property
+        def path(self):
+            return os.path.join(self.repo.root_path, '.git', 'identifiers.json')
+
+        def _fill(self, branch):
+            default_branch = self.repo.default_branch
+            if branch == default_branch:
+                branch_point = None
+            else:
+                branch_point = int(self._hash_to_identifiers[self._ordered_commits[branch][0]].split('@')[0])
+
+            index = len(self._ordered_commits[branch]) - 1
+            while index:
+                identifier = self._hash_to_identifiers.get(self._ordered_commits[branch][index])
+
+                if identifier:
+                    id_branch = identifier.split('@')[-1]
+                    if branch in (default_branch, id_branch):
+                        break
+                    if branch != self.repo.prioritize_branches((branch, id_branch)):
+                        break
+
+                identifier = '{}@{}'.format('{}.{}'.format(branch_point, index) if branch_point else index, branch)
+                self._hash_to_identifiers[self._ordered_commits[branch][index]] = identifier
+                if self._ordered_revisions[branch][index]:
+                    self._revisions_to_identifiers[self._ordered_revisions[branch][index]] = identifier
+                index -= 1
+
+        def populate(self, branch=None):
+            branch = branch or self.repo.branch
+            if not branch:
+                return
+            if self._last_populated.get(branch, 0) + self._guranteed_for > time.time():
+                return
+            default_branch = self.repo.default_branch
+            is_default_branch = branch == default_branch
+            if branch not in self._ordered_commits:
+                self._ordered_commits[branch] = [''] if is_default_branch else []
+                self._ordered_revisions[branch] = [0] if is_default_branch else []
+
+            # If we aren't on the default branch, we will need the default branch to determine when
+            # our  branch  intersects with the default branch.
+            if not is_default_branch:
+                self.populate(branch=self.repo.default_branch)
+            hashes = []
+            revisions = []
+
+            def _append(branch, hash, revision=None):
+                hashes.append(hash)
+                revisions.append(revision)
+                identifier = self._hash_to_identifiers.get(hash, '')
+                return identifier.endswith(default_branch) or identifier.endswith(branch)
+
+            intersected = False
+            log = None
+            try:
+                kwargs = dict()
+                if sys.version_info >= (3, 0):
+                    kwargs = dict(encoding='utf-8')
+                self._last_populated[branch] = time.time()
+                log = subprocess.Popen(
+                    [self.repo.executable(), 'log', branch],
+                    cwd=self.repo.root_path,
+                    stdout=subprocess.PIPE,
+                    stderr=subprocess.PIPE,
+                    ** kwargs
+                )
+                if log.poll():
+                    raise self.repo.Exception("Failed to construct branch history for '{}'".format(branch))
+
+                hash = None
+                revision = None
+
+                line = log.stdout.readline()
+                while line:
+                    if line.startswith('    git-svn-id: '):
+                        match = self.repo.GIT_SVN_REVISION.match(line.lstrip())
+                        if match:
+                            revision = int(match.group('revision'))
+                    if not line.startswith('commit '):
+                        line = log.stdout.readline()
+                        continue
+
+                    if hash and _append(branch, hash, revision=revision):
+                        hash = None
+                        intersected = True
+                        break
+
+                    hash = line.split(' ')[1].rstrip()
+                    revision = None
+                    line = log.stdout.readline()
+
+                if hash:
+                    intersected = _append(branch, hash, revision=revision)
+
+            finally:
+                if log:
+                    log.kill()
+
+            if not hashes or intersected and len(hashes) <= 1:
+                return
+
+            hashes.reverse()
+            revisions.reverse()
+
+            order = len(self._ordered_commits[branch]) - 1
+            while order > 0:
+                if hashes[0] == self._ordered_commits[branch][order]:
+                    order -= 1
+                    break
+                order -= 1
+
+            self._ordered_commits[branch] = self._ordered_commits[branch][:order + 1] + hashes
+            self._ordered_revisions[branch] = self._ordered_revisions[branch][:order + 1] + revisions
+            self._fill(branch)
+
+            try:
+                with open(self.path, 'w') as file:
+                    json.dump(dict(
+                        hashes=self._ordered_commits,
+                        revisions=self._ordered_revisions,
+                    ), file, indent=4)
+            except (IOError, OSError):
+                self.repo.log("Failed to write identifier cache to '{}'".format(self.path))
+
+        def to_hash(self, revision=None, identifier=None, populate=True, branch=None):
+            if revision:
+                identifier = self.to_identifier(revision=revision, populate=populate, branch=branch)
+            parts = Commit._parse_identifier(identifier, do_assert=False)
+            if not parts:
+                return None
+
+            _, b_count, branch = parts
+            if b_count < 0:
+                return None
+            if branch not in self._ordered_commits or len(self._ordered_commits[branch]) <= b_count:
+                if populate:
+                    self.populate(branch=branch)
+                    return self.to_hash(identifier=identifier, populate=False)
+                return None
+            return self._ordered_commits[branch][b_count]
+
+        def to_revision(self, hash=None, identifier=None, populate=True, branch=None):
+            if hash:
+                identifier = self.to_identifier(hash=hash, populate=populate, branch=branch)
+            parts = Commit._parse_identifier(identifier, do_assert=False)
+            if not parts:
+                return None
+
+            _, b_count, branch = parts
+            if b_count < 0:
+                return None
+            if branch not in self._ordered_revisions or len(self._ordered_revisions[branch]) <= b_count:
+                if populate:
+                    self.populate(branch=branch)
+                    return self.to_revision(identifier=identifier, populate=False)
+                return None
+            return self._ordered_revisions[branch][b_count]
+
+        def to_identifier(self, hash=None, revision=None, populate=True, branch=None):
+            revision = Commit._parse_revision(revision, do_assert=False)
+            if revision:
+                if revision in self._revisions_to_identifiers:
+                    return self._revisions_to_identifiers[revision]
+                if populate:
+                    self.populate(branch=branch)
+                    return self.to_identifier(revision=revision, populate=False)
+                return None
+
+            hash = Commit._parse_hash(hash, do_assert=False)
+            if hash:
+                try:
+                    candidate = self._hash_to_identifiers.get(hash)
+                except KeyError:  # Means the hash wasn't specific enough
+                    return None
+
+                if candidate:
+                    return candidate
+                if populate:
+                    self.populate(branch=branch)
+                    return self.to_identifier(hash=hash, populate=False)
+            return None
+
+
     GIT_COMMIT = re.compile(r'commit (?P<hash>[0-9a-f]+)')
 
     @classmethod
@@ -51,6 +263,7 @@
     def __init__(self, path, dev_branches=None, prod_branches=None, contributors=None, id=None):
         super(Git, self).__init__(path, dev_branches=dev_branches, prod_branches=prod_branches, contributors=contributors, id=id)
         self._branch = None
+        self.cache = self.Cache(self) if self.root_path else None
         if not self.root_path:
             raise OSError('Provided path {} is not a git repository'.format(path))
 
@@ -163,12 +376,18 @@
         return sorted(set(['/'.join(branch.split('/')[2:]) if branch.startswith('remotes/origin/') else branch for branch in result]))
 
     def commit(self, hash=None, revision=None, identifier=None, branch=None, tag=None, include_log=True, include_identifier=True):
-        # Only git-svn checkouts can convert revisions to fully qualified commits
-        if revision and not self.is_svn:
+        # Only git-svn checkouts can convert revisions to fully qualified commits, unless we happen to have a SVN cache built
+        if revision:
+            if hash:
+                raise ValueError('Cannot define both hash and revision')
+            hash = self.cache.to_hash(revision=revision, branch=branch) if self.cache else None
+
+        # If we don't have an SVN cache built, and we're not git-svn, we can't reason about revisions
+        if revision and not hash and not self.is_svn:
             raise self.Exception('This git checkout does not support SVN revisions')
 
         # Determine the hash for a provided Subversion revision
-        elif revision:
+        elif revision and not hash:
             if hash:
                 raise ValueError('Cannot define both hash and revision')
 
@@ -206,34 +425,48 @@
                         ),
                     )
                 branch = parsed_branch
+                hash = self.cache.to_hash(identifier='{}@{}'.format(identifier, parsed_branch), branch=branch) if self.cache else None
 
-            baseline = branch or 'HEAD'
-            is_default = baseline == default_branch
-            if baseline == 'HEAD':
-                is_default = default_branch in self._branches_for(baseline)
+            # If the cache managed to convert the identifier to a hash, we can skip some computation
+            if hash:
+                log = run(
+                    [self.executable(), 'log', hash] + log_format,
+                    cwd=self.root_path,
+                    capture_output=True,
+                    encoding='utf-8',
+                )
+                if log.returncode:
+                    raise self.Exception("Failed to retrieve commit information for '{}'".format(hash))
 
-            if is_default and parsed_branch_point:
-                raise self.Exception('Cannot provide a branch point for a commit on the default branch')
+            # The cache has failed to convert the identifier, we need to do it the expensive way
+            else:
+                baseline = branch or 'HEAD'
+                is_default = baseline == default_branch
+                if baseline == 'HEAD':
+                    is_default = default_branch in self._branches_for(baseline)
 
-            base_count = self._commit_count(baseline if is_default else '{}..{}'.format(default_branch, baseline))
+                if is_default and parsed_branch_point:
+                    raise self.Exception('Cannot provide a branch point for a commit on the default branch')
 
-            if identifier > base_count:
-                raise self.Exception('Identifier {} cannot be found on the specified branch in the current checkout'.format(identifier))
-            log = run(
-                [self.executable(), 'log', '{}~{}'.format(branch or 'HEAD', base_count - identifier)] + log_format,
-                cwd=self.root_path,
-                capture_output=True,
-                encoding='utf-8',
-            )
-            if log.returncode:
-                raise self.Exception("Failed to retrieve commit information for 'i{}@{}'".format(identifier, branch or 'HEAD'))
+                base_count = self._commit_count(baseline if is_default else '{}..{}'.format(default_branch, baseline))
 
-            # Negative identifiers are actually commits on the default branch, we will need to re-compute the identifier
-            if identifier < 0 and is_default:
-                raise self.Exception('Illegal negative identifier on the default branch')
-            if identifier < 0:
-                identifier = None
+                if identifier > base_count:
+                    raise self.Exception('Identifier {} cannot be found on the specified branch in the current checkout'.format(identifier))
+                log = run(
+                    [self.executable(), 'log', '{}~{}'.format(branch or 'HEAD', base_count - identifier)] + log_format,
+                    cwd=self.root_path,
+                    capture_output=True,
+                    encoding='utf-8',
+                )
+                if log.returncode:
+                    raise self.Exception("Failed to retrieve commit information for 'i{}@{}'".format(identifier, branch or 'HEAD'))
 
+                # Negative identifiers are actually commits on the default branch, we will need to re-compute the identifier
+                if identifier < 0 and is_default:
+                    raise self.Exception('Illegal negative identifier on the default branch')
+                if identifier < 0:
+                    identifier = None
+
         # Determine the `git log` output for a given branch or tag
         elif branch or tag:
             if hash:
@@ -258,15 +491,23 @@
             raise self.Exception('Invalid commit hash in git log')
         hash = match.group('hash')
 
+        branch_point = None
         # A commit is often on multiple branches, the canonical branch is the one with the highest priority
-        branch = self.prioritize_branches(self._branches_for(hash))
+        if branch != default_branch:
+            branch = self.prioritize_branches(self._branches_for(hash))
 
+        if not identifier and include_identifier:
+            cached_identifier = self.cache.to_identifier(hash=hash, branch=branch) if self.cache else None
+            if cached_identifier:
+                branch_point, identifier, branch = Commit._parse_identifier(cached_identifier)
+
         # Compute the identifier if the function did not receive one and we were asked to
         if not identifier and include_identifier:
             identifier = self._commit_count(hash if branch == default_branch else '{}..{}'.format(default_branch, hash))
 
         # Only compute the branch point we're on something other than the default branch
-        branch_point = None if not include_identifier or branch == default_branch else self._commit_count(hash) - identifier
+        if not branch_point and include_identifier and branch != default_branch:
+            branch_point = self._commit_count(hash) - identifier
         if branch_point and parsed_branch_point and branch_point != parsed_branch_point:
             raise ValueError("Provided 'branch_point' does not match branch point of specified branch")
 

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py (279444 => 279445)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -270,6 +270,31 @@
                     ])
                 )
             ), mocks.Subprocess.Route(
+                self.executable, 'log', re.compile(r'.+'),
+                cwd=self.path,
+                generator=lambda *args, **kwargs: mocks.ProcessCompletion(
+                    returncode=0,
+                    stdout='\n'.join([
+                        'commit {hash}\n'
+                        'Author: {author} <{email}>\n'
+                        'Date:   {date}\n'
+                        '\n{log}\n'.format(
+                            hash=commit.hash,
+                            author=commit.author.name,
+                            email=commit.author.email,
+                            date=datetime.utcfromtimestamp(commit.timestamp + time.timezone).strftime('%a %b %d %H:%M:%S %Y +0000'),
+                            log='\n'.join([
+                                ('    ' + line) if line else '' for line in commit.message.splitlines()
+                            ] + ([
+                                '    git-svn-id: https://svn.{}/repository/{}/trunk@{} 268f45cc-cd09-0410-ab3c-d52691b4dbfc'.format(
+                                    self.remote.split('@')[-1].split(':')[0],
+                                    os.path.basename(path),
+                                   commit.revision,
+                            )] if git_svn else []),
+                        )) for commit in self.commits_in_range(self.commits[self.default_branch][0].hash, args[2])
+                    ])
+                )
+            ), mocks.Subprocess.Route(
                 self.executable, 'rev-list', '--count', '--no-merges', re.compile(r'.+'),
                 cwd=self.path,
                 generator=lambda *args, **kwargs: mocks.ProcessCompletion(
@@ -511,7 +536,7 @@
 
         branches = [self.default_branch]
         if end in self.commits.keys() and end != self.default_branch:
-            branches.append(end)
+            branches.insert(0, end)
         else:
             for branch, commits in self.commits.items():
                 if branch == self.default_branch:

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py (279444 => 279445)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -225,7 +225,7 @@
 
     def test_tag(self):
         for mock in [mocks.local.Git(self.path), mocks.local.Git(self.path, git_svn=True)]:
-            with mock:
+            with mock, LoggerCapture():
                 mock.tags['tag-1'] = mock.commits['branch-a'][-1]
 
                 self.assertEqual(
@@ -235,7 +235,7 @@
 
     def test_checkout(self):
         for mock in [mocks.local.Git(self.path), mocks.local.Git(self.path, git_svn=True)]:
-            with mock:
+            with mock, LoggerCapture():
                 mock.tags['tag-1'] = mock.commits['branch-a'][-1]
 
                 repository = local.Git(self.path)
@@ -251,7 +251,7 @@
 
     def test_no_log(self):
         for mock in [mocks.local.Git(self.path), mocks.local.Git(self.path, git_svn=True)]:
-            with mock:
+            with mock, LoggerCapture():
                 self.assertIsNone(local.Git(self.path).commit(identifier='4@main', include_log=False).message)
 
     def test_alternative_default_branch(self):
@@ -267,7 +267,7 @@
 
     def test_order(self):
         for mock in [mocks.local.Git(self.path), mocks.local.Git(self.path, git_svn=True)]:
-            with mock:
+            with mock, LoggerCapture():
                 self.assertEqual(0, local.Git(self.path).commit(hash='bae5d1e90999').order)
                 self.assertEqual(1, local.Git(self.path).commit(hash='d8bce26fa65c').order)
 
@@ -365,7 +365,33 @@
 '''
             )
 
+    def test_cache(self):
+        for mock in [mocks.local.Git(self.path), mocks.local.Git(self.path, git_svn=True)]:
+            with mock, OutputCapture():
+                repo = local.Git(self.path)
 
+                self.assertEqual(repo.cache.to_hash(identifier='1@main'), '9b8311f25a77ba14923d9d5a6532103f54abefcb')
+                self.assertEqual(repo.cache.to_identifier(hash='d8bce26fa65c'), '5@main')
+                self.assertEqual(repo.cache.to_hash(identifier='2.3@branch-b'), '790725a6d79e28db2ecdde29548d2262c0bd059d')
+                self.assertEqual(repo.cache.to_hash(identifier='2.1@branch-a'), 'a30ce8494bf1ac2807a69844f726be4a9843ca55')
+                self.assertEqual(repo.cache.to_identifier(hash='a30ce8494bf1'), '2.1@branch-a')
+
+                self.assertEqual(repo.cache.to_identifier(hash='badc0dd1f'), None)
+                self.assertEqual(repo.cache.to_hash(identifier='6@main'), None)
+
+    def test_revision_cache(self):
+        with mocks.local.Git(self.path, git_svn=True), OutputCapture():
+            repo = local.Git(self.path)
+
+            self.assertEqual(repo.cache.to_revision(identifier='1@main'), 1)
+            self.assertEqual(repo.cache.to_identifier(revision='r9'), '5@main')
+            self.assertEqual(repo.cache.to_hash(revision='r9'), 'd8bce26fa65c6fc8f39c17927abb77f69fab82fc')
+
+            self.assertEqual(repo.cache.to_identifier(revision=100), None)
+            self.assertEqual(repo.cache.to_revision(hash='badc0dd1f'), None)
+            self.assertEqual(repo.cache.to_revision(identifier='6@main'), None)
+
+
 class TestGitHub(testing.TestCase):
     remote = 'https://github.example.com/WebKit/WebKit'
 

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py (279444 => 279445)


--- trunk/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/models/test_run_results_unittest.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -35,6 +35,7 @@
 from webkitpy.layout_tests.models import test_run_results
 from webkitpy.tool.mocktool import MockOptions
 
+from webkitcorepy import OutputCapture
 from webkitscmpy import mocks
 
 
@@ -166,7 +167,7 @@
             self.assertEquals(summary['revision'], '6')
 
     def test_svn_revision_git(self):
-        with mocks.local.Svn(), mocks.local.Git(path='/', git_svn=True):
+        with mocks.local.Svn(), mocks.local.Git(path='/', git_svn=True), OutputCapture():
             self.port._options.builder_name = 'dummy builder'
             summary = summarized_results(self.port, expected=False, passing=False, flaky=False)
             self.assertEquals(summary['revision'], '9')

Modified: trunk/Tools/Scripts/webkitpy/port/base_unittest.py (279444 => 279445)


--- trunk/Tools/Scripts/webkitpy/port/base_unittest.py	2021-06-30 23:54:40 UTC (rev 279444)
+++ trunk/Tools/Scripts/webkitpy/port/base_unittest.py	2021-07-01 00:16:18 UTC (rev 279445)
@@ -329,7 +329,7 @@
             self.assertEqual([{'repository_id': 'webkit', 'id': '6', 'branch': 'trunk'}], port.commits_for_upload())
 
     def test_commits_for_upload_git_svn(self):
-        with mocks.local.Svn(), mocks.local.Git(path='/', git_svn=True):
+        with mocks.local.Svn(), mocks.local.Git(path='/', git_svn=True), OutputCapture():
             port = self.make_port(port_name='foo')
             self.assertEqual([{
                 'repository_id': 'webkit',
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to