blob: 2081446e53bb2911ac0c2247c61cfe576797134a [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 +000010from shutil import rmtree
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000011from subprocess import Popen, PIPE, STDOUT
maruel@chromium.org428342a2011-11-10 15:46:33 +000012
John Budorick0f7b2002018-01-19 15:46:17 -080013import json
maruel@chromium.org428342a2011-11-10 15:46:33 +000014import logging
15import os
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000016import re
maruel@chromium.org428342a2011-11-10 15:46:33 +000017import sys
msb@chromium.orge28e4982009-09-25 20:51:45 +000018import tempfile
maruel@chromium.org389d6de2010-09-09 14:14:37 +000019import unittest
msb@chromium.orge28e4982009-09-25 20:51:45 +000020
Edward Lemur979fa782019-08-13 22:44:05 +000021if sys.version_info.major == 2:
22 from cStringIO import StringIO
23else:
24 from io import StringIO
25
maruel@chromium.org428342a2011-11-10 15:46:33 +000026sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000027
Edward Lemur979fa782019-08-13 22:44:05 +000028from third_party import mock
Edward Lemurd64781e2018-07-11 23:09:55 +000029from testing_support import fake_repos
Edward Lemur9cafbf42019-08-15 22:03:35 +000030from testing_support import test_case_utils
maruel@chromium.org428342a2011-11-10 15:46:33 +000031
msb@chromium.orge28e4982009-09-25 20:51:45 +000032import gclient_scm
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000033import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000034import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000035
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000036# Disable global git cache
37git_cache.Mirror.SetCachePath(None)
38
maruel@chromium.org795a8c12010-10-05 19:54:29 +000039# Shortcut since this function is used often
40join = gclient_scm.os.path.join
41
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000042TIMESTAMP_RE = re.compile('\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
43def strip_timestamps(value):
44 lines = value.splitlines(True)
Edward Lemur979fa782019-08-13 22:44:05 +000045 for i in range(len(lines)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000046 m = TIMESTAMP_RE.match(lines[i])
47 if m:
48 lines[i] = m.group(1)
49 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000050
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000051
Edward Lemur979fa782019-08-13 22:44:05 +000052class BasicTests(unittest.TestCase):
53 @mock.patch('gclient_scm.scm.GIT.Capture')
54 def testGetFirstRemoteUrl(self, mockCapture):
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000055 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
56 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
57 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
58 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
59 ('remote.origin.url git://what/"do', 'git://what/"do')]
60 FAKE_PATH = '/fake/path'
Edward Lemur979fa782019-08-13 22:44:05 +000061 mockCapture.side_effect = [question for question, _ in REMOTE_STRINGS]
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000062
63 for _, answer in REMOTE_STRINGS:
Edward Lemur979fa782019-08-13 22:44:05 +000064 self.assertEqual(
65 gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH), answer)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000066
Edward Lemur979fa782019-08-13 22:44:05 +000067 expected_calls = [
68 mock.call(['config', '--local', '--get-regexp', r'remote.*.url'],
69 cwd=FAKE_PATH)
70 for _ in REMOTE_STRINGS
71 ]
72 self.assertEqual(mockCapture.mock_calls, expected_calls)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000073
74
Edward Lemur9cafbf42019-08-15 22:03:35 +000075class BaseGitWrapperTestCase(unittest.TestCase, test_case_utils.TestCaseUtils):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000076 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +000077 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +000078 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +000079 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000080 self.verbose = verbose
81 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +000082 self.deps_os = None
83 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +000084 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000085 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +000086 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +000087 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +000088 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +000089 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +000090 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +000091 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +000092 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -040093 self.patch_ref = None
94 self.patch_repo = None
95 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +000096 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +000097
98 sample_git_import = """blob
99mark :1
100data 6
101Hello
102
103blob
104mark :2
105data 4
106Bye
107
108reset refs/heads/master
109commit refs/heads/master
110mark :3
111author Bob <bob@example.com> 1253744361 -0700
112committer Bob <bob@example.com> 1253744361 -0700
113data 8
114A and B
115M 100644 :1 a
116M 100644 :2 b
117
118blob
119mark :4
120data 10
121Hello
122You
123
124blob
125mark :5
126data 8
127Bye
128You
129
130commit refs/heads/origin
131mark :6
132author Alice <alice@example.com> 1253744424 -0700
133committer Alice <alice@example.com> 1253744424 -0700
134data 13
135Personalized
136from :3
137M 100644 :4 a
138M 100644 :5 b
139
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000140blob
141mark :7
142data 5
143Mooh
144
145commit refs/heads/feature
146mark :8
147author Bob <bob@example.com> 1390311986 -0000
148committer Bob <bob@example.com> 1390311986 -0000
149data 6
150Add C
151from :3
152M 100644 :7 c
153
msb@chromium.orge28e4982009-09-25 20:51:45 +0000154reset refs/heads/master
155from :3
156"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000157 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000158 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000159
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000160 def checkstdout(self, expected):
161 value = sys.stdout.getvalue()
162 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800163 # pylint: disable=no-member
Edward Lemur979fa782019-08-13 22:44:05 +0000164 self.assertEqual(expected, strip_timestamps(value))
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000165
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000166 @staticmethod
167 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000168 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000169 try:
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000170 Popen(['git', 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000171 cwd=path).communicate()
172 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000173 # git is not available, skip this test.
174 return False
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000175 Popen(['git', 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
Edward Lemur979fa782019-08-13 22:44:05 +0000176 stderr=STDOUT, cwd=path).communicate(input=git_import.encode())
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000177 Popen(['git', 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
178 cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000179 Popen(['git', 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000180 stderr=STDOUT, cwd=path).communicate()
181 Popen(['git', 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
182 stderr=STDOUT, cwd=path).communicate()
John Budorick882c91e2018-07-12 22:11:41 +0000183 Popen(['git', 'push', 'origin', 'origin/origin:origin/master', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000184 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000185 Popen(['git', 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000186 stderr=STDOUT, cwd=path).communicate()
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000187 Popen(['git', 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
188 stderr=STDOUT, cwd=path).communicate()
189 Popen(['git', 'config', 'user.name', 'Some User'], stdout=PIPE,
190 stderr=STDOUT, cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000191 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000192
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000193 def _GetAskForDataCallback(self, expected_prompt, return_value):
194 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000195 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000196 return return_value
197 return AskForData
198
msb@chromium.orge28e4982009-09-25 20:51:45 +0000199 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000200 unittest.TestCase.setUp(self)
Edward Lemur9cafbf42019-08-15 22:03:35 +0000201 test_case_utils.TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000202 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000203 # The .git suffix allows gclient_scm to recognize the dir as a git repo
204 # when cloning it locally
205 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000206 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000207 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000208 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +0000209 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
Edward Lemur979fa782019-08-13 22:44:05 +0000210 mock.patch('gclient_scm.GitWrapper.BinaryExists',
211 staticmethod(lambda : True)).start()
212 mock.patch('sys.stdout', StringIO()).start()
213 self.addCleanup(mock.patch.stopall)
214 self.addCleanup(lambda: rmtree(self.root_dir))
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000215
msb@chromium.orge28e4982009-09-25 20:51:45 +0000216
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000217class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000218
219 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000220 if not self.enabled:
221 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000222 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000223 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800224 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
225 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000226 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000227 scm.update(options, None, file_list)
228 gclient_scm.os.remove(file_path)
229 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000230 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000231 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000232 file_list = []
233 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000234 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000235 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000236
237 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000238 if not self.enabled:
239 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000240 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800241 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
242 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000243 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000244 scm.update(options, None, file_list)
245 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000246 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000247 self.assertEqual(file_list, [])
248 self.assertEqual(scm.revinfo(options, self.args, None),
249 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000250 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000251
252 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000253 if not self.enabled:
254 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000255 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800256 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
257 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000258 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000259 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000260 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000261 with open(file_path, 'a') as f:
262 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000263 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000264 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000265 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000266 file_list = []
267 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000268 self.assertEqual(file_list, [])
269 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000270 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000271 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000272
273 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000274 if not self.enabled:
275 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000276 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800277 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
278 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000279 file_list = []
280 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000281 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000282 with open(file_path, 'w') as f:
283 f.writelines('new\n')
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000284 Popen(['git', 'add', 'c'], stdout=PIPE,
285 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000286 file_list = []
287 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000288 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000289 file_list = []
290 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000291 self.assertEqual(file_list, [])
292 self.assertEqual(scm.revinfo(options, self.args, None),
293 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000294 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000295
296 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000297 if not self.enabled:
298 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000299 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000300 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000301 with open(file_path, 'a') as f:
302 f.writelines('touched\n')
John Budorick0f7b2002018-01-19 15:46:17 -0800303 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
304 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000305 file_list = []
306 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000307 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000308 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800309 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000310 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000311 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000312
313 def testStatus2New(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()
317 expected_file_list = []
318 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000319 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000320 with open(file_path, 'a') as f:
321 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000322 expected_file_list.extend([file_path])
John Budorick0f7b2002018-01-19 15:46:17 -0800323 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
324 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000325 file_list = []
326 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000327 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000328 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000329 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800330 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000331 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
332 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000333
msb@chromium.orge28e4982009-09-25 20:51:45 +0000334 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000335 if not self.enabled:
336 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000337 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000338 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800339 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
340 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000341 file_list = []
342 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000343 self.assertEqual(file_list, expected_file_list)
344 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000345 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000346 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000347
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000348 def testUpdateMerge(self):
349 if not self.enabled:
350 return
351 options = self.Options()
352 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800353 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
354 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000355 scm._Run(['checkout', '-q', 'feature'], options)
356 rev = scm.revinfo(options, (), None)
357 file_list = []
358 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000359 self.assertEqual(file_list, [join(self.base_path, x)
360 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000361 # The actual commit that is created is unstable, so we verify its tree and
362 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000363 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
364 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
365 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^1']), rev)
366 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^2']),
367 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000368 sys.stdout.close()
369
370 def testUpdateRebase(self):
371 if not self.enabled:
372 return
373 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800374 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
375 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000376 scm._Run(['checkout', '-q', 'feature'], options)
377 file_list = []
378 # Fake a 'y' key press.
379 scm._AskForData = self._GetAskForDataCallback(
380 'Cannot fast-forward merge, attempt to rebase? '
381 '(y)es / (q)uit / (s)kip : ', 'y')
382 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000383 self.assertEqual(file_list, [join(self.base_path, x)
384 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000385 # The actual commit that is created is unstable, so we verify its tree and
386 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000387 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
388 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
389 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^']),
390 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000391 sys.stdout.close()
392
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000393 def testUpdateReset(self):
394 if not self.enabled:
395 return
396 options = self.Options()
397 options.reset = True
398
399 dir_path = join(self.base_path, 'c')
400 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000401 with open(join(dir_path, 'nested'), 'w') as f:
402 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000403
404 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000405 with open(file_path, 'w') as f:
406 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000407
John Budorick0f7b2002018-01-19 15:46:17 -0800408 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
409 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000410 file_list = []
411 scm.update(options, (), file_list)
412 self.assert_(gclient_scm.os.path.isdir(dir_path))
413 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000414 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000415
Edward Lemur579c9862018-07-13 23:17:51 +0000416 def testUpdateResetUnsetsFetchConfig(self):
417 if not self.enabled:
418 return
419 options = self.Options()
420 options.reset = True
421
422 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
423 self.relpath)
424 scm._Run(['config', 'remote.origin.fetch',
425 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
426
427 file_list = []
428 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000429 self.assertEqual(scm.revinfo(options, (), None),
430 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000431 sys.stdout.close()
432
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000433 def testUpdateResetDeleteUnversionedTrees(self):
434 if not self.enabled:
435 return
436 options = self.Options()
437 options.reset = True
438 options.delete_unversioned_trees = True
439
440 dir_path = join(self.base_path, 'dir')
441 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000442 with open(join(dir_path, 'nested'), 'w') as f:
443 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000444
445 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000446 with open(file_path, 'w') as f:
447 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000448
John Budorick0f7b2002018-01-19 15:46:17 -0800449 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
450 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000451 file_list = []
452 scm.update(options, (), file_list)
453 self.assert_(not gclient_scm.os.path.isdir(dir_path))
454 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000455 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000456
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000457 def testUpdateUnstagedConflict(self):
458 if not self.enabled:
459 return
460 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800461 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
462 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000463 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000464 with open(file_path, 'w') as f:
465 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000466 try:
467 scm.update(options, (), [])
468 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000469 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000470 # The exact exception text varies across git versions so it's not worth
471 # verifying it. It's fine as long as it throws.
472 pass
473 # Manually flush stdout since we can't verify it's content accurately across
474 # git versions.
475 sys.stdout.getvalue()
476 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000477
Mike Stipicevice992b612016-12-02 15:32:55 -0800478 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000479 def testUpdateLocked(self):
480 if not self.enabled:
481 return
482 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800483 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
484 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000485 file_path = join(self.base_path, '.git', 'index.lock')
486 with open(file_path, 'w'):
487 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800488 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000489 scm.update(options, (), [])
490 sys.stdout.close()
491
492 def testUpdateLockedBreak(self):
493 if not self.enabled:
494 return
495 options = self.Options()
496 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800497 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
498 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000499 file_path = join(self.base_path, '.git', 'index.lock')
500 with open(file_path, 'w'):
501 pass
502 scm.update(options, (), [])
503 self.assertRegexpMatches(sys.stdout.getvalue(),
504 "breaking lock.*\.git/index\.lock")
505 self.assertFalse(os.path.exists(file_path))
506 sys.stdout.close()
507
msb@chromium.org5bde4852009-12-14 16:47:12 +0000508 def testUpdateConflict(self):
509 if not self.enabled:
510 return
511 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800512 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
513 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000514 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000515 with open(file_path, 'w') as f:
516 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000517 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000518 scm._AskForData = self._GetAskForDataCallback(
519 'Cannot fast-forward merge, attempt to rebase? '
520 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000521
522 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
523 scm.update(options, (), [])
524 self.assertEqual(
525 e.exception.args[0],
526 'Conflict while rebasing this branch.\n'
527 'Fix the conflict and run gclient again.\n'
528 'See \'man git-rebase\' for details.\n')
529
530 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
531 scm.update(options, (), [])
532 self.assertEqual(
533 e.exception.args[0],
534 '\n____ . at refs/remotes/origin/master\n'
535 '\tYou have unstaged changes.\n'
536 '\tPlease commit, stash, or reset.\n')
537
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000538 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000539
msb@chromium.org0f282062009-11-06 20:14:02 +0000540 def testRevinfo(self):
541 if not self.enabled:
542 return
543 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800544 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
545 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000546 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000547 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000548
John Budorick21a51b32018-09-19 19:39:20 +0000549 def testMirrorPushUrl(self):
550 if not self.enabled:
551 return
552 fakes = fake_repos.FakeRepos()
553 fakes.set_up_git()
554 self.url = fakes.git_base + 'repo_1'
555 self.root_dir = fakes.root_dir
556 self.addCleanup(fake_repos.FakeRepos.tear_down_git, fakes)
557
558 mirror = tempfile.mkdtemp()
559 self.addCleanup(rmtree, mirror)
560
561 # This should never happen, but if it does, it'd render the other assertions
562 # in this test meaningless.
563 self.assertFalse(self.url.startswith(mirror))
564
565 git_cache.Mirror.SetCachePath(mirror)
566 self.addCleanup(git_cache.Mirror.SetCachePath, None)
567
568 options = self.Options()
569 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
570 self.assertIsNotNone(scm._GetMirror(self.url, options))
571 scm.update(options, (), [])
572
573 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
574 self.assertTrue(
575 fetch_url.startswith(mirror),
576 msg='\n'.join([
577 'Repository fetch url should be in the git cache mirror directory.',
578 ' fetch_url: %s' % fetch_url,
579 ' mirror: %s' % mirror]))
580 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
Edward Lemur979fa782019-08-13 22:44:05 +0000581 self.assertEqual(push_url, self.url)
John Budorick21a51b32018-09-19 19:39:20 +0000582 sys.stdout.close()
583
msb@chromium.orge28e4982009-09-25 20:51:45 +0000584
Edward Lemur979fa782019-08-13 22:44:05 +0000585class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000586 class OptionsObject(object):
587 def __init__(self, verbose=False, revision=None, force=False):
588 self.verbose = verbose
589 self.revision = revision
590 self.deps_os = None
591 self.force = force
592 self.reset = False
593 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000594 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000595 # TODO(maruel): Test --jobs > 1.
596 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400597 self.patch_ref = None
598 self.patch_repo = None
599 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000600
601 def Options(self, *args, **kwargs):
602 return self.OptionsObject(*args, **kwargs)
603
borenet@google.comb09097a2014-04-09 19:09:08 +0000604 def checkstdout(self, expected):
605 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000606 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800607 # pylint: disable=no-member
Edward Lemur979fa782019-08-13 22:44:05 +0000608 self.assertEqual(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000609
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000610 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000611 self.fake_hash_1 = 't0ta11yf4k3'
612 self.fake_hash_2 = '3v3nf4k3r'
613 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000614 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000615 self.relpath = 'fake'
616 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000617 self.backup_base_path = os.path.join(self.root_dir,
618 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000619 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
620 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
621 mock.patch('gclient_scm.GitWrapper._Fetch').start()
622 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
623 mock.patch('sys.stdout', StringIO()).start()
624 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000625
Edward Lemur979fa782019-08-13 22:44:05 +0000626 @mock.patch('scm.GIT.IsValidRevision')
627 @mock.patch('os.path.isdir', lambda _: True)
628 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800629 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700630 options = self.Options(verbose=True)
631
Edward Lemur979fa782019-08-13 22:44:05 +0000632 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700633
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).
Edward Lemur979fa782019-08-13 22:44:05 +0000638 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
639 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700640 # 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
Edward Lemur979fa782019-08-13 22:44:05 +0000644 @mock.patch('gclient_scm.GitWrapper._Clone')
645 @mock.patch('os.path.isdir')
646 @mock.patch('os.path.exists')
647 @mock.patch('subprocess2.check_output')
648 def testUpdateNoDotGit(
649 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
650 mockIsdir.side_effect = lambda path: path == self.base_path
651 mockExists.side_effect = lambda path: path == self.base_path
652 mockCheckOutput.return_value = b''
653
borenet@google.comb09097a2014-04-09 19:09:08 +0000654 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000655 scm = gclient_scm.GitWrapper(
656 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000657 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000658
659 env = gclient_scm.scm.GIT.ApplyEnvVars({})
660 self.assertEqual(
661 mockCheckOutput.mock_calls,
662 [
663 mock.call(
664 ['git', '-c', 'core.quotePath=false', 'ls-files'],
665 cwd=self.base_path, env=env, stderr=-1),
666 mock.call(
667 ['git', 'rev-parse', '--verify', 'HEAD'],
668 cwd=self.base_path, env=env, stderr=-1),
669 ])
670 mockClone.assert_called_with(
671 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000672 self.checkstdout('\n')
673
Edward Lemur979fa782019-08-13 22:44:05 +0000674 @mock.patch('gclient_scm.GitWrapper._Clone')
675 @mock.patch('os.path.isdir')
676 @mock.patch('os.path.exists')
677 @mock.patch('subprocess2.check_output')
678 def testUpdateConflict(
679 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
680 mockIsdir.side_effect = lambda path: path == self.base_path
681 mockExists.side_effect = lambda path: path == self.base_path
682 mockCheckOutput.return_value = b''
683 mockClone.side_effect = [
684 gclient_scm.subprocess2.CalledProcessError(
685 None, None, None, None, None),
686 None,
687 ]
688
borenet@google.com90fe58b2014-05-01 18:22:00 +0000689 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000690 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800691 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000692 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000693
694 env = gclient_scm.scm.GIT.ApplyEnvVars({})
695 self.assertEqual(
696 mockCheckOutput.mock_calls,
697 [
698 mock.call(
699 ['git', '-c', 'core.quotePath=false', 'ls-files'],
700 cwd=self.base_path, env=env, stderr=-1),
701 mock.call(
702 ['git', 'rev-parse', '--verify', 'HEAD'],
703 cwd=self.base_path, env=env, stderr=-1),
704 ])
705 mockClone.assert_called_with(
706 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000707 self.checkstdout('\n')
708
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000709
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000710class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000711 def checkInStdout(self, expected):
712 value = sys.stdout.getvalue()
713 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800714 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000715 self.assertIn(expected, value)
716
717 def checkNotInStdout(self, expected):
718 value = sys.stdout.getvalue()
719 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800720 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000721 self.assertNotIn(expected, value)
722
smut@google.com27c9c8a2014-09-11 19:57:55 +0000723 def getCurrentBranch(self):
724 # Returns name of current branch or HEAD for detached HEAD
725 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
726 cwd=self.base_path)
727 if branch == 'HEAD':
728 return None
729 return branch
730
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000731 def testUpdateClone(self):
732 if not self.enabled:
733 return
734 options = self.Options()
735
736 origin_root_dir = self.root_dir
737 self.root_dir = tempfile.mkdtemp()
738 self.relpath = '.'
739 self.base_path = join(self.root_dir, self.relpath)
740
John Budorick0f7b2002018-01-19 15:46:17 -0800741 scm = gclient_scm.GitWrapper(origin_root_dir,
742 self.root_dir,
743 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000744
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000745 expected_file_list = [join(self.base_path, "a"),
746 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000747 file_list = []
748 options.revision = 'unmanaged'
749 scm.update(options, (), file_list)
750
Edward Lemur979fa782019-08-13 22:44:05 +0000751 self.assertEqual(file_list, expected_file_list)
752 self.assertEqual(scm.revinfo(options, (), None),
753 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000754 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000755 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000756 self.checkInStdout(
757 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000758
759 rmtree(origin_root_dir)
760
761 def testUpdateCloneOnCommit(self):
762 if not self.enabled:
763 return
764 options = self.Options()
765
766 origin_root_dir = self.root_dir
767 self.root_dir = tempfile.mkdtemp()
768 self.relpath = '.'
769 self.base_path = join(self.root_dir, self.relpath)
770 url_with_commit_ref = origin_root_dir +\
771 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
772
John Budorick0f7b2002018-01-19 15:46:17 -0800773 scm = gclient_scm.GitWrapper(url_with_commit_ref,
774 self.root_dir,
775 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000776
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000777 expected_file_list = [join(self.base_path, "a"),
778 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000779 file_list = []
780 options.revision = 'unmanaged'
781 scm.update(options, (), file_list)
782
Edward Lemur979fa782019-08-13 22:44:05 +0000783 self.assertEqual(file_list, expected_file_list)
784 self.assertEqual(scm.revinfo(options, (), None),
785 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000786 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000787 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000788 self.checkInStdout(
789 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000790
791 rmtree(origin_root_dir)
792
793 def testUpdateCloneOnBranch(self):
794 if not self.enabled:
795 return
796 options = self.Options()
797
798 origin_root_dir = self.root_dir
799 self.root_dir = tempfile.mkdtemp()
800 self.relpath = '.'
801 self.base_path = join(self.root_dir, self.relpath)
802 url_with_branch_ref = origin_root_dir + '@feature'
803
John Budorick0f7b2002018-01-19 15:46:17 -0800804 scm = gclient_scm.GitWrapper(url_with_branch_ref,
805 self.root_dir,
806 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000807
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000808 expected_file_list = [join(self.base_path, "a"),
809 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000810 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000811 file_list = []
812 options.revision = 'unmanaged'
813 scm.update(options, (), file_list)
814
Edward Lemur979fa782019-08-13 22:44:05 +0000815 self.assertEqual(file_list, expected_file_list)
816 self.assertEqual(scm.revinfo(options, (), None),
817 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200818 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000819 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200820 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000821 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200822 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000823
824 rmtree(origin_root_dir)
825
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000826 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000827 if not self.enabled:
828 return
829 options = self.Options()
830
831 origin_root_dir = self.root_dir
832 self.root_dir = tempfile.mkdtemp()
833 self.relpath = '.'
834 self.base_path = join(self.root_dir, self.relpath)
835 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
836
John Budorick0f7b2002018-01-19 15:46:17 -0800837 scm = gclient_scm.GitWrapper(url_with_branch_ref,
838 self.root_dir,
839 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000840
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000841 expected_file_list = [join(self.base_path, "a"),
842 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000843 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000844 file_list = []
845 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000846 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000847
Edward Lemur979fa782019-08-13 22:44:05 +0000848 self.assertEqual(file_list, expected_file_list)
849 self.assertEqual(scm.revinfo(options, (), None),
850 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000851 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000852 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000853 self.checkInStdout(
854 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000855
856 rmtree(origin_root_dir)
857
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000858 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000859 if not self.enabled:
860 return
861 options = self.Options()
862
863 origin_root_dir = self.root_dir
864 self.root_dir = tempfile.mkdtemp()
865 self.relpath = '.'
866 self.base_path = join(self.root_dir, self.relpath)
867 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
868
John Budorick0f7b2002018-01-19 15:46:17 -0800869 scm = gclient_scm.GitWrapper(url_with_branch_ref,
870 self.root_dir,
871 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000872
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000873 expected_file_list = [join(self.base_path, "a"),
874 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000875 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000876 file_list = []
877 options.revision = 'unmanaged'
878 scm.update(options, (), file_list)
879
Edward Lemur979fa782019-08-13 22:44:05 +0000880 self.assertEqual(file_list, expected_file_list)
881 self.assertEqual(scm.revinfo(options, (), None),
882 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000883 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000884 # should be treated as such by gclient.
885 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
886 # as they are known in the upstream repo, since the mapping into the local
887 # repo can be modified by users (or we might even want to change the gclient
888 # defaults at some point). But that will take more work to stop using
889 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
890 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000891 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000892 self.checkInStdout(
893 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000894
895 rmtree(origin_root_dir)
896
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000897 def testUpdateUpdate(self):
898 if not self.enabled:
899 return
900 options = self.Options()
901 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800902 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
903 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000904 file_list = []
905 options.revision = 'unmanaged'
906 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000907 self.assertEqual(file_list, expected_file_list)
908 self.assertEqual(scm.revinfo(options, (), None),
909 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000910 self.checkstdout('________ unmanaged solution; skipping .\n')
911
912
Edward Lemur979fa782019-08-13 22:44:05 +0000913class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800914
915 def setUp(self):
916 # Create this before setting up mocks.
917 self._cipd_root_dir = tempfile.mkdtemp()
918 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800919
920 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
921 self._cipd_root = gclient_scm.CipdRoot(
922 self._cipd_root_dir,
923 self._cipd_instance_url)
924 self._cipd_packages = [
925 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
926 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
927 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
928 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000929 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
930 mock.patch('gclient_scm.CipdRoot.add_package').start()
931 mock.patch('gclient_scm.CipdRoot.clobber').start()
932 mock.patch('gclient_scm.CipdRoot.ensure').start()
933 self.addCleanup(mock.patch.stopall)
John Budorick0f7b2002018-01-19 15:46:17 -0800934
935 def tearDown(self):
John Budorick0f7b2002018-01-19 15:46:17 -0800936 rmtree(self._cipd_root_dir)
937 rmtree(self._workdir)
938
939 def createScmWithPackageThatSatisfies(self, condition):
940 return gclient_scm.CipdWrapper(
941 url=self._cipd_instance_url,
942 root_dir=self._cipd_root_dir,
943 relpath='fake_relpath',
944 root=self._cipd_root,
945 package=self.getPackageThatSatisfies(condition))
946
947 def getPackageThatSatisfies(self, condition):
948 for p in self._cipd_packages:
949 if condition(p):
950 return p
951
952 self.fail('Unable to find a satisfactory package.')
953
John Budorick0f7b2002018-01-19 15:46:17 -0800954 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700955 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000956 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800957 scm.revert(None, (), [])
958
Edward Lemur979fa782019-08-13 22:44:05 +0000959 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
960 @mock.patch('gclient_scm.gclient_utils.rmtree')
961 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -0800962 """Checks that revinfo uses the JSON from cipd describe."""
963 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
964
965 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
966 json_contents = {
967 'result': {
968 'pin': {
969 'instance_id': expected_revinfo,
970 }
971 }
972 }
973 describe_json_path = join(self._workdir, 'describe.json')
974 with open(describe_json_path, 'w') as describe_json:
975 json.dump(json_contents, describe_json)
976
Edward Lemur979fa782019-08-13 22:44:05 +0000977 revinfo = scm.revinfo(None, (), [])
978 self.assertEqual(revinfo, expected_revinfo)
979
980 mockRmtree.assert_called_with(self._workdir)
981 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -0800982 'cipd', 'describe', 'foo_package',
983 '-log-level', 'error',
984 '-version', 'foo_version',
985 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +0000986 ])
John Budorick0f7b2002018-01-19 15:46:17 -0800987
988 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700989 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000990 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800991 scm.update(None, (), [])
992
993
Edward Lemurd64781e2018-07-11 23:09:55 +0000994class GerritChangesFakeRepo(fake_repos.FakeReposBase):
995 def populateGit(self):
996 # Creates a tree that looks like this:
997 #
Edward Lemurca7d8812018-07-24 17:42:45 +0000998 # 6 refs/changes/35/1235/1
999 # |
1000 # 5 refs/changes/34/1234/1
1001 # |
Edward Lemurd64781e2018-07-11 23:09:55 +00001002 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +00001003 # | |
1004 # | 11(5)--12 refs/heads/master-with-5
1005 # |
1006 # 7--8--9 refs/heads/feature
1007 # |
1008 # 10 refs/changes/36/1236/1
1009 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001010
1011 self._commit_git('repo_1', {'commit 1': 'touched'})
1012 self._commit_git('repo_1', {'commit 2': 'touched'})
1013 self._commit_git('repo_1', {'commit 3': 'touched'})
1014 self._commit_git('repo_1', {'commit 4': 'touched'})
1015 self._create_ref('repo_1', 'refs/heads/master', 4)
1016
1017 # Create a change on top of commit 3 that consists of two commits.
1018 self._commit_git('repo_1',
1019 {'commit 5': 'touched',
1020 'change': '1234'},
1021 base=3)
1022 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1023 self._commit_git('repo_1',
1024 {'commit 6': 'touched',
1025 'change': '1235'})
1026 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1027
Edward Lemurca7d8812018-07-24 17:42:45 +00001028 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1029 # commits.
1030 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1031 self._commit_git('repo_1', {'commit 8': 'touched'})
1032 self._commit_git('repo_1', {'commit 9': 'touched'})
1033 self._create_ref('repo_1', 'refs/heads/feature', 9)
1034
1035 # Create a change of top of commit 8.
1036 self._commit_git('repo_1',
1037 {'commit 10': 'touched',
1038 'change': '1236'},
1039 base=8)
1040 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1041
1042 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1043 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1044 self._commit_git('repo_1',
1045 # This is really commit 11, but has the changes of commit 5
1046 {'commit 5': 'touched',
1047 'change': '1234'},
1048 base=3)
1049 self._commit_git('repo_1', {'commit 12': 'touched'})
1050 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1051
Edward Lemurd64781e2018-07-11 23:09:55 +00001052
1053class GerritChangesTest(fake_repos.FakeReposTestBase):
1054 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1055
1056 def setUp(self):
1057 super(GerritChangesTest, self).setUp()
1058 self.enabled = self.FAKE_REPOS.set_up_git()
1059 self.options = BaseGitWrapperTestCase.OptionsObject()
1060 self.url = self.git_base + 'repo_1'
1061 self.mirror = None
1062
1063 def setUpMirror(self):
1064 self.mirror = tempfile.mkdtemp()
1065 git_cache.Mirror.SetCachePath(self.mirror)
1066 self.addCleanup(rmtree, self.mirror)
1067 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1068
Edward Lemurca7d8812018-07-24 17:42:45 +00001069 def assertCommits(self, commits):
1070 """Check that all, and only |commits| are present in the current checkout.
1071 """
1072 for i in commits:
1073 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001074 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001075
1076 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1077 for i in all_commits - set(commits):
1078 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001079 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001080
Edward Lemurd64781e2018-07-11 23:09:55 +00001081 def testCanCloneGerritChange(self):
1082 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1083 file_list = []
1084
1085 self.options.revision = 'refs/changes/35/1235/1'
1086 scm.update(self.options, None, file_list)
1087 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1088
1089 def testCanSyncToGerritChange(self):
1090 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1091 file_list = []
1092
1093 self.options.revision = self.githash('repo_1', 1)
1094 scm.update(self.options, None, file_list)
1095 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1096
1097 self.options.revision = 'refs/changes/35/1235/1'
1098 scm.update(self.options, None, file_list)
1099 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1100
1101 def testCanCloneGerritChangeMirror(self):
1102 self.setUpMirror()
1103 self.testCanCloneGerritChange()
1104
1105 def testCanSyncToGerritChangeMirror(self):
1106 self.setUpMirror()
1107 self.testCanSyncToGerritChange()
1108
Edward Lemurca7d8812018-07-24 17:42:45 +00001109 def testAppliesPatchOnTopOfMasterByDefault(self):
1110 """Test the default case, where we apply a patch on top of master."""
1111 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1112 file_list = []
1113
1114 # Make sure we don't specify a revision.
1115 self.options.revision = None
1116 scm.update(self.options, None, file_list)
1117 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1118
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001119 scm.apply_patch_ref(
1120 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1121 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001122
1123 self.assertCommits([1, 2, 3, 4, 5, 6])
1124 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1125
1126 def testCheckoutOlderThanPatchBase(self):
1127 """Test applying a patch on an old checkout.
1128
1129 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1130 contains commits 5 and 6, and is based on top of commit 3.
1131 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1132 """
1133 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1134 file_list = []
1135
1136 # Sync to commit 1
1137 self.options.revision = self.githash('repo_1', 1)
1138 scm.update(self.options, None, file_list)
1139 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1140
1141 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001142 scm.apply_patch_ref(
1143 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1144 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001145
1146 self.assertCommits([1, 5, 6])
1147 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1148
1149 def testCheckoutOriginFeature(self):
1150 """Tests that we can apply a patch on a branch other than master."""
1151 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1152 file_list = []
1153
Edward Lemur8c665652019-05-08 20:23:33 +00001154 # Sync to remote's refs/heads/feature
1155 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001156 scm.update(self.options, None, file_list)
1157 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1158
1159 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001160 scm.apply_patch_ref(
1161 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1162 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001163
1164 self.assertCommits([1, 2, 7, 8, 9, 10])
1165 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1166
1167 def testCheckoutOriginFeatureOnOldRevision(self):
1168 """Tests that we can apply a patch on an old checkout, on a branch other
1169 than master."""
1170 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1171 file_list = []
1172
Edward Lemur8c665652019-05-08 20:23:33 +00001173 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001174 self.options.revision = self.githash('repo_1', 7)
1175 scm.update(self.options, None, file_list)
1176 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1177
1178 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001179 scm.apply_patch_ref(
1180 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1181 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001182
1183 # We shouldn't have rebased on top of 2 (which is the merge base between
Edward Lemur8c665652019-05-08 20:23:33 +00001184 # remote's master branch and the change) but on top of 7 (which is the
1185 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001186 self.assertCommits([1, 2, 7, 10])
1187 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1188
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001189 def testCheckoutOriginFeaturePatchBranch(self):
1190 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1191 file_list = []
1192
Edward Lemur8c665652019-05-08 20:23:33 +00001193 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001194 self.options.revision = self.githash('repo_1', 9)
1195 scm.update(self.options, None, file_list)
1196 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1197
Edward Lemur8c665652019-05-08 20:23:33 +00001198 # Apply refs/changes/34/1234/1, created for remote's master branch on top of
1199 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001200 scm.apply_patch_ref(
1201 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1202 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001203
1204 # Commits 5 and 6 are part of the patch, and commits 1, 2, 7, 8 and 9 are
Edward Lemur8c665652019-05-08 20:23:33 +00001205 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001206 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1207 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001208
1209 def testDoesntRebasePatchMaster(self):
1210 """Tests that we can apply a patch without rebasing it.
1211 """
1212 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1213 file_list = []
1214
1215 self.options.rebase_patch_ref = False
1216 scm.update(self.options, None, file_list)
1217 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1218
1219 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001220 scm.apply_patch_ref(
1221 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1222 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001223
1224 self.assertCommits([1, 2, 3, 5, 6])
1225 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1226
1227 def testDoesntRebasePatchOldCheckout(self):
1228 """Tests that we can apply a patch without rebasing it on an old checkout.
1229 """
1230 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1231 file_list = []
1232
1233 # Sync to commit 1
1234 self.options.revision = self.githash('repo_1', 1)
1235 self.options.rebase_patch_ref = False
1236 scm.update(self.options, None, file_list)
1237 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1238
1239 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001240 scm.apply_patch_ref(
1241 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1242 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001243
1244 self.assertCommits([1, 2, 3, 5, 6])
1245 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1246
1247 def testDoesntSoftResetIfNotAskedTo(self):
1248 """Test that we can apply a patch without doing a soft reset."""
1249 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1250 file_list = []
1251
1252 self.options.reset_patch_ref = False
1253 scm.update(self.options, None, file_list)
1254 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1255
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001256 scm.apply_patch_ref(
1257 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1258 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001259
1260 self.assertCommits([1, 2, 3, 4, 5, 6])
1261 # The commit hash after cherry-picking is not known, but it must be
1262 # different from what the repo was synced at before patching.
1263 self.assertNotEqual(self.githash('repo_1', 4),
1264 self.gitrevparse(self.root_dir))
1265
1266 def testRecoversAfterPatchFailure(self):
1267 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1268 file_list = []
1269
1270 self.options.revision = 'refs/changes/34/1234/1'
1271 scm.update(self.options, None, file_list)
1272 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1273
1274 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1275 # patch 'refs/changes/36/1236/1' creates a patch failure.
1276 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001277 scm.apply_patch_ref(
1278 self.url, 'refs/changes/36/1236/1', 'refs/heads/master', self.options,
1279 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001280 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001281 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001282
1283 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1284 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001285 scm.apply_patch_ref(
1286 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1287 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001288 self.assertCommits([1, 2, 3, 5, 6])
1289 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1290
1291 def testIgnoresAlreadyMergedCommits(self):
1292 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1293 file_list = []
1294
1295 self.options.revision = 'refs/heads/master-with-5'
1296 scm.update(self.options, None, file_list)
1297 self.assertEqual(self.githash('repo_1', 12),
1298 self.gitrevparse(self.root_dir))
1299
1300 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1301 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1302 # already present in the tree as commit 11.
1303 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001304 scm.apply_patch_ref(
1305 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1306 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001307 self.assertCommits([1, 2, 3, 5, 6, 12])
1308 self.assertEqual(self.githash('repo_1', 12),
1309 self.gitrevparse(self.root_dir))
1310
1311 def testRecoversFromExistingCherryPick(self):
1312 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1313 file_list = []
1314
1315 self.options.revision = 'refs/changes/34/1234/1'
1316 scm.update(self.options, None, file_list)
1317 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1318
1319 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1320 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1321 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1322 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1323 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1324 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1325
1326 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1327 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001328 scm.apply_patch_ref(
1329 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1330 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001331 self.assertCommits([1, 2, 3, 5, 6])
1332 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1333
Edward Lemurd64781e2018-07-11 23:09:55 +00001334
msb@chromium.orge28e4982009-09-25 20:51:45 +00001335if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001336 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1337 logging.basicConfig(
1338 level=level,
1339 format='%(asctime).19s %(levelname)s %(filename)s:'
1340 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001341 unittest.main()
1342
1343# vim: ts=2:sw=2:tw=80:et: