mirror of
https://chromium.googlesource.com/chromium/tools/depot_tools.git
synced 2026-01-11 02:31:29 +00:00
Speed up presubmit by caching code-owners check
Currently, presubmit queries the gerrit server every run to check if the code-owners plugin is enabled. This CL caches that result in a /tmp file per repository. This saves as much as 1.5s per `git cl presubmit`. Change-Id: I1a3631074d1bcbb1a254caa6654fd8434f069aa2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/7227749 Commit-Queue: Josiah Kiehl <kiehl@google.com> Reviewed-by: Yiwei Zhang <yiwzhang@google.com>
This commit is contained in:
@@ -1421,7 +1421,7 @@ class GitWrapper(SCMWrapper):
|
|||||||
logging.debug(
|
logging.debug(
|
||||||
'Cloned into temporary dir, moving to checkout_path')
|
'Cloned into temporary dir, moving to checkout_path')
|
||||||
gclient_utils.safe_makedirs(self.checkout_path)
|
gclient_utils.safe_makedirs(self.checkout_path)
|
||||||
gclient_utils.safe_rename(
|
gclient_utils.safe_replace(
|
||||||
os.path.join(tmp_dir, '.git'),
|
os.path.join(tmp_dir, '.git'),
|
||||||
os.path.join(self.checkout_path, '.git'))
|
os.path.join(self.checkout_path, '.git'))
|
||||||
except:
|
except:
|
||||||
|
|||||||
@@ -250,10 +250,10 @@ def temporary_file():
|
|||||||
os.remove(name)
|
os.remove(name)
|
||||||
|
|
||||||
|
|
||||||
def safe_rename(old, new):
|
def safe_replace(old, new):
|
||||||
"""Renames a file reliably.
|
"""Renames a file reliably, overwriting the destination if it exists.
|
||||||
|
|
||||||
Sometimes os.rename does not work because a dying git process keeps a handle
|
Sometimes os.replace does not work because a dying git process keeps a handle
|
||||||
on it for a few seconds. An exception is then thrown, which make the program
|
on it for a few seconds. An exception is then thrown, which make the program
|
||||||
give up what it was doing and remove what was deleted.
|
give up what it was doing and remove what was deleted.
|
||||||
The only solution is to catch the exception and try again until it works.
|
The only solution is to catch the exception and try again until it works.
|
||||||
@@ -262,7 +262,7 @@ def safe_rename(old, new):
|
|||||||
retries = 100
|
retries = 100
|
||||||
for i in range(retries):
|
for i in range(retries):
|
||||||
try:
|
try:
|
||||||
os.rename(old, new)
|
os.replace(old, new)
|
||||||
break
|
break
|
||||||
except OSError:
|
except OSError:
|
||||||
if i == (retries - 1):
|
if i == (retries - 1):
|
||||||
|
|||||||
140
gerrit_cache.py
Normal file
140
gerrit_cache.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Copyright (c) 2025 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import gclient_utils
|
||||||
|
import lockfile
|
||||||
|
import scm
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _AtomicFileWriter(path, mode='w'):
|
||||||
|
"""Atomic file writer context manager."""
|
||||||
|
# Create temp file in same directory to ensure atomic rename works
|
||||||
|
fd, temp_path = tempfile.mkstemp(dir=os.path.dirname(path),
|
||||||
|
text='b' not in mode)
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, mode) as f:
|
||||||
|
yield f
|
||||||
|
# Atomic rename
|
||||||
|
gclient_utils.safe_replace(temp_path, path)
|
||||||
|
except Exception:
|
||||||
|
# Cleanup on failure
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
os.remove(temp_path)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class GerritCache(object):
|
||||||
|
"""Simple JSON file-based cache for Gerrit API results."""
|
||||||
|
|
||||||
|
def __init__(self, root_dir):
|
||||||
|
self.root_dir = root_dir
|
||||||
|
self.cache_path = self._get_cache_path()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_repo_code_owners_enabled_key(host, project):
|
||||||
|
return 'code-owners.%s.%s.enabled' % (host, project)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_host_code_owners_enabled_key(host):
|
||||||
|
return 'code-owners.%s.enabled' % host
|
||||||
|
|
||||||
|
def _get_cache_path(self):
|
||||||
|
path = os.environ.get('DEPOT_TOOLS_GERRIT_CACHE_PATH')
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
|
||||||
|
try:
|
||||||
|
path = scm.GIT.GetConfig(self.root_dir,
|
||||||
|
'depot-tools.gerrit-cache-path')
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.root_dir:
|
||||||
|
# Use a deterministic name based on the repo path
|
||||||
|
sanitized_path = re.sub(r'[^a-zA-Z0-9]', '_',
|
||||||
|
os.path.abspath(self.root_dir))
|
||||||
|
filename = 'depot_tools_gerrit_cache_%s.json' % sanitized_path
|
||||||
|
path = os.path.join(tempfile.gettempdir(), filename)
|
||||||
|
else:
|
||||||
|
fd, path = tempfile.mkstemp(prefix='depot_tools_gerrit_cache_',
|
||||||
|
suffix='.json')
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
# Initialize with empty JSON object
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
json.dump({}, f)
|
||||||
|
|
||||||
|
try:
|
||||||
|
scm.GIT.SetConfig(self.root_dir,
|
||||||
|
'depot-tools.gerrit-cache-path', path)
|
||||||
|
except Exception:
|
||||||
|
# If we can't set config (e.g. not a git repo and no env var
|
||||||
|
# set), just return the temp path. It will be a per-process
|
||||||
|
# cache in that case.
|
||||||
|
pass
|
||||||
|
return path
|
||||||
|
except Exception:
|
||||||
|
# Fallback to random temp file if everything else fails
|
||||||
|
fd, path = tempfile.mkstemp(prefix='gerrit_cache_', suffix='.json')
|
||||||
|
os.close(fd)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
if not self.cache_path:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
with lockfile.lock(self.cache_path, timeout=1):
|
||||||
|
if os.path.exists(self.cache_path):
|
||||||
|
with open(self.cache_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
data = json.load(f)
|
||||||
|
return data.get(key)
|
||||||
|
except ValueError:
|
||||||
|
# Corrupt cache file, treat as miss
|
||||||
|
return None
|
||||||
|
except Exception:
|
||||||
|
# Ignore cache errors
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set(self, key, value):
|
||||||
|
if not self.cache_path:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with lockfile.lock(self.cache_path, timeout=1):
|
||||||
|
data = {}
|
||||||
|
if os.path.exists(self.cache_path):
|
||||||
|
with open(self.cache_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
data = json.load(f)
|
||||||
|
except ValueError:
|
||||||
|
# Corrupt cache, start fresh
|
||||||
|
data = {}
|
||||||
|
data[key] = value
|
||||||
|
with _AtomicFileWriter(self.cache_path, 'w') as f:
|
||||||
|
json.dump(data, f)
|
||||||
|
except Exception:
|
||||||
|
# Ignore cache errors
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getBoolean(self, key):
|
||||||
|
"""Returns the value for key as a boolean, or None if missing."""
|
||||||
|
val = self.get(key)
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
|
return bool(val)
|
||||||
|
|
||||||
|
def setBoolean(self, key, value):
|
||||||
|
"""Sets the value for key as a boolean."""
|
||||||
|
self.set(key, bool(value))
|
||||||
@@ -6,6 +6,7 @@ import os
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
import gerrit_util
|
import gerrit_util
|
||||||
|
from gerrit_cache import GerritCache
|
||||||
import git_common
|
import git_common
|
||||||
|
|
||||||
|
|
||||||
@@ -213,12 +214,22 @@ class GerritClient(OwnersClient):
|
|||||||
paths))
|
paths))
|
||||||
|
|
||||||
|
|
||||||
def GetCodeOwnersClient(host, project, branch):
|
def GetCodeOwnersClient(host, project, branch, cache=None):
|
||||||
"""Get a new OwnersClient.
|
"""Get a new OwnersClient.
|
||||||
|
|
||||||
Uses GerritClient and raises an exception if code-owners plugin is not
|
Uses GerritClient and raises an exception if code-owners plugin is not
|
||||||
available."""
|
available."""
|
||||||
if gerrit_util.IsCodeOwnersEnabledOnHost(host):
|
key = GerritCache.get_host_code_owners_enabled_key(host)
|
||||||
|
is_enabled = None
|
||||||
|
if cache:
|
||||||
|
is_enabled = cache.getBoolean(key)
|
||||||
|
|
||||||
|
if is_enabled is None:
|
||||||
|
is_enabled = gerrit_util.IsCodeOwnersEnabledOnHost(host)
|
||||||
|
if cache:
|
||||||
|
cache.setBoolean(key, is_enabled)
|
||||||
|
|
||||||
|
if is_enabled:
|
||||||
return GerritClient(host, project, branch)
|
return GerritClient(host, project, branch)
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'code-owners plugin is not enabled. Ask your host admin to enable it '
|
'code-owners plugin is not enabled. Ask your host admin to enable it '
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import gclient_paths # Exposed through the API
|
|||||||
import gclient_utils
|
import gclient_utils
|
||||||
import git_footers
|
import git_footers
|
||||||
import gerrit_util
|
import gerrit_util
|
||||||
|
from gerrit_cache import GerritCache
|
||||||
|
import lockfile
|
||||||
import owners_client
|
import owners_client
|
||||||
import owners_finder
|
import owners_finder
|
||||||
import presubmit_canned_checks
|
import presubmit_canned_checks
|
||||||
@@ -499,12 +501,14 @@ class GerritAccessor(object):
|
|||||||
|
|
||||||
To avoid excessive Gerrit calls, caches the results.
|
To avoid excessive Gerrit calls, caches the results.
|
||||||
"""
|
"""
|
||||||
def __init__(self, url=None, project=None, branch=None):
|
|
||||||
|
def __init__(self, url=None, project=None, branch=None, root=None):
|
||||||
self.host = urlparse.urlparse(url).netloc if url else None
|
self.host = urlparse.urlparse(url).netloc if url else None
|
||||||
self.project = project
|
self.project = project
|
||||||
self.branch = branch
|
self.branch = branch
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.code_owners_enabled = None
|
self.code_owners_enabled = None
|
||||||
|
self.persistent_cache = GerritCache(root)
|
||||||
|
|
||||||
def _FetchChangeDetail(self, issue):
|
def _FetchChangeDetail(self, issue):
|
||||||
# Separate function to be easily mocked in tests.
|
# Separate function to be easily mocked in tests.
|
||||||
@@ -597,8 +601,19 @@ class GerritAccessor(object):
|
|||||||
|
|
||||||
def IsCodeOwnersEnabledOnRepo(self):
|
def IsCodeOwnersEnabledOnRepo(self):
|
||||||
if self.code_owners_enabled is None:
|
if self.code_owners_enabled is None:
|
||||||
self.code_owners_enabled = gerrit_util.IsCodeOwnersEnabledOnRepo(
|
if self.persistent_cache:
|
||||||
self.host, self.project)
|
key = GerritCache.get_repo_code_owners_enabled_key(
|
||||||
|
self.host, self.project)
|
||||||
|
self.code_owners_enabled = self.persistent_cache.getBoolean(key)
|
||||||
|
if self.code_owners_enabled is None:
|
||||||
|
self.code_owners_enabled = gerrit_util.IsCodeOwnersEnabledOnRepo(
|
||||||
|
self.host, self.project)
|
||||||
|
self.persistent_cache.setBoolean(key,
|
||||||
|
self.code_owners_enabled)
|
||||||
|
else:
|
||||||
|
self.code_owners_enabled = gerrit_util.IsCodeOwnersEnabledOnRepo(
|
||||||
|
self.host, self.project)
|
||||||
|
|
||||||
return self.code_owners_enabled
|
return self.code_owners_enabled
|
||||||
|
|
||||||
|
|
||||||
@@ -790,7 +805,8 @@ class InputApi(object):
|
|||||||
self.owners_client = owners_client.GetCodeOwnersClient(
|
self.owners_client = owners_client.GetCodeOwnersClient(
|
||||||
host=self.gerrit.host,
|
host=self.gerrit.host,
|
||||||
project=self.gerrit.project,
|
project=self.gerrit.project,
|
||||||
branch=self.gerrit.branch)
|
branch=self.gerrit.branch,
|
||||||
|
cache=self.gerrit.persistent_cache)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Failed to set owners_client - %s' % str(e))
|
print('Failed to set owners_client - %s' % str(e))
|
||||||
self.owners_finder = owners_finder.OwnersFinder
|
self.owners_finder = owners_finder.OwnersFinder
|
||||||
@@ -808,6 +824,7 @@ class InputApi(object):
|
|||||||
(a, b, header) for (a, b, header) in cpplint._re_pattern_templates
|
(a, b, header) for (a, b, header) in cpplint._re_pattern_templates
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def SetTimeout(self, timeout):
|
def SetTimeout(self, timeout):
|
||||||
self.thread_pool.timeout = timeout
|
self.thread_pool.timeout = timeout
|
||||||
|
|
||||||
@@ -2335,7 +2352,8 @@ def _parse_gerrit_options(parser, options):
|
|||||||
if options.gerrit_url:
|
if options.gerrit_url:
|
||||||
gerrit_obj = GerritAccessor(url=options.gerrit_url,
|
gerrit_obj = GerritAccessor(url=options.gerrit_url,
|
||||||
project=options.gerrit_project,
|
project=options.gerrit_project,
|
||||||
branch=options.gerrit_branch)
|
branch=options.gerrit_branch,
|
||||||
|
root=options.root)
|
||||||
|
|
||||||
if not options.gerrit_fetch:
|
if not options.gerrit_fetch:
|
||||||
return gerrit_obj
|
return gerrit_obj
|
||||||
|
|||||||
@@ -419,4 +419,31 @@ class GClientUtilsTest(trial_dir.TestCase):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
||||||
|
class SafeReplaceTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mock_os = mock.patch('gclient_utils.os').start()
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
|
||||||
|
def testReplace(self):
|
||||||
|
gclient_utils.safe_replace('old', 'new')
|
||||||
|
self.mock_os.replace.assert_called_with('old', 'new')
|
||||||
|
|
||||||
|
def testReplaceRetry(self):
|
||||||
|
self.mock_os.replace.side_effect = [OSError, OSError, None]
|
||||||
|
with mock.patch('time.sleep') as mock_sleep:
|
||||||
|
gclient_utils.safe_replace('old', 'new')
|
||||||
|
self.assertEqual(self.mock_os.replace.call_count, 3)
|
||||||
|
self.assertEqual(mock_sleep.call_count, 2)
|
||||||
|
|
||||||
|
def testReplaceFailure(self):
|
||||||
|
self.mock_os.replace.side_effect = OSError
|
||||||
|
with mock.patch('time.sleep'):
|
||||||
|
with self.assertRaises(OSError):
|
||||||
|
gclient_utils.safe_replace('old', 'new')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# vim: ts=2:sw=2:tw=80:et:
|
# vim: ts=2:sw=2:tw=80:et:
|
||||||
|
|||||||
163
tests/gerrit_cache_test.py
Executable file
163
tests/gerrit_cache_test.py
Executable file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env vpython3
|
||||||
|
# Copyright (c) 2025 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, ROOT_DIR)
|
||||||
|
|
||||||
|
import gerrit_cache
|
||||||
|
import unittest.mock as mock
|
||||||
|
|
||||||
|
|
||||||
|
class AtomicFileWriterTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
self.addCleanup(shutil.rmtree, self.test_dir)
|
||||||
|
|
||||||
|
def testSuccess(self):
|
||||||
|
path = os.path.join(self.test_dir, 'test_file')
|
||||||
|
with gerrit_cache._AtomicFileWriter(path, 'w') as f:
|
||||||
|
f.write('content')
|
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(path))
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
self.assertEqual(f.read(), 'content')
|
||||||
|
|
||||||
|
def testFailure(self):
|
||||||
|
path = os.path.join(self.test_dir, 'test_file')
|
||||||
|
try:
|
||||||
|
with gerrit_cache._AtomicFileWriter(path, 'w') as f:
|
||||||
|
f.write('content')
|
||||||
|
raise Exception('oops')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertFalse(os.path.exists(path))
|
||||||
|
|
||||||
|
def testOverwrite(self):
|
||||||
|
path = os.path.join(self.test_dir, 'test_file')
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write('old')
|
||||||
|
|
||||||
|
with gerrit_cache._AtomicFileWriter(path, 'w') as f:
|
||||||
|
f.write('new')
|
||||||
|
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
self.assertEqual(f.read(), 'new')
|
||||||
|
|
||||||
|
def testOverwriteFailure(self):
|
||||||
|
path = os.path.join(self.test_dir, 'test_file')
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write('old')
|
||||||
|
|
||||||
|
try:
|
||||||
|
with gerrit_cache._AtomicFileWriter(path, 'w') as f:
|
||||||
|
f.write('new')
|
||||||
|
raise Exception('oops')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
self.assertEqual(f.read(), 'old')
|
||||||
|
|
||||||
|
|
||||||
|
class GerritCacheTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.test_dir = tempfile.mkdtemp()
|
||||||
|
self.addCleanup(shutil.rmtree, self.test_dir)
|
||||||
|
self.mock_git = mock.patch('gerrit_cache.scm.GIT').start()
|
||||||
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
self.mock_git.GetConfig.return_value = None
|
||||||
|
|
||||||
|
def testGetSet(self):
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
cache.set('key', 'value')
|
||||||
|
self.assertEqual(cache.get('key'), 'value')
|
||||||
|
|
||||||
|
# Verify persistence
|
||||||
|
cache2 = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
self.assertEqual(cache2.get('key'), 'value')
|
||||||
|
|
||||||
|
def testGetSetBoolean(self):
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
cache.setBoolean('bool_key', True)
|
||||||
|
self.assertTrue(cache.getBoolean('bool_key'))
|
||||||
|
|
||||||
|
cache2 = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
self.assertTrue(cache2.getBoolean('bool_key'))
|
||||||
|
|
||||||
|
def testGetMissing(self):
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
self.assertIsNone(cache.get('missing'))
|
||||||
|
|
||||||
|
def testCachePathFromEnv(self):
|
||||||
|
with mock.patch.dict(
|
||||||
|
os.environ, {
|
||||||
|
'DEPOT_TOOLS_GERRIT_CACHE_PATH':
|
||||||
|
os.path.join(self.test_dir, 'env_cache.json')
|
||||||
|
}):
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
cache.set('key', 'env_value')
|
||||||
|
self.assertEqual(cache.get('key'), 'env_value')
|
||||||
|
self.assertTrue(
|
||||||
|
os.path.exists(os.path.join(self.test_dir, 'env_cache.json')))
|
||||||
|
|
||||||
|
def testCachePathFromGitConfig(self):
|
||||||
|
config_path = os.path.join(self.test_dir, 'config_cache.json')
|
||||||
|
self.mock_git.GetConfig.return_value = config_path
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
self.assertEqual(cache.cache_path, config_path)
|
||||||
|
|
||||||
|
def testCachePathCreation(self):
|
||||||
|
# When no env var and no config, it should create a deterministic temp file and set config
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
sanitized_path = re.sub(r'[^a-zA-Z0-9]', '_',
|
||||||
|
os.path.abspath(self.test_dir))
|
||||||
|
expected_filename = 'depot_tools_gerrit_cache_%s.json' % sanitized_path
|
||||||
|
self.assertTrue(cache.cache_path.endswith(expected_filename))
|
||||||
|
self.assertTrue(cache.cache_path.startswith(tempfile.gettempdir()))
|
||||||
|
self.mock_git.SetConfig.assert_called_with(
|
||||||
|
self.test_dir, 'depot-tools.gerrit-cache-path', cache.cache_path)
|
||||||
|
|
||||||
|
def testCachePathCreationLinux(self):
|
||||||
|
root_dir = '/usr/local/google/home/user/repo'
|
||||||
|
with mock.patch('os.path.abspath', return_value=root_dir):
|
||||||
|
cache = gerrit_cache.GerritCache(root_dir)
|
||||||
|
sanitized_path = re.sub(r'[^a-zA-Z0-9]', '_', root_dir)
|
||||||
|
expected_filename = 'depot_tools_gerrit_cache_%s.json' % sanitized_path
|
||||||
|
self.assertTrue(cache.cache_path.endswith(expected_filename))
|
||||||
|
|
||||||
|
def testCachePathCreationWindows(self):
|
||||||
|
root_dir = r'C:\Users\user\repo'
|
||||||
|
# On Linux, os.path.abspath('C:\Users\user\repo') will prepend current cwd.
|
||||||
|
with mock.patch('os.path.abspath', return_value=root_dir):
|
||||||
|
cache = gerrit_cache.GerritCache(root_dir)
|
||||||
|
sanitized_path = re.sub(r'[^a-zA-Z0-9]', '_', root_dir)
|
||||||
|
expected_filename = 'depot_tools_gerrit_cache_%s.json' % sanitized_path
|
||||||
|
self.assertTrue(cache.cache_path.endswith(expected_filename))
|
||||||
|
|
||||||
|
def testCorruptCache(self):
|
||||||
|
cache = gerrit_cache.GerritCache(self.test_dir)
|
||||||
|
# Write garbage to the cache file
|
||||||
|
with open(cache.cache_path, 'w') as f:
|
||||||
|
f.write('this is not json')
|
||||||
|
|
||||||
|
# Should not crash, just return None
|
||||||
|
self.assertIsNone(cache.get('key'))
|
||||||
|
|
||||||
|
cache.set('key', 'value')
|
||||||
|
self.assertEqual(cache.get('key'), 'value')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user