git_cache_test.py 9.6 KB

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