blob: cb85612874b520f0a7c27a3232679f73c388bfb6 [file] [log] [blame]
maruel@chromium.org45d8db02011-03-31 20:43:56 +00001#!/usr/bin/env python
steveblock@chromium.org93567042012-02-15 01:02:26 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.orgba551772010-02-03 18:21:42 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
msb@chromium.orge28e4982009-09-25 20:51:45 +00005
6"""Unit tests for gclient_scm.py."""
7
maruel@chromium.org428342a2011-11-10 15:46:33 +00008# pylint: disable=E1103
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +00009
maruel@chromium.orgba551772010-02-03 18:21:42 +000010# Import before super_mox to keep valid references.
maruel@chromium.orgba551772010-02-03 18:21:42 +000011from shutil import rmtree
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000012from subprocess import Popen, PIPE, STDOUT
maruel@chromium.org428342a2011-11-10 15:46:33 +000013
John Budorick0f7b2002018-01-19 15:46:17 -080014import json
maruel@chromium.org428342a2011-11-10 15:46:33 +000015import logging
16import os
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000017import re
maruel@chromium.org428342a2011-11-10 15:46:33 +000018import sys
msb@chromium.orge28e4982009-09-25 20:51:45 +000019import tempfile
maruel@chromium.org389d6de2010-09-09 14:14:37 +000020import unittest
msb@chromium.orge28e4982009-09-25 20:51:45 +000021
maruel@chromium.org428342a2011-11-10 15:46:33 +000022sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000023
Edward Lemurd64781e2018-07-11 23:09:55 +000024from testing_support import fake_repos
maruel@chromium.org0927b7e2011-11-11 16:06:22 +000025from testing_support.super_mox import mox, StdoutCheck, SuperMoxTestBase
26from testing_support.super_mox import TestCaseUtils
maruel@chromium.org428342a2011-11-10 15:46:33 +000027
msb@chromium.orge28e4982009-09-25 20:51:45 +000028import gclient_scm
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000029import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000030import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000031
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000032# Disable global git cache
33git_cache.Mirror.SetCachePath(None)
34
maruel@chromium.org795a8c12010-10-05 19:54:29 +000035# Shortcut since this function is used often
36join = gclient_scm.os.path.join
37
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000038TIMESTAMP_RE = re.compile('\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
39def strip_timestamps(value):
40 lines = value.splitlines(True)
41 for i in xrange(len(lines)):
42 m = TIMESTAMP_RE.match(lines[i])
43 if m:
44 lines[i] = m.group(1)
45 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000046
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000047
maruel@chromium.org389d6de2010-09-09 14:14:37 +000048class GCBaseTestCase(object):
maruel@chromium.org96913eb2010-06-01 16:22:47 +000049 def assertRaisesError(self, msg, fn, *args, **kwargs):
maruel@chromium.org389d6de2010-09-09 14:14:37 +000050 """Like unittest's assertRaises() but checks for Gclient.Error."""
maruel@chromium.org96913eb2010-06-01 16:22:47 +000051 try:
52 fn(*args, **kwargs)
53 except gclient_scm.gclient_utils.Error, e:
54 self.assertEquals(e.args[0], msg)
55 else:
56 self.fail('%s not raised' % msg)
msb@chromium.orge28e4982009-09-25 20:51:45 +000057
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000058
maruel@chromium.org389d6de2010-09-09 14:14:37 +000059class BaseTestCase(GCBaseTestCase, SuperMoxTestBase):
60 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +000061 SuperMoxTestBase.setUp(self)
maruel@chromium.org389d6de2010-09-09 14:14:37 +000062 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'CheckCallAndFilter')
63 self.mox.StubOutWithMock(gclient_scm.gclient_utils,
64 'CheckCallAndFilterAndHeader')
65 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead')
66 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
digit@chromium.orgdc112ac2013-04-24 13:00:19 +000067 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'rmtree')
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000068 self.mox.StubOutWithMock(subprocess2, 'communicate')
69 self.mox.StubOutWithMock(subprocess2, 'Popen')
John Budorick0f7b2002018-01-19 15:46:17 -080070 self._scm_wrapper = gclient_scm.GitWrapper
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +000071 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +000072 gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
maruel@chromium.org795a8c12010-10-05 19:54:29 +000073 # Absolute path of the fake checkout directory.
74 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.org389d6de2010-09-09 14:14:37 +000075
76 def tearDown(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +000077 SuperMoxTestBase.tearDown(self)
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +000078 gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
maruel@chromium.org389d6de2010-09-09 14:14:37 +000079
80
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000081class BasicTests(SuperMoxTestBase):
82 def setUp(self):
83 SuperMoxTestBase.setUp(self)
84
85 def testGetFirstRemoteUrl(self):
86 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
87 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
88 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
89 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
90 ('remote.origin.url git://what/"do', 'git://what/"do')]
91 FAKE_PATH = '/fake/path'
92 self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'Capture')
93 for question, _ in REMOTE_STRINGS:
94 gclient_scm.scm.GIT.Capture(
95 ['config', '--local', '--get-regexp', r'remote.*.url'],
96 cwd=FAKE_PATH).AndReturn(question)
97
98 self.mox.ReplayAll()
99
100 for _, answer in REMOTE_STRINGS:
101 self.assertEquals(gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH),
102 answer)
103
104 def tearDown(self):
105 SuperMoxTestBase.tearDown(self)
106
107
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000108class BaseGitWrapperTestCase(GCBaseTestCase, StdoutCheck, TestCaseUtils,
109 unittest.TestCase):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000110 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000111 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000112 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +0000113 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +0000114 self.verbose = verbose
115 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +0000116 self.deps_os = None
117 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +0000118 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +0000119 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000120 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +0000121 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +0000122 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +0000123 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +0000124 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000125 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000126 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -0400127 self.patch_ref = None
128 self.patch_repo = None
129 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +0000130 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000131
132 sample_git_import = """blob
133mark :1
134data 6
135Hello
136
137blob
138mark :2
139data 4
140Bye
141
142reset refs/heads/master
143commit refs/heads/master
144mark :3
145author Bob <bob@example.com> 1253744361 -0700
146committer Bob <bob@example.com> 1253744361 -0700
147data 8
148A and B
149M 100644 :1 a
150M 100644 :2 b
151
152blob
153mark :4
154data 10
155Hello
156You
157
158blob
159mark :5
160data 8
161Bye
162You
163
164commit refs/heads/origin
165mark :6
166author Alice <alice@example.com> 1253744424 -0700
167committer Alice <alice@example.com> 1253744424 -0700
168data 13
169Personalized
170from :3
171M 100644 :4 a
172M 100644 :5 b
173
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000174blob
175mark :7
176data 5
177Mooh
178
179commit refs/heads/feature
180mark :8
181author Bob <bob@example.com> 1390311986 -0000
182committer Bob <bob@example.com> 1390311986 -0000
183data 6
184Add C
185from :3
186M 100644 :7 c
187
msb@chromium.orge28e4982009-09-25 20:51:45 +0000188reset refs/heads/master
189from :3
190"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000191 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000192 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000193
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000194 def checkstdout(self, expected):
195 value = sys.stdout.getvalue()
196 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800197 # pylint: disable=no-member
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000198 self.assertEquals(expected, strip_timestamps(value))
199
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000200 @staticmethod
201 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000202 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000203 try:
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000204 Popen(['git', 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000205 cwd=path).communicate()
206 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000207 # git is not available, skip this test.
208 return False
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000209 Popen(['git', 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
210 stderr=STDOUT, cwd=path).communicate(input=git_import)
211 Popen(['git', 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
212 cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000213 Popen(['git', 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000214 stderr=STDOUT, cwd=path).communicate()
215 Popen(['git', 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
216 stderr=STDOUT, cwd=path).communicate()
John Budorick882c91e2018-07-12 22:11:41 +0000217 Popen(['git', 'push', 'origin', 'origin/origin:origin/master', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000218 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000219 Popen(['git', 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000220 stderr=STDOUT, cwd=path).communicate()
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000221 Popen(['git', 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
222 stderr=STDOUT, cwd=path).communicate()
223 Popen(['git', 'config', 'user.name', 'Some User'], stdout=PIPE,
224 stderr=STDOUT, cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000225 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000226
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000227 def _GetAskForDataCallback(self, expected_prompt, return_value):
228 def AskForData(prompt, options):
229 self.assertEquals(prompt, expected_prompt)
230 return return_value
231 return AskForData
232
msb@chromium.orge28e4982009-09-25 20:51:45 +0000233 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000234 TestCaseUtils.setUp(self)
235 unittest.TestCase.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000236 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000237 # The .git suffix allows gclient_scm to recognize the dir as a git repo
238 # when cloning it locally
239 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000240 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000241 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000242 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
maruel@chromium.orgdd057f32010-09-20 19:33:31 +0000243 StdoutCheck.setUp(self)
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +0000244 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +0000245 gclient_scm.GitWrapper.BinaryExists = staticmethod(lambda : True)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000246
247 def tearDown(self):
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000248 try:
249 rmtree(self.root_dir)
250 StdoutCheck.tearDown(self)
251 TestCaseUtils.tearDown(self)
252 unittest.TestCase.tearDown(self)
253 finally:
254 # TODO(maruel): Use auto_stub.TestCase.
255 gclient_scm.GitWrapper.BinaryExists = self._original_GitBinaryExists
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000256
msb@chromium.orge28e4982009-09-25 20:51:45 +0000257
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000258class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000259
260 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000261 if not self.enabled:
262 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000263 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000264 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800265 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
266 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000267 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000268 scm.update(options, None, file_list)
269 gclient_scm.os.remove(file_path)
270 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000271 scm.revert(options, self.args, file_list)
272 self.assertEquals(file_list, [file_path])
273 file_list = []
274 scm.diff(options, self.args, file_list)
275 self.assertEquals(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000276 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000277
278 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000279 if not self.enabled:
280 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000281 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800282 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
283 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000284 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000285 scm.update(options, None, file_list)
286 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000287 scm.revert(options, self.args, file_list)
288 self.assertEquals(file_list, [])
msb@chromium.org0f282062009-11-06 20:14:02 +0000289 self.assertEquals(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000290 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000291 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000292
293 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000294 if not self.enabled:
295 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000296 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800297 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
298 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000299 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000300 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000301 file_path = join(self.base_path, 'a')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000302 open(file_path, 'a').writelines('touched\n')
303 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000304 scm.revert(options, self.args, file_list)
305 self.assertEquals(file_list, [file_path])
306 file_list = []
307 scm.diff(options, self.args, file_list)
308 self.assertEquals(file_list, [])
msb@chromium.org0f282062009-11-06 20:14:02 +0000309 self.assertEquals(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000310 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000311 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000312
313 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000314 if not self.enabled:
315 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000316 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800317 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
318 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000319 file_list = []
320 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000321 file_path = join(self.base_path, 'c')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000322 f = open(file_path, 'w')
323 f.writelines('new\n')
324 f.close()
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000325 Popen(['git', 'add', 'c'], stdout=PIPE,
326 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000327 file_list = []
328 scm.revert(options, self.args, file_list)
329 self.assertEquals(file_list, [file_path])
330 file_list = []
331 scm.diff(options, self.args, file_list)
332 self.assertEquals(file_list, [])
msb@chromium.org0f282062009-11-06 20:14:02 +0000333 self.assertEquals(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000334 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000335 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000336
337 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000338 if not self.enabled:
339 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000340 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000341 file_path = join(self.base_path, 'a')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000342 open(file_path, 'a').writelines('touched\n')
John Budorick0f7b2002018-01-19 15:46:17 -0800343 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
344 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000345 file_list = []
346 scm.status(options, self.args, file_list)
347 self.assertEquals(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000348 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800349 ('\n________ running \'git -c core.quotePath=false diff --name-status '
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000350 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000351 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000352
353 def testStatus2New(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000354 if not self.enabled:
355 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000356 options = self.Options()
357 expected_file_list = []
358 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000359 file_path = join(self.base_path, f)
360 open(file_path, 'a').writelines('touched\n')
361 expected_file_list.extend([file_path])
John Budorick0f7b2002018-01-19 15:46:17 -0800362 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
363 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000364 file_list = []
365 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000366 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
msb@chromium.orge28e4982009-09-25 20:51:45 +0000367 self.assertEquals(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000368 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800369 ('\n________ running \'git -c core.quotePath=false diff --name-status '
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000370 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\nM\ta\nM\tb\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000371 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000372
msb@chromium.orge28e4982009-09-25 20:51:45 +0000373 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000374 if not self.enabled:
375 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000376 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000377 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800378 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
379 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000380 file_list = []
381 scm.update(options, (), file_list)
msb@chromium.org0f282062009-11-06 20:14:02 +0000382 self.assertEquals(file_list, expected_file_list)
383 self.assertEquals(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000384 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000385 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000386
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000387 def testUpdateMerge(self):
388 if not self.enabled:
389 return
390 options = self.Options()
391 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800392 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
393 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000394 scm._Run(['checkout', '-q', 'feature'], options)
395 rev = scm.revinfo(options, (), None)
396 file_list = []
397 scm.update(options, (), file_list)
398 self.assertEquals(file_list, [join(self.base_path, x)
John Budorick882c91e2018-07-12 22:11:41 +0000399 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000400 # The actual commit that is created is unstable, so we verify its tree and
401 # parents instead.
402 self.assertEquals(scm._Capture(['rev-parse', 'HEAD:']),
John Budorick882c91e2018-07-12 22:11:41 +0000403 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000404 self.assertEquals(scm._Capture(['rev-parse', 'HEAD^1']), rev)
405 self.assertEquals(scm._Capture(['rev-parse', 'HEAD^2']),
406 scm._Capture(['rev-parse', 'origin/master']))
407 sys.stdout.close()
408
409 def testUpdateRebase(self):
410 if not self.enabled:
411 return
412 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800413 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
414 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000415 scm._Run(['checkout', '-q', 'feature'], options)
416 file_list = []
417 # Fake a 'y' key press.
418 scm._AskForData = self._GetAskForDataCallback(
419 'Cannot fast-forward merge, attempt to rebase? '
420 '(y)es / (q)uit / (s)kip : ', 'y')
421 scm.update(options, (), file_list)
422 self.assertEquals(file_list, [join(self.base_path, x)
John Budorick882c91e2018-07-12 22:11:41 +0000423 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000424 # The actual commit that is created is unstable, so we verify its tree and
425 # parent instead.
426 self.assertEquals(scm._Capture(['rev-parse', 'HEAD:']),
John Budorick882c91e2018-07-12 22:11:41 +0000427 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
428 self.assertEquals(scm._Capture(['rev-parse', 'HEAD^']),
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000429 scm._Capture(['rev-parse', 'origin/master']))
430 sys.stdout.close()
431
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000432 def testUpdateReset(self):
433 if not self.enabled:
434 return
435 options = self.Options()
436 options.reset = True
437
438 dir_path = join(self.base_path, 'c')
439 os.mkdir(dir_path)
440 open(join(dir_path, 'nested'), 'w').writelines('new\n')
441
442 file_path = join(self.base_path, 'file')
443 open(file_path, 'w').writelines('new\n')
444
John Budorick0f7b2002018-01-19 15:46:17 -0800445 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
446 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000447 file_list = []
448 scm.update(options, (), file_list)
449 self.assert_(gclient_scm.os.path.isdir(dir_path))
450 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000451 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000452
Edward Lemur579c9862018-07-13 23:17:51 +0000453 def testUpdateResetUnsetsFetchConfig(self):
454 if not self.enabled:
455 return
456 options = self.Options()
457 options.reset = True
458
459 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
460 self.relpath)
461 scm._Run(['config', 'remote.origin.fetch',
462 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
463
464 file_list = []
465 scm.update(options, (), file_list)
466 self.assertEquals(scm.revinfo(options, (), None),
467 '069c602044c5388d2d15c3f875b057c852003458')
468 sys.stdout.close()
469
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000470 def testUpdateResetDeleteUnversionedTrees(self):
471 if not self.enabled:
472 return
473 options = self.Options()
474 options.reset = True
475 options.delete_unversioned_trees = True
476
477 dir_path = join(self.base_path, 'dir')
478 os.mkdir(dir_path)
479 open(join(dir_path, 'nested'), 'w').writelines('new\n')
480
481 file_path = join(self.base_path, 'file')
482 open(file_path, 'w').writelines('new\n')
483
John Budorick0f7b2002018-01-19 15:46:17 -0800484 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
485 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000486 file_list = []
487 scm.update(options, (), file_list)
488 self.assert_(not gclient_scm.os.path.isdir(dir_path))
489 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000490 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000491
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000492 def testUpdateUnstagedConflict(self):
493 if not self.enabled:
494 return
495 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800496 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
497 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000498 file_path = join(self.base_path, 'b')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000499 open(file_path, 'w').writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000500 try:
501 scm.update(options, (), [])
502 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000503 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000504 # The exact exception text varies across git versions so it's not worth
505 # verifying it. It's fine as long as it throws.
506 pass
507 # Manually flush stdout since we can't verify it's content accurately across
508 # git versions.
509 sys.stdout.getvalue()
510 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000511
Mike Stipicevice992b612016-12-02 15:32:55 -0800512 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000513 def testUpdateLocked(self):
514 if not self.enabled:
515 return
516 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800517 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
518 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000519 file_path = join(self.base_path, '.git', 'index.lock')
520 with open(file_path, 'w'):
521 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800522 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000523 scm.update(options, (), [])
524 sys.stdout.close()
525
526 def testUpdateLockedBreak(self):
527 if not self.enabled:
528 return
529 options = self.Options()
530 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800531 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
532 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000533 file_path = join(self.base_path, '.git', 'index.lock')
534 with open(file_path, 'w'):
535 pass
536 scm.update(options, (), [])
537 self.assertRegexpMatches(sys.stdout.getvalue(),
538 "breaking lock.*\.git/index\.lock")
539 self.assertFalse(os.path.exists(file_path))
540 sys.stdout.close()
541
msb@chromium.org5bde4852009-12-14 16:47:12 +0000542 def testUpdateConflict(self):
543 if not self.enabled:
544 return
545 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800546 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
547 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000548 file_path = join(self.base_path, 'b')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000549 open(file_path, 'w').writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000550 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000551 scm._AskForData = self._GetAskForDataCallback(
552 'Cannot fast-forward merge, attempt to rebase? '
553 '(y)es / (q)uit / (s)kip : ', 'y')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000554 exception = ('Conflict while rebasing this branch.\n'
555 'Fix the conflict and run gclient again.\n'
556 'See \'man git-rebase\' for details.\n')
msb@chromium.org5bde4852009-12-14 16:47:12 +0000557 self.assertRaisesError(exception, scm.update, options, (), [])
John Budorick882c91e2018-07-12 22:11:41 +0000558 exception = ('\n____ . at refs/remotes/origin/master\n'
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000559 '\tYou have unstaged changes.\n'
560 '\tPlease commit, stash, or reset.\n')
msb@chromium.org5bde4852009-12-14 16:47:12 +0000561 self.assertRaisesError(exception, scm.update, options, (), [])
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000562 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000563
msb@chromium.org0f282062009-11-06 20:14:02 +0000564 def testRevinfo(self):
565 if not self.enabled:
566 return
567 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800568 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
569 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000570 rev_info = scm.revinfo(options, (), None)
571 self.assertEquals(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
572
msb@chromium.orge28e4982009-09-25 20:51:45 +0000573
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000574class ManagedGitWrapperTestCaseMox(BaseTestCase):
575 class OptionsObject(object):
576 def __init__(self, verbose=False, revision=None, force=False):
577 self.verbose = verbose
578 self.revision = revision
579 self.deps_os = None
580 self.force = force
581 self.reset = False
582 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000583 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000584 # TODO(maruel): Test --jobs > 1.
585 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400586 self.patch_ref = None
587 self.patch_repo = None
588 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000589
590 def Options(self, *args, **kwargs):
591 return self.OptionsObject(*args, **kwargs)
592
borenet@google.comb09097a2014-04-09 19:09:08 +0000593 def checkstdout(self, expected):
594 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000595 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800596 # pylint: disable=no-member
borenet@google.comb09097a2014-04-09 19:09:08 +0000597 self.assertEquals(expected, strip_timestamps(value))
598
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000599 def setUp(self):
600 BaseTestCase.setUp(self)
601 self.fake_hash_1 = 't0ta11yf4k3'
602 self.fake_hash_2 = '3v3nf4k3r'
603 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000604 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000605 self.relpath = 'fake'
606 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000607 self.backup_base_path = os.path.join(self.root_dir,
608 'old_%s.git' % self.relpath)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000609
610 def tearDown(self):
611 BaseTestCase.tearDown(self)
612
smutae7ea312016-07-18 11:59:41 -0700613 def testGetUsableRevGit(self):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800614 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700615 options = self.Options(verbose=True)
616
617 self.mox.StubOutWithMock(gclient_scm.scm.GIT, 'IsValidRevision', True)
618 gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev=self.fake_hash_1
619 ).AndReturn(True)
agable41e3a6c2016-10-20 11:36:56 -0700620 gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
smutae7ea312016-07-18 11:59:41 -0700621 ).AndReturn(False)
agable41e3a6c2016-10-20 11:36:56 -0700622 gclient_scm.scm.GIT.IsValidRevision(cwd=self.base_path, rev='1'
623 ).AndReturn(False)
624
625 self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_Fetch', True)
626 # pylint: disable=no-value-for-parameter
627 gclient_scm.GitWrapper._Fetch(options).AndReturn(None)
smutae7ea312016-07-18 11:59:41 -0700628
629 gclient_scm.scm.os.path.isdir(self.base_path).AndReturn(True)
630 gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
631
632 self.mox.ReplayAll()
633
John Budorick0f7b2002018-01-19 15:46:17 -0800634 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
635 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700636 # A [fake] git sha1 with a git repo should work (this is in the case that
637 # the LKGR gets flipped to git sha1's some day).
638 self.assertEquals(git_scm.GetUsableRev(self.fake_hash_1, options),
639 self.fake_hash_1)
640 # An SVN rev with an existing purely git repo should raise an exception.
641 self.assertRaises(gclient_scm.gclient_utils.Error,
642 git_scm.GetUsableRev, '1', options)
643
borenet@google.comb09097a2014-04-09 19:09:08 +0000644 def testUpdateNoDotGit(self):
645 options = self.Options()
646
szager@chromium.org8a139702014-06-20 15:55:01 +0000647 gclient_scm.os.path.isdir(
648 os.path.join(self.base_path, '.git', 'hooks')).AndReturn(False)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000649 gclient_scm.os.path.exists(self.backup_base_path).AndReturn(False)
borenet@google.comb09097a2014-04-09 19:09:08 +0000650 gclient_scm.os.path.exists(self.base_path).AndReturn(True)
651 gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
652 gclient_scm.os.path.exists(os.path.join(self.base_path, '.git')
653 ).AndReturn(False)
borenet@google.comb09097a2014-04-09 19:09:08 +0000654 self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_Clone', True)
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800655 # pylint: disable=no-value-for-parameter
John Budorick882c91e2018-07-12 22:11:41 +0000656 gclient_scm.GitWrapper._Clone('refs/remotes/origin/master', self.url,
657 options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000658 self.mox.StubOutWithMock(gclient_scm.subprocess2, 'check_output', True)
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000659 gclient_scm.subprocess2.check_output(
Aaron Gable7817f022017-12-12 09:43:17 -0800660 ['git', '-c', 'core.quotePath=false', 'ls-files'], cwd=self.base_path,
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000661 env=gclient_scm.scm.GIT.ApplyEnvVars({}), stderr=-1,).AndReturn('')
borenet@google.comb09097a2014-04-09 19:09:08 +0000662 gclient_scm.subprocess2.check_output(
663 ['git', 'rev-parse', '--verify', 'HEAD'],
664 cwd=self.base_path,
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000665 env=gclient_scm.scm.GIT.ApplyEnvVars({}),
borenet@google.comb09097a2014-04-09 19:09:08 +0000666 stderr=-1,
667 ).AndReturn('')
668
669 self.mox.ReplayAll()
John Budorick0f7b2002018-01-19 15:46:17 -0800670 scm = self._scm_wrapper(self.url, self.root_dir,
671 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000672 scm.update(options, None, [])
673 self.checkstdout('\n')
674
borenet@google.com90fe58b2014-05-01 18:22:00 +0000675 def testUpdateConflict(self):
676 options = self.Options()
borenet@google.comb09097a2014-04-09 19:09:08 +0000677
szager@chromium.org8a139702014-06-20 15:55:01 +0000678 gclient_scm.os.path.isdir(
679 os.path.join(self.base_path, '.git', 'hooks')).AndReturn(False)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000680 gclient_scm.os.path.exists(self.backup_base_path).AndReturn(False)
borenet@google.comb09097a2014-04-09 19:09:08 +0000681 gclient_scm.os.path.exists(self.base_path).AndReturn(True)
682 gclient_scm.os.path.isdir(self.base_path).AndReturn(True)
683 gclient_scm.os.path.exists(os.path.join(self.base_path, '.git')
684 ).AndReturn(False)
borenet@google.comb09097a2014-04-09 19:09:08 +0000685 self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_Clone', True)
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800686 # pylint: disable=no-value-for-parameter
borenet@google.com90fe58b2014-05-01 18:22:00 +0000687 gclient_scm.GitWrapper._Clone(
John Budorick882c91e2018-07-12 22:11:41 +0000688 'refs/remotes/origin/master', self.url, options
borenet@google.com90fe58b2014-05-01 18:22:00 +0000689 ).AndRaise(gclient_scm.subprocess2.CalledProcessError(None, None, None,
690 None, None))
691 self.mox.StubOutWithMock(gclient_scm.GitWrapper, '_DeleteOrMove', True)
692 gclient_scm.GitWrapper._DeleteOrMove(False)
John Budorick882c91e2018-07-12 22:11:41 +0000693 gclient_scm.GitWrapper._Clone('refs/remotes/origin/master', self.url,
694 options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000695 self.mox.StubOutWithMock(gclient_scm.subprocess2, 'check_output', True)
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000696 gclient_scm.subprocess2.check_output(
Aaron Gable7817f022017-12-12 09:43:17 -0800697 ['git', '-c', 'core.quotePath=false', 'ls-files'], cwd=self.base_path,
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000698 env=gclient_scm.scm.GIT.ApplyEnvVars({}), stderr=-1,).AndReturn('')
borenet@google.comb09097a2014-04-09 19:09:08 +0000699 gclient_scm.subprocess2.check_output(
700 ['git', 'rev-parse', '--verify', 'HEAD'],
701 cwd=self.base_path,
szager@chromium.org6d8115d2014-04-23 20:59:23 +0000702 env=gclient_scm.scm.GIT.ApplyEnvVars({}),
borenet@google.comb09097a2014-04-09 19:09:08 +0000703 stderr=-1,
704 ).AndReturn('')
705
706 self.mox.ReplayAll()
John Budorick0f7b2002018-01-19 15:46:17 -0800707 scm = self._scm_wrapper(self.url, self.root_dir,
708 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000709 scm.update(options, None, [])
710 self.checkstdout('\n')
711
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000712
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000713class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000714 def checkInStdout(self, expected):
715 value = sys.stdout.getvalue()
716 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800717 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000718 self.assertIn(expected, value)
719
720 def checkNotInStdout(self, expected):
721 value = sys.stdout.getvalue()
722 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800723 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000724 self.assertNotIn(expected, value)
725
smut@google.com27c9c8a2014-09-11 19:57:55 +0000726 def getCurrentBranch(self):
727 # Returns name of current branch or HEAD for detached HEAD
728 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
729 cwd=self.base_path)
730 if branch == 'HEAD':
731 return None
732 return branch
733
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000734 def testUpdateClone(self):
735 if not self.enabled:
736 return
737 options = self.Options()
738
739 origin_root_dir = self.root_dir
740 self.root_dir = tempfile.mkdtemp()
741 self.relpath = '.'
742 self.base_path = join(self.root_dir, self.relpath)
743
John Budorick0f7b2002018-01-19 15:46:17 -0800744 scm = gclient_scm.GitWrapper(origin_root_dir,
745 self.root_dir,
746 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000747
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000748 expected_file_list = [join(self.base_path, "a"),
749 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000750 file_list = []
751 options.revision = 'unmanaged'
752 scm.update(options, (), file_list)
753
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000754 self.assertEquals(file_list, expected_file_list)
755 self.assertEquals(scm.revinfo(options, (), None),
John Budorick882c91e2018-07-12 22:11:41 +0000756 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000757 # indicates detached HEAD
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000758 self.assertEquals(self.getCurrentBranch(), None)
759 self.checkInStdout(
760 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000761
762 rmtree(origin_root_dir)
763
764 def testUpdateCloneOnCommit(self):
765 if not self.enabled:
766 return
767 options = self.Options()
768
769 origin_root_dir = self.root_dir
770 self.root_dir = tempfile.mkdtemp()
771 self.relpath = '.'
772 self.base_path = join(self.root_dir, self.relpath)
773 url_with_commit_ref = origin_root_dir +\
774 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
775
John Budorick0f7b2002018-01-19 15:46:17 -0800776 scm = gclient_scm.GitWrapper(url_with_commit_ref,
777 self.root_dir,
778 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000779
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000780 expected_file_list = [join(self.base_path, "a"),
781 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000782 file_list = []
783 options.revision = 'unmanaged'
784 scm.update(options, (), file_list)
785
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000786 self.assertEquals(file_list, expected_file_list)
787 self.assertEquals(scm.revinfo(options, (), None),
788 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000789 # indicates detached HEAD
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000790 self.assertEquals(self.getCurrentBranch(), None)
791 self.checkInStdout(
792 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000793
794 rmtree(origin_root_dir)
795
796 def testUpdateCloneOnBranch(self):
797 if not self.enabled:
798 return
799 options = self.Options()
800
801 origin_root_dir = self.root_dir
802 self.root_dir = tempfile.mkdtemp()
803 self.relpath = '.'
804 self.base_path = join(self.root_dir, self.relpath)
805 url_with_branch_ref = origin_root_dir + '@feature'
806
John Budorick0f7b2002018-01-19 15:46:17 -0800807 scm = gclient_scm.GitWrapper(url_with_branch_ref,
808 self.root_dir,
809 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000810
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000811 expected_file_list = [join(self.base_path, "a"),
812 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000813 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000814 file_list = []
815 options.revision = 'unmanaged'
816 scm.update(options, (), file_list)
817
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000818 self.assertEquals(file_list, expected_file_list)
819 self.assertEquals(scm.revinfo(options, (), None),
John Budorick882c91e2018-07-12 22:11:41 +0000820 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200821 # indicates detached HEAD
822 self.assertEquals(self.getCurrentBranch(), None)
823 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000824 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200825 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000826
827 rmtree(origin_root_dir)
828
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000829 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000830 if not self.enabled:
831 return
832 options = self.Options()
833
834 origin_root_dir = self.root_dir
835 self.root_dir = tempfile.mkdtemp()
836 self.relpath = '.'
837 self.base_path = join(self.root_dir, self.relpath)
838 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
839
John Budorick0f7b2002018-01-19 15:46:17 -0800840 scm = gclient_scm.GitWrapper(url_with_branch_ref,
841 self.root_dir,
842 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000843
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000844 expected_file_list = [join(self.base_path, "a"),
845 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000846 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000847 file_list = []
848 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000849 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000850
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000851 self.assertEquals(file_list, expected_file_list)
852 self.assertEquals(scm.revinfo(options, (), None),
John Budorick882c91e2018-07-12 22:11:41 +0000853 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000854 # indicates detached HEAD
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000855 self.assertEquals(self.getCurrentBranch(), None)
856 self.checkInStdout(
857 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000858
859 rmtree(origin_root_dir)
860
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000861 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000862 if not self.enabled:
863 return
864 options = self.Options()
865
866 origin_root_dir = self.root_dir
867 self.root_dir = tempfile.mkdtemp()
868 self.relpath = '.'
869 self.base_path = join(self.root_dir, self.relpath)
870 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
871
John Budorick0f7b2002018-01-19 15:46:17 -0800872 scm = gclient_scm.GitWrapper(url_with_branch_ref,
873 self.root_dir,
874 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000875
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000876 expected_file_list = [join(self.base_path, "a"),
877 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000878 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000879 file_list = []
880 options.revision = 'unmanaged'
881 scm.update(options, (), file_list)
882
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000883 self.assertEquals(file_list, expected_file_list)
884 self.assertEquals(scm.revinfo(options, (), None),
John Budorick882c91e2018-07-12 22:11:41 +0000885 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
886 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000887 # should be treated as such by gclient.
888 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
889 # as they are known in the upstream repo, since the mapping into the local
890 # repo can be modified by users (or we might even want to change the gclient
891 # defaults at some point). But that will take more work to stop using
892 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
893 # always resolve locally, like when passing them to show-ref or rev-list).
894 self.assertEquals(self.getCurrentBranch(), None)
895 self.checkInStdout(
896 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000897
898 rmtree(origin_root_dir)
899
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000900 def testUpdateUpdate(self):
901 if not self.enabled:
902 return
903 options = self.Options()
904 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800905 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
906 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000907 file_list = []
908 options.revision = 'unmanaged'
909 scm.update(options, (), file_list)
910 self.assertEquals(file_list, expected_file_list)
911 self.assertEquals(scm.revinfo(options, (), None),
912 '069c602044c5388d2d15c3f875b057c852003458')
913 self.checkstdout('________ unmanaged solution; skipping .\n')
914
915
John Budorick0f7b2002018-01-19 15:46:17 -0800916class CipdWrapperTestCase(BaseTestCase):
917
918 def setUp(self):
919 # Create this before setting up mocks.
920 self._cipd_root_dir = tempfile.mkdtemp()
921 self._workdir = tempfile.mkdtemp()
922 BaseTestCase.setUp(self)
923
924 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
925 self._cipd_root = gclient_scm.CipdRoot(
926 self._cipd_root_dir,
927 self._cipd_instance_url)
928 self._cipd_packages = [
929 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
930 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
931 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
932 ]
John Budorickfb5286f2018-01-26 11:49:07 -0800933 self.mox.StubOutWithMock(self._cipd_root, 'add_package')
934 self.mox.StubOutWithMock(self._cipd_root, 'clobber')
935 self.mox.StubOutWithMock(self._cipd_root, 'ensure')
John Budorick0f7b2002018-01-19 15:46:17 -0800936
937 def tearDown(self):
938 BaseTestCase.tearDown(self)
939 rmtree(self._cipd_root_dir)
940 rmtree(self._workdir)
941
942 def createScmWithPackageThatSatisfies(self, condition):
943 return gclient_scm.CipdWrapper(
944 url=self._cipd_instance_url,
945 root_dir=self._cipd_root_dir,
946 relpath='fake_relpath',
947 root=self._cipd_root,
948 package=self.getPackageThatSatisfies(condition))
949
950 def getPackageThatSatisfies(self, condition):
951 for p in self._cipd_packages:
952 if condition(p):
953 return p
954
955 self.fail('Unable to find a satisfactory package.')
956
John Budorick0f7b2002018-01-19 15:46:17 -0800957 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700958 """Checks that revert does nothing."""
John Budorick0f7b2002018-01-19 15:46:17 -0800959 scm = self.createScmWithPackageThatSatisfies(
John Budorickd3ba72b2018-03-20 12:27:42 -0700960 lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800961
962 self.mox.ReplayAll()
963
964 scm.revert(None, (), [])
965
966 def testRevinfo(self):
967 """Checks that revinfo uses the JSON from cipd describe."""
968 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
969
970 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
971 json_contents = {
972 'result': {
973 'pin': {
974 'instance_id': expected_revinfo,
975 }
976 }
977 }
978 describe_json_path = join(self._workdir, 'describe.json')
979 with open(describe_json_path, 'w') as describe_json:
980 json.dump(json_contents, describe_json)
981
982 cmd = [
983 'cipd', 'describe', 'foo_package',
984 '-log-level', 'error',
985 '-version', 'foo_version',
986 '-json-output', describe_json_path,
987 ]
988
989 self.mox.StubOutWithMock(tempfile, 'mkdtemp')
990
991 tempfile.mkdtemp().AndReturn(self._workdir)
992 gclient_scm.gclient_utils.CheckCallAndFilter(
993 cmd, filter_fn=mox.IgnoreArg(), print_stdout=False)
994 gclient_scm.gclient_utils.rmtree(self._workdir)
995
996 self.mox.ReplayAll()
997
998 revinfo = scm.revinfo(None, (), [])
999 self.assertEquals(revinfo, expected_revinfo)
1000
1001 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -07001002 """Checks that update does nothing."""
John Budorick0f7b2002018-01-19 15:46:17 -08001003 scm = self.createScmWithPackageThatSatisfies(
John Budorickd3ba72b2018-03-20 12:27:42 -07001004 lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -08001005
1006 self.mox.ReplayAll()
1007
1008 scm.update(None, (), [])
1009
1010
Edward Lemurd64781e2018-07-11 23:09:55 +00001011class GerritChangesFakeRepo(fake_repos.FakeReposBase):
1012 def populateGit(self):
1013 # Creates a tree that looks like this:
1014 #
Edward Lemurca7d8812018-07-24 17:42:45 +00001015 # 6 refs/changes/35/1235/1
1016 # |
1017 # 5 refs/changes/34/1234/1
1018 # |
Edward Lemurd64781e2018-07-11 23:09:55 +00001019 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +00001020 # | |
1021 # | 11(5)--12 refs/heads/master-with-5
1022 # |
1023 # 7--8--9 refs/heads/feature
1024 # |
1025 # 10 refs/changes/36/1236/1
1026 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001027
1028 self._commit_git('repo_1', {'commit 1': 'touched'})
1029 self._commit_git('repo_1', {'commit 2': 'touched'})
1030 self._commit_git('repo_1', {'commit 3': 'touched'})
1031 self._commit_git('repo_1', {'commit 4': 'touched'})
1032 self._create_ref('repo_1', 'refs/heads/master', 4)
1033
1034 # Create a change on top of commit 3 that consists of two commits.
1035 self._commit_git('repo_1',
1036 {'commit 5': 'touched',
1037 'change': '1234'},
1038 base=3)
1039 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1040 self._commit_git('repo_1',
1041 {'commit 6': 'touched',
1042 'change': '1235'})
1043 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1044
Edward Lemurca7d8812018-07-24 17:42:45 +00001045 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1046 # commits.
1047 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1048 self._commit_git('repo_1', {'commit 8': 'touched'})
1049 self._commit_git('repo_1', {'commit 9': 'touched'})
1050 self._create_ref('repo_1', 'refs/heads/feature', 9)
1051
1052 # Create a change of top of commit 8.
1053 self._commit_git('repo_1',
1054 {'commit 10': 'touched',
1055 'change': '1236'},
1056 base=8)
1057 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1058
1059 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1060 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1061 self._commit_git('repo_1',
1062 # This is really commit 11, but has the changes of commit 5
1063 {'commit 5': 'touched',
1064 'change': '1234'},
1065 base=3)
1066 self._commit_git('repo_1', {'commit 12': 'touched'})
1067 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1068
Edward Lemurd64781e2018-07-11 23:09:55 +00001069
1070class GerritChangesTest(fake_repos.FakeReposTestBase):
1071 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1072
1073 def setUp(self):
1074 super(GerritChangesTest, self).setUp()
1075 self.enabled = self.FAKE_REPOS.set_up_git()
1076 self.options = BaseGitWrapperTestCase.OptionsObject()
1077 self.url = self.git_base + 'repo_1'
1078 self.mirror = None
1079
1080 def setUpMirror(self):
1081 self.mirror = tempfile.mkdtemp()
1082 git_cache.Mirror.SetCachePath(self.mirror)
1083 self.addCleanup(rmtree, self.mirror)
1084 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1085
Edward Lemurca7d8812018-07-24 17:42:45 +00001086 def assertCommits(self, commits):
1087 """Check that all, and only |commits| are present in the current checkout.
1088 """
1089 for i in commits:
1090 name = os.path.join(self.root_dir, 'commit ' + str(i))
1091 self.assertTrue(os.path.exists(name))
1092
1093 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1094 for i in all_commits - set(commits):
1095 name = os.path.join(self.root_dir, 'commit ' + str(i))
1096 self.assertFalse(os.path.exists(name))
1097
Edward Lemurd64781e2018-07-11 23:09:55 +00001098 def testCanCloneGerritChange(self):
1099 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1100 file_list = []
1101
1102 self.options.revision = 'refs/changes/35/1235/1'
1103 scm.update(self.options, None, file_list)
1104 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1105
1106 def testCanSyncToGerritChange(self):
1107 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1108 file_list = []
1109
1110 self.options.revision = self.githash('repo_1', 1)
1111 scm.update(self.options, None, file_list)
1112 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1113
1114 self.options.revision = 'refs/changes/35/1235/1'
1115 scm.update(self.options, None, file_list)
1116 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1117
1118 def testCanCloneGerritChangeMirror(self):
1119 self.setUpMirror()
1120 self.testCanCloneGerritChange()
1121
1122 def testCanSyncToGerritChangeMirror(self):
1123 self.setUpMirror()
1124 self.testCanSyncToGerritChange()
1125
Edward Lemurca7d8812018-07-24 17:42:45 +00001126 def testAppliesPatchOnTopOfMasterByDefault(self):
1127 """Test the default case, where we apply a patch on top of master."""
1128 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1129 file_list = []
1130
1131 # Make sure we don't specify a revision.
1132 self.options.revision = None
1133 scm.update(self.options, None, file_list)
1134 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1135
1136 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1137 file_list)
1138
1139 self.assertCommits([1, 2, 3, 4, 5, 6])
1140 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1141
1142 def testCheckoutOlderThanPatchBase(self):
1143 """Test applying a patch on an old checkout.
1144
1145 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1146 contains commits 5 and 6, and is based on top of commit 3.
1147 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1148 """
1149 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1150 file_list = []
1151
1152 # Sync to commit 1
1153 self.options.revision = self.githash('repo_1', 1)
1154 scm.update(self.options, None, file_list)
1155 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1156
1157 # Apply the change on top of that.
1158 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1159 file_list)
1160
1161 self.assertCommits([1, 5, 6])
1162 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1163
1164 def testCheckoutOriginFeature(self):
1165 """Tests that we can apply a patch on a branch other than master."""
1166 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1167 file_list = []
1168
1169 # Sync to origin/feature
1170 self.options.revision = 'origin/feature'
1171 scm.update(self.options, None, file_list)
1172 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1173
1174 # Apply the change on top of that.
1175 scm.apply_patch_ref(self.url, 'refs/changes/36/1236/1', self.options,
1176 file_list)
1177
1178 self.assertCommits([1, 2, 7, 8, 9, 10])
1179 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1180
1181 def testCheckoutOriginFeatureOnOldRevision(self):
1182 """Tests that we can apply a patch on an old checkout, on a branch other
1183 than master."""
1184 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1185 file_list = []
1186
1187 # Sync to origin/feature on an old revision
1188 self.options.revision = self.githash('repo_1', 7)
1189 scm.update(self.options, None, file_list)
1190 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1191
1192 # Apply the change on top of that.
1193 scm.apply_patch_ref(self.url, 'refs/changes/36/1236/1', self.options,
1194 file_list)
1195
1196 # We shouldn't have rebased on top of 2 (which is the merge base between
1197 # origin/master and the change) but on top of 7 (which is the merge base
1198 # between origin/feature and the change).
1199 self.assertCommits([1, 2, 7, 10])
1200 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1201
1202
1203 def testDoesntRebasePatchMaster(self):
1204 """Tests that we can apply a patch without rebasing it.
1205 """
1206 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1207 file_list = []
1208
1209 self.options.rebase_patch_ref = False
1210 scm.update(self.options, None, file_list)
1211 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1212
1213 # Apply the change on top of that.
1214 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1215 file_list)
1216
1217 self.assertCommits([1, 2, 3, 5, 6])
1218 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1219
1220 def testDoesntRebasePatchOldCheckout(self):
1221 """Tests that we can apply a patch without rebasing it on an old checkout.
1222 """
1223 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1224 file_list = []
1225
1226 # Sync to commit 1
1227 self.options.revision = self.githash('repo_1', 1)
1228 self.options.rebase_patch_ref = False
1229 scm.update(self.options, None, file_list)
1230 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1231
1232 # Apply the change on top of that.
1233 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1234 file_list)
1235
1236 self.assertCommits([1, 2, 3, 5, 6])
1237 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1238
1239 def testDoesntSoftResetIfNotAskedTo(self):
1240 """Test that we can apply a patch without doing a soft reset."""
1241 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1242 file_list = []
1243
1244 self.options.reset_patch_ref = False
1245 scm.update(self.options, None, file_list)
1246 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1247
1248 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1249 file_list)
1250
1251 self.assertCommits([1, 2, 3, 4, 5, 6])
1252 # The commit hash after cherry-picking is not known, but it must be
1253 # different from what the repo was synced at before patching.
1254 self.assertNotEqual(self.githash('repo_1', 4),
1255 self.gitrevparse(self.root_dir))
1256
1257 def testRecoversAfterPatchFailure(self):
1258 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1259 file_list = []
1260
1261 self.options.revision = 'refs/changes/34/1234/1'
1262 scm.update(self.options, None, file_list)
1263 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1264
1265 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1266 # patch 'refs/changes/36/1236/1' creates a patch failure.
1267 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1268 scm.apply_patch_ref(self.url, 'refs/changes/36/1236/1', self.options,
1269 file_list)
1270 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1271 self.assertIn('error: could not apply', cm.exception.stderr)
1272
1273 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1274 # conflict.
1275 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1276 file_list)
1277 self.assertCommits([1, 2, 3, 5, 6])
1278 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1279
1280 def testIgnoresAlreadyMergedCommits(self):
1281 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1282 file_list = []
1283
1284 self.options.revision = 'refs/heads/master-with-5'
1285 scm.update(self.options, None, file_list)
1286 self.assertEqual(self.githash('repo_1', 12),
1287 self.gitrevparse(self.root_dir))
1288
1289 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1290 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1291 # already present in the tree as commit 11.
1292 # Make sure we deal with this gracefully.
1293 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1294 file_list)
1295 self.assertCommits([1, 2, 3, 5, 6, 12])
1296 self.assertEqual(self.githash('repo_1', 12),
1297 self.gitrevparse(self.root_dir))
1298
1299 def testRecoversFromExistingCherryPick(self):
1300 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1301 file_list = []
1302
1303 self.options.revision = 'refs/changes/34/1234/1'
1304 scm.update(self.options, None, file_list)
1305 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1306
1307 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1308 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1309 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1310 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1311 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1312 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1313
1314 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1315 # conflict.
1316 scm.apply_patch_ref(self.url, 'refs/changes/35/1235/1', self.options,
1317 file_list)
1318 self.assertCommits([1, 2, 3, 5, 6])
1319 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1320
Edward Lemurd64781e2018-07-11 23:09:55 +00001321
msb@chromium.orge28e4982009-09-25 20:51:45 +00001322if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001323 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1324 logging.basicConfig(
1325 level=level,
1326 format='%(asctime).19s %(levelname)s %(filename)s:'
1327 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001328 unittest.main()
1329
1330# vim: ts=2:sw=2:tw=80:et: