blob: 6fb0b76daaef47c98b666d8e207322bf3f6bdecb [file] [log] [blame]
Edward Lemura877ee62019-09-03 20:23:17 +00001#!/usr/bin/env vpython3
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
Edward Lesmese79107e2019-10-25 22:47:33 +000010from __future__ import unicode_literals
11
12
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000013from subprocess import Popen, PIPE, STDOUT
maruel@chromium.org428342a2011-11-10 15:46:33 +000014
John Budorick0f7b2002018-01-19 15:46:17 -080015import json
maruel@chromium.org428342a2011-11-10 15:46:33 +000016import logging
17import os
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000018import re
maruel@chromium.org428342a2011-11-10 15:46:33 +000019import sys
msb@chromium.orge28e4982009-09-25 20:51:45 +000020import tempfile
maruel@chromium.org389d6de2010-09-09 14:14:37 +000021import unittest
msb@chromium.orge28e4982009-09-25 20:51:45 +000022
Edward Lemur979fa782019-08-13 22:44:05 +000023if sys.version_info.major == 2:
24 from cStringIO import StringIO
25else:
26 from io import StringIO
27
maruel@chromium.org428342a2011-11-10 15:46:33 +000028sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000029
Edward Lemur979fa782019-08-13 22:44:05 +000030from third_party import mock
Edward Lemurd64781e2018-07-11 23:09:55 +000031from testing_support import fake_repos
Edward Lemur9cafbf42019-08-15 22:03:35 +000032from testing_support import test_case_utils
maruel@chromium.org428342a2011-11-10 15:46:33 +000033
msb@chromium.orge28e4982009-09-25 20:51:45 +000034import gclient_scm
Edward Lesmese79107e2019-10-25 22:47:33 +000035import gclient_utils
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000036import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000037import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000038
Edward Lesmese79107e2019-10-25 22:47:33 +000039
40GIT = 'git' if sys.platform != 'win32' else 'git.bat'
41
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000042# Disable global git cache
43git_cache.Mirror.SetCachePath(None)
44
maruel@chromium.org795a8c12010-10-05 19:54:29 +000045# Shortcut since this function is used often
46join = gclient_scm.os.path.join
47
Raul Tambrea79f0e52019-09-21 07:27:39 +000048TIMESTAMP_RE = re.compile(r'\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000049def strip_timestamps(value):
50 lines = value.splitlines(True)
Edward Lemur979fa782019-08-13 22:44:05 +000051 for i in range(len(lines)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000052 m = TIMESTAMP_RE.match(lines[i])
53 if m:
54 lines[i] = m.group(1)
55 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000056
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000057
Edward Lemur979fa782019-08-13 22:44:05 +000058class BasicTests(unittest.TestCase):
59 @mock.patch('gclient_scm.scm.GIT.Capture')
60 def testGetFirstRemoteUrl(self, mockCapture):
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000061 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
62 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
63 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
64 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
65 ('remote.origin.url git://what/"do', 'git://what/"do')]
66 FAKE_PATH = '/fake/path'
Edward Lemur979fa782019-08-13 22:44:05 +000067 mockCapture.side_effect = [question for question, _ in REMOTE_STRINGS]
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000068
69 for _, answer in REMOTE_STRINGS:
Edward Lemur979fa782019-08-13 22:44:05 +000070 self.assertEqual(
71 gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH), answer)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000072
Edward Lemur979fa782019-08-13 22:44:05 +000073 expected_calls = [
74 mock.call(['config', '--local', '--get-regexp', r'remote.*.url'],
75 cwd=FAKE_PATH)
76 for _ in REMOTE_STRINGS
77 ]
78 self.assertEqual(mockCapture.mock_calls, expected_calls)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000079
80
Edward Lemur9cafbf42019-08-15 22:03:35 +000081class BaseGitWrapperTestCase(unittest.TestCase, test_case_utils.TestCaseUtils):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000082 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +000083 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +000084 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +000085 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000086 self.verbose = verbose
87 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +000088 self.deps_os = None
89 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +000090 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000091 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +000092 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +000093 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +000094 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +000095 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +000096 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +000097 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +000098 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -040099 self.patch_ref = None
100 self.patch_repo = None
101 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +0000102 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000103
104 sample_git_import = """blob
105mark :1
106data 6
107Hello
108
109blob
110mark :2
111data 4
112Bye
113
114reset refs/heads/master
115commit refs/heads/master
116mark :3
117author Bob <bob@example.com> 1253744361 -0700
118committer Bob <bob@example.com> 1253744361 -0700
119data 8
120A and B
121M 100644 :1 a
122M 100644 :2 b
123
124blob
125mark :4
126data 10
127Hello
128You
129
130blob
131mark :5
132data 8
133Bye
134You
135
136commit refs/heads/origin
137mark :6
138author Alice <alice@example.com> 1253744424 -0700
139committer Alice <alice@example.com> 1253744424 -0700
140data 13
141Personalized
142from :3
143M 100644 :4 a
144M 100644 :5 b
145
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000146blob
147mark :7
148data 5
149Mooh
150
151commit refs/heads/feature
152mark :8
153author Bob <bob@example.com> 1390311986 -0000
154committer Bob <bob@example.com> 1390311986 -0000
155data 6
156Add C
157from :3
158M 100644 :7 c
159
msb@chromium.orge28e4982009-09-25 20:51:45 +0000160reset refs/heads/master
161from :3
162"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000163 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000164 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000165
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000166 def checkstdout(self, expected):
167 value = sys.stdout.getvalue()
168 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000169 # Check that the expected output appears.
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800170 # pylint: disable=no-member
Erik Chene16ffff2019-10-14 20:35:53 +0000171 self.assertIn(expected, strip_timestamps(value))
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000172
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000173 @staticmethod
174 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000175 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000176 try:
Edward Lesmese79107e2019-10-25 22:47:33 +0000177 Popen([GIT, 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000178 cwd=path).communicate()
179 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000180 # git is not available, skip this test.
181 return False
Edward Lesmese79107e2019-10-25 22:47:33 +0000182 Popen([GIT, 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
Edward Lemur979fa782019-08-13 22:44:05 +0000183 stderr=STDOUT, cwd=path).communicate(input=git_import.encode())
Edward Lesmese79107e2019-10-25 22:47:33 +0000184 Popen([GIT, 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000185 cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000186 Popen([GIT, 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000187 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000188 Popen([GIT, 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000189 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000190 Popen([GIT, 'push', 'origin', 'origin/origin:origin/master', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000191 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000192 Popen([GIT, 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000193 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000194 Popen([GIT, 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000195 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000196 Popen([GIT, 'config', 'user.name', 'Some User'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000197 stderr=STDOUT, cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000198 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000199
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000200 def _GetAskForDataCallback(self, expected_prompt, return_value):
201 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000202 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000203 return return_value
204 return AskForData
205
msb@chromium.orge28e4982009-09-25 20:51:45 +0000206 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000207 unittest.TestCase.setUp(self)
Edward Lemur9cafbf42019-08-15 22:03:35 +0000208 test_case_utils.TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000209 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000210 # The .git suffix allows gclient_scm to recognize the dir as a git repo
211 # when cloning it locally
212 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000213 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000214 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000215 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
mukai@chromium.org9e3e82c2012-04-18 12:55:43 +0000216 self._original_GitBinaryExists = gclient_scm.GitWrapper.BinaryExists
Edward Lemur979fa782019-08-13 22:44:05 +0000217 mock.patch('gclient_scm.GitWrapper.BinaryExists',
218 staticmethod(lambda : True)).start()
219 mock.patch('sys.stdout', StringIO()).start()
220 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000221 self.addCleanup(gclient_utils.rmtree, self.root_dir)
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000222
msb@chromium.orge28e4982009-09-25 20:51:45 +0000223
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000224class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000225
226 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000227 if not self.enabled:
228 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000229 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000230 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800231 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
232 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000233 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000234 scm.update(options, None, file_list)
235 gclient_scm.os.remove(file_path)
236 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000237 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000238 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000239 file_list = []
240 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000241 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000242 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000243
244 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000245 if not self.enabled:
246 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000247 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800248 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
249 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000250 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000251 scm.update(options, None, file_list)
252 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000253 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000254 self.assertEqual(file_list, [])
255 self.assertEqual(scm.revinfo(options, self.args, None),
256 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000257 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000258
259 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000260 if not self.enabled:
261 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000262 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800263 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
264 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000265 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000266 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000267 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000268 with open(file_path, 'a') as f:
269 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000270 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000271 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000272 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000273 file_list = []
274 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000275 self.assertEqual(file_list, [])
276 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000277 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000278 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000279
280 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000281 if not self.enabled:
282 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000283 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800284 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
285 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000286 file_list = []
287 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000288 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000289 with open(file_path, 'w') as f:
290 f.writelines('new\n')
Edward Lesmese79107e2019-10-25 22:47:33 +0000291 Popen([GIT, 'add', 'c'], stdout=PIPE,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000292 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000293 file_list = []
294 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000295 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000296 file_list = []
297 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000298 self.assertEqual(file_list, [])
299 self.assertEqual(scm.revinfo(options, self.args, None),
300 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000301 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000302
303 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000304 if not self.enabled:
305 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000306 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000307 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000308 with open(file_path, 'a') as f:
309 f.writelines('touched\n')
Anthony Politobb457342019-11-15 22:26:01 +0000310 scm = gclient_scm.GitWrapper(
311 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
312 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000313 file_list = []
314 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000315 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000316 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800317 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000318 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000319 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000320
Anthony Politobb457342019-11-15 22:26:01 +0000321
322 def testStatusNewNoBaseRev(self):
323 if not self.enabled:
324 return
325 options = self.Options()
326 file_path = join(self.base_path, 'a')
327 with open(file_path, 'a') as f:
328 f.writelines('touched\n')
329 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
330 file_list = []
331 scm.status(options, self.args, file_list)
332 self.assertEqual(file_list, [file_path])
333 self.checkstdout(
334 ('\n________ running \'git -c core.quotePath=false diff --name-status'
335 '\' in \'%s\'\n\nM\ta\n') % join(self.root_dir, '.'))
336
msb@chromium.orge28e4982009-09-25 20:51:45 +0000337 def testStatus2New(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000338 if not self.enabled:
339 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000340 options = self.Options()
341 expected_file_list = []
342 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000343 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000344 with open(file_path, 'a') as f:
345 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000346 expected_file_list.extend([file_path])
Anthony Politobb457342019-11-15 22:26:01 +0000347 scm = gclient_scm.GitWrapper(
348 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
349 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000350 file_list = []
351 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000352 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000353 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000354 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800355 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000356 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
357 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000358
msb@chromium.orge28e4982009-09-25 20:51:45 +0000359 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000360 if not self.enabled:
361 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000362 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000363 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800364 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
365 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000366 file_list = []
367 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000368 self.assertEqual(file_list, expected_file_list)
369 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000370 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000371 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000372
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000373 def testUpdateMerge(self):
374 if not self.enabled:
375 return
376 options = self.Options()
377 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800378 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
379 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000380 scm._Run(['checkout', '-q', 'feature'], options)
381 rev = scm.revinfo(options, (), None)
382 file_list = []
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 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000388 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
389 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000390 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
391 self.assertEqual(scm._Capture(['rev-parse', parent + '1']), rev)
392 self.assertEqual(scm._Capture(['rev-parse', parent + '2']),
Edward Lemur979fa782019-08-13 22:44:05 +0000393 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000394 sys.stdout.close()
395
396 def testUpdateRebase(self):
397 if not self.enabled:
398 return
399 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800400 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
401 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000402 scm._Run(['checkout', '-q', 'feature'], options)
403 file_list = []
404 # Fake a 'y' key press.
405 scm._AskForData = self._GetAskForDataCallback(
406 'Cannot fast-forward merge, attempt to rebase? '
407 '(y)es / (q)uit / (s)kip : ', 'y')
408 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000409 self.assertEqual(file_list, [join(self.base_path, x)
410 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000411 # The actual commit that is created is unstable, so we verify its tree and
412 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000413 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
414 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000415 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
416 self.assertEqual(scm._Capture(['rev-parse', parent + '1']),
Edward Lemur979fa782019-08-13 22:44:05 +0000417 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000418 sys.stdout.close()
419
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000420 def testUpdateReset(self):
421 if not self.enabled:
422 return
423 options = self.Options()
424 options.reset = True
425
426 dir_path = join(self.base_path, 'c')
427 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000428 with open(join(dir_path, 'nested'), 'w') as f:
429 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000430
431 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000432 with open(file_path, 'w') as f:
433 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000434
John Budorick0f7b2002018-01-19 15:46:17 -0800435 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
436 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000437 file_list = []
438 scm.update(options, (), file_list)
439 self.assert_(gclient_scm.os.path.isdir(dir_path))
440 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000441 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000442
Edward Lemur579c9862018-07-13 23:17:51 +0000443 def testUpdateResetUnsetsFetchConfig(self):
444 if not self.enabled:
445 return
446 options = self.Options()
447 options.reset = True
448
449 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
450 self.relpath)
451 scm._Run(['config', 'remote.origin.fetch',
452 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
453
454 file_list = []
455 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000456 self.assertEqual(scm.revinfo(options, (), None),
457 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000458 sys.stdout.close()
459
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000460 def testUpdateResetDeleteUnversionedTrees(self):
461 if not self.enabled:
462 return
463 options = self.Options()
464 options.reset = True
465 options.delete_unversioned_trees = True
466
467 dir_path = join(self.base_path, 'dir')
468 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000469 with open(join(dir_path, 'nested'), 'w') as f:
470 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000471
472 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000473 with open(file_path, 'w') as f:
474 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000475
John Budorick0f7b2002018-01-19 15:46:17 -0800476 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
477 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000478 file_list = []
479 scm.update(options, (), file_list)
480 self.assert_(not gclient_scm.os.path.isdir(dir_path))
481 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000482 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000483
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000484 def testUpdateUnstagedConflict(self):
485 if not self.enabled:
486 return
487 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800488 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
489 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000490 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000491 with open(file_path, 'w') as f:
492 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000493 try:
494 scm.update(options, (), [])
495 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000496 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000497 # The exact exception text varies across git versions so it's not worth
498 # verifying it. It's fine as long as it throws.
499 pass
500 # Manually flush stdout since we can't verify it's content accurately across
501 # git versions.
502 sys.stdout.getvalue()
503 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000504
Mike Stipicevice992b612016-12-02 15:32:55 -0800505 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000506 def testUpdateLocked(self):
507 if not self.enabled:
508 return
509 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800510 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
511 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000512 file_path = join(self.base_path, '.git', 'index.lock')
513 with open(file_path, 'w'):
514 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800515 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000516 scm.update(options, (), [])
517 sys.stdout.close()
518
519 def testUpdateLockedBreak(self):
520 if not self.enabled:
521 return
522 options = self.Options()
523 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800524 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
525 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000526 file_path = join(self.base_path, '.git', 'index.lock')
527 with open(file_path, 'w'):
528 pass
529 scm.update(options, (), [])
530 self.assertRegexpMatches(sys.stdout.getvalue(),
Raul Tambrea79f0e52019-09-21 07:27:39 +0000531 r'breaking lock.*\.git[/|\\]index\.lock')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000532 self.assertFalse(os.path.exists(file_path))
533 sys.stdout.close()
534
msb@chromium.org5bde4852009-12-14 16:47:12 +0000535 def testUpdateConflict(self):
536 if not self.enabled:
537 return
538 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800539 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
540 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000541 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000542 with open(file_path, 'w') as f:
543 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000544 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000545 scm._AskForData = self._GetAskForDataCallback(
546 'Cannot fast-forward merge, attempt to rebase? '
547 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000548
549 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
550 scm.update(options, (), [])
551 self.assertEqual(
552 e.exception.args[0],
553 'Conflict while rebasing this branch.\n'
554 'Fix the conflict and run gclient again.\n'
555 'See \'man git-rebase\' for details.\n')
556
557 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
558 scm.update(options, (), [])
559 self.assertEqual(
560 e.exception.args[0],
561 '\n____ . at refs/remotes/origin/master\n'
562 '\tYou have unstaged changes.\n'
563 '\tPlease commit, stash, or reset.\n')
564
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000565 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000566
msb@chromium.org0f282062009-11-06 20:14:02 +0000567 def testRevinfo(self):
568 if not self.enabled:
569 return
570 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800571 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
572 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000573 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000574 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000575
msb@chromium.orge28e4982009-09-25 20:51:45 +0000576
Edward Lemur979fa782019-08-13 22:44:05 +0000577class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000578 class OptionsObject(object):
579 def __init__(self, verbose=False, revision=None, force=False):
580 self.verbose = verbose
581 self.revision = revision
582 self.deps_os = None
583 self.force = force
584 self.reset = False
585 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000586 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000587 # TODO(maruel): Test --jobs > 1.
588 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400589 self.patch_ref = None
590 self.patch_repo = None
591 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000592
593 def Options(self, *args, **kwargs):
594 return self.OptionsObject(*args, **kwargs)
595
borenet@google.comb09097a2014-04-09 19:09:08 +0000596 def checkstdout(self, expected):
597 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000598 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000599 # Check that the expected output appears.
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800600 # pylint: disable=no-member
Erik Chene16ffff2019-10-14 20:35:53 +0000601 self.assertIn(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000602
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000603 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000604 self.fake_hash_1 = 't0ta11yf4k3'
605 self.fake_hash_2 = '3v3nf4k3r'
606 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000607 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000608 self.relpath = 'fake'
609 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000610 self.backup_base_path = os.path.join(self.root_dir,
611 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000612 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
613 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
614 mock.patch('gclient_scm.GitWrapper._Fetch').start()
615 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
616 mock.patch('sys.stdout', StringIO()).start()
617 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000618
Edward Lemur979fa782019-08-13 22:44:05 +0000619 @mock.patch('scm.GIT.IsValidRevision')
620 @mock.patch('os.path.isdir', lambda _: True)
621 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800622 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700623 options = self.Options(verbose=True)
624
Edward Lemur979fa782019-08-13 22:44:05 +0000625 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700626
John Budorick0f7b2002018-01-19 15:46:17 -0800627 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
628 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700629 # A [fake] git sha1 with a git repo should work (this is in the case that
630 # the LKGR gets flipped to git sha1's some day).
Edward Lemur979fa782019-08-13 22:44:05 +0000631 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
632 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700633 # An SVN rev with an existing purely git repo should raise an exception.
634 self.assertRaises(gclient_scm.gclient_utils.Error,
635 git_scm.GetUsableRev, '1', options)
636
Edward Lemur979fa782019-08-13 22:44:05 +0000637 @mock.patch('gclient_scm.GitWrapper._Clone')
638 @mock.patch('os.path.isdir')
639 @mock.patch('os.path.exists')
640 @mock.patch('subprocess2.check_output')
641 def testUpdateNoDotGit(
642 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
643 mockIsdir.side_effect = lambda path: path == self.base_path
644 mockExists.side_effect = lambda path: path == self.base_path
645 mockCheckOutput.return_value = b''
646
borenet@google.comb09097a2014-04-09 19:09:08 +0000647 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000648 scm = gclient_scm.GitWrapper(
649 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000650 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000651
652 env = gclient_scm.scm.GIT.ApplyEnvVars({})
653 self.assertEqual(
654 mockCheckOutput.mock_calls,
655 [
656 mock.call(
657 ['git', '-c', 'core.quotePath=false', 'ls-files'],
658 cwd=self.base_path, env=env, stderr=-1),
659 mock.call(
660 ['git', 'rev-parse', '--verify', 'HEAD'],
661 cwd=self.base_path, env=env, stderr=-1),
662 ])
663 mockClone.assert_called_with(
664 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000665 self.checkstdout('\n')
666
Edward Lemur979fa782019-08-13 22:44:05 +0000667 @mock.patch('gclient_scm.GitWrapper._Clone')
668 @mock.patch('os.path.isdir')
669 @mock.patch('os.path.exists')
670 @mock.patch('subprocess2.check_output')
671 def testUpdateConflict(
672 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
673 mockIsdir.side_effect = lambda path: path == self.base_path
674 mockExists.side_effect = lambda path: path == self.base_path
675 mockCheckOutput.return_value = b''
676 mockClone.side_effect = [
677 gclient_scm.subprocess2.CalledProcessError(
678 None, None, None, None, None),
679 None,
680 ]
681
borenet@google.com90fe58b2014-05-01 18:22:00 +0000682 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000683 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800684 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000685 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000686
687 env = gclient_scm.scm.GIT.ApplyEnvVars({})
688 self.assertEqual(
689 mockCheckOutput.mock_calls,
690 [
691 mock.call(
692 ['git', '-c', 'core.quotePath=false', 'ls-files'],
693 cwd=self.base_path, env=env, stderr=-1),
694 mock.call(
695 ['git', 'rev-parse', '--verify', 'HEAD'],
696 cwd=self.base_path, env=env, stderr=-1),
697 ])
698 mockClone.assert_called_with(
699 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000700 self.checkstdout('\n')
701
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000702
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000703class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000704 def checkInStdout(self, expected):
705 value = sys.stdout.getvalue()
706 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800707 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000708 self.assertIn(expected, value)
709
710 def checkNotInStdout(self, expected):
711 value = sys.stdout.getvalue()
712 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800713 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000714 self.assertNotIn(expected, value)
715
smut@google.com27c9c8a2014-09-11 19:57:55 +0000716 def getCurrentBranch(self):
717 # Returns name of current branch or HEAD for detached HEAD
718 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
719 cwd=self.base_path)
720 if branch == 'HEAD':
721 return None
722 return branch
723
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000724 def testUpdateClone(self):
725 if not self.enabled:
726 return
727 options = self.Options()
728
729 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000730 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
731
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000732 self.root_dir = tempfile.mkdtemp()
733 self.relpath = '.'
734 self.base_path = join(self.root_dir, self.relpath)
735
John Budorick0f7b2002018-01-19 15:46:17 -0800736 scm = gclient_scm.GitWrapper(origin_root_dir,
737 self.root_dir,
738 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000739
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000740 expected_file_list = [join(self.base_path, "a"),
741 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000742 file_list = []
743 options.revision = 'unmanaged'
744 scm.update(options, (), file_list)
745
Edward Lemur979fa782019-08-13 22:44:05 +0000746 self.assertEqual(file_list, expected_file_list)
747 self.assertEqual(scm.revinfo(options, (), None),
748 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000749 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000750 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000751 self.checkInStdout(
752 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000753
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000754
755 def testUpdateCloneOnCommit(self):
756 if not self.enabled:
757 return
758 options = self.Options()
759
760 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000761 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
762
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000763 self.root_dir = tempfile.mkdtemp()
764 self.relpath = '.'
765 self.base_path = join(self.root_dir, self.relpath)
766 url_with_commit_ref = origin_root_dir +\
767 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
768
John Budorick0f7b2002018-01-19 15:46:17 -0800769 scm = gclient_scm.GitWrapper(url_with_commit_ref,
770 self.root_dir,
771 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000772
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000773 expected_file_list = [join(self.base_path, "a"),
774 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000775 file_list = []
776 options.revision = 'unmanaged'
777 scm.update(options, (), file_list)
778
Edward Lemur979fa782019-08-13 22:44:05 +0000779 self.assertEqual(file_list, expected_file_list)
780 self.assertEqual(scm.revinfo(options, (), None),
781 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000782 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000783 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000784 self.checkInStdout(
785 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000786
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000787 def testUpdateCloneOnBranch(self):
788 if not self.enabled:
789 return
790 options = self.Options()
791
792 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000793 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
794
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000795 self.root_dir = tempfile.mkdtemp()
796 self.relpath = '.'
797 self.base_path = join(self.root_dir, self.relpath)
798 url_with_branch_ref = origin_root_dir + '@feature'
799
John Budorick0f7b2002018-01-19 15:46:17 -0800800 scm = gclient_scm.GitWrapper(url_with_branch_ref,
801 self.root_dir,
802 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000803
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000804 expected_file_list = [join(self.base_path, "a"),
805 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000806 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000807 file_list = []
808 options.revision = 'unmanaged'
809 scm.update(options, (), file_list)
810
Edward Lemur979fa782019-08-13 22:44:05 +0000811 self.assertEqual(file_list, expected_file_list)
812 self.assertEqual(scm.revinfo(options, (), None),
813 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200814 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000815 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200816 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000817 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200818 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000819
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000820 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000821 if not self.enabled:
822 return
823 options = self.Options()
824
825 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000826 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
827
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000828 self.root_dir = tempfile.mkdtemp()
829 self.relpath = '.'
830 self.base_path = join(self.root_dir, self.relpath)
831 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
832
John Budorick0f7b2002018-01-19 15:46:17 -0800833 scm = gclient_scm.GitWrapper(url_with_branch_ref,
834 self.root_dir,
835 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000836
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000837 expected_file_list = [join(self.base_path, "a"),
838 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000839 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000840 file_list = []
841 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000842 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000843
Edward Lemur979fa782019-08-13 22:44:05 +0000844 self.assertEqual(file_list, expected_file_list)
845 self.assertEqual(scm.revinfo(options, (), None),
846 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000847 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000848 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000849 self.checkInStdout(
850 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000851
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000852 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000853 if not self.enabled:
854 return
855 options = self.Options()
856
857 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000858 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
859
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000860 self.root_dir = tempfile.mkdtemp()
861 self.relpath = '.'
862 self.base_path = join(self.root_dir, self.relpath)
863 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
864
John Budorick0f7b2002018-01-19 15:46:17 -0800865 scm = gclient_scm.GitWrapper(url_with_branch_ref,
866 self.root_dir,
867 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000868
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000869 expected_file_list = [join(self.base_path, "a"),
870 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000871 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000872 file_list = []
873 options.revision = 'unmanaged'
874 scm.update(options, (), file_list)
875
Edward Lemur979fa782019-08-13 22:44:05 +0000876 self.assertEqual(file_list, expected_file_list)
877 self.assertEqual(scm.revinfo(options, (), None),
878 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000879 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000880 # should be treated as such by gclient.
881 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
882 # as they are known in the upstream repo, since the mapping into the local
883 # repo can be modified by users (or we might even want to change the gclient
884 # defaults at some point). But that will take more work to stop using
885 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
886 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000887 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000888 self.checkInStdout(
889 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000890
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000891 def testUpdateUpdate(self):
892 if not self.enabled:
893 return
894 options = self.Options()
895 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800896 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
897 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000898 file_list = []
899 options.revision = 'unmanaged'
900 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000901 self.assertEqual(file_list, expected_file_list)
902 self.assertEqual(scm.revinfo(options, (), None),
903 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000904 self.checkstdout('________ unmanaged solution; skipping .\n')
905
906
Edward Lemur979fa782019-08-13 22:44:05 +0000907class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800908
909 def setUp(self):
910 # Create this before setting up mocks.
911 self._cipd_root_dir = tempfile.mkdtemp()
912 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800913
914 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
915 self._cipd_root = gclient_scm.CipdRoot(
916 self._cipd_root_dir,
917 self._cipd_instance_url)
918 self._cipd_packages = [
919 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
920 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
921 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
922 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000923 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
924 mock.patch('gclient_scm.CipdRoot.add_package').start()
925 mock.patch('gclient_scm.CipdRoot.clobber').start()
926 mock.patch('gclient_scm.CipdRoot.ensure').start()
927 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000928 self.addCleanup(gclient_utils.rmtree, self._cipd_root_dir)
929 self.addCleanup(gclient_utils.rmtree, self._workdir)
John Budorick0f7b2002018-01-19 15:46:17 -0800930
931 def createScmWithPackageThatSatisfies(self, condition):
932 return gclient_scm.CipdWrapper(
933 url=self._cipd_instance_url,
934 root_dir=self._cipd_root_dir,
935 relpath='fake_relpath',
936 root=self._cipd_root,
937 package=self.getPackageThatSatisfies(condition))
938
939 def getPackageThatSatisfies(self, condition):
940 for p in self._cipd_packages:
941 if condition(p):
942 return p
943
944 self.fail('Unable to find a satisfactory package.')
945
John Budorick0f7b2002018-01-19 15:46:17 -0800946 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700947 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000948 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800949 scm.revert(None, (), [])
950
Edward Lemur979fa782019-08-13 22:44:05 +0000951 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
952 @mock.patch('gclient_scm.gclient_utils.rmtree')
953 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -0800954 """Checks that revinfo uses the JSON from cipd describe."""
955 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
956
957 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
958 json_contents = {
959 'result': {
960 'pin': {
961 'instance_id': expected_revinfo,
962 }
963 }
964 }
965 describe_json_path = join(self._workdir, 'describe.json')
966 with open(describe_json_path, 'w') as describe_json:
967 json.dump(json_contents, describe_json)
968
Edward Lemur979fa782019-08-13 22:44:05 +0000969 revinfo = scm.revinfo(None, (), [])
970 self.assertEqual(revinfo, expected_revinfo)
971
972 mockRmtree.assert_called_with(self._workdir)
973 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -0800974 'cipd', 'describe', 'foo_package',
975 '-log-level', 'error',
976 '-version', 'foo_version',
977 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +0000978 ])
John Budorick0f7b2002018-01-19 15:46:17 -0800979
980 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700981 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000982 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800983 scm.update(None, (), [])
984
985
Edward Lemurd64781e2018-07-11 23:09:55 +0000986class GerritChangesFakeRepo(fake_repos.FakeReposBase):
987 def populateGit(self):
988 # Creates a tree that looks like this:
989 #
Edward Lemurca7d8812018-07-24 17:42:45 +0000990 # 6 refs/changes/35/1235/1
991 # |
992 # 5 refs/changes/34/1234/1
993 # |
Edward Lemurd64781e2018-07-11 23:09:55 +0000994 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +0000995 # | |
996 # | 11(5)--12 refs/heads/master-with-5
997 # |
998 # 7--8--9 refs/heads/feature
999 # |
1000 # 10 refs/changes/36/1236/1
1001 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001002
1003 self._commit_git('repo_1', {'commit 1': 'touched'})
1004 self._commit_git('repo_1', {'commit 2': 'touched'})
1005 self._commit_git('repo_1', {'commit 3': 'touched'})
1006 self._commit_git('repo_1', {'commit 4': 'touched'})
1007 self._create_ref('repo_1', 'refs/heads/master', 4)
1008
1009 # Create a change on top of commit 3 that consists of two commits.
1010 self._commit_git('repo_1',
1011 {'commit 5': 'touched',
1012 'change': '1234'},
1013 base=3)
1014 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1015 self._commit_git('repo_1',
1016 {'commit 6': 'touched',
1017 'change': '1235'})
1018 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1019
Edward Lemurca7d8812018-07-24 17:42:45 +00001020 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1021 # commits.
1022 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1023 self._commit_git('repo_1', {'commit 8': 'touched'})
1024 self._commit_git('repo_1', {'commit 9': 'touched'})
1025 self._create_ref('repo_1', 'refs/heads/feature', 9)
1026
1027 # Create a change of top of commit 8.
1028 self._commit_git('repo_1',
1029 {'commit 10': 'touched',
1030 'change': '1236'},
1031 base=8)
1032 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1033
1034 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1035 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1036 self._commit_git('repo_1',
1037 # This is really commit 11, but has the changes of commit 5
1038 {'commit 5': 'touched',
1039 'change': '1234'},
1040 base=3)
1041 self._commit_git('repo_1', {'commit 12': 'touched'})
1042 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1043
Edward Lemurd64781e2018-07-11 23:09:55 +00001044
1045class GerritChangesTest(fake_repos.FakeReposTestBase):
1046 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1047
1048 def setUp(self):
1049 super(GerritChangesTest, self).setUp()
1050 self.enabled = self.FAKE_REPOS.set_up_git()
1051 self.options = BaseGitWrapperTestCase.OptionsObject()
1052 self.url = self.git_base + 'repo_1'
1053 self.mirror = None
Edward Lesmese79107e2019-10-25 22:47:33 +00001054 mock.patch('sys.stdout').start()
1055 self.addCleanup(mock.patch.stopall)
Edward Lemurd64781e2018-07-11 23:09:55 +00001056
1057 def setUpMirror(self):
1058 self.mirror = tempfile.mkdtemp()
1059 git_cache.Mirror.SetCachePath(self.mirror)
Edward Lesmese79107e2019-10-25 22:47:33 +00001060 self.addCleanup(gclient_utils.rmtree, self.mirror)
Edward Lemurd64781e2018-07-11 23:09:55 +00001061 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1062
Edward Lemurca7d8812018-07-24 17:42:45 +00001063 def assertCommits(self, commits):
1064 """Check that all, and only |commits| are present in the current checkout.
1065 """
1066 for i in commits:
1067 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001068 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001069
1070 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1071 for i in all_commits - set(commits):
1072 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001073 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001074
Edward Lemurd64781e2018-07-11 23:09:55 +00001075 def testCanCloneGerritChange(self):
1076 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1077 file_list = []
1078
1079 self.options.revision = 'refs/changes/35/1235/1'
1080 scm.update(self.options, None, file_list)
1081 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1082
1083 def testCanSyncToGerritChange(self):
1084 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1085 file_list = []
1086
1087 self.options.revision = self.githash('repo_1', 1)
1088 scm.update(self.options, None, file_list)
1089 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1090
1091 self.options.revision = 'refs/changes/35/1235/1'
1092 scm.update(self.options, None, file_list)
1093 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1094
1095 def testCanCloneGerritChangeMirror(self):
1096 self.setUpMirror()
1097 self.testCanCloneGerritChange()
1098
1099 def testCanSyncToGerritChangeMirror(self):
1100 self.setUpMirror()
1101 self.testCanSyncToGerritChange()
1102
Edward Lesmese79107e2019-10-25 22:47:33 +00001103 def testMirrorPushUrl(self):
1104 self.setUpMirror()
1105
1106 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1107 file_list = []
1108 self.assertIsNotNone(scm._GetMirror(self.url, self.options))
1109
1110 scm.update(self.options, None, file_list)
1111
1112 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
1113 self.assertTrue(
1114 fetch_url.startswith(self.mirror),
1115 msg='\n'.join([
1116 'Repository fetch url should be in the git cache mirror directory.',
1117 ' fetch_url: %s' % fetch_url,
1118 ' mirror: %s' % self.mirror]))
1119 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
1120 self.assertEqual(push_url, self.url)
1121
Edward Lemurca7d8812018-07-24 17:42:45 +00001122 def testAppliesPatchOnTopOfMasterByDefault(self):
1123 """Test the default case, where we apply a patch on top of master."""
1124 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1125 file_list = []
1126
1127 # Make sure we don't specify a revision.
1128 self.options.revision = None
1129 scm.update(self.options, None, file_list)
1130 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1131
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001132 scm.apply_patch_ref(
1133 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1134 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001135
1136 self.assertCommits([1, 2, 3, 4, 5, 6])
1137 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1138
1139 def testCheckoutOlderThanPatchBase(self):
1140 """Test applying a patch on an old checkout.
1141
1142 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1143 contains commits 5 and 6, and is based on top of commit 3.
1144 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1145 """
1146 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1147 file_list = []
1148
1149 # Sync to commit 1
1150 self.options.revision = self.githash('repo_1', 1)
1151 scm.update(self.options, None, file_list)
1152 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1153
1154 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001155 scm.apply_patch_ref(
1156 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1157 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001158
1159 self.assertCommits([1, 5, 6])
1160 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1161
1162 def testCheckoutOriginFeature(self):
1163 """Tests that we can apply a patch on a branch other than master."""
1164 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1165 file_list = []
1166
Edward Lemur8c665652019-05-08 20:23:33 +00001167 # Sync to remote's refs/heads/feature
1168 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001169 scm.update(self.options, None, file_list)
1170 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1171
1172 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001173 scm.apply_patch_ref(
1174 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1175 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001176
1177 self.assertCommits([1, 2, 7, 8, 9, 10])
1178 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1179
1180 def testCheckoutOriginFeatureOnOldRevision(self):
1181 """Tests that we can apply a patch on an old checkout, on a branch other
1182 than master."""
1183 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1184 file_list = []
1185
Edward Lemur8c665652019-05-08 20:23:33 +00001186 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001187 self.options.revision = self.githash('repo_1', 7)
1188 scm.update(self.options, None, file_list)
1189 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1190
1191 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001192 scm.apply_patch_ref(
1193 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1194 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001195
1196 # We shouldn't have rebased on top of 2 (which is the merge base between
Edward Lemur8c665652019-05-08 20:23:33 +00001197 # remote's master branch and the change) but on top of 7 (which is the
1198 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001199 self.assertCommits([1, 2, 7, 10])
1200 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1201
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001202 def testCheckoutOriginFeaturePatchBranch(self):
1203 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1204 file_list = []
1205
Edward Lemur8c665652019-05-08 20:23:33 +00001206 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001207 self.options.revision = self.githash('repo_1', 9)
1208 scm.update(self.options, None, file_list)
1209 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1210
Edward Lemur8c665652019-05-08 20:23:33 +00001211 # Apply refs/changes/34/1234/1, created for remote's master branch on top of
1212 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001213 scm.apply_patch_ref(
1214 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1215 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001216
1217 # 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 +00001218 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001219 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1220 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001221
1222 def testDoesntRebasePatchMaster(self):
1223 """Tests that we can apply a patch without rebasing it.
1224 """
1225 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1226 file_list = []
1227
1228 self.options.rebase_patch_ref = False
1229 scm.update(self.options, None, file_list)
1230 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1231
1232 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001233 scm.apply_patch_ref(
1234 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1235 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001236
1237 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001238 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001239
1240 def testDoesntRebasePatchOldCheckout(self):
1241 """Tests that we can apply a patch without rebasing it on an old checkout.
1242 """
1243 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1244 file_list = []
1245
1246 # Sync to commit 1
1247 self.options.revision = self.githash('repo_1', 1)
1248 self.options.rebase_patch_ref = False
1249 scm.update(self.options, None, file_list)
1250 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1251
1252 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001253 scm.apply_patch_ref(
1254 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1255 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001256
1257 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001258 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001259
1260 def testDoesntSoftResetIfNotAskedTo(self):
1261 """Test that we can apply a patch without doing a soft reset."""
1262 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1263 file_list = []
1264
1265 self.options.reset_patch_ref = False
1266 scm.update(self.options, None, file_list)
1267 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1268
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001269 scm.apply_patch_ref(
1270 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1271 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001272
1273 self.assertCommits([1, 2, 3, 4, 5, 6])
1274 # The commit hash after cherry-picking is not known, but it must be
1275 # different from what the repo was synced at before patching.
1276 self.assertNotEqual(self.githash('repo_1', 4),
1277 self.gitrevparse(self.root_dir))
1278
1279 def testRecoversAfterPatchFailure(self):
1280 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1281 file_list = []
1282
1283 self.options.revision = 'refs/changes/34/1234/1'
1284 scm.update(self.options, None, file_list)
1285 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1286
1287 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1288 # patch 'refs/changes/36/1236/1' creates a patch failure.
1289 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001290 scm.apply_patch_ref(
1291 self.url, 'refs/changes/36/1236/1', 'refs/heads/master', self.options,
1292 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001293 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001294 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001295
1296 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1297 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001298 scm.apply_patch_ref(
1299 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1300 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001301 self.assertCommits([1, 2, 3, 5, 6])
1302 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1303
1304 def testIgnoresAlreadyMergedCommits(self):
1305 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1306 file_list = []
1307
1308 self.options.revision = 'refs/heads/master-with-5'
1309 scm.update(self.options, None, file_list)
1310 self.assertEqual(self.githash('repo_1', 12),
1311 self.gitrevparse(self.root_dir))
1312
1313 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1314 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1315 # already present in the tree as commit 11.
1316 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001317 scm.apply_patch_ref(
1318 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1319 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001320 self.assertCommits([1, 2, 3, 5, 6, 12])
1321 self.assertEqual(self.githash('repo_1', 12),
1322 self.gitrevparse(self.root_dir))
1323
1324 def testRecoversFromExistingCherryPick(self):
1325 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1326 file_list = []
1327
1328 self.options.revision = 'refs/changes/34/1234/1'
1329 scm.update(self.options, None, file_list)
1330 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1331
1332 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1333 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1334 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1335 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1336 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1337 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1338
1339 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1340 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001341 scm.apply_patch_ref(
1342 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1343 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001344 self.assertCommits([1, 2, 3, 5, 6])
1345 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1346
Edward Lemurd64781e2018-07-11 23:09:55 +00001347
msb@chromium.orge28e4982009-09-25 20:51:45 +00001348if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001349 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1350 logging.basicConfig(
1351 level=level,
1352 format='%(asctime).19s %(levelname)s %(filename)s:'
1353 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001354 unittest.main()
1355
1356# vim: ts=2:sw=2:tw=80:et: