gclient_git_smoketest.py 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604
  1. #!/usr/bin/env vpython3
  2. # Copyright (c) 2020 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. """Smoke tests for gclient.py.
  6. Shell out 'gclient' and run git tests.
  7. """
  8. import json
  9. import logging
  10. import os
  11. import sys
  12. import unittest
  13. import gclient_smoketest_base
  14. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  15. sys.path.insert(0, ROOT_DIR)
  16. import subprocess2
  17. from testing_support.fake_repos import join, write
  18. # TODO: Should fix these warnings.
  19. # pylint: disable=line-too-long
  20. class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
  21. def setUp(self):
  22. super(GClientSmokeGIT, self).setUp()
  23. self.env['PATH'] = (os.path.join(ROOT_DIR, 'testing_support') +
  24. os.pathsep + self.env['PATH'])
  25. self.env['GCLIENT_SUPPRESS_GIT_VERSION_WARNING'] = '1'
  26. self.env['GCLIENT_SUPPRESS_SUBMODULE_WARNING'] = '1'
  27. self.enabled = self.FAKE_REPOS.set_up_git()
  28. if not self.enabled:
  29. self.skipTest('git fake repos not available')
  30. def testGitmodules_relative(self):
  31. self.gclient(['config', self.git_base + 'repo_19', '--name', 'dir'],
  32. cwd=self.git_base + 'repo_19')
  33. self.gclient(['sync'], cwd=self.git_base + 'repo_19')
  34. self.gclient(['gitmodules'],
  35. cwd=self.git_base + os.path.join('repo_19', 'dir'))
  36. gitmodules = os.path.join(self.git_base, 'repo_19', 'dir',
  37. '.gitmodules')
  38. with open(gitmodules) as f:
  39. contents = f.read().splitlines()
  40. self.assertEqual([
  41. '[submodule "some_repo"]', '\tpath = some_repo',
  42. '\turl = /repo_2', '\tgclient-condition = not foo_checkout',
  43. '[submodule "chicken/dickens"]', '\tpath = chicken/dickens',
  44. '\turl = /repo_3', '\tgclient-recursedeps = true'
  45. ], contents)
  46. def testGitmodules_not_relative(self):
  47. self.gclient(['config', self.git_base + 'repo_20', '--name', 'foo'],
  48. cwd=self.git_base + 'repo_20')
  49. self.gclient(['sync'], cwd=self.git_base + 'repo_20')
  50. self.gclient(['gitmodules'],
  51. cwd=self.git_base + os.path.join('repo_20', 'foo'))
  52. gitmodules = os.path.join(self.git_base, 'repo_20', 'foo',
  53. '.gitmodules')
  54. with open(gitmodules) as f:
  55. contents = f.read().splitlines()
  56. self.assertEqual([
  57. '[submodule "some_repo"]', '\tpath = some_repo',
  58. '\turl = /repo_2', '\tgclient-condition = not foo_checkout',
  59. '[submodule "chicken/dickens"]', '\tpath = chicken/dickens',
  60. '\turl = /repo_3', '\tgclient-recursedeps = true'
  61. ], contents)
  62. def testGitmodules_migration_no_git_suffix(self):
  63. self.gclient(['config', self.git_base + 'repo_21', '--name', 'foo'],
  64. cwd=self.git_base + 'repo_21')
  65. # We are not running gclient sync since dependencies don't exist
  66. self.gclient(['gitmodules'], cwd=self.git_base + 'repo_21')
  67. gitmodules = os.path.join(self.git_base, 'repo_21', '.gitmodules')
  68. with open(gitmodules) as f:
  69. contents = f.read().splitlines()
  70. self.assertEqual([
  71. '[submodule "bar"]',
  72. '\tpath = bar',
  73. '\turl = https://example.googlesource.com/repo.git',
  74. ], contents)
  75. # force migration
  76. os.remove(gitmodules)
  77. self.gclient(['gitmodules'], cwd=self.git_base + 'repo_21')
  78. gitmodules = os.path.join(self.git_base, 'repo_21', '.gitmodules')
  79. with open(gitmodules) as f:
  80. contents = f.read().splitlines()
  81. self.assertEqual([
  82. '[submodule "bar"]',
  83. '\tpath = bar',
  84. '\turl = https://example.googlesource.com/repo',
  85. ], contents)
  86. def testGitmodules_migration_recursdeps(self):
  87. self.gclient(['config', self.git_base + 'repo_24', '--name', 'foo'],
  88. cwd=self.git_base + 'repo_24')
  89. # We are not running gclient sync since dependencies don't exist
  90. self.gclient(['gitmodules'], cwd=self.git_base + 'repo_24')
  91. gitmodules = os.path.join(self.git_base, 'repo_24', '.gitmodules')
  92. with open(gitmodules) as f:
  93. contents = f.read().splitlines()
  94. self.assertEqual([
  95. '[submodule "bar"]',
  96. '\tpath = bar',
  97. '\turl = https://example.googlesource.com/repo',
  98. ], contents)
  99. # force migration
  100. os.remove(gitmodules)
  101. self.gclient(['gitmodules'], cwd=self.git_base + 'repo_24')
  102. gitmodules = os.path.join(self.git_base, 'repo_24', '.gitmodules')
  103. with open(gitmodules) as f:
  104. contents = f.read().splitlines()
  105. self.assertEqual([
  106. '[submodule "bar"]',
  107. '\tpath = bar',
  108. '\turl = https://example.googlesource.com/repo',
  109. '\tgclient-recursedeps = true'
  110. ], contents)
  111. def testGitmodules_not_in_gclient(self):
  112. with self.assertRaisesRegex(AssertionError, 'from a gclient workspace'):
  113. self.gclient(['gitmodules'], cwd=self.root_dir)
  114. def testSync(self):
  115. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  116. # Test unversioned checkout.
  117. self.parseGclient(['sync', '--deps', 'mac', '--jobs', '1'],
  118. ['running', 'running', 'running'])
  119. # TODO(maruel): http://crosbug.com/3582 hooks run even if not matching,
  120. # must add sync parsing to get the list of updated files.
  121. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  122. ('repo_2@1', 'src/repo2'),
  123. ('repo_3@2', 'src/repo2/repo_renamed'))
  124. tree['src/git_hooked1'] = 'git_hooked1'
  125. tree['src/git_hooked2'] = 'git_hooked2'
  126. self.assertTree(tree)
  127. # Manually remove git_hooked1 before syncing to make sure it's not
  128. # recreated.
  129. os.remove(join(self.root_dir, 'src', 'git_hooked1'))
  130. # Test incremental versioned sync: sync backward.
  131. self.parseGclient([
  132. 'sync', '--jobs', '1', '--revision',
  133. 'src@' + self.githash('repo_1', 1), '--deps', 'mac',
  134. '--delete_unversioned_trees'
  135. ], ['deleting'])
  136. tree = self.mangle_git_tree(
  137. ('repo_1@1', 'src'), ('repo_2@2', 'src/repo2'),
  138. ('repo_3@1', 'src/repo2/repo3'), ('repo_4@2', 'src/repo4'))
  139. tree['src/git_hooked2'] = 'git_hooked2'
  140. tree['src/repo2/gclient.args'] = '\n'.join([
  141. '# Generated from \'DEPS\'',
  142. 'false_var = false',
  143. 'false_str_var = false',
  144. 'true_var = true',
  145. 'true_str_var = true',
  146. 'str_var = "abc"',
  147. 'cond_var = false',
  148. ])
  149. self.assertTree(tree)
  150. # Test incremental sync: delete-unversioned_trees isn't there.
  151. self.parseGclient(['sync', '--deps', 'mac', '--jobs', '1'],
  152. ['running', 'running'])
  153. tree = self.mangle_git_tree(
  154. ('repo_1@2', 'src'), ('repo_2@1', 'src/repo2'),
  155. ('repo_3@1', 'src/repo2/repo3'),
  156. ('repo_3@2', 'src/repo2/repo_renamed'), ('repo_4@2', 'src/repo4'))
  157. tree['src/git_hooked1'] = 'git_hooked1'
  158. tree['src/git_hooked2'] = 'git_hooked2'
  159. tree['src/repo2/gclient.args'] = '\n'.join([
  160. '# Generated from \'DEPS\'',
  161. 'false_var = false',
  162. 'false_str_var = false',
  163. 'true_var = true',
  164. 'true_str_var = true',
  165. 'str_var = "abc"',
  166. 'cond_var = false',
  167. ])
  168. self.assertTree(tree)
  169. def testSyncJsonOutput(self):
  170. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  171. output_json = os.path.join(self.root_dir, 'output.json')
  172. self.gclient(['sync', '--deps', 'mac', '--output-json', output_json])
  173. with open(output_json) as f:
  174. output_json = json.load(f)
  175. out = {
  176. 'solutions': {
  177. 'src/': {
  178. 'scm': 'git',
  179. 'url': self.git_base + 'repo_1',
  180. 'revision': self.githash('repo_1', 2),
  181. 'was_processed': True,
  182. 'was_synced': True,
  183. },
  184. 'src/repo2/': {
  185. 'scm': 'git',
  186. 'url':
  187. self.git_base + 'repo_2@' + self.githash('repo_2', 1)[:7],
  188. 'revision': self.githash('repo_2', 1),
  189. 'was_processed': True,
  190. 'was_synced': True,
  191. },
  192. 'src/repo2/repo_renamed/': {
  193. 'scm': 'git',
  194. 'url': self.git_base + 'repo_3',
  195. 'revision': self.githash('repo_3', 2),
  196. 'was_processed': True,
  197. 'was_synced': True,
  198. },
  199. 'src/should_not_process/': {
  200. 'scm': None,
  201. 'url': self.git_base + 'repo_4',
  202. 'revision': None,
  203. 'was_processed': False,
  204. 'was_synced': True,
  205. },
  206. },
  207. }
  208. self.assertEqual(out, output_json)
  209. def testSyncIgnoredSolutionName(self):
  210. """TODO(maruel): This will become an error soon."""
  211. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  212. self.parseGclient(
  213. [
  214. 'sync', '--deps', 'mac', '--jobs', '1', '--revision',
  215. 'invalid@' + self.githash('repo_1', 1)
  216. ], ['running', 'running', 'running'],
  217. 'Please fix your script, having invalid --revision flags '
  218. 'will soon be considered an error.\n')
  219. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  220. ('repo_2@1', 'src/repo2'),
  221. ('repo_3@2', 'src/repo2/repo_renamed'))
  222. tree['src/git_hooked1'] = 'git_hooked1'
  223. tree['src/git_hooked2'] = 'git_hooked2'
  224. self.assertTree(tree)
  225. def testSyncNoSolutionName(self):
  226. # When no solution name is provided, gclient uses the first solution
  227. # listed.
  228. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  229. self.parseGclient([
  230. 'sync', '--deps', 'mac', '--jobs', '1', '--revision',
  231. self.githash('repo_1', 1)
  232. ], ['running'])
  233. tree = self.mangle_git_tree(
  234. ('repo_1@1', 'src'), ('repo_2@2', 'src/repo2'),
  235. ('repo_3@1', 'src/repo2/repo3'), ('repo_4@2', 'src/repo4'))
  236. tree['src/repo2/gclient.args'] = '\n'.join([
  237. '# Generated from \'DEPS\'',
  238. 'false_var = false',
  239. 'false_str_var = false',
  240. 'true_var = true',
  241. 'true_str_var = true',
  242. 'str_var = "abc"',
  243. 'cond_var = false',
  244. ])
  245. self.assertTree(tree)
  246. def testSyncJobs(self):
  247. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  248. # Test unversioned checkout.
  249. self.parseGclient(['sync', '--deps', 'mac', '--jobs', '8'],
  250. ['running', 'running', 'running'],
  251. untangle=True)
  252. # TODO(maruel): http://crosbug.com/3582 hooks run even if not matching,
  253. # must add sync parsing to get the list of updated files.
  254. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  255. ('repo_2@1', 'src/repo2'),
  256. ('repo_3@2', 'src/repo2/repo_renamed'))
  257. tree['src/git_hooked1'] = 'git_hooked1'
  258. tree['src/git_hooked2'] = 'git_hooked2'
  259. self.assertTree(tree)
  260. # Manually remove git_hooked1 before syncing to make sure it's not
  261. # recreated.
  262. os.remove(join(self.root_dir, 'src', 'git_hooked1'))
  263. # Test incremental versioned sync: sync backward.
  264. # Use --jobs 1 otherwise the order is not deterministic.
  265. self.parseGclient([
  266. 'sync', '--revision', 'src@' + self.githash('repo_1', 1), '--deps',
  267. 'mac', '--delete_unversioned_trees', '--jobs', '1'
  268. ], ['deleting'],
  269. untangle=True)
  270. tree = self.mangle_git_tree(
  271. ('repo_1@1', 'src'), ('repo_2@2', 'src/repo2'),
  272. ('repo_3@1', 'src/repo2/repo3'), ('repo_4@2', 'src/repo4'))
  273. tree['src/git_hooked2'] = 'git_hooked2'
  274. tree['src/repo2/gclient.args'] = '\n'.join([
  275. '# Generated from \'DEPS\'',
  276. 'false_var = false',
  277. 'false_str_var = false',
  278. 'true_var = true',
  279. 'true_str_var = true',
  280. 'str_var = "abc"',
  281. 'cond_var = false',
  282. ])
  283. self.assertTree(tree)
  284. # Test incremental sync: delete-unversioned_trees isn't there.
  285. self.parseGclient(['sync', '--deps', 'mac', '--jobs', '8'],
  286. ['running', 'running'],
  287. untangle=True)
  288. tree = self.mangle_git_tree(
  289. ('repo_1@2', 'src'), ('repo_2@1', 'src/repo2'),
  290. ('repo_3@1', 'src/repo2/repo3'),
  291. ('repo_3@2', 'src/repo2/repo_renamed'), ('repo_4@2', 'src/repo4'))
  292. tree['src/git_hooked1'] = 'git_hooked1'
  293. tree['src/git_hooked2'] = 'git_hooked2'
  294. tree['src/repo2/gclient.args'] = '\n'.join([
  295. '# Generated from \'DEPS\'',
  296. 'false_var = false',
  297. 'false_str_var = false',
  298. 'true_var = true',
  299. 'true_str_var = true',
  300. 'str_var = "abc"',
  301. 'cond_var = false',
  302. ])
  303. self.assertTree(tree)
  304. def testSyncFetch(self):
  305. self.gclient(['config', self.git_base + 'repo_13', '--name', 'src'])
  306. self.gclient([
  307. 'sync', '-v', '-v', '-v', '--revision',
  308. self.githash('repo_13', 2)
  309. ])
  310. def testSyncFetchUpdate(self):
  311. self.gclient(['config', self.git_base + 'repo_13', '--name', 'src'])
  312. # Sync to an earlier revision first, one that doesn't refer to
  313. # non-standard refs.
  314. self.gclient([
  315. 'sync', '-v', '-v', '-v', '--revision',
  316. self.githash('repo_13', 1)
  317. ])
  318. # Make sure update that pulls a non-standard ref works.
  319. self.gclient([
  320. 'sync', '-v', '-v', '-v', '--revision',
  321. self.githash('repo_13', 2)
  322. ])
  323. def testSyncDirect(self):
  324. self.gclient(['config', self.git_base + 'repo_12', '--name', 'src'])
  325. self.gclient(
  326. ['sync', '-v', '-v', '-v', '--revision', 'refs/changes/1212'])
  327. def testSyncUnmanaged(self):
  328. self.gclient([
  329. 'config', '--spec',
  330. 'solutions=[{"name":"src", "url": %r, "managed": False}]' %
  331. (self.git_base + 'repo_5')
  332. ])
  333. self.gclient(['sync', '--revision', 'src@' + self.githash('repo_5', 2)])
  334. self.gclient(
  335. ['sync', '--revision',
  336. 'src/repo1@%s' % self.githash('repo_1', 1)])
  337. # src is unmanaged, so gclient shouldn't have updated it. It should've
  338. # stayed synced at @2
  339. tree = self.mangle_git_tree(('repo_5@2', 'src'),
  340. ('repo_1@1', 'src/repo1'),
  341. ('repo_2@1', 'src/repo2'))
  342. tree['src/git_pre_deps_hooked'] = 'git_pre_deps_hooked'
  343. self.assertTree(tree)
  344. def testSyncUrl(self):
  345. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  346. self.gclient([
  347. 'sync', '-v', '-v', '-v', '--revision',
  348. 'src/repo2@%s' % self.githash('repo_2', 1), '--revision',
  349. '%srepo_2@%s' % (self.git_base, self.githash('repo_2', 2))
  350. ])
  351. # repo_2 should've been synced to @2 instead of @1, since URLs override
  352. # paths.
  353. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  354. ('repo_2@2', 'src/repo2'),
  355. ('repo_3@2', 'src/repo2/repo_renamed'))
  356. tree['src/git_hooked1'] = 'git_hooked1'
  357. tree['src/git_hooked2'] = 'git_hooked2'
  358. self.assertTree(tree)
  359. def testSyncPatchRef(self):
  360. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  361. self.gclient([
  362. 'sync',
  363. '-v',
  364. '-v',
  365. '-v',
  366. '--revision',
  367. 'src/repo2@%s' % self.githash('repo_2', 1),
  368. '--patch-ref',
  369. '%srepo_2@refs/heads/main:%s' %
  370. (self.git_base, self.githash('repo_2', 2)),
  371. ])
  372. # Assert that repo_2 files coincide with revision @2 (the patch ref)
  373. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  374. ('repo_2@2', 'src/repo2'),
  375. ('repo_3@2', 'src/repo2/repo_renamed'))
  376. tree['src/git_hooked1'] = 'git_hooked1'
  377. tree['src/git_hooked2'] = 'git_hooked2'
  378. self.assertTree(tree)
  379. # Assert that HEAD revision of repo_2 is @1 (the base we synced to)
  380. # since we should have done a soft reset.
  381. self.assertEqual(
  382. self.githash('repo_2', 1),
  383. self.gitrevparse(os.path.join(self.root_dir, 'src/repo2')))
  384. def testSyncPatchRefNoHooks(self):
  385. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  386. self.gclient([
  387. 'sync',
  388. '-v',
  389. '-v',
  390. '-v',
  391. '--revision',
  392. 'src/repo2@%s' % self.githash('repo_2', 1),
  393. '--patch-ref',
  394. '%srepo_2@refs/heads/main:%s' %
  395. (self.git_base, self.githash('repo_2', 2)),
  396. '--nohooks',
  397. ])
  398. # Assert that repo_2 files coincide with revision @2 (the patch ref)
  399. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  400. ('repo_2@2', 'src/repo2'),
  401. ('repo_3@2', 'src/repo2/repo_renamed'))
  402. self.assertTree(tree)
  403. # Assert that HEAD revision of repo_2 is @1 (the base we synced to)
  404. # since we should have done a soft reset.
  405. self.assertEqual(
  406. self.githash('repo_2', 1),
  407. self.gitrevparse(os.path.join(self.root_dir, 'src/repo2')))
  408. def testInstallHooks_no_existing_hook(self):
  409. repo = os.path.join(self.git_base, 'repo_18')
  410. self.gclient(['config', repo, '--name', 'src'], cwd=repo)
  411. self.gclient(['sync'], cwd=repo)
  412. self.gclient(['installhooks'], cwd=repo)
  413. hook = os.path.join(repo, 'src', '.git', 'hooks', 'pre-commit')
  414. with open(hook) as f:
  415. contents = f.read()
  416. self.assertIn('python3 "$GCLIENT_PRECOMMIT"', contents)
  417. # Later runs should only inform that the hook was already installed.
  418. stdout, _, _ = self.gclient(['installhooks'], cwd=repo)
  419. self.assertIn(f'{hook} already contains the gclient pre-commit hook.',
  420. stdout)
  421. def testInstallHooks_existing_hook(self):
  422. repo = os.path.join(self.git_base, 'repo_19')
  423. self.gclient(['config', repo, '--name', 'src'], cwd=repo)
  424. self.gclient(['sync'], cwd=repo)
  425. # Create an existing pre-commit hook.
  426. hook = os.path.join(repo, 'src', '.git', 'hooks', 'pre-commit')
  427. hook_contents = ['#!/bin/sh', 'echo hook']
  428. with open(hook, 'w') as f:
  429. f.write('\n'.join(hook_contents))
  430. stdout, _, _ = self.gclient(['installhooks'], cwd=repo)
  431. self.assertIn('Please append the following lines to the hook', stdout)
  432. # The orignal hook is left unchanged.
  433. with open(hook) as f:
  434. contents = f.read().splitlines()
  435. self.assertEqual(hook_contents, contents)
  436. def testRunHooks(self):
  437. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  438. self.gclient(['sync', '--deps', 'mac'])
  439. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  440. ('repo_2@1', 'src/repo2'),
  441. ('repo_3@2', 'src/repo2/repo_renamed'))
  442. tree['src/git_hooked1'] = 'git_hooked1'
  443. tree['src/git_hooked2'] = 'git_hooked2'
  444. self.assertTree(tree)
  445. os.remove(join(self.root_dir, 'src', 'git_hooked1'))
  446. os.remove(join(self.root_dir, 'src', 'git_hooked2'))
  447. # runhooks runs all hooks even if not matching by design.
  448. out = self.parseGclient(['runhooks', '--deps', 'mac'],
  449. ['running', 'running'])
  450. self.assertEqual(1, len(out[0]))
  451. self.assertEqual(1, len(out[1]))
  452. tree = self.mangle_git_tree(('repo_1@2', 'src'),
  453. ('repo_2@1', 'src/repo2'),
  454. ('repo_3@2', 'src/repo2/repo_renamed'))
  455. tree['src/git_hooked1'] = 'git_hooked1'
  456. tree['src/git_hooked2'] = 'git_hooked2'
  457. self.assertTree(tree)
  458. def testRunHooksCondition(self):
  459. self.gclient(['config', self.git_base + 'repo_7', '--name', 'src'])
  460. self.gclient(['sync', '--deps', 'mac'])
  461. tree = self.mangle_git_tree(('repo_7@1', 'src'))
  462. tree['src/should_run'] = 'should_run'
  463. self.assertTree(tree)
  464. def testPreDepsHooks(self):
  465. self.gclient(['config', self.git_base + 'repo_5', '--name', 'src'])
  466. expectation = [
  467. ('running', self.root_dir), # git clone
  468. ('running', self.root_dir), # pre-deps hook
  469. ]
  470. out = self.parseGclient([
  471. 'sync', '--deps', 'mac', '--jobs=1', '--revision',
  472. 'src@' + self.githash('repo_5', 2)
  473. ], expectation)
  474. self.assertEqual('Cloning into ', out[0][1][:13])
  475. # parseGClient may produce hook slowness warning, so we expect either 2
  476. # or 3 blocks.
  477. self.assertIn(len(out[1]), [2, 3], out[1])
  478. self.assertEqual('pre-deps hook', out[1][1])
  479. tree = self.mangle_git_tree(('repo_5@2', 'src'),
  480. ('repo_1@2', 'src/repo1'),
  481. ('repo_2@1', 'src/repo2'))
  482. tree['src/git_pre_deps_hooked'] = 'git_pre_deps_hooked'
  483. self.assertTree(tree)
  484. os.remove(join(self.root_dir, 'src', 'git_pre_deps_hooked'))
  485. # Pre-DEPS hooks don't run with runhooks.
  486. self.gclient(['runhooks', '--deps', 'mac'])
  487. tree = self.mangle_git_tree(('repo_5@2', 'src'),
  488. ('repo_1@2', 'src/repo1'),
  489. ('repo_2@1', 'src/repo2'))
  490. self.assertTree(tree)
  491. # Pre-DEPS hooks run when syncing with --nohooks.
  492. self.gclient([
  493. 'sync', '--deps', 'mac', '--nohooks', '--revision',
  494. 'src@' + self.githash('repo_5', 2)
  495. ])
  496. tree = self.mangle_git_tree(('repo_5@2', 'src'),
  497. ('repo_1@2', 'src/repo1'),
  498. ('repo_2@1', 'src/repo2'))
  499. tree['src/git_pre_deps_hooked'] = 'git_pre_deps_hooked'
  500. self.assertTree(tree)
  501. os.remove(join(self.root_dir, 'src', 'git_pre_deps_hooked'))
  502. # Pre-DEPS hooks don't run with --noprehooks
  503. self.gclient([
  504. 'sync', '--deps', 'mac', '--noprehooks', '--revision',
  505. 'src@' + self.githash('repo_5', 2)
  506. ])
  507. tree = self.mangle_git_tree(('repo_5@2', 'src'),
  508. ('repo_1@2', 'src/repo1'),
  509. ('repo_2@1', 'src/repo2'))
  510. self.assertTree(tree)
  511. def testPreDepsHooksError(self):
  512. self.gclient(['config', self.git_base + 'repo_5', '--name', 'src'])
  513. expectated_stdout = [
  514. ('running', self.root_dir), # git clone
  515. ('running', self.root_dir), # pre-deps hook
  516. ('running', self.root_dir), # pre-deps hook (fails)
  517. ]
  518. expected_stderr = (
  519. "Error: Command 'python3 -c import sys; "
  520. "sys.exit(1)' returned non-zero exit status 1 in %s\n" %
  521. (self.root_dir))
  522. stdout, stderr, retcode = self.gclient([
  523. 'sync', '--deps', 'mac', '--jobs=1', '--revision',
  524. 'src@' + self.githash('repo_5', 3)
  525. ],
  526. error_ok=True)
  527. self.assertEqual(stderr, expected_stderr)
  528. self.assertEqual(2, retcode)
  529. self.checkBlock(stdout, expectated_stdout)
  530. def testRevInfo(self):
  531. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  532. self.gclient(['sync', '--deps', 'mac'])
  533. results = self.gclient(['revinfo', '--deps', 'mac'])
  534. out = ('src: %(base)srepo_1\n'
  535. 'src/repo2: %(base)srepo_2@%(hash2)s\n'
  536. 'src/repo2/repo_renamed: %(base)srepo_3\n' % {
  537. 'base': self.git_base,
  538. 'hash2': self.githash('repo_2', 1)[:7],
  539. })
  540. self.check((out, '', 0), results)
  541. def testRevInfoActual(self):
  542. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  543. self.gclient(['sync', '--deps', 'mac'])
  544. results = self.gclient(['revinfo', '--deps', 'mac', '--actual'])
  545. out = ('src: %(base)srepo_1@%(hash1)s\n'
  546. 'src/repo2: %(base)srepo_2@%(hash2)s\n'
  547. 'src/repo2/repo_renamed: %(base)srepo_3@%(hash3)s\n' % {
  548. 'base': self.git_base,
  549. 'hash1': self.githash('repo_1', 2),
  550. 'hash2': self.githash('repo_2', 1),
  551. 'hash3': self.githash('repo_3', 2),
  552. })
  553. self.check((out, '', 0), results)
  554. def testRevInfoFilterPath(self):
  555. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  556. self.gclient(['sync', '--deps', 'mac'])
  557. results = self.gclient(['revinfo', '--deps', 'mac', '--filter', 'src'])
  558. out = ('src: %(base)srepo_1\n' % {
  559. 'base': self.git_base,
  560. })
  561. self.check((out, '', 0), results)
  562. def testRevInfoFilterURL(self):
  563. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  564. self.gclient(['sync', '--deps', 'mac'])
  565. results = self.gclient([
  566. 'revinfo', '--deps', 'mac', '--filter',
  567. '%srepo_2' % self.git_base
  568. ])
  569. out = ('src/repo2: %(base)srepo_2@%(hash2)s\n' % {
  570. 'base': self.git_base,
  571. 'hash2': self.githash('repo_2', 1)[:7],
  572. })
  573. self.check((out, '', 0), results)
  574. def testRevInfoFilterURLOrPath(self):
  575. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  576. self.gclient(['sync', '--deps', 'mac'])
  577. results = self.gclient([
  578. 'revinfo', '--deps', 'mac', '--filter', 'src', '--filter',
  579. '%srepo_2' % self.git_base
  580. ])
  581. out = ('src: %(base)srepo_1\n'
  582. 'src/repo2: %(base)srepo_2@%(hash2)s\n' % {
  583. 'base': self.git_base,
  584. 'hash2': self.githash('repo_2', 1)[:7],
  585. })
  586. self.check((out, '', 0), results)
  587. def testRevInfoJsonOutput(self):
  588. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  589. self.gclient(['sync', '--deps', 'mac'])
  590. output_json = os.path.join(self.root_dir, 'output.json')
  591. self.gclient(['revinfo', '--deps', 'mac', '--output-json', output_json])
  592. with open(output_json) as f:
  593. output_json = json.load(f)
  594. out = {
  595. 'src': {
  596. 'url': self.git_base + 'repo_1',
  597. 'rev': None,
  598. },
  599. 'src/repo2': {
  600. 'url': self.git_base + 'repo_2',
  601. 'rev': self.githash('repo_2', 1)[:7],
  602. },
  603. 'src/repo2/repo_renamed': {
  604. 'url': self.git_base + 'repo_3',
  605. 'rev': None,
  606. },
  607. }
  608. self.assertEqual(out, output_json)
  609. def testRevInfoJsonOutputSnapshot(self):
  610. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  611. self.gclient(['sync', '--deps', 'mac'])
  612. output_json = os.path.join(self.root_dir, 'output.json')
  613. self.gclient([
  614. 'revinfo', '--deps', 'mac', '--snapshot', '--output-json',
  615. output_json
  616. ])
  617. with open(output_json) as f:
  618. output_json = json.load(f)
  619. out = [{
  620. 'solution_url': self.git_base + 'repo_1',
  621. 'managed': True,
  622. 'name': 'src',
  623. 'deps_file': 'DEPS',
  624. 'custom_deps': {
  625. 'src/repo2':
  626. '%srepo_2@%s' % (self.git_base, self.githash('repo_2', 1)),
  627. 'src/repo2/repo_renamed':
  628. '%srepo_3@%s' % (self.git_base, self.githash('repo_3', 2)),
  629. 'src/should_not_process':
  630. None,
  631. },
  632. }]
  633. self.assertEqual(out, output_json)
  634. def testSetDep(self):
  635. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  636. fake_deps = os.path.join(self.git_base, 'repo_1', 'DEPS')
  637. with open(fake_deps, 'w') as f:
  638. f.write('\n'.join([
  639. 'vars = { ',
  640. ' "foo_var": "foo_val",',
  641. ' "foo_rev": "foo_rev",',
  642. '}',
  643. 'deps = {',
  644. ' "foo": {',
  645. ' "url": "url@{foo_rev}",',
  646. ' },',
  647. ' "bar": "url@bar_rev",',
  648. '}',
  649. ]))
  650. self.gclient([
  651. 'setdep', '-r', 'foo@new_foo', '-r', 'bar@new_bar', '--var',
  652. 'foo_var=new_val', '--deps-file', fake_deps
  653. ],
  654. cwd=self.git_base + 'repo_1')
  655. with open(fake_deps) as f:
  656. contents = f.read().splitlines()
  657. self.assertEqual([
  658. 'vars = { ',
  659. ' "foo_var": "new_val",',
  660. ' "foo_rev": "new_foo",',
  661. '}',
  662. 'deps = {',
  663. ' "foo": {',
  664. ' "url": "url@{foo_rev}",',
  665. ' },',
  666. ' "bar": "url@new_bar",',
  667. '}',
  668. ], contents)
  669. def testSetDep_Submodules(self):
  670. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  671. with open(os.path.join(self.git_base, '.gclient'), 'w') as f:
  672. f.write('')
  673. fake_deps = os.path.join(self.git_base, 'repo_1', 'DEPS')
  674. gitmodules = os.path.join(self.git_base, 'repo_1', '.gitmodules')
  675. with open(fake_deps, 'w') as f:
  676. f.write('\n'.join([
  677. 'git_dependencies = "SYNC"',
  678. 'vars = { ',
  679. ' "foo_var": "foo_val",',
  680. ' "foo_rev": "foo_rev",',
  681. '}',
  682. 'deps = { ',
  683. ' "repo_1/foo": "https://foo" + Var("foo_rev"),',
  684. ' "repo_1/bar": "https://bar@barrev",',
  685. '}',
  686. ]))
  687. with open(gitmodules, 'w') as f:
  688. f.write('\n'.join([
  689. '[submodule "foo"]', ' url = https://foo', ' path = foo',
  690. '[submodule "bar"]', ' url = https://bar', ' path = bar'
  691. ]))
  692. subprocess2.call([
  693. 'git', 'update-index', '--add', '--cacheinfo',
  694. '160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,foo'
  695. ],
  696. cwd=self.git_base + 'repo_1')
  697. subprocess2.call([
  698. 'git', 'update-index', '--add', '--cacheinfo',
  699. '160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,bar'
  700. ],
  701. cwd=self.git_base + 'repo_1')
  702. self.gclient([
  703. 'setdep',
  704. '-r',
  705. 'repo_1/foo@new_foo',
  706. '--var',
  707. 'foo_var=new_val',
  708. '-r',
  709. 'repo_1/bar@bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
  710. ],
  711. cwd=self.git_base + 'repo_1')
  712. with open(fake_deps) as f:
  713. contents = f.read().splitlines()
  714. self.assertEqual([
  715. 'git_dependencies = "SYNC"',
  716. 'vars = { ',
  717. ' "foo_var": "new_val",',
  718. ' "foo_rev": "new_foo",',
  719. '}',
  720. 'deps = { ',
  721. ' "repo_1/foo": "https://foo" + Var("foo_rev"),',
  722. ' "repo_1/bar": '
  723. '"https://bar@bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",',
  724. '}',
  725. ], contents)
  726. def testSetDep_Submodules_relative(self):
  727. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  728. fake_deps = os.path.join(self.git_base, 'repo_1', 'DEPS')
  729. gitmodules = os.path.join(self.git_base, 'repo_1', '.gitmodules')
  730. with open(fake_deps, 'w') as f:
  731. f.write('\n'.join([
  732. 'git_dependencies = "SUBMODULES"',
  733. 'use_relative_paths = True',
  734. 'vars = { ',
  735. ' "foo_var": "foo_val",',
  736. ' "foo_rev": "foo_rev",',
  737. '}',
  738. ]))
  739. with open(gitmodules, 'w') as f:
  740. f.write('\n'.join(
  741. ['[submodule "foo"]', ' url = https://foo', ' path = foo']))
  742. subprocess2.call([
  743. 'git', 'update-index', '--add', '--cacheinfo',
  744. '160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,foo'
  745. ],
  746. cwd=self.git_base + 'repo_1')
  747. self.gclient([
  748. 'setdep', '-r', 'foo@new_foo', '--var', 'foo_var=new_val',
  749. '--deps-file', fake_deps
  750. ],
  751. cwd=self.git_base + 'repo_1')
  752. with open(fake_deps) as f:
  753. contents = f.read().splitlines()
  754. self.assertEqual([
  755. 'git_dependencies = "SUBMODULES"',
  756. 'use_relative_paths = True',
  757. 'vars = { ',
  758. ' "foo_var": "new_val",',
  759. ' "foo_rev": "foo_rev",',
  760. '}',
  761. ], contents)
  762. def testSetDep_BuiltinVariables(self):
  763. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'],
  764. cwd=self.git_base)
  765. fake_deps = os.path.join(self.root_dir, 'DEPS')
  766. with open(fake_deps, 'w') as f:
  767. f.write('\n'.join([
  768. 'vars = { ',
  769. ' "foo_var": "foo_val",',
  770. ' "foo_rev": "foo_rev",',
  771. '}',
  772. 'deps = {',
  773. ' "foo": {',
  774. ' "url": "url@{foo_rev}",',
  775. ' },',
  776. ' "bar": "url@bar_rev",',
  777. '}',
  778. 'hooks = [{',
  779. ' "name": "uses_builtin_var",',
  780. ' "pattern": ".",',
  781. ' "action": ["python3", "fake.py",',
  782. ' "--with-android={checkout_android}"],',
  783. '}]',
  784. ]))
  785. self.gclient([
  786. 'setdep', '-r', 'foo@new_foo', '-r', 'bar@new_bar', '--var',
  787. 'foo_var=new_val', '--deps-file', fake_deps
  788. ],
  789. cwd=self.git_base + 'repo_1')
  790. with open(fake_deps) as f:
  791. contents = f.read().splitlines()
  792. self.assertEqual([
  793. 'vars = { ',
  794. ' "foo_var": "new_val",',
  795. ' "foo_rev": "new_foo",',
  796. '}',
  797. 'deps = {',
  798. ' "foo": {',
  799. ' "url": "url@{foo_rev}",',
  800. ' },',
  801. ' "bar": "url@new_bar",',
  802. '}',
  803. 'hooks = [{',
  804. ' "name": "uses_builtin_var",',
  805. ' "pattern": ".",',
  806. ' "action": ["python3", "fake.py",',
  807. ' "--with-android={checkout_android}"],',
  808. '}]',
  809. ], contents)
  810. def testGetDep(self):
  811. fake_deps = os.path.join(self.root_dir, 'DEPS.fake')
  812. with open(fake_deps, 'w') as f:
  813. f.write('\n'.join([
  814. 'vars = { ',
  815. ' "foo_var": "foo_val",',
  816. ' "foo_rev": "foo_rev",',
  817. '}',
  818. 'deps = {',
  819. ' "foo": {',
  820. ' "url": "url@{foo_rev}",',
  821. ' },',
  822. ' "bar": "url@bar_rev",',
  823. '}',
  824. ]))
  825. results = self.gclient([
  826. 'getdep', '-r', 'foo', '-r', 'bar', '--var', 'foo_var',
  827. '--deps-file', fake_deps
  828. ])
  829. self.assertEqual([
  830. 'foo_val',
  831. 'foo_rev',
  832. 'bar_rev',
  833. ], results[0].splitlines())
  834. def testGetDep_Submodule(self):
  835. self.gclient(['config', self.git_base + 'repo_20', '--name', 'src'])
  836. subprocess2.call([
  837. 'git', 'update-index', '--add', '--cacheinfo',
  838. '160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,bar'
  839. ],
  840. cwd=self.git_base + 'repo_20')
  841. results = self.gclient([
  842. 'getdep', '-r', 'foo/bar:lemur', '-r', 'bar', '--var',
  843. 'foo_checkout'
  844. ],
  845. cwd=self.git_base + 'repo_20')
  846. self.assertEqual([
  847. 'True', 'version:1234', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  848. ], results[0].splitlines())
  849. def testGetDep_BuiltinVariables(self):
  850. self.gclient(['config', self.git_base + 'repo_1', '--name', 'src'])
  851. fake_deps = os.path.join(self.root_dir, 'DEPS.fake')
  852. with open(fake_deps, 'w') as f:
  853. f.write('\n'.join([
  854. 'vars = { ',
  855. ' "foo_var": "foo_val",',
  856. ' "foo_rev": "foo_rev",',
  857. '}',
  858. 'deps = {',
  859. ' "foo": {',
  860. ' "url": "url@{foo_rev}",',
  861. ' },',
  862. ' "bar": "url@bar_rev",',
  863. '}',
  864. 'hooks = [{',
  865. ' "name": "uses_builtin_var",',
  866. ' "pattern": ".",',
  867. ' "action": ["python3", "fake.py",',
  868. ' "--with-android={checkout_android}"],',
  869. '}]',
  870. ]))
  871. results = self.gclient([
  872. 'getdep', '-r', 'foo', '-r', 'bar', '--var', 'foo_var',
  873. '--deps-file', fake_deps
  874. ])
  875. self.assertEqual([
  876. 'foo_val',
  877. 'foo_rev',
  878. 'bar_rev',
  879. ], results[0].splitlines())
  880. # TODO(crbug.com/1024683): Enable for windows.
  881. @unittest.skipIf(sys.platform == 'win32', 'not yet fixed on win')
  882. def testFlatten(self):
  883. output_deps = os.path.join(self.root_dir, 'DEPS.flattened')
  884. self.assertFalse(os.path.exists(output_deps))
  885. self.gclient([
  886. 'config',
  887. self.git_base + 'repo_6',
  888. '--name',
  889. 'src',
  890. # This should be ignored because 'custom_true_var' isn't
  891. # defined in the DEPS.
  892. '--custom-var',
  893. 'custom_true_var=True',
  894. # This should override 'true_var=True' from the DEPS.
  895. '--custom-var',
  896. 'true_var="False"'
  897. ])
  898. self.gclient(['sync'])
  899. self.gclient(
  900. ['flatten', '-v', '-v', '-v', '--output-deps', output_deps])
  901. # Assert we can sync to the flattened DEPS we just wrote.
  902. solutions = [{
  903. "url": self.git_base + 'repo_6',
  904. 'name': 'src',
  905. 'deps_file': output_deps
  906. }]
  907. self.gclient(['sync', '--spec=solutions=%s' % solutions])
  908. with open(output_deps) as f:
  909. deps_contents = f.read()
  910. self.assertEqual([
  911. 'gclient_gn_args_file = "src/repo2/gclient.args"',
  912. 'gclient_gn_args = [\'false_var\', \'false_str_var\', \'true_var\', '
  913. '\'true_str_var\', \'str_var\', \'cond_var\']',
  914. 'allowed_hosts = [',
  915. ' "' + self.git_base + '",',
  916. ']',
  917. '',
  918. 'deps = {',
  919. ' # "src" -> "src/repo2" -> "foo/bar"',
  920. ' "foo/bar": {',
  921. ' "url": "' + self.git_base + 'repo_3",',
  922. ' "condition": \'(repo2_false_var) and (true_str_var)\',',
  923. ' },',
  924. '',
  925. ' # "src"',
  926. ' "src": {',
  927. ' "url": "' + self.git_base + 'repo_6",',
  928. ' },',
  929. '',
  930. ' # "src" -> "src/mac_repo"',
  931. ' "src/mac_repo": {',
  932. ' "url": "' + self.git_base + 'repo_5",',
  933. ' "condition": \'checkout_mac\',',
  934. ' },',
  935. '',
  936. ' # "src" -> "src/repo8" -> "src/recursed_os_repo"',
  937. ' "src/recursed_os_repo": {',
  938. ' "url": "' + self.git_base + 'repo_5",',
  939. ' "condition": \'(checkout_linux) or (checkout_mac)\',',
  940. ' },',
  941. '',
  942. ' # "src" -> "src/repo15"',
  943. ' "src/repo15": {',
  944. ' "url": "' + self.git_base + 'repo_15",',
  945. ' },',
  946. '',
  947. ' # "src" -> "src/repo16"',
  948. ' "src/repo16": {',
  949. ' "url": "' + self.git_base + 'repo_16",',
  950. ' },',
  951. '',
  952. ' # "src" -> "src/repo2"',
  953. ' "src/repo2": {',
  954. ' "url": "' + self.git_base + 'repo_2@%s",' %
  955. (self.githash('repo_2', 1)[:7]),
  956. ' "condition": \'true_str_var\',',
  957. ' },',
  958. '',
  959. ' # "src" -> "src/repo4"',
  960. ' "src/repo4": {',
  961. ' "url": "' + self.git_base + 'repo_4",',
  962. ' "condition": \'False\',',
  963. ' },',
  964. '',
  965. ' # "src" -> "src/repo8"',
  966. ' "src/repo8": {',
  967. ' "url": "' + self.git_base + 'repo_8",',
  968. ' },',
  969. '',
  970. ' # "src" -> "src/unix_repo"',
  971. ' "src/unix_repo": {',
  972. ' "url": "' + self.git_base + 'repo_5",',
  973. ' "condition": \'checkout_linux\',',
  974. ' },',
  975. '',
  976. ' # "src" -> "src/win_repo"',
  977. ' "src/win_repo": {',
  978. ' "url": "' + self.git_base + 'repo_5",',
  979. ' "condition": \'checkout_win\',',
  980. ' },',
  981. '',
  982. '}',
  983. '',
  984. 'hooks = [',
  985. ' # "src"',
  986. ' {',
  987. ' "pattern": ".",',
  988. ' "condition": \'True\',',
  989. ' "cwd": ".",',
  990. ' "action": [',
  991. ' "python3",',
  992. ' "-c",',
  993. ' "open(\'src/git_hooked1\', \'w\')'
  994. '.write(\'git_hooked1\')",',
  995. ' ]',
  996. ' },',
  997. '',
  998. ' # "src"',
  999. ' {',
  1000. ' "pattern": "nonexistent",',
  1001. ' "cwd": ".",',
  1002. ' "action": [',
  1003. ' "python3",',
  1004. ' "-c",',
  1005. ' "open(\'src/git_hooked2\', \'w\').write(\'git_hooked2\')",',
  1006. ' ]',
  1007. ' },',
  1008. '',
  1009. ' # "src"',
  1010. ' {',
  1011. ' "pattern": ".",',
  1012. ' "condition": \'checkout_mac\',',
  1013. ' "cwd": ".",',
  1014. ' "action": [',
  1015. ' "python3",',
  1016. ' "-c",',
  1017. ' "open(\'src/git_hooked_mac\', \'w\').write('
  1018. '\'git_hooked_mac\')",',
  1019. ' ]',
  1020. ' },',
  1021. '',
  1022. ' # "src" -> "src/repo15"',
  1023. ' {',
  1024. ' "name": "absolute_cwd",',
  1025. ' "pattern": ".",',
  1026. ' "cwd": ".",',
  1027. ' "action": [',
  1028. ' "python3",',
  1029. ' "-c",',
  1030. ' "pass",',
  1031. ' ]',
  1032. ' },',
  1033. '',
  1034. ' # "src" -> "src/repo16"',
  1035. ' {',
  1036. ' "name": "relative_cwd",',
  1037. ' "pattern": ".",',
  1038. ' "cwd": "src/repo16",',
  1039. ' "action": [',
  1040. ' "python3",',
  1041. ' "relative.py",',
  1042. ' ]',
  1043. ' },',
  1044. '',
  1045. ']',
  1046. '',
  1047. 'vars = {',
  1048. ' # "src"',
  1049. ' "DummyVariable": \'repo\',',
  1050. '',
  1051. ' # "src"',
  1052. ' "cond_var": \'false_str_var and true_var\',',
  1053. '',
  1054. ' # "src"',
  1055. ' "false_str_var": \'False\',',
  1056. '',
  1057. ' # "src"',
  1058. ' "false_var": False,',
  1059. '',
  1060. ' # "src"',
  1061. ' "git_base": \'' + self.git_base + '\',',
  1062. '',
  1063. ' # "src"',
  1064. ' "hook1_contents": \'git_hooked1\',',
  1065. '',
  1066. ' # "src" -> "src/repo2"',
  1067. ' "repo2_false_var": \'False\',',
  1068. '',
  1069. ' # "src"',
  1070. ' "repo5_var": \'/repo_5\',',
  1071. '',
  1072. ' # "src"',
  1073. ' "str_var": \'abc\',',
  1074. '',
  1075. ' # "src"',
  1076. ' "true_str_var": \'True\',',
  1077. '',
  1078. ' # "src" [custom_var override]',
  1079. ' "true_var": \'False\',',
  1080. '',
  1081. '}',
  1082. '',
  1083. '# ' + self.git_base + 'repo_15, DEPS',
  1084. '# ' + self.git_base + 'repo_16, DEPS',
  1085. '# ' + self.git_base + 'repo_2@%s, DEPS' %
  1086. (self.githash('repo_2', 1)[:7]),
  1087. '# ' + self.git_base + 'repo_6, DEPS',
  1088. '# ' + self.git_base + 'repo_8, DEPS',
  1089. ], deps_contents.splitlines())
  1090. # TODO(crbug.com/1024683): Enable for windows.
  1091. @unittest.skipIf(sys.platform == 'win32', 'not yet fixed on win')
  1092. def testFlattenPinAllDeps(self):
  1093. output_deps = os.path.join(self.root_dir, 'DEPS.flattened')
  1094. self.assertFalse(os.path.exists(output_deps))
  1095. self.gclient(['config', self.git_base + 'repo_6', '--name', 'src'])
  1096. self.gclient(['sync', '--process-all-deps'])
  1097. self.gclient([
  1098. 'flatten', '-v', '-v', '-v', '--output-deps', output_deps,
  1099. '--pin-all-deps'
  1100. ])
  1101. with open(output_deps) as f:
  1102. deps_contents = f.read()
  1103. self.assertEqual([
  1104. 'gclient_gn_args_file = "src/repo2/gclient.args"',
  1105. 'gclient_gn_args = [\'false_var\', \'false_str_var\', \'true_var\', '
  1106. '\'true_str_var\', \'str_var\', \'cond_var\']',
  1107. 'allowed_hosts = [',
  1108. ' "' + self.git_base + '",',
  1109. ']',
  1110. '',
  1111. 'deps = {',
  1112. ' # "src" -> "src/repo2" -> "foo/bar"',
  1113. ' "foo/bar": {',
  1114. ' "url": "' + self.git_base + 'repo_3@%s",' %
  1115. (self.githash('repo_3', 2)),
  1116. ' "condition": \'(repo2_false_var) and (true_str_var)\',',
  1117. ' },',
  1118. '',
  1119. ' # "src"',
  1120. ' "src": {',
  1121. ' "url": "' + self.git_base + 'repo_6@%s",' %
  1122. (self.githash('repo_6', 1)),
  1123. ' },',
  1124. '',
  1125. ' # "src" -> "src/mac_repo"',
  1126. ' "src/mac_repo": {',
  1127. ' "url": "' + self.git_base + 'repo_5@%s",' %
  1128. (self.githash('repo_5', 3)),
  1129. ' "condition": \'checkout_mac\',',
  1130. ' },',
  1131. '',
  1132. ' # "src" -> "src/repo8" -> "src/recursed_os_repo"',
  1133. ' "src/recursed_os_repo": {',
  1134. ' "url": "' + self.git_base + 'repo_5@%s",' %
  1135. (self.githash('repo_5', 3)),
  1136. ' "condition": \'(checkout_linux) or (checkout_mac)\',',
  1137. ' },',
  1138. '',
  1139. ' # "src" -> "src/repo15"',
  1140. ' "src/repo15": {',
  1141. ' "url": "' + self.git_base + 'repo_15@%s",' %
  1142. (self.githash('repo_15', 1)),
  1143. ' },',
  1144. '',
  1145. ' # "src" -> "src/repo16"',
  1146. ' "src/repo16": {',
  1147. ' "url": "' + self.git_base + 'repo_16@%s",' %
  1148. (self.githash('repo_16', 1)),
  1149. ' },',
  1150. '',
  1151. ' # "src" -> "src/repo2"',
  1152. ' "src/repo2": {',
  1153. ' "url": "' + self.git_base + 'repo_2@%s",' %
  1154. (self.githash('repo_2', 1)),
  1155. ' "condition": \'true_str_var\',',
  1156. ' },',
  1157. '',
  1158. ' # "src" -> "src/repo4"',
  1159. ' "src/repo4": {',
  1160. ' "url": "' + self.git_base + 'repo_4@%s",' %
  1161. (self.githash('repo_4', 2)),
  1162. ' "condition": \'False\',',
  1163. ' },',
  1164. '',
  1165. ' # "src" -> "src/repo8"',
  1166. ' "src/repo8": {',
  1167. ' "url": "' + self.git_base + 'repo_8@%s",' %
  1168. (self.githash('repo_8', 1)),
  1169. ' },',
  1170. '',
  1171. ' # "src" -> "src/unix_repo"',
  1172. ' "src/unix_repo": {',
  1173. ' "url": "' + self.git_base + 'repo_5@%s",' %
  1174. (self.githash('repo_5', 3)),
  1175. ' "condition": \'checkout_linux\',',
  1176. ' },',
  1177. '',
  1178. ' # "src" -> "src/win_repo"',
  1179. ' "src/win_repo": {',
  1180. ' "url": "' + self.git_base + 'repo_5@%s",' %
  1181. (self.githash('repo_5', 3)),
  1182. ' "condition": \'checkout_win\',',
  1183. ' },',
  1184. '',
  1185. '}',
  1186. '',
  1187. 'hooks = [',
  1188. ' # "src"',
  1189. ' {',
  1190. ' "pattern": ".",',
  1191. ' "condition": \'True\',',
  1192. ' "cwd": ".",',
  1193. ' "action": [',
  1194. ' "python3",',
  1195. ' "-c",',
  1196. ' "open(\'src/git_hooked1\', \'w\')'
  1197. '.write(\'git_hooked1\')",',
  1198. ' ]',
  1199. ' },',
  1200. '',
  1201. ' # "src"',
  1202. ' {',
  1203. ' "pattern": "nonexistent",',
  1204. ' "cwd": ".",',
  1205. ' "action": [',
  1206. ' "python3",',
  1207. ' "-c",',
  1208. ' "open(\'src/git_hooked2\', \'w\').write(\'git_hooked2\')",',
  1209. ' ]',
  1210. ' },',
  1211. '',
  1212. ' # "src"',
  1213. ' {',
  1214. ' "pattern": ".",',
  1215. ' "condition": \'checkout_mac\',',
  1216. ' "cwd": ".",',
  1217. ' "action": [',
  1218. ' "python3",',
  1219. ' "-c",',
  1220. ' "open(\'src/git_hooked_mac\', \'w\').write('
  1221. '\'git_hooked_mac\')",',
  1222. ' ]',
  1223. ' },',
  1224. '',
  1225. ' # "src" -> "src/repo15"',
  1226. ' {',
  1227. ' "name": "absolute_cwd",',
  1228. ' "pattern": ".",',
  1229. ' "cwd": ".",',
  1230. ' "action": [',
  1231. ' "python3",',
  1232. ' "-c",',
  1233. ' "pass",',
  1234. ' ]',
  1235. ' },',
  1236. '',
  1237. ' # "src" -> "src/repo16"',
  1238. ' {',
  1239. ' "name": "relative_cwd",',
  1240. ' "pattern": ".",',
  1241. ' "cwd": "src/repo16",',
  1242. ' "action": [',
  1243. ' "python3",',
  1244. ' "relative.py",',
  1245. ' ]',
  1246. ' },',
  1247. '',
  1248. ']',
  1249. '',
  1250. 'vars = {',
  1251. ' # "src"',
  1252. ' "DummyVariable": \'repo\',',
  1253. '',
  1254. ' # "src"',
  1255. ' "cond_var": \'false_str_var and true_var\',',
  1256. '',
  1257. ' # "src"',
  1258. ' "false_str_var": \'False\',',
  1259. '',
  1260. ' # "src"',
  1261. ' "false_var": False,',
  1262. '',
  1263. ' # "src"',
  1264. ' "git_base": \'' + self.git_base + '\',',
  1265. '',
  1266. ' # "src"',
  1267. ' "hook1_contents": \'git_hooked1\',',
  1268. '',
  1269. ' # "src" -> "src/repo2"',
  1270. ' "repo2_false_var": \'False\',',
  1271. '',
  1272. ' # "src"',
  1273. ' "repo5_var": \'/repo_5\',',
  1274. '',
  1275. ' # "src"',
  1276. ' "str_var": \'abc\',',
  1277. '',
  1278. ' # "src"',
  1279. ' "true_str_var": \'True\',',
  1280. '',
  1281. ' # "src"',
  1282. ' "true_var": True,',
  1283. '',
  1284. '}',
  1285. '',
  1286. '# ' + self.git_base + 'repo_15@%s, DEPS' %
  1287. (self.githash('repo_15', 1)),
  1288. '# ' + self.git_base + 'repo_16@%s, DEPS' %
  1289. (self.githash('repo_16', 1)),
  1290. '# ' + self.git_base + 'repo_2@%s, DEPS' %
  1291. (self.githash('repo_2', 1)),
  1292. '# ' + self.git_base + 'repo_6@%s, DEPS' %
  1293. (self.githash('repo_6', 1)),
  1294. '# ' + self.git_base + 'repo_8@%s, DEPS' %
  1295. (self.githash('repo_8', 1)),
  1296. ], deps_contents.splitlines())
  1297. # TODO(crbug.com/1024683): Enable for windows.
  1298. @unittest.skipIf(sys.platform == 'win32', 'not yet fixed on win')
  1299. def testFlattenRecursedeps(self):
  1300. output_deps = os.path.join(self.root_dir, 'DEPS.flattened')
  1301. self.assertFalse(os.path.exists(output_deps))
  1302. output_deps_files = os.path.join(self.root_dir, 'DEPS.files')
  1303. self.assertFalse(os.path.exists(output_deps_files))
  1304. self.gclient(['config', self.git_base + 'repo_10', '--name', 'src'])
  1305. self.gclient(['sync', '--process-all-deps'])
  1306. self.gclient([
  1307. 'flatten', '-v', '-v', '-v', '--output-deps', output_deps,
  1308. '--output-deps-files', output_deps_files
  1309. ])
  1310. with open(output_deps) as f:
  1311. deps_contents = f.read()
  1312. self.assertEqual([
  1313. 'gclient_gn_args_file = "src/repo8/gclient.args"',
  1314. "gclient_gn_args = ['str_var']",
  1315. 'deps = {',
  1316. ' # "src"',
  1317. ' "src": {',
  1318. ' "url": "' + self.git_base + 'repo_10",',
  1319. ' },',
  1320. '',
  1321. ' # "src" -> "src/repo9" -> "src/repo8" -> "src/recursed_os_repo"',
  1322. ' "src/recursed_os_repo": {',
  1323. ' "url": "' + self.git_base + 'repo_5",',
  1324. ' "condition": \'(checkout_linux) or (checkout_mac)\',',
  1325. ' },',
  1326. '',
  1327. ' # "src" -> "src/repo11"',
  1328. ' "src/repo11": {',
  1329. ' "url": "' + self.git_base + 'repo_11",',
  1330. ' "condition": \'(checkout_ios) or (checkout_mac)\',',
  1331. ' },',
  1332. '',
  1333. ' # "src" -> "src/repo11" -> "src/repo12"',
  1334. ' "src/repo12": {',
  1335. ' "url": "' + self.git_base + 'repo_12",',
  1336. ' "condition": \'(checkout_ios) or (checkout_mac)\',',
  1337. ' },',
  1338. '',
  1339. ' # "src" -> "src/repo9" -> "src/repo4"',
  1340. ' "src/repo4": {',
  1341. ' "url": "' + self.git_base + 'repo_4",',
  1342. ' "condition": \'checkout_android\',',
  1343. ' },',
  1344. '',
  1345. ' # "src" -> "src/repo6"',
  1346. ' "src/repo6": {',
  1347. ' "url": "' + self.git_base + 'repo_6",',
  1348. ' },',
  1349. '',
  1350. ' # "src" -> "src/repo9" -> "src/repo7"',
  1351. ' "src/repo7": {',
  1352. ' "url": "' + self.git_base + 'repo_7",',
  1353. ' },',
  1354. '',
  1355. ' # "src" -> "src/repo9" -> "src/repo8"',
  1356. ' "src/repo8": {',
  1357. ' "url": "' + self.git_base + 'repo_8",',
  1358. ' },',
  1359. '',
  1360. ' # "src" -> "src/repo9"',
  1361. ' "src/repo9": {',
  1362. ' "url": "' + self.git_base + 'repo_9",',
  1363. ' },',
  1364. '',
  1365. '}',
  1366. '',
  1367. 'vars = {',
  1368. ' # "src" -> "src/repo9"',
  1369. ' "str_var": \'xyz\',',
  1370. '',
  1371. '}',
  1372. '',
  1373. '# ' + self.git_base + 'repo_10, DEPS',
  1374. '# ' + self.git_base + 'repo_11, DEPS',
  1375. '# ' + self.git_base + 'repo_8, DEPS',
  1376. '# ' + self.git_base + 'repo_9, DEPS',
  1377. ], deps_contents.splitlines())
  1378. with open(output_deps_files) as f:
  1379. deps_files_contents = json.load(f)
  1380. self.assertEqual([
  1381. {
  1382. 'url': self.git_base + 'repo_10',
  1383. 'deps_file': 'DEPS',
  1384. 'hierarchy': [['src', self.git_base + 'repo_10']]
  1385. },
  1386. {
  1387. 'url':
  1388. self.git_base + 'repo_11',
  1389. 'deps_file':
  1390. 'DEPS',
  1391. 'hierarchy': [['src', self.git_base + 'repo_10'],
  1392. ['src/repo11', self.git_base + 'repo_11']]
  1393. },
  1394. {
  1395. 'url':
  1396. self.git_base + 'repo_8',
  1397. 'deps_file':
  1398. 'DEPS',
  1399. 'hierarchy': [['src', self.git_base + 'repo_10'],
  1400. ['src/repo9', self.git_base + 'repo_9'],
  1401. ['src/repo8', self.git_base + 'repo_8']]
  1402. },
  1403. {
  1404. 'url':
  1405. self.git_base + 'repo_9',
  1406. 'deps_file':
  1407. 'DEPS',
  1408. 'hierarchy': [['src', self.git_base + 'repo_10'],
  1409. ['src/repo9', self.git_base + 'repo_9']]
  1410. },
  1411. ], deps_files_contents)
  1412. # TODO(crbug.com/1024683): Enable for windows.
  1413. @unittest.skipIf(sys.platform == 'win32', 'not yet fixed on win')
  1414. def testFlattenCipd(self):
  1415. output_deps = os.path.join(self.root_dir, 'DEPS.flattened')
  1416. self.assertFalse(os.path.exists(output_deps))
  1417. self.gclient(['config', self.git_base + 'repo_14', '--name', 'src'])
  1418. self.gclient(['sync'])
  1419. self.gclient(
  1420. ['flatten', '-v', '-v', '-v', '--output-deps', output_deps])
  1421. with open(output_deps) as f:
  1422. deps_contents = f.read()
  1423. self.assertEqual([
  1424. 'deps = {',
  1425. ' # "src"',
  1426. ' "src": {',
  1427. ' "url": "' + self.git_base + 'repo_14",',
  1428. ' },',
  1429. '',
  1430. ' # "src" -> src/another_cipd_dep',
  1431. ' "src/another_cipd_dep": {',
  1432. ' "packages": [',
  1433. ' {',
  1434. ' "package": "package1",',
  1435. ' "version": "1.1-cr0",',
  1436. ' },',
  1437. ' {',
  1438. ' "package": "package2",',
  1439. ' "version": "1.13",',
  1440. ' },',
  1441. ' ],',
  1442. ' "dep_type": "cipd",',
  1443. ' },',
  1444. '',
  1445. ' # "src" -> src/cipd_dep',
  1446. ' "src/cipd_dep": {',
  1447. ' "packages": [',
  1448. ' {',
  1449. ' "package": "package0",',
  1450. ' "version": "0.1",',
  1451. ' },',
  1452. ' ],',
  1453. ' "dep_type": "cipd",',
  1454. ' },',
  1455. '',
  1456. ' # "src" -> src/cipd_dep_with_cipd_variable',
  1457. ' "src/cipd_dep_with_cipd_variable": {',
  1458. ' "packages": [',
  1459. ' {',
  1460. ' "package": "package3/${{platform}}",',
  1461. ' "version": "1.2",',
  1462. ' },',
  1463. ' ],',
  1464. ' "dep_type": "cipd",',
  1465. ' },',
  1466. '',
  1467. '}',
  1468. '',
  1469. '# ' + self.git_base + 'repo_14, DEPS',
  1470. ], deps_contents.splitlines())
  1471. def testRelativeGNArgsFile(self):
  1472. self.gclient(['config', self.git_base + 'repo_17', '--name', 'src'])
  1473. self.gclient([
  1474. 'sync',
  1475. ])
  1476. tree = self.mangle_git_tree(('repo_17@1', 'src'))
  1477. tree['src/repo17_gclient.args'] = '\n'.join([
  1478. '# Generated from \'DEPS\'',
  1479. 'toto = "tata"',
  1480. ])
  1481. self.assertTree(tree)
  1482. if __name__ == '__main__':
  1483. if '-v' in sys.argv:
  1484. logging.basicConfig(level=logging.DEBUG)
  1485. unittest.main()