blob: a9aad58b2a5fe754370d52d8dbd001736760fc4f [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
Edward Lemur979fa782019-08-13 22:44:05 +000022if sys.version_info.major == 2:
23 from cStringIO import StringIO
24else:
25 from io import StringIO
26
maruel@chromium.org428342a2011-11-10 15:46:33 +000027sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000028
Edward Lemur979fa782019-08-13 22:44:05 +000029from third_party import mock
Edward Lemurd64781e2018-07-11 23:09:55 +000030from testing_support import fake_repos
maruel@chromium.org0927b7e2011-11-11 16:06:22 +000031from testing_support.super_mox import TestCaseUtils
maruel@chromium.org428342a2011-11-10 15:46:33 +000032
msb@chromium.orge28e4982009-09-25 20:51:45 +000033import gclient_scm
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000034import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000035import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000036
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000037# Disable global git cache
38git_cache.Mirror.SetCachePath(None)
39
maruel@chromium.org795a8c12010-10-05 19:54:29 +000040# Shortcut since this function is used often
41join = gclient_scm.os.path.join
42
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000043TIMESTAMP_RE = re.compile('\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
44def strip_timestamps(value):
45 lines = value.splitlines(True)
Edward Lemur979fa782019-08-13 22:44:05 +000046 for i in range(len(lines)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000047 m = TIMESTAMP_RE.match(lines[i])
48 if m:
49 lines[i] = m.group(1)
50 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000051
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000052
Edward Lemur979fa782019-08-13 22:44:05 +000053class BasicTests(unittest.TestCase):
54 @mock.patch('gclient_scm.scm.GIT.Capture')
55 def testGetFirstRemoteUrl(self, mockCapture):
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000056 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
57 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
58 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
59 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
60 ('remote.origin.url git://what/"do', 'git://what/"do')]
61 FAKE_PATH = '/fake/path'
Edward Lemur979fa782019-08-13 22:44:05 +000062 mockCapture.side_effect = [question for question, _ in REMOTE_STRINGS]
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000063
64 for _, answer in REMOTE_STRINGS:
Edward Lemur979fa782019-08-13 22:44:05 +000065 self.assertEqual(
66 gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH), answer)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000067
Edward Lemur979fa782019-08-13 22:44:05 +000068 expected_calls = [
69 mock.call(['config', '--local', '--get-regexp', r'remote.*.url'],
70 cwd=FAKE_PATH)
71 for _ in REMOTE_STRINGS
72 ]
73 self.assertEqual(mockCapture.mock_calls, expected_calls)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000074
75
Edward Lemur979fa782019-08-13 22:44:05 +000076class BaseGitWrapperTestCase(unittest.TestCase, TestCaseUtils):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000077 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +000078 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +000079 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +000080 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000081 self.verbose = verbose
82 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +000083 self.deps_os = None
84 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +000085 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000086 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +000087 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +000088 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +000089 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +000090 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +000091 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +000092 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +000093 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -040094 self.patch_ref = None
95 self.patch_repo = None
96 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +000097 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +000098
99 sample_git_import = """blob
100mark :1
101data 6
102Hello
103
104blob
105mark :2
106data 4
107Bye
108
109reset refs/heads/master
110commit refs/heads/master
111mark :3
112author Bob <bob@example.com> 1253744361 -0700
113committer Bob <bob@example.com> 1253744361 -0700
114data 8
115A and B
116M 100644 :1 a
117M 100644 :2 b
118
119blob
120mark :4
121data 10
122Hello
123You
124
125blob
126mark :5
127data 8
128Bye
129You
130
131commit refs/heads/origin
132mark :6
133author Alice <alice@example.com> 1253744424 -0700
134committer Alice <alice@example.com> 1253744424 -0700
135data 13
136Personalized
137from :3
138M 100644 :4 a
139M 100644 :5 b
140
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000141blob
142mark :7
143data 5
144Mooh
145
146commit refs/heads/feature
147mark :8
148author Bob <bob@example.com> 1390311986 -0000
149committer Bob <bob@example.com> 1390311986 -0000
150data 6
151Add C
152from :3
153M 100644 :7 c
154
msb@chromium.orge28e4982009-09-25 20:51:45 +0000155reset refs/heads/master
156from :3
157"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000158 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000159 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000160
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000161 def checkstdout(self, expected):
162 value = sys.stdout.getvalue()
163 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800164 # pylint: disable=no-member
Edward Lemur979fa782019-08-13 22:44:05 +0000165 self.assertEqual(expected, strip_timestamps(value))
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000166
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000167 @staticmethod
168 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000169 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000170 try:
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000171 Popen(['git', 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000172 cwd=path).communicate()
173 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000174 # git is not available, skip this test.
175 return False
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000176 Popen(['git', 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
Edward Lemur979fa782019-08-13 22:44:05 +0000177 stderr=STDOUT, cwd=path).communicate(input=git_import.encode())
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000178 Popen(['git', 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
179 cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000180 Popen(['git', 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000181 stderr=STDOUT, cwd=path).communicate()
182 Popen(['git', 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
183 stderr=STDOUT, cwd=path).communicate()
John Budorick882c91e2018-07-12 22:11:41 +0000184 Popen(['git', 'push', 'origin', 'origin/origin:origin/master', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000185 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000186 Popen(['git', 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000187 stderr=STDOUT, cwd=path).communicate()
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000188 Popen(['git', 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
189 stderr=STDOUT, cwd=path).communicate()
190 Popen(['git', 'config', 'user.name', 'Some User'], stdout=PIPE,
191 stderr=STDOUT, cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000192 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000193
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000194 def _GetAskForDataCallback(self, expected_prompt, return_value):
195 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000196 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000197 return return_value
198 return AskForData
199
msb@chromium.orge28e4982009-09-25 20:51:45 +0000200 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000201 unittest.TestCase.setUp(self)
Edward Lemur979fa782019-08-13 22:44:05 +0000202 TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000203 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000204 # The .git suffix allows gclient_scm to recognize the dir as a git repo
205 # when cloning it locally
206 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000207 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000208 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000209 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +0000210 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
Edward Lemur979fa782019-08-13 22:44:05 +0000211 mock.patch('gclient_scm.GitWrapper.BinaryExists',
212 staticmethod(lambda : True)).start()
213 mock.patch('sys.stdout', StringIO()).start()
214 self.addCleanup(mock.patch.stopall)
215 self.addCleanup(lambda: rmtree(self.root_dir))
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000216
msb@chromium.orge28e4982009-09-25 20:51:45 +0000217
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000218class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000219
220 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000221 if not self.enabled:
222 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000223 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000224 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800225 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
226 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000227 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000228 scm.update(options, None, file_list)
229 gclient_scm.os.remove(file_path)
230 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000231 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000232 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000233 file_list = []
234 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000235 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000236 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000237
238 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000239 if not self.enabled:
240 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000241 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800242 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
243 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000244 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000245 scm.update(options, None, file_list)
246 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000247 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000248 self.assertEqual(file_list, [])
249 self.assertEqual(scm.revinfo(options, self.args, None),
250 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000251 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000252
253 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000254 if not self.enabled:
255 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000256 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800257 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
258 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000259 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000260 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000261 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000262 with open(file_path, 'a') as f:
263 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000264 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000265 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000266 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000267 file_list = []
268 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000269 self.assertEqual(file_list, [])
270 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000271 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000272 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000273
274 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000275 if not self.enabled:
276 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000277 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800278 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
279 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000280 file_list = []
281 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000282 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000283 with open(file_path, 'w') as f:
284 f.writelines('new\n')
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000285 Popen(['git', 'add', 'c'], stdout=PIPE,
286 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000287 file_list = []
288 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000289 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000290 file_list = []
291 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000292 self.assertEqual(file_list, [])
293 self.assertEqual(scm.revinfo(options, self.args, None),
294 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000295 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000296
297 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000298 if not self.enabled:
299 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000300 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000301 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000302 with open(file_path, 'a') as f:
303 f.writelines('touched\n')
John Budorick0f7b2002018-01-19 15:46:17 -0800304 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
305 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000306 file_list = []
307 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000308 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000309 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800310 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000311 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000312 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000313
314 def testStatus2New(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000315 if not self.enabled:
316 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000317 options = self.Options()
318 expected_file_list = []
319 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000320 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000321 with open(file_path, 'a') as f:
322 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000323 expected_file_list.extend([file_path])
John Budorick0f7b2002018-01-19 15:46:17 -0800324 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
325 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000326 file_list = []
327 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000328 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000329 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000330 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800331 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000332 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
333 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000334
msb@chromium.orge28e4982009-09-25 20:51:45 +0000335 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000336 if not self.enabled:
337 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000338 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000339 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800340 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
341 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000342 file_list = []
343 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000344 self.assertEqual(file_list, expected_file_list)
345 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000346 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000347 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000348
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000349 def testUpdateMerge(self):
350 if not self.enabled:
351 return
352 options = self.Options()
353 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800354 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
355 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000356 scm._Run(['checkout', '-q', 'feature'], options)
357 rev = scm.revinfo(options, (), None)
358 file_list = []
359 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000360 self.assertEqual(file_list, [join(self.base_path, x)
361 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000362 # The actual commit that is created is unstable, so we verify its tree and
363 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000364 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
365 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
366 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^1']), rev)
367 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^2']),
368 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000369 sys.stdout.close()
370
371 def testUpdateRebase(self):
372 if not self.enabled:
373 return
374 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800375 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
376 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000377 scm._Run(['checkout', '-q', 'feature'], options)
378 file_list = []
379 # Fake a 'y' key press.
380 scm._AskForData = self._GetAskForDataCallback(
381 'Cannot fast-forward merge, attempt to rebase? '
382 '(y)es / (q)uit / (s)kip : ', 'y')
383 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000384 self.assertEqual(file_list, [join(self.base_path, x)
385 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000386 # The actual commit that is created is unstable, so we verify its tree and
387 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000388 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
389 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
390 self.assertEqual(scm._Capture(['rev-parse', 'HEAD^']),
391 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000392 sys.stdout.close()
393
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000394 def testUpdateReset(self):
395 if not self.enabled:
396 return
397 options = self.Options()
398 options.reset = True
399
400 dir_path = join(self.base_path, 'c')
401 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000402 with open(join(dir_path, 'nested'), 'w') as f:
403 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000404
405 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000406 with open(file_path, 'w') as f:
407 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000408
John Budorick0f7b2002018-01-19 15:46:17 -0800409 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
410 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000411 file_list = []
412 scm.update(options, (), file_list)
413 self.assert_(gclient_scm.os.path.isdir(dir_path))
414 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000415 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000416
Edward Lemur579c9862018-07-13 23:17:51 +0000417 def testUpdateResetUnsetsFetchConfig(self):
418 if not self.enabled:
419 return
420 options = self.Options()
421 options.reset = True
422
423 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
424 self.relpath)
425 scm._Run(['config', 'remote.origin.fetch',
426 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
427
428 file_list = []
429 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000430 self.assertEqual(scm.revinfo(options, (), None),
431 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000432 sys.stdout.close()
433
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000434 def testUpdateResetDeleteUnversionedTrees(self):
435 if not self.enabled:
436 return
437 options = self.Options()
438 options.reset = True
439 options.delete_unversioned_trees = True
440
441 dir_path = join(self.base_path, 'dir')
442 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000443 with open(join(dir_path, 'nested'), 'w') as f:
444 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000445
446 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000447 with open(file_path, 'w') as f:
448 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000449
John Budorick0f7b2002018-01-19 15:46:17 -0800450 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
451 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000452 file_list = []
453 scm.update(options, (), file_list)
454 self.assert_(not gclient_scm.os.path.isdir(dir_path))
455 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000456 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000457
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000458 def testUpdateUnstagedConflict(self):
459 if not self.enabled:
460 return
461 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800462 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
463 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000464 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000465 with open(file_path, 'w') as f:
466 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000467 try:
468 scm.update(options, (), [])
469 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000470 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000471 # The exact exception text varies across git versions so it's not worth
472 # verifying it. It's fine as long as it throws.
473 pass
474 # Manually flush stdout since we can't verify it's content accurately across
475 # git versions.
476 sys.stdout.getvalue()
477 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000478
Mike Stipicevice992b612016-12-02 15:32:55 -0800479 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000480 def testUpdateLocked(self):
481 if not self.enabled:
482 return
483 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800484 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
485 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000486 file_path = join(self.base_path, '.git', 'index.lock')
487 with open(file_path, 'w'):
488 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800489 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000490 scm.update(options, (), [])
491 sys.stdout.close()
492
493 def testUpdateLockedBreak(self):
494 if not self.enabled:
495 return
496 options = self.Options()
497 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800498 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
499 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000500 file_path = join(self.base_path, '.git', 'index.lock')
501 with open(file_path, 'w'):
502 pass
503 scm.update(options, (), [])
504 self.assertRegexpMatches(sys.stdout.getvalue(),
505 "breaking lock.*\.git/index\.lock")
506 self.assertFalse(os.path.exists(file_path))
507 sys.stdout.close()
508
msb@chromium.org5bde4852009-12-14 16:47:12 +0000509 def testUpdateConflict(self):
510 if not self.enabled:
511 return
512 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800513 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
514 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000515 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000516 with open(file_path, 'w') as f:
517 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000518 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000519 scm._AskForData = self._GetAskForDataCallback(
520 'Cannot fast-forward merge, attempt to rebase? '
521 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000522
523 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
524 scm.update(options, (), [])
525 self.assertEqual(
526 e.exception.args[0],
527 'Conflict while rebasing this branch.\n'
528 'Fix the conflict and run gclient again.\n'
529 'See \'man git-rebase\' for details.\n')
530
531 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
532 scm.update(options, (), [])
533 self.assertEqual(
534 e.exception.args[0],
535 '\n____ . at refs/remotes/origin/master\n'
536 '\tYou have unstaged changes.\n'
537 '\tPlease commit, stash, or reset.\n')
538
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000539 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000540
msb@chromium.org0f282062009-11-06 20:14:02 +0000541 def testRevinfo(self):
542 if not self.enabled:
543 return
544 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800545 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
546 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000547 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000548 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000549
John Budorick21a51b32018-09-19 19:39:20 +0000550 def testMirrorPushUrl(self):
551 if not self.enabled:
552 return
553 fakes = fake_repos.FakeRepos()
554 fakes.set_up_git()
555 self.url = fakes.git_base + 'repo_1'
556 self.root_dir = fakes.root_dir
557 self.addCleanup(fake_repos.FakeRepos.tear_down_git, fakes)
558
559 mirror = tempfile.mkdtemp()
560 self.addCleanup(rmtree, mirror)
561
562 # This should never happen, but if it does, it'd render the other assertions
563 # in this test meaningless.
564 self.assertFalse(self.url.startswith(mirror))
565
566 git_cache.Mirror.SetCachePath(mirror)
567 self.addCleanup(git_cache.Mirror.SetCachePath, None)
568
569 options = self.Options()
570 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
571 self.assertIsNotNone(scm._GetMirror(self.url, options))
572 scm.update(options, (), [])
573
574 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
575 self.assertTrue(
576 fetch_url.startswith(mirror),
577 msg='\n'.join([
578 'Repository fetch url should be in the git cache mirror directory.',
579 ' fetch_url: %s' % fetch_url,
580 ' mirror: %s' % mirror]))
581 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
Edward Lemur979fa782019-08-13 22:44:05 +0000582 self.assertEqual(push_url, self.url)
John Budorick21a51b32018-09-19 19:39:20 +0000583 sys.stdout.close()
584
msb@chromium.orge28e4982009-09-25 20:51:45 +0000585
Edward Lemur979fa782019-08-13 22:44:05 +0000586class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000587 class OptionsObject(object):
588 def __init__(self, verbose=False, revision=None, force=False):
589 self.verbose = verbose
590 self.revision = revision
591 self.deps_os = None
592 self.force = force
593 self.reset = False
594 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000595 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000596 # TODO(maruel): Test --jobs > 1.
597 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400598 self.patch_ref = None
599 self.patch_repo = None
600 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000601
602 def Options(self, *args, **kwargs):
603 return self.OptionsObject(*args, **kwargs)
604
borenet@google.comb09097a2014-04-09 19:09:08 +0000605 def checkstdout(self, expected):
606 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000607 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800608 # pylint: disable=no-member
Edward Lemur979fa782019-08-13 22:44:05 +0000609 self.assertEqual(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000610
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000611 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000612 self.fake_hash_1 = 't0ta11yf4k3'
613 self.fake_hash_2 = '3v3nf4k3r'
614 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000615 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000616 self.relpath = 'fake'
617 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000618 self.backup_base_path = os.path.join(self.root_dir,
619 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000620 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
621 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
622 mock.patch('gclient_scm.GitWrapper._Fetch').start()
623 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
624 mock.patch('sys.stdout', StringIO()).start()
625 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000626
Edward Lemur979fa782019-08-13 22:44:05 +0000627 @mock.patch('scm.GIT.IsValidRevision')
628 @mock.patch('os.path.isdir', lambda _: True)
629 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800630 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700631 options = self.Options(verbose=True)
632
Edward Lemur979fa782019-08-13 22:44:05 +0000633 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700634
John Budorick0f7b2002018-01-19 15:46:17 -0800635 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
636 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700637 # A [fake] git sha1 with a git repo should work (this is in the case that
638 # the LKGR gets flipped to git sha1's some day).
Edward Lemur979fa782019-08-13 22:44:05 +0000639 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
640 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700641 # An SVN rev with an existing purely git repo should raise an exception.
642 self.assertRaises(gclient_scm.gclient_utils.Error,
643 git_scm.GetUsableRev, '1', options)
644
Edward Lemur979fa782019-08-13 22:44:05 +0000645 @mock.patch('gclient_scm.GitWrapper._Clone')
646 @mock.patch('os.path.isdir')
647 @mock.patch('os.path.exists')
648 @mock.patch('subprocess2.check_output')
649 def testUpdateNoDotGit(
650 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
651 mockIsdir.side_effect = lambda path: path == self.base_path
652 mockExists.side_effect = lambda path: path == self.base_path
653 mockCheckOutput.return_value = b''
654
borenet@google.comb09097a2014-04-09 19:09:08 +0000655 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000656 scm = gclient_scm.GitWrapper(
657 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000658 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000659
660 env = gclient_scm.scm.GIT.ApplyEnvVars({})
661 self.assertEqual(
662 mockCheckOutput.mock_calls,
663 [
664 mock.call(
665 ['git', '-c', 'core.quotePath=false', 'ls-files'],
666 cwd=self.base_path, env=env, stderr=-1),
667 mock.call(
668 ['git', 'rev-parse', '--verify', 'HEAD'],
669 cwd=self.base_path, env=env, stderr=-1),
670 ])
671 mockClone.assert_called_with(
672 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000673 self.checkstdout('\n')
674
Edward Lemur979fa782019-08-13 22:44:05 +0000675 @mock.patch('gclient_scm.GitWrapper._Clone')
676 @mock.patch('os.path.isdir')
677 @mock.patch('os.path.exists')
678 @mock.patch('subprocess2.check_output')
679 def testUpdateConflict(
680 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
681 mockIsdir.side_effect = lambda path: path == self.base_path
682 mockExists.side_effect = lambda path: path == self.base_path
683 mockCheckOutput.return_value = b''
684 mockClone.side_effect = [
685 gclient_scm.subprocess2.CalledProcessError(
686 None, None, None, None, None),
687 None,
688 ]
689
borenet@google.com90fe58b2014-05-01 18:22:00 +0000690 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000691 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800692 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000693 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000694
695 env = gclient_scm.scm.GIT.ApplyEnvVars({})
696 self.assertEqual(
697 mockCheckOutput.mock_calls,
698 [
699 mock.call(
700 ['git', '-c', 'core.quotePath=false', 'ls-files'],
701 cwd=self.base_path, env=env, stderr=-1),
702 mock.call(
703 ['git', 'rev-parse', '--verify', 'HEAD'],
704 cwd=self.base_path, env=env, stderr=-1),
705 ])
706 mockClone.assert_called_with(
707 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000708 self.checkstdout('\n')
709
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000710
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000711class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000712 def checkInStdout(self, expected):
713 value = sys.stdout.getvalue()
714 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800715 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000716 self.assertIn(expected, value)
717
718 def checkNotInStdout(self, expected):
719 value = sys.stdout.getvalue()
720 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800721 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000722 self.assertNotIn(expected, value)
723
smut@google.com27c9c8a2014-09-11 19:57:55 +0000724 def getCurrentBranch(self):
725 # Returns name of current branch or HEAD for detached HEAD
726 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
727 cwd=self.base_path)
728 if branch == 'HEAD':
729 return None
730 return branch
731
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000732 def testUpdateClone(self):
733 if not self.enabled:
734 return
735 options = self.Options()
736
737 origin_root_dir = self.root_dir
738 self.root_dir = tempfile.mkdtemp()
739 self.relpath = '.'
740 self.base_path = join(self.root_dir, self.relpath)
741
John Budorick0f7b2002018-01-19 15:46:17 -0800742 scm = gclient_scm.GitWrapper(origin_root_dir,
743 self.root_dir,
744 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000745
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000746 expected_file_list = [join(self.base_path, "a"),
747 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000748 file_list = []
749 options.revision = 'unmanaged'
750 scm.update(options, (), file_list)
751
Edward Lemur979fa782019-08-13 22:44:05 +0000752 self.assertEqual(file_list, expected_file_list)
753 self.assertEqual(scm.revinfo(options, (), None),
754 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000755 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000756 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000757 self.checkInStdout(
758 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000759
760 rmtree(origin_root_dir)
761
762 def testUpdateCloneOnCommit(self):
763 if not self.enabled:
764 return
765 options = self.Options()
766
767 origin_root_dir = self.root_dir
768 self.root_dir = tempfile.mkdtemp()
769 self.relpath = '.'
770 self.base_path = join(self.root_dir, self.relpath)
771 url_with_commit_ref = origin_root_dir +\
772 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
773
John Budorick0f7b2002018-01-19 15:46:17 -0800774 scm = gclient_scm.GitWrapper(url_with_commit_ref,
775 self.root_dir,
776 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000777
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000778 expected_file_list = [join(self.base_path, "a"),
779 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000780 file_list = []
781 options.revision = 'unmanaged'
782 scm.update(options, (), file_list)
783
Edward Lemur979fa782019-08-13 22:44:05 +0000784 self.assertEqual(file_list, expected_file_list)
785 self.assertEqual(scm.revinfo(options, (), None),
786 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000787 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000788 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000789 self.checkInStdout(
790 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000791
792 rmtree(origin_root_dir)
793
794 def testUpdateCloneOnBranch(self):
795 if not self.enabled:
796 return
797 options = self.Options()
798
799 origin_root_dir = self.root_dir
800 self.root_dir = tempfile.mkdtemp()
801 self.relpath = '.'
802 self.base_path = join(self.root_dir, self.relpath)
803 url_with_branch_ref = origin_root_dir + '@feature'
804
John Budorick0f7b2002018-01-19 15:46:17 -0800805 scm = gclient_scm.GitWrapper(url_with_branch_ref,
806 self.root_dir,
807 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000808
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000809 expected_file_list = [join(self.base_path, "a"),
810 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000811 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000812 file_list = []
813 options.revision = 'unmanaged'
814 scm.update(options, (), file_list)
815
Edward Lemur979fa782019-08-13 22:44:05 +0000816 self.assertEqual(file_list, expected_file_list)
817 self.assertEqual(scm.revinfo(options, (), None),
818 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200819 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000820 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200821 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000822 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200823 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000824
825 rmtree(origin_root_dir)
826
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000827 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000828 if not self.enabled:
829 return
830 options = self.Options()
831
832 origin_root_dir = self.root_dir
833 self.root_dir = tempfile.mkdtemp()
834 self.relpath = '.'
835 self.base_path = join(self.root_dir, self.relpath)
836 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
837
John Budorick0f7b2002018-01-19 15:46:17 -0800838 scm = gclient_scm.GitWrapper(url_with_branch_ref,
839 self.root_dir,
840 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000841
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000842 expected_file_list = [join(self.base_path, "a"),
843 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000844 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000845 file_list = []
846 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000847 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000848
Edward Lemur979fa782019-08-13 22:44:05 +0000849 self.assertEqual(file_list, expected_file_list)
850 self.assertEqual(scm.revinfo(options, (), None),
851 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000852 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000853 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000854 self.checkInStdout(
855 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000856
857 rmtree(origin_root_dir)
858
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000859 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000860 if not self.enabled:
861 return
862 options = self.Options()
863
864 origin_root_dir = self.root_dir
865 self.root_dir = tempfile.mkdtemp()
866 self.relpath = '.'
867 self.base_path = join(self.root_dir, self.relpath)
868 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
869
John Budorick0f7b2002018-01-19 15:46:17 -0800870 scm = gclient_scm.GitWrapper(url_with_branch_ref,
871 self.root_dir,
872 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000873
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000874 expected_file_list = [join(self.base_path, "a"),
875 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000876 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000877 file_list = []
878 options.revision = 'unmanaged'
879 scm.update(options, (), file_list)
880
Edward Lemur979fa782019-08-13 22:44:05 +0000881 self.assertEqual(file_list, expected_file_list)
882 self.assertEqual(scm.revinfo(options, (), None),
883 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000884 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000885 # should be treated as such by gclient.
886 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
887 # as they are known in the upstream repo, since the mapping into the local
888 # repo can be modified by users (or we might even want to change the gclient
889 # defaults at some point). But that will take more work to stop using
890 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
891 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000892 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000893 self.checkInStdout(
894 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000895
896 rmtree(origin_root_dir)
897
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000898 def testUpdateUpdate(self):
899 if not self.enabled:
900 return
901 options = self.Options()
902 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800903 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
904 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000905 file_list = []
906 options.revision = 'unmanaged'
907 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000908 self.assertEqual(file_list, expected_file_list)
909 self.assertEqual(scm.revinfo(options, (), None),
910 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000911 self.checkstdout('________ unmanaged solution; skipping .\n')
912
913
Edward Lemur979fa782019-08-13 22:44:05 +0000914class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800915
916 def setUp(self):
917 # Create this before setting up mocks.
918 self._cipd_root_dir = tempfile.mkdtemp()
919 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800920
921 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
922 self._cipd_root = gclient_scm.CipdRoot(
923 self._cipd_root_dir,
924 self._cipd_instance_url)
925 self._cipd_packages = [
926 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
927 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
928 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
929 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000930 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
931 mock.patch('gclient_scm.CipdRoot.add_package').start()
932 mock.patch('gclient_scm.CipdRoot.clobber').start()
933 mock.patch('gclient_scm.CipdRoot.ensure').start()
934 self.addCleanup(mock.patch.stopall)
John Budorick0f7b2002018-01-19 15:46:17 -0800935
936 def tearDown(self):
John Budorick0f7b2002018-01-19 15:46:17 -0800937 rmtree(self._cipd_root_dir)
938 rmtree(self._workdir)
939
940 def createScmWithPackageThatSatisfies(self, condition):
941 return gclient_scm.CipdWrapper(
942 url=self._cipd_instance_url,
943 root_dir=self._cipd_root_dir,
944 relpath='fake_relpath',
945 root=self._cipd_root,
946 package=self.getPackageThatSatisfies(condition))
947
948 def getPackageThatSatisfies(self, condition):
949 for p in self._cipd_packages:
950 if condition(p):
951 return p
952
953 self.fail('Unable to find a satisfactory package.')
954
John Budorick0f7b2002018-01-19 15:46:17 -0800955 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700956 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000957 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800958 scm.revert(None, (), [])
959
Edward Lemur979fa782019-08-13 22:44:05 +0000960 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
961 @mock.patch('gclient_scm.gclient_utils.rmtree')
962 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -0800963 """Checks that revinfo uses the JSON from cipd describe."""
964 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
965
966 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
967 json_contents = {
968 'result': {
969 'pin': {
970 'instance_id': expected_revinfo,
971 }
972 }
973 }
974 describe_json_path = join(self._workdir, 'describe.json')
975 with open(describe_json_path, 'w') as describe_json:
976 json.dump(json_contents, describe_json)
977
Edward Lemur979fa782019-08-13 22:44:05 +0000978 revinfo = scm.revinfo(None, (), [])
979 self.assertEqual(revinfo, expected_revinfo)
980
981 mockRmtree.assert_called_with(self._workdir)
982 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -0800983 'cipd', 'describe', 'foo_package',
984 '-log-level', 'error',
985 '-version', 'foo_version',
986 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +0000987 ])
John Budorick0f7b2002018-01-19 15:46:17 -0800988
989 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700990 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000991 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800992 scm.update(None, (), [])
993
994
Edward Lemurd64781e2018-07-11 23:09:55 +0000995class GerritChangesFakeRepo(fake_repos.FakeReposBase):
996 def populateGit(self):
997 # Creates a tree that looks like this:
998 #
Edward Lemurca7d8812018-07-24 17:42:45 +0000999 # 6 refs/changes/35/1235/1
1000 # |
1001 # 5 refs/changes/34/1234/1
1002 # |
Edward Lemurd64781e2018-07-11 23:09:55 +00001003 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +00001004 # | |
1005 # | 11(5)--12 refs/heads/master-with-5
1006 # |
1007 # 7--8--9 refs/heads/feature
1008 # |
1009 # 10 refs/changes/36/1236/1
1010 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001011
1012 self._commit_git('repo_1', {'commit 1': 'touched'})
1013 self._commit_git('repo_1', {'commit 2': 'touched'})
1014 self._commit_git('repo_1', {'commit 3': 'touched'})
1015 self._commit_git('repo_1', {'commit 4': 'touched'})
1016 self._create_ref('repo_1', 'refs/heads/master', 4)
1017
1018 # Create a change on top of commit 3 that consists of two commits.
1019 self._commit_git('repo_1',
1020 {'commit 5': 'touched',
1021 'change': '1234'},
1022 base=3)
1023 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1024 self._commit_git('repo_1',
1025 {'commit 6': 'touched',
1026 'change': '1235'})
1027 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1028
Edward Lemurca7d8812018-07-24 17:42:45 +00001029 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1030 # commits.
1031 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1032 self._commit_git('repo_1', {'commit 8': 'touched'})
1033 self._commit_git('repo_1', {'commit 9': 'touched'})
1034 self._create_ref('repo_1', 'refs/heads/feature', 9)
1035
1036 # Create a change of top of commit 8.
1037 self._commit_git('repo_1',
1038 {'commit 10': 'touched',
1039 'change': '1236'},
1040 base=8)
1041 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1042
1043 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1044 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1045 self._commit_git('repo_1',
1046 # This is really commit 11, but has the changes of commit 5
1047 {'commit 5': 'touched',
1048 'change': '1234'},
1049 base=3)
1050 self._commit_git('repo_1', {'commit 12': 'touched'})
1051 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1052
Edward Lemurd64781e2018-07-11 23:09:55 +00001053
1054class GerritChangesTest(fake_repos.FakeReposTestBase):
1055 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1056
1057 def setUp(self):
1058 super(GerritChangesTest, self).setUp()
1059 self.enabled = self.FAKE_REPOS.set_up_git()
1060 self.options = BaseGitWrapperTestCase.OptionsObject()
1061 self.url = self.git_base + 'repo_1'
1062 self.mirror = None
1063
1064 def setUpMirror(self):
1065 self.mirror = tempfile.mkdtemp()
1066 git_cache.Mirror.SetCachePath(self.mirror)
1067 self.addCleanup(rmtree, self.mirror)
1068 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1069
Edward Lemurca7d8812018-07-24 17:42:45 +00001070 def assertCommits(self, commits):
1071 """Check that all, and only |commits| are present in the current checkout.
1072 """
1073 for i in commits:
1074 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001075 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001076
1077 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1078 for i in all_commits - set(commits):
1079 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001080 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001081
Edward Lemurd64781e2018-07-11 23:09:55 +00001082 def testCanCloneGerritChange(self):
1083 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1084 file_list = []
1085
1086 self.options.revision = 'refs/changes/35/1235/1'
1087 scm.update(self.options, None, file_list)
1088 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1089
1090 def testCanSyncToGerritChange(self):
1091 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1092 file_list = []
1093
1094 self.options.revision = self.githash('repo_1', 1)
1095 scm.update(self.options, None, file_list)
1096 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1097
1098 self.options.revision = 'refs/changes/35/1235/1'
1099 scm.update(self.options, None, file_list)
1100 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1101
1102 def testCanCloneGerritChangeMirror(self):
1103 self.setUpMirror()
1104 self.testCanCloneGerritChange()
1105
1106 def testCanSyncToGerritChangeMirror(self):
1107 self.setUpMirror()
1108 self.testCanSyncToGerritChange()
1109
Edward Lemurca7d8812018-07-24 17:42:45 +00001110 def testAppliesPatchOnTopOfMasterByDefault(self):
1111 """Test the default case, where we apply a patch on top of master."""
1112 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1113 file_list = []
1114
1115 # Make sure we don't specify a revision.
1116 self.options.revision = None
1117 scm.update(self.options, None, file_list)
1118 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1119
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001120 scm.apply_patch_ref(
1121 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1122 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001123
1124 self.assertCommits([1, 2, 3, 4, 5, 6])
1125 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1126
1127 def testCheckoutOlderThanPatchBase(self):
1128 """Test applying a patch on an old checkout.
1129
1130 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1131 contains commits 5 and 6, and is based on top of commit 3.
1132 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1133 """
1134 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1135 file_list = []
1136
1137 # Sync to commit 1
1138 self.options.revision = self.githash('repo_1', 1)
1139 scm.update(self.options, None, file_list)
1140 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1141
1142 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001143 scm.apply_patch_ref(
1144 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1145 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001146
1147 self.assertCommits([1, 5, 6])
1148 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1149
1150 def testCheckoutOriginFeature(self):
1151 """Tests that we can apply a patch on a branch other than master."""
1152 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1153 file_list = []
1154
Edward Lemur8c665652019-05-08 20:23:33 +00001155 # Sync to remote's refs/heads/feature
1156 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001157 scm.update(self.options, None, file_list)
1158 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1159
1160 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001161 scm.apply_patch_ref(
1162 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1163 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001164
1165 self.assertCommits([1, 2, 7, 8, 9, 10])
1166 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1167
1168 def testCheckoutOriginFeatureOnOldRevision(self):
1169 """Tests that we can apply a patch on an old checkout, on a branch other
1170 than master."""
1171 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1172 file_list = []
1173
Edward Lemur8c665652019-05-08 20:23:33 +00001174 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001175 self.options.revision = self.githash('repo_1', 7)
1176 scm.update(self.options, None, file_list)
1177 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1178
1179 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001180 scm.apply_patch_ref(
1181 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1182 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001183
1184 # We shouldn't have rebased on top of 2 (which is the merge base between
Edward Lemur8c665652019-05-08 20:23:33 +00001185 # remote's master branch and the change) but on top of 7 (which is the
1186 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001187 self.assertCommits([1, 2, 7, 10])
1188 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1189
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001190 def testCheckoutOriginFeaturePatchBranch(self):
1191 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1192 file_list = []
1193
Edward Lemur8c665652019-05-08 20:23:33 +00001194 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001195 self.options.revision = self.githash('repo_1', 9)
1196 scm.update(self.options, None, file_list)
1197 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1198
Edward Lemur8c665652019-05-08 20:23:33 +00001199 # Apply refs/changes/34/1234/1, created for remote's master branch on top of
1200 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001201 scm.apply_patch_ref(
1202 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1203 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001204
1205 # 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 +00001206 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001207 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1208 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001209
1210 def testDoesntRebasePatchMaster(self):
1211 """Tests that we can apply a patch without rebasing it.
1212 """
1213 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1214 file_list = []
1215
1216 self.options.rebase_patch_ref = False
1217 scm.update(self.options, None, file_list)
1218 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1219
1220 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001221 scm.apply_patch_ref(
1222 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1223 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001224
1225 self.assertCommits([1, 2, 3, 5, 6])
1226 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1227
1228 def testDoesntRebasePatchOldCheckout(self):
1229 """Tests that we can apply a patch without rebasing it on an old checkout.
1230 """
1231 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1232 file_list = []
1233
1234 # Sync to commit 1
1235 self.options.revision = self.githash('repo_1', 1)
1236 self.options.rebase_patch_ref = False
1237 scm.update(self.options, None, file_list)
1238 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1239
1240 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001241 scm.apply_patch_ref(
1242 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1243 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001244
1245 self.assertCommits([1, 2, 3, 5, 6])
1246 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1247
1248 def testDoesntSoftResetIfNotAskedTo(self):
1249 """Test that we can apply a patch without doing a soft reset."""
1250 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1251 file_list = []
1252
1253 self.options.reset_patch_ref = False
1254 scm.update(self.options, None, file_list)
1255 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1256
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001257 scm.apply_patch_ref(
1258 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1259 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001260
1261 self.assertCommits([1, 2, 3, 4, 5, 6])
1262 # The commit hash after cherry-picking is not known, but it must be
1263 # different from what the repo was synced at before patching.
1264 self.assertNotEqual(self.githash('repo_1', 4),
1265 self.gitrevparse(self.root_dir))
1266
1267 def testRecoversAfterPatchFailure(self):
1268 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1269 file_list = []
1270
1271 self.options.revision = 'refs/changes/34/1234/1'
1272 scm.update(self.options, None, file_list)
1273 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1274
1275 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1276 # patch 'refs/changes/36/1236/1' creates a patch failure.
1277 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001278 scm.apply_patch_ref(
1279 self.url, 'refs/changes/36/1236/1', 'refs/heads/master', self.options,
1280 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001281 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001282 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001283
1284 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1285 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001286 scm.apply_patch_ref(
1287 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1288 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001289 self.assertCommits([1, 2, 3, 5, 6])
1290 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1291
1292 def testIgnoresAlreadyMergedCommits(self):
1293 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1294 file_list = []
1295
1296 self.options.revision = 'refs/heads/master-with-5'
1297 scm.update(self.options, None, file_list)
1298 self.assertEqual(self.githash('repo_1', 12),
1299 self.gitrevparse(self.root_dir))
1300
1301 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1302 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1303 # already present in the tree as commit 11.
1304 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001305 scm.apply_patch_ref(
1306 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1307 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001308 self.assertCommits([1, 2, 3, 5, 6, 12])
1309 self.assertEqual(self.githash('repo_1', 12),
1310 self.gitrevparse(self.root_dir))
1311
1312 def testRecoversFromExistingCherryPick(self):
1313 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1314 file_list = []
1315
1316 self.options.revision = 'refs/changes/34/1234/1'
1317 scm.update(self.options, None, file_list)
1318 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1319
1320 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1321 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1322 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1323 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1324 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1325 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1326
1327 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1328 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001329 scm.apply_patch_ref(
1330 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1331 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001332 self.assertCommits([1, 2, 3, 5, 6])
1333 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1334
Edward Lemurd64781e2018-07-11 23:09:55 +00001335
msb@chromium.orge28e4982009-09-25 20:51:45 +00001336if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001337 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1338 logging.basicConfig(
1339 level=level,
1340 format='%(asctime).19s %(levelname)s %(filename)s:'
1341 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001342 unittest.main()
1343
1344# vim: ts=2:sw=2:tw=80:et: