gclient_scm_test.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. #!/usr/bin/python
  2. # Copyright (c) 2010 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 gclient_scm.py."""
  6. # pylint: disable=E1101,E1103,W0403
  7. # Import before super_mox to keep valid references.
  8. from os import rename
  9. from shutil import rmtree
  10. from subprocess import Popen, PIPE, STDOUT
  11. import tempfile
  12. import unittest
  13. import __builtin__
  14. # Fixes include path.
  15. from super_mox import mox, StdoutCheck, TestCaseUtils, SuperMoxTestBase
  16. import sys
  17. import gclient_scm
  18. # Shortcut since this function is used often
  19. join = gclient_scm.os.path.join
  20. class GCBaseTestCase(object):
  21. def assertRaisesError(self, msg, fn, *args, **kwargs):
  22. """Like unittest's assertRaises() but checks for Gclient.Error."""
  23. try:
  24. fn(*args, **kwargs)
  25. except gclient_scm.gclient_utils.Error, e:
  26. self.assertEquals(e.args[0], msg)
  27. else:
  28. self.fail('%s not raised' % msg)
  29. class BaseTestCase(GCBaseTestCase, SuperMoxTestBase):
  30. def setUp(self):
  31. SuperMoxTestBase.setUp(self)
  32. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'CheckCall')
  33. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'CheckCallAndFilter')
  34. self.mox.StubOutWithMock(gclient_scm.gclient_utils,
  35. 'CheckCallAndFilterAndHeader')
  36. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead')
  37. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
  38. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'Popen')
  39. self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory')
  40. self._CaptureSVNInfo = gclient_scm.scm.SVN.CaptureInfo
  41. self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Capture')
  42. self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureInfo')
  43. self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureStatus')
  44. self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'RunAndGetFileList')
  45. self._scm_wrapper = gclient_scm.CreateSCM
  46. gclient_scm.scm.SVN.current_version = None
  47. # Absolute path of the fake checkout directory.
  48. self.base_path = join(self.root_dir, self.relpath)
  49. def tearDown(self):
  50. SuperMoxTestBase.tearDown(self)
  51. class SVNWrapperTestCase(BaseTestCase):
  52. class OptionsObject(object):
  53. def __init__(self, verbose=False, revision=None):
  54. self.verbose = verbose
  55. self.revision = revision
  56. self.manually_grab_svn_rev = True
  57. self.deps_os = None
  58. self.force = False
  59. self.reset = False
  60. self.nohooks = False
  61. def Options(self, *args, **kwargs):
  62. return self.OptionsObject(*args, **kwargs)
  63. def setUp(self):
  64. BaseTestCase.setUp(self)
  65. self.url = self.SvnUrl()
  66. def testDir(self):
  67. members = [
  68. 'FullUrlForRelativeUrl', 'RunCommand',
  69. 'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
  70. 'revinfo', 'runhooks', 'status', 'update',
  71. 'updatesingle', 'url',
  72. ]
  73. # If you add a member, be sure to add the relevant test!
  74. self.compareMembers(self._scm_wrapper('svn://a'), members)
  75. def testUnsupportedSCM(self):
  76. args = ['gopher://foo', self.root_dir, self.relpath]
  77. exception_msg = 'No SCM found for url gopher://foo'
  78. self.assertRaisesError(exception_msg, self._scm_wrapper, *args)
  79. def testSVNFullUrlForRelativeUrl(self):
  80. self.url = 'svn://a/b/c/d'
  81. self.mox.ReplayAll()
  82. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  83. relpath=self.relpath)
  84. self.assertEqual(scm.FullUrlForRelativeUrl('/crap'), 'svn://a/b/crap')
  85. def testGITFullUrlForRelativeUrl(self):
  86. self.url = 'git://a/b/c/d'
  87. self.mox.ReplayAll()
  88. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  89. relpath=self.relpath)
  90. self.assertEqual(scm.FullUrlForRelativeUrl('/crap'), 'git://a/b/c/crap')
  91. def testRunCommandException(self):
  92. options = self.Options(verbose=False)
  93. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  94. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  95. self.mox.ReplayAll()
  96. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  97. relpath=self.relpath)
  98. exception = "Unsupported argument(s): %s" % ','.join(self.args)
  99. self.assertRaisesError(exception, scm.RunCommand,
  100. 'update', options, self.args)
  101. def testRunCommandUnknown(self):
  102. # TODO(maruel): if ever used.
  103. pass
  104. def testRevertMissing(self):
  105. options = self.Options(verbose=True)
  106. gclient_scm.os.path.isdir(self.base_path).AndReturn(False)
  107. gclient_scm.scm.SVN.Capture(['--version']
  108. ).AndReturn('svn, version 1.5.1 (r32289)')
  109. # It'll to a checkout instead.
  110. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  111. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  112. # Checkout.
  113. gclient_scm.os.path.exists(self.base_path).AndReturn(False)
  114. files_list = self.mox.CreateMockAnything()
  115. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose,
  116. ['checkout', self.url, self.base_path,
  117. '--force'],
  118. cwd=self.root_dir,
  119. file_list=files_list)
  120. self.mox.ReplayAll()
  121. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  122. relpath=self.relpath)
  123. scm.revert(options, self.args, files_list)
  124. self.checkstdout(
  125. ('\n_____ %s is missing, synching instead\n' % self.relpath))
  126. def testRevertNone(self):
  127. options = self.Options(verbose=True)
  128. gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
  129. gclient_scm.scm.SVN.CaptureStatus(self.base_path).AndReturn([])
  130. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose,
  131. ['update', '--revision', 'BASE'],
  132. cwd=self.base_path,
  133. file_list=mox.IgnoreArg())
  134. self.mox.ReplayAll()
  135. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  136. relpath=self.relpath)
  137. file_list = []
  138. scm.revert(options, self.args, file_list)
  139. def testRevert2Files(self):
  140. options = self.Options(verbose=True)
  141. gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
  142. items = [
  143. ('M ', 'a'),
  144. ('A ', 'b'),
  145. ]
  146. file_path1 = join(self.base_path, 'a')
  147. file_path2 = join(self.base_path, 'b')
  148. gclient_scm.scm.SVN.CaptureStatus(self.base_path).AndReturn(items)
  149. gclient_scm.os.path.exists(file_path1).AndReturn(True)
  150. gclient_scm.os.path.isfile(file_path1).AndReturn(True)
  151. gclient_scm.os.remove(file_path1)
  152. gclient_scm.os.path.exists(file_path2).AndReturn(True)
  153. gclient_scm.os.path.isfile(file_path2).AndReturn(True)
  154. gclient_scm.os.remove(file_path2)
  155. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose,
  156. ['update', '--revision', 'BASE'],
  157. cwd=self.base_path,
  158. file_list=mox.IgnoreArg())
  159. self.mox.ReplayAll()
  160. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  161. relpath=self.relpath)
  162. file_list = []
  163. scm.revert(options, self.args, file_list)
  164. self.checkstdout(
  165. ('%s\n%s\n' % (join(self.base_path, 'a'),
  166. join(self.base_path, 'b'))))
  167. def testRevertDirectory(self):
  168. options = self.Options(verbose=True)
  169. gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
  170. items = [
  171. ('~ ', 'a'),
  172. ]
  173. gclient_scm.scm.SVN.CaptureStatus(self.base_path).AndReturn(items)
  174. file_path = join(self.base_path, 'a')
  175. gclient_scm.os.path.exists(file_path).AndReturn(True)
  176. gclient_scm.os.path.isfile(file_path).AndReturn(False)
  177. gclient_scm.os.path.islink(file_path).AndReturn(False)
  178. gclient_scm.os.path.isdir(file_path).AndReturn(True)
  179. gclient_scm.gclient_utils.RemoveDirectory(file_path)
  180. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose,
  181. ['update', '--revision', 'BASE'],
  182. cwd=self.base_path,
  183. file_list=mox.IgnoreArg())
  184. self.mox.ReplayAll()
  185. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  186. relpath=self.relpath)
  187. file_list2 = []
  188. scm.revert(options, self.args, file_list2)
  189. self.checkstdout(('%s\n' % file_path))
  190. def testStatus(self):
  191. options = self.Options(verbose=True)
  192. gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
  193. gclient_scm.scm.SVN.RunAndGetFileList(
  194. options.verbose, ['status'] + self.args,
  195. cwd=self.base_path, file_list=[]).AndReturn(None)
  196. self.mox.ReplayAll()
  197. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  198. relpath=self.relpath)
  199. file_list = []
  200. self.assertEqual(scm.status(options, self.args, file_list), None)
  201. # TODO(maruel): TEST REVISIONS!!!
  202. # TODO(maruel): TEST RELOCATE!!!
  203. def testUpdateCheckout(self):
  204. options = self.Options(verbose=True)
  205. file_info = gclient_scm.gclient_utils.PrintableObject()
  206. file_info.root = 'blah'
  207. file_info.url = self.url
  208. file_info.uuid = 'ABC'
  209. file_info.revision = 42
  210. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  211. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  212. # Checkout.
  213. gclient_scm.os.path.exists(self.base_path).AndReturn(False)
  214. files_list = self.mox.CreateMockAnything()
  215. gclient_scm.scm.SVN.Capture(['--version']
  216. ).AndReturn('svn, version 1.5.1 (r32289)')
  217. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose,
  218. ['checkout', self.url, self.base_path,
  219. '--force'],
  220. cwd=self.root_dir,
  221. file_list=files_list)
  222. self.mox.ReplayAll()
  223. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  224. relpath=self.relpath)
  225. scm.update(options, (), files_list)
  226. def testUpdateUpdate(self):
  227. options = self.Options(verbose=True)
  228. options.force = True
  229. options.nohooks = False
  230. file_info = {
  231. 'Repository Root': 'blah',
  232. 'URL': self.url,
  233. 'UUID': 'ABC',
  234. 'Revision': 42,
  235. }
  236. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  237. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  238. # Verify no locked files.
  239. dotted_path = join(self.base_path, '.')
  240. gclient_scm.scm.SVN.CaptureStatus(dotted_path).AndReturn([])
  241. # Checkout or update.
  242. gclient_scm.os.path.exists(self.base_path).AndReturn(True)
  243. gclient_scm.scm.SVN.CaptureInfo(dotted_path).AndReturn(file_info)
  244. # Cheat a bit here.
  245. gclient_scm.scm.SVN.CaptureInfo(file_info['URL']).AndReturn(file_info)
  246. additional_args = []
  247. if options.manually_grab_svn_rev:
  248. additional_args = ['--revision', str(file_info['Revision'])]
  249. gclient_scm.scm.SVN.Capture(['--version']
  250. ).AndReturn('svn, version 1.5.1 (r32289)')
  251. additional_args.append('--force')
  252. files_list = []
  253. gclient_scm.scm.SVN.RunAndGetFileList(
  254. options.verbose,
  255. ['update', self.base_path] + additional_args,
  256. cwd=self.root_dir, file_list=files_list)
  257. self.mox.ReplayAll()
  258. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  259. relpath=self.relpath)
  260. scm.update(options, (), files_list)
  261. def testUpdateSingleCheckout(self):
  262. options = self.Options(verbose=True)
  263. file_info = {
  264. 'URL': self.url,
  265. 'Revision': 42,
  266. }
  267. # Checks to make sure that we support svn co --depth.
  268. gclient_scm.scm.SVN.current_version = None
  269. gclient_scm.scm.SVN.Capture(['--version']
  270. ).AndReturn('svn, version 1.5.1 (r32289)')
  271. gclient_scm.os.path.exists(join(self.base_path, '.svn')).AndReturn(False)
  272. gclient_scm.os.path.exists(join(self.base_path, 'DEPS')).AndReturn(False)
  273. # Verify no locked files.
  274. dotted_path = join(self.base_path, '.')
  275. gclient_scm.scm.SVN.CaptureStatus(dotted_path).AndReturn([])
  276. # When checking out a single file, we issue an svn checkout and svn update.
  277. files_list = self.mox.CreateMockAnything()
  278. gclient_scm.gclient_utils.CheckCallAndFilterAndHeader(
  279. ['svn', 'checkout', '--depth', 'empty', self.url, self.base_path],
  280. always=True, cwd=self.root_dir)
  281. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose, ['update', 'DEPS'],
  282. cwd=self.base_path, file_list=files_list)
  283. # Now we fall back on scm.update().
  284. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  285. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  286. gclient_scm.os.path.exists(self.base_path).AndReturn(True)
  287. gclient_scm.scm.SVN.CaptureInfo(dotted_path).AndReturn(file_info)
  288. gclient_scm.scm.SVN.CaptureInfo(file_info['URL']).AndReturn(file_info)
  289. self.mox.ReplayAll()
  290. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  291. relpath=self.relpath)
  292. scm.updatesingle(options, ['DEPS'], files_list)
  293. self.checkstdout('\n_____ %s at 42\n' % self.relpath)
  294. def testUpdateSingleCheckoutSVN14(self):
  295. options = self.Options(verbose=True)
  296. # Checks to make sure that we support svn co --depth.
  297. gclient_scm.scm.SVN.current_version = None
  298. gclient_scm.scm.SVN.Capture(['--version']
  299. ).AndReturn('svn, version 1.4.4 (r25188)')
  300. gclient_scm.os.path.exists(self.base_path).AndReturn(True)
  301. # When checking out a single file with svn 1.4, we use svn export
  302. files_list = self.mox.CreateMockAnything()
  303. gclient_scm.gclient_utils.CheckCallAndFilterAndHeader(
  304. ['svn', 'export', join(self.url, 'DEPS'), join(self.base_path, 'DEPS')],
  305. always=True, cwd=self.root_dir)
  306. self.mox.ReplayAll()
  307. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  308. relpath=self.relpath)
  309. scm.updatesingle(options, ['DEPS'], files_list)
  310. def testUpdateSingleCheckoutSVNUpgrade(self):
  311. options = self.Options(verbose=True)
  312. file_info = {
  313. 'URL': self.url,
  314. 'Revision': 42,
  315. }
  316. # Checks to make sure that we support svn co --depth.
  317. gclient_scm.scm.SVN.current_version = None
  318. gclient_scm.scm.SVN.Capture(['--version']
  319. ).AndReturn('svn, version 1.5.1 (r32289)')
  320. gclient_scm.os.path.exists(join(self.base_path, '.svn')).AndReturn(False)
  321. # If DEPS already exists, assume we're upgrading from svn1.4, so delete
  322. # the old DEPS file.
  323. gclient_scm.os.path.exists(join(self.base_path, 'DEPS')).AndReturn(True)
  324. gclient_scm.os.remove(join(self.base_path, 'DEPS'))
  325. # Verify no locked files.
  326. gclient_scm.scm.SVN.CaptureStatus(join(self.base_path, '.')).AndReturn([])
  327. # When checking out a single file, we issue an svn checkout and svn update.
  328. files_list = self.mox.CreateMockAnything()
  329. gclient_scm.gclient_utils.CheckCallAndFilterAndHeader(
  330. ['svn', 'checkout', '--depth', 'empty', self.url, self.base_path],
  331. always=True, cwd=self.root_dir)
  332. gclient_scm.scm.SVN.RunAndGetFileList(options.verbose, ['update', 'DEPS'],
  333. cwd=self.base_path, file_list=files_list)
  334. # Now we fall back on scm.update().
  335. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  336. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  337. gclient_scm.os.path.exists(self.base_path).AndReturn(True)
  338. gclient_scm.scm.SVN.CaptureInfo(
  339. join(self.base_path, ".")).AndReturn(file_info)
  340. gclient_scm.scm.SVN.CaptureInfo(file_info['URL']).AndReturn(file_info)
  341. self.mox.ReplayAll()
  342. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  343. relpath=self.relpath)
  344. scm.updatesingle(options, ['DEPS'], files_list)
  345. self.checkstdout(
  346. ('\n_____ %s at 42\n' % self.relpath))
  347. def testUpdateSingleUpdate(self):
  348. options = self.Options(verbose=True)
  349. file_info = {
  350. 'URL': self.url,
  351. 'Revision': 42,
  352. }
  353. # Checks to make sure that we support svn co --depth.
  354. gclient_scm.scm.SVN.current_version = None
  355. gclient_scm.scm.SVN.Capture(['--version']
  356. ).AndReturn('svn, version 1.5.1 (r32289)')
  357. gclient_scm.os.path.exists(join(self.base_path, '.svn')).AndReturn(True)
  358. # Verify no locked files.
  359. gclient_scm.scm.SVN.CaptureStatus(join(self.base_path, '.')).AndReturn([])
  360. # Now we fall back on scm.update().
  361. files_list = self.mox.CreateMockAnything()
  362. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  363. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(False)
  364. gclient_scm.os.path.exists(self.base_path).AndReturn(True)
  365. gclient_scm.scm.SVN.CaptureInfo(
  366. join(self.base_path, '.')).AndReturn(file_info)
  367. gclient_scm.scm.SVN.CaptureInfo(file_info['URL']).AndReturn(file_info)
  368. self.mox.ReplayAll()
  369. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  370. relpath=self.relpath)
  371. scm.updatesingle(options, ['DEPS'], files_list)
  372. self.checkstdout('\n_____ %s at 42\n' % self.relpath)
  373. def testUpdateGit(self):
  374. options = self.Options(verbose=True)
  375. file_path = gclient_scm.os.path.join(self.root_dir, self.relpath, '.git')
  376. gclient_scm.os.path.exists(file_path).AndReturn(True)
  377. self.mox.ReplayAll()
  378. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  379. relpath=self.relpath)
  380. file_list = []
  381. scm.update(options, self.args, file_list)
  382. self.checkstdout(
  383. ('________ found .git directory; skipping %s\n' % self.relpath))
  384. def testUpdateHg(self):
  385. options = self.Options(verbose=True)
  386. gclient_scm.os.path.exists(join(self.base_path, '.git')).AndReturn(False)
  387. gclient_scm.os.path.exists(join(self.base_path, '.hg')).AndReturn(True)
  388. self.mox.ReplayAll()
  389. scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
  390. relpath=self.relpath)
  391. file_list = []
  392. scm.update(options, self.args, file_list)
  393. self.checkstdout(
  394. ('________ found .hg directory; skipping %s\n' % self.relpath))
  395. class GitWrapperTestCase(GCBaseTestCase, StdoutCheck, TestCaseUtils,
  396. unittest.TestCase):
  397. """This class doesn't use pymox."""
  398. class OptionsObject(object):
  399. def __init__(self, verbose=False, revision=None):
  400. self.verbose = verbose
  401. self.revision = revision
  402. self.manually_grab_svn_rev = True
  403. self.deps_os = None
  404. self.force = False
  405. self.reset = False
  406. self.nohooks = False
  407. sample_git_import = """blob
  408. mark :1
  409. data 6
  410. Hello
  411. blob
  412. mark :2
  413. data 4
  414. Bye
  415. reset refs/heads/master
  416. commit refs/heads/master
  417. mark :3
  418. author Bob <bob@example.com> 1253744361 -0700
  419. committer Bob <bob@example.com> 1253744361 -0700
  420. data 8
  421. A and B
  422. M 100644 :1 a
  423. M 100644 :2 b
  424. blob
  425. mark :4
  426. data 10
  427. Hello
  428. You
  429. blob
  430. mark :5
  431. data 8
  432. Bye
  433. You
  434. commit refs/heads/origin
  435. mark :6
  436. author Alice <alice@example.com> 1253744424 -0700
  437. committer Alice <alice@example.com> 1253744424 -0700
  438. data 13
  439. Personalized
  440. from :3
  441. M 100644 :4 a
  442. M 100644 :5 b
  443. reset refs/heads/master
  444. from :3
  445. """
  446. def Options(self, *args, **kwargs):
  447. return self.OptionsObject(*args, **kwargs)
  448. @staticmethod
  449. def CreateGitRepo(git_import, path):
  450. """Do it for real."""
  451. try:
  452. Popen(['git', 'init', '-q'], stdout=PIPE, stderr=STDOUT,
  453. cwd=path).communicate()
  454. except OSError:
  455. # git is not available, skip this test.
  456. return False
  457. Popen(['git', 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
  458. stderr=STDOUT, cwd=path).communicate(input=git_import)
  459. Popen(['git', 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
  460. cwd=path).communicate()
  461. Popen(['git', 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
  462. stderr=STDOUT, cwd=path).communicate()
  463. Popen(['git', 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
  464. stderr=STDOUT, cwd=path).communicate()
  465. Popen(['git', 'push', 'origin', 'origin/origin:origin/master', '-q'],
  466. stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
  467. Popen(['git', 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
  468. stderr=STDOUT, cwd=path).communicate()
  469. return True
  470. def setUp(self):
  471. TestCaseUtils.setUp(self)
  472. unittest.TestCase.setUp(self)
  473. self.url = 'git://foo'
  474. self.root_dir = tempfile.mkdtemp()
  475. self.relpath = '.'
  476. self.base_path = join(self.root_dir, self.relpath)
  477. self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
  478. StdoutCheck.setUp(self)
  479. def tearDown(self):
  480. StdoutCheck.tearDown(self)
  481. TestCaseUtils.tearDown(self)
  482. unittest.TestCase.tearDown(self)
  483. rmtree(self.root_dir)
  484. def testDir(self):
  485. members = [
  486. 'FullUrlForRelativeUrl', 'RunCommand',
  487. 'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
  488. 'revinfo', 'runhooks', 'status', 'update', 'url',
  489. ]
  490. # If you add a member, be sure to add the relevant test!
  491. self.compareMembers(gclient_scm.CreateSCM(url=self.url), members)
  492. def testRevertMissing(self):
  493. if not self.enabled:
  494. return
  495. options = self.Options()
  496. file_path = join(self.base_path, 'a')
  497. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  498. relpath=self.relpath)
  499. file_list = []
  500. scm.update(options, None, file_list)
  501. gclient_scm.os.remove(file_path)
  502. file_list = []
  503. scm.revert(options, self.args, file_list)
  504. self.assertEquals(file_list, [file_path])
  505. file_list = []
  506. scm.diff(options, self.args, file_list)
  507. self.assertEquals(file_list, [])
  508. self.checkstdout(
  509. ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
  510. 'Fast-forward\n a | 1 +\n b | 1 +\n'
  511. ' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
  512. '________ running \'git reset --hard origin/master\' in \'%s\'\n'
  513. 'HEAD is now at a7142dc Personalized\n') %
  514. join(self.root_dir, '.'))
  515. def testRevertNone(self):
  516. if not self.enabled:
  517. return
  518. options = self.Options()
  519. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  520. relpath=self.relpath)
  521. file_list = []
  522. scm.update(options, None, file_list)
  523. file_list = []
  524. scm.revert(options, self.args, file_list)
  525. self.assertEquals(file_list, [])
  526. self.assertEquals(scm.revinfo(options, self.args, None),
  527. 'a7142dc9f0009350b96a11f372b6ea658592aa95')
  528. self.checkstdout(
  529. ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
  530. 'Fast-forward\n a | 1 +\n b | 1 +\n'
  531. ' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
  532. '________ running \'git reset --hard origin/master\' in \'%s\'\n'
  533. 'HEAD is now at a7142dc Personalized\n') %
  534. join(self.root_dir, '.'))
  535. def testRevertModified(self):
  536. if not self.enabled:
  537. return
  538. options = self.Options()
  539. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  540. relpath=self.relpath)
  541. file_list = []
  542. scm.update(options, None, file_list)
  543. file_path = join(self.base_path, 'a')
  544. open(file_path, 'a').writelines('touched\n')
  545. file_list = []
  546. scm.revert(options, self.args, file_list)
  547. self.assertEquals(file_list, [file_path])
  548. file_list = []
  549. scm.diff(options, self.args, file_list)
  550. self.assertEquals(file_list, [])
  551. self.assertEquals(scm.revinfo(options, self.args, None),
  552. 'a7142dc9f0009350b96a11f372b6ea658592aa95')
  553. self.checkstdout(
  554. ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
  555. 'Fast-forward\n a | 1 +\n b | 1 +\n'
  556. ' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
  557. '________ running \'git reset --hard origin/master\' in \'%s\'\n'
  558. 'HEAD is now at a7142dc Personalized\n') %
  559. join(self.root_dir, '.'))
  560. def testRevertNew(self):
  561. if not self.enabled:
  562. return
  563. options = self.Options()
  564. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  565. relpath=self.relpath)
  566. file_list = []
  567. scm.update(options, None, file_list)
  568. file_path = join(self.base_path, 'c')
  569. f = open(file_path, 'w')
  570. f.writelines('new\n')
  571. f.close()
  572. Popen(['git', 'add', 'c'], stdout=PIPE,
  573. stderr=STDOUT, cwd=self.base_path).communicate()
  574. file_list = []
  575. scm.revert(options, self.args, file_list)
  576. self.assertEquals(file_list, [file_path])
  577. file_list = []
  578. scm.diff(options, self.args, file_list)
  579. self.assertEquals(file_list, [])
  580. self.assertEquals(scm.revinfo(options, self.args, None),
  581. 'a7142dc9f0009350b96a11f372b6ea658592aa95')
  582. self.checkstdout(
  583. ('\n_____ . at refs/heads/master\nUpdating 069c602..a7142dc\n'
  584. 'Fast-forward\n a | 1 +\n b | 1 +\n'
  585. ' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n\n'
  586. '________ running \'git reset --hard origin/master\' in \'%s\'\n'
  587. 'HEAD is now at a7142dc Personalized\n') %
  588. join(self.root_dir, '.'))
  589. def testStatusNew(self):
  590. if not self.enabled:
  591. return
  592. options = self.Options()
  593. file_path = join(self.base_path, 'a')
  594. open(file_path, 'a').writelines('touched\n')
  595. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  596. relpath=self.relpath)
  597. file_list = []
  598. scm.status(options, self.args, file_list)
  599. self.assertEquals(file_list, [file_path])
  600. self.checkstdout(
  601. ('\n________ running \'git diff --name-status '
  602. '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\nM\ta\n') %
  603. join(self.root_dir, '.'))
  604. def testStatus2New(self):
  605. if not self.enabled:
  606. return
  607. options = self.Options()
  608. expected_file_list = []
  609. for f in ['a', 'b']:
  610. file_path = join(self.base_path, f)
  611. open(file_path, 'a').writelines('touched\n')
  612. expected_file_list.extend([file_path])
  613. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  614. relpath=self.relpath)
  615. file_list = []
  616. scm.status(options, self.args, file_list)
  617. expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
  618. self.assertEquals(sorted(file_list), expected_file_list)
  619. self.checkstdout(
  620. ('\n________ running \'git diff --name-status '
  621. '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\nM\ta\nM\tb\n') %
  622. join(self.root_dir, '.'))
  623. def testUpdateCheckout(self):
  624. if not self.enabled:
  625. return
  626. options = self.Options(verbose=True)
  627. root_dir = tempfile.mkdtemp()
  628. relpath = 'foo'
  629. base_path = join(root_dir, relpath)
  630. url = join(self.base_path, '.git')
  631. try:
  632. scm = gclient_scm.CreateSCM(url=url, root_dir=root_dir,
  633. relpath=relpath)
  634. file_list = []
  635. scm.update(options, (), file_list)
  636. self.assertEquals(len(file_list), 2)
  637. self.assert_(gclient_scm.os.path.isfile(join(base_path, 'a')))
  638. self.assertEquals(scm.revinfo(options, (), None),
  639. '069c602044c5388d2d15c3f875b057c852003458')
  640. finally:
  641. rmtree(root_dir)
  642. self.checkstdout(
  643. ('\n_____ foo at refs/heads/master\n\n'
  644. '________ running \'git clone -b master --verbose %s %s\' in \'%s\'\n'
  645. 'Initialized empty Git repository in %s\n') %
  646. (join(self.root_dir, '.', '.git'), join(root_dir, 'foo'), root_dir,
  647. join(gclient_scm.os.path.realpath(root_dir), 'foo', '.git') + '/'))
  648. def testUpdateUpdate(self):
  649. if not self.enabled:
  650. return
  651. options = self.Options()
  652. expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
  653. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  654. relpath=self.relpath)
  655. file_list = []
  656. scm.update(options, (), file_list)
  657. self.assertEquals(file_list, expected_file_list)
  658. self.assertEquals(scm.revinfo(options, (), None),
  659. 'a7142dc9f0009350b96a11f372b6ea658592aa95')
  660. self.checkstdout(
  661. '\n_____ . at refs/heads/master\n'
  662. 'Updating 069c602..a7142dc\nFast-forward\n a | 1 +\n b | 1 +\n'
  663. ' 2 files changed, 2 insertions(+), 0 deletions(-)\n\n')
  664. def testUpdateUnstagedConflict(self):
  665. if not self.enabled:
  666. return
  667. options = self.Options()
  668. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  669. relpath=self.relpath)
  670. file_path = join(self.base_path, 'b')
  671. open(file_path, 'w').writelines('conflict\n')
  672. exception = (
  673. "error: Your local changes to 'b' would be overwritten by merge. "
  674. "Aborting.\n"
  675. "Please, commit your changes or stash them before you can merge.\n")
  676. self.assertRaisesError(exception, scm.update, options, (), [])
  677. self.checkstdout('\n_____ . at refs/heads/master\n')
  678. def testUpdateConflict(self):
  679. if not self.enabled:
  680. return
  681. options = self.Options()
  682. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  683. relpath=self.relpath)
  684. file_path = join(self.base_path, 'b')
  685. open(file_path, 'w').writelines('conflict\n')
  686. # pylint: disable=W0212
  687. scm._Run(['commit', '-am', 'test'], options)
  688. __builtin__.raw_input = lambda x: 'y'
  689. exception = ('Conflict while rebasing this branch.\n'
  690. 'Fix the conflict and run gclient again.\n'
  691. 'See \'man git-rebase\' for details.\n')
  692. self.assertRaisesError(exception, scm.update, options, (), [])
  693. exception = ('\n____ . at refs/heads/master\n'
  694. '\tYou have unstaged changes.\n'
  695. '\tPlease commit, stash, or reset.\n')
  696. self.assertRaisesError(exception, scm.update, options, (), [])
  697. # The hash always changes. Use a cheap trick.
  698. start = ('\n________ running \'git commit -am test\' in \'%s\'\n'
  699. '[new ') % join(self.root_dir, '.')
  700. end = ('] test\n 1 files changed, 1 insertions(+), '
  701. '1 deletions(-)\n\n_____ . at refs/heads/master\n'
  702. 'Attempting rebase onto refs/remotes/origin/master...\n')
  703. self.assertTrue(sys.stdout.getvalue().startswith(start))
  704. self.assertTrue(sys.stdout.getvalue().endswith(end))
  705. self.assertEquals(len(sys.stdout.getvalue()),
  706. len(start) + len(end) + 7)
  707. sys.stdout.close()
  708. def testUpdateNotGit(self):
  709. if not self.enabled:
  710. return
  711. options = self.Options()
  712. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  713. relpath=self.relpath)
  714. git_path = join(self.base_path, '.git')
  715. rename(git_path, git_path + 'foo')
  716. exception = ('\n____ . at refs/heads/master\n'
  717. '\tPath is not a git repo. No .git dir.\n'
  718. '\tTo resolve:\n'
  719. '\t\trm -rf .\n'
  720. '\tAnd run gclient sync again\n')
  721. self.assertRaisesError(exception, scm.update, options, (), [])
  722. def testRevinfo(self):
  723. if not self.enabled:
  724. return
  725. options = self.Options()
  726. scm = gclient_scm.CreateSCM(url=self.url, root_dir=self.root_dir,
  727. relpath=self.relpath)
  728. rev_info = scm.revinfo(options, (), None)
  729. self.assertEquals(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
  730. if __name__ == '__main__':
  731. unittest.main()
  732. # vim: ts=2:sw=2:tw=80:et: