scm_unittest.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #!/usr/bin/env vpython3
  2. # Copyright (c) 2012 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 scm.py."""
  6. import logging
  7. import os
  8. import sys
  9. import unittest
  10. if sys.version_info.major == 2:
  11. import mock
  12. else:
  13. from unittest import mock
  14. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  15. from testing_support import fake_repos
  16. import scm
  17. import subprocess2
  18. def callError(code=1, cmd='', cwd='', stdout=b'', stderr=b''):
  19. return subprocess2.CalledProcessError(code, cmd, cwd, stdout, stderr)
  20. class GitWrapperTestCase(unittest.TestCase):
  21. def setUp(self):
  22. super(GitWrapperTestCase, self).setUp()
  23. self.root_dir = '/foo/bar'
  24. @mock.patch('scm.GIT.Capture')
  25. def testGetEmail(self, mockCapture):
  26. mockCapture.return_value = 'mini@me.com'
  27. self.assertEqual(scm.GIT.GetEmail(self.root_dir), 'mini@me.com')
  28. mockCapture.assert_called_with(['config', 'user.email'], cwd=self.root_dir)
  29. @mock.patch('scm.GIT.Capture')
  30. def testAssertVersion(self, mockCapture):
  31. cases = [
  32. ('1.7', True),
  33. ('1.7.9', True),
  34. ('1.7.9.foo-bar-baz', True),
  35. ('1.8', True),
  36. ('1.6.9', False),
  37. ]
  38. for expected_version, expected_ok in cases:
  39. class GIT(scm.GIT):
  40. pass
  41. mockCapture.return_value = 'git version ' + expected_version
  42. ok, version = GIT.AssertVersion('1.7')
  43. self.assertEqual(expected_ok, ok)
  44. self.assertEqual(expected_version, version)
  45. def testRefToRemoteRef(self):
  46. remote = 'origin'
  47. refs = {
  48. 'refs/branch-heads/1234': ('refs/remotes/branch-heads/', '1234'),
  49. # local refs for upstream branch
  50. 'refs/remotes/%s/foobar' % remote: ('refs/remotes/%s/' % remote,
  51. 'foobar'),
  52. '%s/foobar' % remote: ('refs/remotes/%s/' % remote, 'foobar'),
  53. # upstream ref for branch
  54. 'refs/heads/foobar': ('refs/remotes/%s/' % remote, 'foobar'),
  55. # could be either local or upstream ref, assumed to refer to
  56. # upstream, but probably don't want to encourage refs like this.
  57. 'heads/foobar': ('refs/remotes/%s/' % remote, 'foobar'),
  58. # underspecified, probably intended to refer to a local branch
  59. 'foobar': None,
  60. # tags and other refs
  61. 'refs/tags/TAG': None,
  62. 'refs/changes/34/1234': None,
  63. }
  64. for k, v in refs.items():
  65. r = scm.GIT.RefToRemoteRef(k, remote)
  66. self.assertEqual(r, v, msg='%s -> %s, expected %s' % (k, r, v))
  67. def testRemoteRefToRef(self):
  68. remote = 'origin'
  69. refs = {
  70. 'refs/remotes/branch-heads/1234': 'refs/branch-heads/1234',
  71. # local refs for upstream branch
  72. 'refs/remotes/origin/foobar': 'refs/heads/foobar',
  73. # tags and other refs
  74. 'refs/tags/TAG': 'refs/tags/TAG',
  75. 'refs/changes/34/1234': 'refs/changes/34/1234',
  76. # different remote
  77. 'refs/remotes/other-remote/foobar': None,
  78. # underspecified, probably intended to refer to a local branch
  79. 'heads/foobar': None,
  80. 'origin/foobar': None,
  81. 'foobar': None,
  82. None: None,
  83. }
  84. for k, v in refs.items():
  85. r = scm.GIT.RemoteRefToRef(k, remote)
  86. self.assertEqual(r, v, msg='%s -> %s, expected %s' % (k, r, v))
  87. @mock.patch('scm.GIT.Capture')
  88. @mock.patch('os.path.exists', lambda _:True)
  89. def testGetRemoteHeadRefLocal(self, mockCapture):
  90. mockCapture.side_effect = ['refs/remotes/origin/main']
  91. self.assertEqual('refs/remotes/origin/main',
  92. scm.GIT.GetRemoteHeadRef('foo', 'proto://url', 'origin'))
  93. self.assertEqual(mockCapture.call_count, 1)
  94. @mock.patch('scm.GIT.Capture')
  95. @mock.patch('os.path.exists', lambda _: True)
  96. def testGetRemoteHeadRefLocalUpdateHead(self, mockCapture):
  97. mockCapture.side_effect = [
  98. 'refs/remotes/origin/master', # first symbolic-ref call
  99. 'foo', # set-head call
  100. 'refs/remotes/origin/main', # second symbolic-ref call
  101. ]
  102. self.assertEqual('refs/remotes/origin/main',
  103. scm.GIT.GetRemoteHeadRef('foo', 'proto://url', 'origin'))
  104. self.assertEqual(mockCapture.call_count, 3)
  105. @mock.patch('scm.GIT.Capture')
  106. @mock.patch('os.path.exists', lambda _:True)
  107. def testGetRemoteHeadRefRemote(self, mockCapture):
  108. mockCapture.side_effect = [
  109. subprocess2.CalledProcessError(1, '', '', '', ''),
  110. 'ref: refs/heads/main\tHEAD\n' +
  111. '0000000000000000000000000000000000000000\tHEAD',
  112. ]
  113. self.assertEqual('refs/remotes/origin/main',
  114. scm.GIT.GetRemoteHeadRef('foo', 'proto://url', 'origin'))
  115. self.assertEqual(mockCapture.call_count, 2)
  116. class RealGitTest(fake_repos.FakeReposTestBase):
  117. def setUp(self):
  118. super(RealGitTest, self).setUp()
  119. self.enabled = self.FAKE_REPOS.set_up_git()
  120. if self.enabled:
  121. self.cwd = scm.os.path.join(self.FAKE_REPOS.git_base, 'repo_1')
  122. else:
  123. self.skipTest('git fake repos not available')
  124. def testResolveCommit(self):
  125. self.assertIsNone(scm.GIT.ResolveCommit(self.cwd, 'zebra'))
  126. self.assertIsNone(scm.GIT.ResolveCommit(self.cwd, 'r123456'))
  127. first_rev = self.githash('repo_1', 1)
  128. self.assertEqual(first_rev, scm.GIT.ResolveCommit(self.cwd, first_rev))
  129. self.assertEqual(
  130. self.githash('repo_1', 2), scm.GIT.ResolveCommit(self.cwd, 'HEAD'))
  131. def testIsValidRevision(self):
  132. # Sha1's are [0-9a-z]{32}, so starting with a 'z' or 'r' should always fail.
  133. self.assertFalse(scm.GIT.IsValidRevision(cwd=self.cwd, rev='zebra'))
  134. self.assertFalse(scm.GIT.IsValidRevision(cwd=self.cwd, rev='r123456'))
  135. # Valid cases
  136. first_rev = self.githash('repo_1', 1)
  137. self.assertTrue(scm.GIT.IsValidRevision(cwd=self.cwd, rev=first_rev))
  138. self.assertTrue(scm.GIT.IsValidRevision(cwd=self.cwd, rev='HEAD'))
  139. def testIsAncestor(self):
  140. self.assertTrue(scm.GIT.IsAncestor(
  141. self.cwd, self.githash('repo_1', 1), self.githash('repo_1', 2)))
  142. self.assertFalse(scm.GIT.IsAncestor(
  143. self.cwd, self.githash('repo_1', 2), self.githash('repo_1', 1)))
  144. self.assertFalse(scm.GIT.IsAncestor(
  145. self.cwd, self.githash('repo_1', 1), 'zebra'))
  146. def testGetAllFiles(self):
  147. self.assertEqual(['DEPS','origin'], scm.GIT.GetAllFiles(self.cwd))
  148. def testGetSetConfig(self):
  149. key = 'scm.test-key'
  150. self.assertIsNone(scm.GIT.GetConfig(self.cwd, key))
  151. self.assertEqual(
  152. 'default-value', scm.GIT.GetConfig(self.cwd, key, 'default-value'))
  153. scm.GIT.SetConfig(self.cwd, key, 'set-value')
  154. self.assertEqual('set-value', scm.GIT.GetConfig(self.cwd, key))
  155. self.assertEqual(
  156. 'set-value', scm.GIT.GetConfig(self.cwd, key, 'default-value'))
  157. scm.GIT.SetConfig(self.cwd, key)
  158. self.assertIsNone(scm.GIT.GetConfig(self.cwd, key))
  159. self.assertEqual(
  160. 'default-value', scm.GIT.GetConfig(self.cwd, key, 'default-value'))
  161. def testGetSetBranchConfig(self):
  162. branch = scm.GIT.GetBranch(self.cwd)
  163. key = 'scm.test-key'
  164. self.assertIsNone(scm.GIT.GetBranchConfig(self.cwd, branch, key))
  165. self.assertEqual(
  166. 'default-value',
  167. scm.GIT.GetBranchConfig(self.cwd, branch, key, 'default-value'))
  168. scm.GIT.SetBranchConfig(self.cwd, branch, key, 'set-value')
  169. self.assertEqual(
  170. 'set-value', scm.GIT.GetBranchConfig(self.cwd, branch, key))
  171. self.assertEqual(
  172. 'set-value',
  173. scm.GIT.GetBranchConfig(self.cwd, branch, key, 'default-value'))
  174. self.assertEqual(
  175. 'set-value',
  176. scm.GIT.GetConfig(self.cwd, 'branch.%s.%s' % (branch, key)))
  177. scm.GIT.SetBranchConfig(self.cwd, branch, key)
  178. self.assertIsNone(scm.GIT.GetBranchConfig(self.cwd, branch, key))
  179. def testFetchUpstreamTuple_NoUpstreamFound(self):
  180. self.assertEqual(
  181. (None, None), scm.GIT.FetchUpstreamTuple(self.cwd))
  182. @mock.patch('scm.GIT.GetRemoteBranches', return_value=['origin/main'])
  183. def testFetchUpstreamTuple_GuessOriginMaster(self, _mockGetRemoteBranches):
  184. self.assertEqual(('origin', 'refs/heads/main'),
  185. scm.GIT.FetchUpstreamTuple(self.cwd))
  186. @mock.patch('scm.GIT.GetRemoteBranches',
  187. return_value=['origin/master', 'origin/main'])
  188. def testFetchUpstreamTuple_GuessOriginMain(self, _mockGetRemoteBranches):
  189. self.assertEqual(('origin', 'refs/heads/main'),
  190. scm.GIT.FetchUpstreamTuple(self.cwd))
  191. def testFetchUpstreamTuple_RietveldUpstreamConfig(self):
  192. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-branch', 'rietveld-upstream')
  193. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-remote', 'rietveld-remote')
  194. self.assertEqual(
  195. ('rietveld-remote', 'rietveld-upstream'),
  196. scm.GIT.FetchUpstreamTuple(self.cwd))
  197. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-branch')
  198. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-remote')
  199. @mock.patch('scm.GIT.GetBranch', side_effect=callError())
  200. def testFetchUpstreamTuple_NotOnBranch(self, _mockGetBranch):
  201. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-branch', 'rietveld-upstream')
  202. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-remote', 'rietveld-remote')
  203. self.assertEqual(
  204. ('rietveld-remote', 'rietveld-upstream'),
  205. scm.GIT.FetchUpstreamTuple(self.cwd))
  206. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-branch')
  207. scm.GIT.SetConfig(self.cwd, 'rietveld.upstream-remote')
  208. def testFetchUpstreamTuple_BranchConfig(self):
  209. branch = scm.GIT.GetBranch(self.cwd)
  210. scm.GIT.SetBranchConfig(self.cwd, branch, 'merge', 'branch-merge')
  211. scm.GIT.SetBranchConfig(self.cwd, branch, 'remote', 'branch-remote')
  212. self.assertEqual(
  213. ('branch-remote', 'branch-merge'), scm.GIT.FetchUpstreamTuple(self.cwd))
  214. scm.GIT.SetBranchConfig(self.cwd, branch, 'merge')
  215. scm.GIT.SetBranchConfig(self.cwd, branch, 'remote')
  216. def testFetchUpstreamTuple_AnotherBranchConfig(self):
  217. branch = 'scm-test-branch'
  218. scm.GIT.SetBranchConfig(self.cwd, branch, 'merge', 'other-merge')
  219. scm.GIT.SetBranchConfig(self.cwd, branch, 'remote', 'other-remote')
  220. self.assertEqual(
  221. ('other-remote', 'other-merge'),
  222. scm.GIT.FetchUpstreamTuple(self.cwd, branch))
  223. scm.GIT.SetBranchConfig(self.cwd, branch, 'merge')
  224. scm.GIT.SetBranchConfig(self.cwd, branch, 'remote')
  225. def testGetBranchRef(self):
  226. self.assertEqual('refs/heads/main', scm.GIT.GetBranchRef(self.cwd))
  227. HEAD = scm.GIT.Capture(['rev-parse', 'HEAD'], cwd=self.cwd)
  228. scm.GIT.Capture(['checkout', HEAD], cwd=self.cwd)
  229. self.assertIsNone(scm.GIT.GetBranchRef(self.cwd))
  230. scm.GIT.Capture(['checkout', 'main'], cwd=self.cwd)
  231. def testGetBranch(self):
  232. self.assertEqual('main', scm.GIT.GetBranch(self.cwd))
  233. HEAD = scm.GIT.Capture(['rev-parse', 'HEAD'], cwd=self.cwd)
  234. scm.GIT.Capture(['checkout', HEAD], cwd=self.cwd)
  235. self.assertIsNone(scm.GIT.GetBranchRef(self.cwd))
  236. scm.GIT.Capture(['checkout', 'main'], cwd=self.cwd)
  237. if __name__ == '__main__':
  238. if '-v' in sys.argv:
  239. logging.basicConfig(level=logging.DEBUG)
  240. unittest.main()
  241. # vim: ts=2:sw=2:tw=80:et: