git_cache_test.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #!/usr/bin/env vpython3
  2. # Copyright 2015 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Unit tests for git_cache.py"""
  6. from io import StringIO
  7. import logging
  8. import os
  9. import shutil
  10. import subprocess
  11. import sys
  12. import tempfile
  13. import unittest
  14. from unittest import mock
  15. DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  16. sys.path.insert(0, DEPOT_TOOLS_ROOT)
  17. from testing_support import coverage_utils
  18. import git_cache
  19. class GitCacheTest(unittest.TestCase):
  20. def setUp(self):
  21. self.cache_dir = tempfile.mkdtemp(prefix='git_cache_test_')
  22. self.addCleanup(shutil.rmtree, self.cache_dir, ignore_errors=True)
  23. self.origin_dir = tempfile.mkdtemp(suffix='origin.git')
  24. self.addCleanup(shutil.rmtree, self.origin_dir, ignore_errors=True)
  25. git_cache.Mirror.SetCachePath(self.cache_dir)
  26. # Ensure git_cache works with safe.bareRepository.
  27. mock.patch.dict(
  28. 'os.environ', {
  29. 'GIT_CONFIG_GLOBAL': os.path.join(self.cache_dir, '.gitconfig'),
  30. }).start()
  31. self.addCleanup(mock.patch.stopall)
  32. self.git([
  33. 'config', '--file',
  34. os.path.join(self.cache_dir, '.gitconfig'), '--add',
  35. 'safe.bareRepository', 'explicit'
  36. ])
  37. def git(self, cmd, cwd=None):
  38. cwd = cwd or self.origin_dir
  39. git = 'git.bat' if sys.platform == 'win32' else 'git'
  40. subprocess.check_call([git] + cmd, cwd=cwd)
  41. def testParseFetchSpec(self):
  42. testData = [
  43. ([], []),
  44. (['main'], [('+refs/heads/main:refs/heads/main',
  45. r'\+refs/heads/main:.*')]),
  46. (['main/'], [('+refs/heads/main:refs/heads/main',
  47. r'\+refs/heads/main:.*')]),
  48. (['+main'], [('+refs/heads/main:refs/heads/main',
  49. r'\+refs/heads/main:.*')]),
  50. (['master'], [('+refs/heads/master:refs/heads/master',
  51. r'\+refs/heads/master:.*')]),
  52. (['master/'], [('+refs/heads/master:refs/heads/master',
  53. r'\+refs/heads/master:.*')]),
  54. (['+master'], [('+refs/heads/master:refs/heads/master',
  55. r'\+refs/heads/master:.*')]),
  56. (['refs/heads/*'], [('+refs/heads/*:refs/heads/*',
  57. r'\+refs/heads/\*:.*')]),
  58. (['foo/bar/*', 'baz'], [('+refs/heads/foo/bar/*:refs/heads/foo/bar/*',
  59. r'\+refs/heads/foo/bar/\*:.*'),
  60. ('+refs/heads/baz:refs/heads/baz',
  61. r'\+refs/heads/baz:.*')]),
  62. (['refs/foo/*:refs/bar/*'], [('+refs/foo/*:refs/bar/*',
  63. r'\+refs/foo/\*:.*')])
  64. ]
  65. mirror = git_cache.Mirror('test://phony.example.biz')
  66. for fetch_specs, expected in testData:
  67. mirror = git_cache.Mirror('test://phony.example.biz', refs=fetch_specs)
  68. self.assertEqual(mirror.fetch_specs, set(expected))
  69. def testPopulate(self):
  70. self.git(['init', '-q'])
  71. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  72. f.write('touched\n')
  73. self.git(['add', 'foo'])
  74. self.git([
  75. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  76. '-m', 'foo'
  77. ])
  78. mirror = git_cache.Mirror(self.origin_dir)
  79. mirror.populate()
  80. def testPopulateResetFetchConfig(self):
  81. self.git(['init', '-q'])
  82. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  83. f.write('touched\n')
  84. self.git(['add', 'foo'])
  85. self.git([
  86. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  87. '-m', 'foo'
  88. ])
  89. mirror = git_cache.Mirror(self.origin_dir)
  90. mirror.populate()
  91. # Add a bad refspec to the cache's fetch config.
  92. cache_dir = os.path.join(
  93. self.cache_dir, mirror.UrlToCacheDir(self.origin_dir))
  94. self.git([
  95. '--git-dir', cache_dir, 'config', '--add', 'remote.origin.fetch',
  96. '+refs/heads/foo:refs/heads/foo'
  97. ],
  98. cwd=cache_dir)
  99. mirror.populate(reset_fetch_config=True)
  100. def testPopulateTwice(self):
  101. self.git(['init', '-q'])
  102. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  103. f.write('touched\n')
  104. self.git(['add', 'foo'])
  105. self.git([
  106. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  107. '-m', 'foo'
  108. ])
  109. mirror = git_cache.Mirror(self.origin_dir)
  110. mirror.populate()
  111. mirror.populate()
  112. @mock.patch('sys.stdout', StringIO())
  113. def testPruneRequired(self):
  114. self.git(['init', '-q'])
  115. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  116. f.write('touched\n')
  117. self.git(['checkout', '-b', 'foo'])
  118. self.git(['add', 'foo'])
  119. self.git([
  120. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  121. '-m', 'foo'
  122. ])
  123. mirror = git_cache.Mirror(self.origin_dir)
  124. mirror.populate()
  125. self.git(['checkout', '-b', 'foo_tmp', 'foo'])
  126. self.git(['branch', '-D', 'foo'])
  127. self.git(['checkout', '-b', 'foo/bar', 'foo_tmp'])
  128. mirror.populate()
  129. self.assertNotIn(git_cache.GIT_CACHE_CORRUPT_MESSAGE, sys.stdout.getvalue())
  130. def _makeGitRepoWithTag(self):
  131. self.git(['init', '-q'])
  132. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  133. f.write('touched\n')
  134. self.git(['add', 'foo'])
  135. self.git([
  136. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  137. '-m', 'foo'
  138. ])
  139. self.git(['tag', 'TAG'])
  140. self.git(['pack-refs'])
  141. def testPopulateFetchTagsByDefault(self):
  142. self._makeGitRepoWithTag()
  143. # Default behaviour includes tags.
  144. mirror = git_cache.Mirror(self.origin_dir)
  145. mirror.populate()
  146. cache_dir = os.path.join(self.cache_dir,
  147. mirror.UrlToCacheDir(self.origin_dir))
  148. self.assertTrue(os.path.exists(cache_dir + '/refs/tags/TAG'))
  149. def testPopulateFetchWithoutTags(self):
  150. self._makeGitRepoWithTag()
  151. # Ask to not include tags.
  152. mirror = git_cache.Mirror(self.origin_dir)
  153. mirror.populate(no_fetch_tags=True)
  154. cache_dir = os.path.join(self.cache_dir,
  155. mirror.UrlToCacheDir(self.origin_dir))
  156. self.assertFalse(os.path.exists(cache_dir + '/refs/tags/TAG'))
  157. def testPopulateResetFetchConfigEmptyFetchConfig(self):
  158. self.git(['init', '-q'])
  159. with open(os.path.join(self.origin_dir, 'foo'), 'w') as f:
  160. f.write('touched\n')
  161. self.git(['add', 'foo'])
  162. self.git([
  163. '-c', 'user.name=Test user', '-c', 'user.email=joj@test.com', 'commit',
  164. '-m', 'foo'
  165. ])
  166. mirror = git_cache.Mirror(self.origin_dir)
  167. mirror.populate(reset_fetch_config=True)
  168. class GitCacheDirTest(unittest.TestCase):
  169. def setUp(self):
  170. try:
  171. delattr(git_cache.Mirror, 'cachepath')
  172. except AttributeError:
  173. pass
  174. super(GitCacheDirTest, self).setUp()
  175. def tearDown(self):
  176. try:
  177. delattr(git_cache.Mirror, 'cachepath')
  178. except AttributeError:
  179. pass
  180. super(GitCacheDirTest, self).tearDown()
  181. def test_git_config_read(self):
  182. (fd, tmpFile) = tempfile.mkstemp()
  183. old = git_cache.Mirror._GIT_CONFIG_LOCATION
  184. try:
  185. try:
  186. os.write(fd, b'[cache]\n cachepath="hello world"\n')
  187. finally:
  188. os.close(fd)
  189. git_cache.Mirror._GIT_CONFIG_LOCATION = ['-f', tmpFile]
  190. self.assertEqual(git_cache.Mirror.GetCachePath(), 'hello world')
  191. finally:
  192. git_cache.Mirror._GIT_CONFIG_LOCATION = old
  193. os.remove(tmpFile)
  194. def test_environ_read(self):
  195. path = os.environ.get('GIT_CACHE_PATH')
  196. config = os.environ.get('GIT_CONFIG')
  197. try:
  198. os.environ['GIT_CACHE_PATH'] = 'hello world'
  199. os.environ['GIT_CONFIG'] = 'disabled'
  200. self.assertEqual(git_cache.Mirror.GetCachePath(), 'hello world')
  201. finally:
  202. for name, val in zip(('GIT_CACHE_PATH', 'GIT_CONFIG'), (path, config)):
  203. if val is None:
  204. os.environ.pop(name, None)
  205. else:
  206. os.environ[name] = val
  207. def test_manual_set(self):
  208. git_cache.Mirror.SetCachePath('hello world')
  209. self.assertEqual(git_cache.Mirror.GetCachePath(), 'hello world')
  210. def test_unconfigured(self):
  211. path = os.environ.get('GIT_CACHE_PATH')
  212. config = os.environ.get('GIT_CONFIG')
  213. try:
  214. os.environ.pop('GIT_CACHE_PATH', None)
  215. os.environ['GIT_CONFIG'] = 'disabled'
  216. with self.assertRaisesRegexp(RuntimeError, 'cache\.cachepath'):
  217. git_cache.Mirror.GetCachePath()
  218. # negatively cached value still raises
  219. with self.assertRaisesRegexp(RuntimeError, 'cache\.cachepath'):
  220. git_cache.Mirror.GetCachePath()
  221. finally:
  222. for name, val in zip(('GIT_CACHE_PATH', 'GIT_CONFIG'), (path, config)):
  223. if val is None:
  224. os.environ.pop(name, None)
  225. else:
  226. os.environ[name] = val
  227. class MirrorTest(unittest.TestCase):
  228. def test_same_cache_for_authenticated_and_unauthenticated_urls(self):
  229. # GoB can fetch a repo via two different URLs; if the url contains '/a/'
  230. # it forces authenticated access instead of allowing anonymous access,
  231. # even in the case where a repo is public. We want this in order to make
  232. # sure bots are authenticated and get the right quotas. However, we
  233. # only want to maintain a single cache for the repo.
  234. self.assertEqual(git_cache.Mirror.UrlToCacheDir(
  235. 'https://chromium.googlesource.com/a/chromium/src.git'),
  236. 'chromium.googlesource.com-chromium-src')
  237. if __name__ == '__main__':
  238. logging.basicConfig(
  239. level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
  240. sys.exit(coverage_utils.covered_main((
  241. os.path.join(DEPOT_TOOLS_ROOT, 'git_cache.py')
  242. ), required_percentage=0))