gclient_git_smoketest.py 57 KB

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