blob: 81cc23329864cccb621c17bb1c72ba32f80cc46b [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
Edward Lemura8145022020-01-06 18:47:54 +000025 import mock
Edward Lemur979fa782019-08-13 22:44:05 +000026else:
27 from io import StringIO
Edward Lemura8145022020-01-06 18:47:54 +000028 from unittest import mock
Edward Lemur979fa782019-08-13 22:44:05 +000029
maruel@chromium.org428342a2011-11-10 15:46:33 +000030sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
maruel@chromium.orgba551772010-02-03 18:21:42 +000031
Edward Lemurd64781e2018-07-11 23:09:55 +000032from testing_support import fake_repos
Edward Lemur9cafbf42019-08-15 22:03:35 +000033from testing_support import test_case_utils
maruel@chromium.org428342a2011-11-10 15:46:33 +000034
msb@chromium.orge28e4982009-09-25 20:51:45 +000035import gclient_scm
Edward Lesmese79107e2019-10-25 22:47:33 +000036import gclient_utils
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000037import git_cache
maruel@chromium.orgfae707b2011-09-15 18:57:58 +000038import subprocess2
maruel@chromium.org96913eb2010-06-01 16:22:47 +000039
Edward Lesmese79107e2019-10-25 22:47:33 +000040
41GIT = 'git' if sys.platform != 'win32' else 'git.bat'
42
szager@chromium.orgb0a13a22014-06-18 00:52:25 +000043# Disable global git cache
44git_cache.Mirror.SetCachePath(None)
45
maruel@chromium.org795a8c12010-10-05 19:54:29 +000046# Shortcut since this function is used often
47join = gclient_scm.os.path.join
48
Raul Tambrea79f0e52019-09-21 07:27:39 +000049TIMESTAMP_RE = re.compile(r'\[[0-9]{1,2}:[0-9]{2}:[0-9]{2}\] (.*)', re.DOTALL)
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000050def strip_timestamps(value):
51 lines = value.splitlines(True)
Edward Lemur979fa782019-08-13 22:44:05 +000052 for i in range(len(lines)):
szager@chromium.orgfe0d1902014-04-08 20:50:44 +000053 m = TIMESTAMP_RE.match(lines[i])
54 if m:
55 lines[i] = m.group(1)
56 return ''.join(lines)
maruel@chromium.org96913eb2010-06-01 16:22:47 +000057
maruel@chromium.orgd579fcf2011-12-13 20:36:03 +000058
Edward Lemur979fa782019-08-13 22:44:05 +000059class BasicTests(unittest.TestCase):
60 @mock.patch('gclient_scm.scm.GIT.Capture')
61 def testGetFirstRemoteUrl(self, mockCapture):
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000062 REMOTE_STRINGS = [('remote.origin.url E:\\foo\\bar', 'E:\\foo\\bar'),
63 ('remote.origin.url /b/foo/bar', '/b/foo/bar'),
64 ('remote.origin.url https://foo/bar', 'https://foo/bar'),
65 ('remote.origin.url E:\\Fo Bar\\bax', 'E:\\Fo Bar\\bax'),
66 ('remote.origin.url git://what/"do', 'git://what/"do')]
67 FAKE_PATH = '/fake/path'
Edward Lemur979fa782019-08-13 22:44:05 +000068 mockCapture.side_effect = [question for question, _ in REMOTE_STRINGS]
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000069
70 for _, answer in REMOTE_STRINGS:
Edward Lemur979fa782019-08-13 22:44:05 +000071 self.assertEqual(
72 gclient_scm.SCMWrapper._get_first_remote_url(FAKE_PATH), answer)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000073
Edward Lemur979fa782019-08-13 22:44:05 +000074 expected_calls = [
75 mock.call(['config', '--local', '--get-regexp', r'remote.*.url'],
76 cwd=FAKE_PATH)
77 for _ in REMOTE_STRINGS
78 ]
79 self.assertEqual(mockCapture.mock_calls, expected_calls)
hinoka@chromium.orgfa2b9b42014-08-22 18:08:53 +000080
81
Edward Lemur9cafbf42019-08-15 22:03:35 +000082class BaseGitWrapperTestCase(unittest.TestCase, test_case_utils.TestCaseUtils):
maruel@chromium.org8ef5f542009-11-12 02:05:02 +000083 """This class doesn't use pymox."""
msb@chromium.orge28e4982009-09-25 20:51:45 +000084 class OptionsObject(object):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +000085 def __init__(self, verbose=False, revision=None):
dnj@chromium.org5b23e872015-02-20 21:25:57 +000086 self.auto_rebase = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000087 self.verbose = verbose
88 self.revision = revision
msb@chromium.orge28e4982009-09-25 20:51:45 +000089 self.deps_os = None
90 self.force = False
davemoore@chromium.org8bf27312010-02-19 17:29:44 +000091 self.reset = False
msb@chromium.orge28e4982009-09-25 20:51:45 +000092 self.nohooks = False
primiano@chromium.org5439ea52014-08-06 17:18:18 +000093 self.no_history = False
iannucci@chromium.orgd4fffee2013-06-28 00:35:26 +000094 self.upstream = False
iannucci@chromium.org53456aa2013-07-03 19:38:34 +000095 self.cache_dir = None
bauerb@chromium.org4dd09372011-07-22 14:41:51 +000096 self.merge = False
bratell@opera.com18fa4542013-05-21 13:30:46 +000097 self.jobs = 1
iannucci@chromium.org30a07982016-04-07 21:35:19 +000098 self.break_repo_locks = False
steveblock@chromium.org98e69452012-02-16 16:36:43 +000099 self.delete_unversioned_trees = False
Edward Lesmesc621b212018-03-21 20:26:56 -0400100 self.patch_ref = None
101 self.patch_repo = None
102 self.rebase_patch_ref = True
Edward Lemurca7d8812018-07-24 17:42:45 +0000103 self.reset_patch_ref = True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000104
105 sample_git_import = """blob
106mark :1
107data 6
108Hello
109
110blob
111mark :2
112data 4
113Bye
114
115reset refs/heads/master
116commit refs/heads/master
117mark :3
118author Bob <bob@example.com> 1253744361 -0700
119committer Bob <bob@example.com> 1253744361 -0700
120data 8
121A and B
122M 100644 :1 a
123M 100644 :2 b
124
125blob
126mark :4
127data 10
128Hello
129You
130
131blob
132mark :5
133data 8
134Bye
135You
136
137commit refs/heads/origin
138mark :6
139author Alice <alice@example.com> 1253744424 -0700
140committer Alice <alice@example.com> 1253744424 -0700
141data 13
142Personalized
143from :3
144M 100644 :4 a
145M 100644 :5 b
146
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000147blob
148mark :7
149data 5
150Mooh
151
152commit refs/heads/feature
153mark :8
154author Bob <bob@example.com> 1390311986 -0000
155committer Bob <bob@example.com> 1390311986 -0000
156data 6
157Add C
158from :3
159M 100644 :7 c
160
msb@chromium.orge28e4982009-09-25 20:51:45 +0000161reset refs/heads/master
162from :3
163"""
msb@chromium.orge28e4982009-09-25 20:51:45 +0000164 def Options(self, *args, **kwargs):
maruel@chromium.org8071c282010-09-20 19:44:19 +0000165 return self.OptionsObject(*args, **kwargs)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000166
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000167 def checkstdout(self, expected):
168 value = sys.stdout.getvalue()
169 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000170 # Check that the expected output appears.
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800171 # pylint: disable=no-member
Erik Chene16ffff2019-10-14 20:35:53 +0000172 self.assertIn(expected, strip_timestamps(value))
szager@chromium.orgfe0d1902014-04-08 20:50:44 +0000173
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000174 @staticmethod
175 def CreateGitRepo(git_import, path):
maruel@chromium.orgd5800f12009-11-12 20:03:43 +0000176 """Do it for real."""
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000177 try:
Edward Lesmese79107e2019-10-25 22:47:33 +0000178 Popen([GIT, 'init', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000179 cwd=path).communicate()
180 except OSError:
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000181 # git is not available, skip this test.
182 return False
Edward Lesmese79107e2019-10-25 22:47:33 +0000183 Popen([GIT, 'fast-import', '--quiet'], stdin=PIPE, stdout=PIPE,
Edward Lemur979fa782019-08-13 22:44:05 +0000184 stderr=STDOUT, cwd=path).communicate(input=git_import.encode())
Edward Lesmese79107e2019-10-25 22:47:33 +0000185 Popen([GIT, 'checkout', '-q'], stdout=PIPE, stderr=STDOUT,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000186 cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000187 Popen([GIT, 'remote', 'add', '-f', 'origin', '.'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000188 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000189 Popen([GIT, 'checkout', '-b', 'new', 'origin/master', '-q'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000190 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000191 Popen([GIT, 'push', 'origin', 'origin/origin:origin/master', '-q'],
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000192 stdout=PIPE, stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000193 Popen([GIT, 'config', '--unset', 'remote.origin.fetch'], stdout=PIPE,
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000194 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000195 Popen([GIT, 'config', 'user.email', 'someuser@chromium.org'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000196 stderr=STDOUT, cwd=path).communicate()
Edward Lesmese79107e2019-10-25 22:47:33 +0000197 Popen([GIT, 'config', 'user.name', 'Some User'], stdout=PIPE,
iannucci@chromium.org1e7187a2013-02-17 20:54:05 +0000198 stderr=STDOUT, cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000199 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000200
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000201 def _GetAskForDataCallback(self, expected_prompt, return_value):
202 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000203 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000204 return return_value
205 return AskForData
206
msb@chromium.orge28e4982009-09-25 20:51:45 +0000207 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000208 unittest.TestCase.setUp(self)
Edward Lemur9cafbf42019-08-15 22:03:35 +0000209 test_case_utils.TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000210 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000211 # The .git suffix allows gclient_scm to recognize the dir as a git repo
212 # when cloning it locally
213 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000214 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000215 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000216 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000217 mock.patch('sys.stdout', StringIO()).start()
218 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000219 self.addCleanup(gclient_utils.rmtree, self.root_dir)
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000220
msb@chromium.orge28e4982009-09-25 20:51:45 +0000221
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000222class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000223
224 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000225 if not self.enabled:
226 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000227 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000228 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800229 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
230 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000231 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000232 scm.update(options, None, file_list)
233 gclient_scm.os.remove(file_path)
234 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000235 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000236 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000237 file_list = []
238 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000239 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000240 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000241
242 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000243 if not self.enabled:
244 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000245 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800246 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
247 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000248 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000249 scm.update(options, None, file_list)
250 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000251 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000252 self.assertEqual(file_list, [])
253 self.assertEqual(scm.revinfo(options, self.args, None),
254 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000255 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000256
257 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000258 if not self.enabled:
259 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000260 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800261 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
262 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000263 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000264 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000265 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000266 with open(file_path, 'a') as f:
267 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000268 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000269 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000270 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000271 file_list = []
272 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000273 self.assertEqual(file_list, [])
274 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000275 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000276 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000277
278 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000279 if not self.enabled:
280 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000281 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800282 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
283 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000284 file_list = []
285 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000286 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000287 with open(file_path, 'w') as f:
288 f.writelines('new\n')
Edward Lesmese79107e2019-10-25 22:47:33 +0000289 Popen([GIT, 'add', 'c'], stdout=PIPE,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000290 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000291 file_list = []
292 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000293 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000294 file_list = []
295 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000296 self.assertEqual(file_list, [])
297 self.assertEqual(scm.revinfo(options, self.args, None),
298 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000299 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000300
301 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000302 if not self.enabled:
303 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000304 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000305 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000306 with open(file_path, 'a') as f:
307 f.writelines('touched\n')
Anthony Politobb457342019-11-15 22:26:01 +0000308 scm = gclient_scm.GitWrapper(
309 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
310 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000311 file_list = []
312 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000313 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000314 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800315 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000316 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000317 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000318
Anthony Politobb457342019-11-15 22:26:01 +0000319
320 def testStatusNewNoBaseRev(self):
321 if not self.enabled:
322 return
323 options = self.Options()
324 file_path = join(self.base_path, 'a')
325 with open(file_path, 'a') as f:
326 f.writelines('touched\n')
327 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
328 file_list = []
329 scm.status(options, self.args, file_list)
330 self.assertEqual(file_list, [file_path])
331 self.checkstdout(
332 ('\n________ running \'git -c core.quotePath=false diff --name-status'
333 '\' in \'%s\'\n\nM\ta\n') % join(self.root_dir, '.'))
334
msb@chromium.orge28e4982009-09-25 20:51:45 +0000335 def testStatus2New(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()
339 expected_file_list = []
340 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000341 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000342 with open(file_path, 'a') as f:
343 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000344 expected_file_list.extend([file_path])
Anthony Politobb457342019-11-15 22:26:01 +0000345 scm = gclient_scm.GitWrapper(
346 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
347 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000348 file_list = []
349 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000350 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000351 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000352 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800353 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000354 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
355 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000356
msb@chromium.orge28e4982009-09-25 20:51:45 +0000357 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000358 if not self.enabled:
359 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000360 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000361 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800362 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
363 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000364 file_list = []
365 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000366 self.assertEqual(file_list, expected_file_list)
367 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000368 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000369 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000370
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000371 def testUpdateMerge(self):
372 if not self.enabled:
373 return
374 options = self.Options()
375 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800376 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
377 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000378 scm._Run(['checkout', '-q', 'feature'], options)
379 rev = scm.revinfo(options, (), None)
380 file_list = []
381 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000382 self.assertEqual(file_list, [join(self.base_path, x)
383 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000384 # The actual commit that is created is unstable, so we verify its tree and
385 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000386 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
387 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000388 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
389 self.assertEqual(scm._Capture(['rev-parse', parent + '1']), rev)
390 self.assertEqual(scm._Capture(['rev-parse', parent + '2']),
Edward Lemur979fa782019-08-13 22:44:05 +0000391 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000392 sys.stdout.close()
393
394 def testUpdateRebase(self):
395 if not self.enabled:
396 return
397 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800398 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
399 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000400 scm._Run(['checkout', '-q', 'feature'], options)
401 file_list = []
402 # Fake a 'y' key press.
403 scm._AskForData = self._GetAskForDataCallback(
404 'Cannot fast-forward merge, attempt to rebase? '
405 '(y)es / (q)uit / (s)kip : ', 'y')
406 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000407 self.assertEqual(file_list, [join(self.base_path, x)
408 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000409 # The actual commit that is created is unstable, so we verify its tree and
410 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000411 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
412 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000413 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
414 self.assertEqual(scm._Capture(['rev-parse', parent + '1']),
Edward Lemur979fa782019-08-13 22:44:05 +0000415 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000416 sys.stdout.close()
417
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000418 def testUpdateReset(self):
419 if not self.enabled:
420 return
421 options = self.Options()
422 options.reset = True
423
424 dir_path = join(self.base_path, 'c')
425 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000426 with open(join(dir_path, 'nested'), 'w') as f:
427 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000428
429 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000430 with open(file_path, 'w') as f:
431 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000432
John Budorick0f7b2002018-01-19 15:46:17 -0800433 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
434 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000435 file_list = []
436 scm.update(options, (), file_list)
437 self.assert_(gclient_scm.os.path.isdir(dir_path))
438 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000439 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000440
Edward Lemur579c9862018-07-13 23:17:51 +0000441 def testUpdateResetUnsetsFetchConfig(self):
442 if not self.enabled:
443 return
444 options = self.Options()
445 options.reset = True
446
447 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
448 self.relpath)
449 scm._Run(['config', 'remote.origin.fetch',
450 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
451
452 file_list = []
453 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000454 self.assertEqual(scm.revinfo(options, (), None),
455 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000456 sys.stdout.close()
457
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000458 def testUpdateResetDeleteUnversionedTrees(self):
459 if not self.enabled:
460 return
461 options = self.Options()
462 options.reset = True
463 options.delete_unversioned_trees = True
464
465 dir_path = join(self.base_path, 'dir')
466 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000467 with open(join(dir_path, 'nested'), 'w') as f:
468 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000469
470 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000471 with open(file_path, 'w') as f:
472 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000473
John Budorick0f7b2002018-01-19 15:46:17 -0800474 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
475 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000476 file_list = []
477 scm.update(options, (), file_list)
478 self.assert_(not gclient_scm.os.path.isdir(dir_path))
479 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000480 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000481
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000482 def testUpdateUnstagedConflict(self):
483 if not self.enabled:
484 return
485 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800486 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
487 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000488 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000489 with open(file_path, 'w') as f:
490 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000491 try:
492 scm.update(options, (), [])
493 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000494 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000495 # The exact exception text varies across git versions so it's not worth
496 # verifying it. It's fine as long as it throws.
497 pass
498 # Manually flush stdout since we can't verify it's content accurately across
499 # git versions.
500 sys.stdout.getvalue()
501 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000502
Mike Stipicevice992b612016-12-02 15:32:55 -0800503 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000504 def testUpdateLocked(self):
505 if not self.enabled:
506 return
507 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800508 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
509 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000510 file_path = join(self.base_path, '.git', 'index.lock')
511 with open(file_path, 'w'):
512 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800513 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000514 scm.update(options, (), [])
515 sys.stdout.close()
516
517 def testUpdateLockedBreak(self):
518 if not self.enabled:
519 return
520 options = self.Options()
521 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800522 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
523 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000524 file_path = join(self.base_path, '.git', 'index.lock')
525 with open(file_path, 'w'):
526 pass
527 scm.update(options, (), [])
528 self.assertRegexpMatches(sys.stdout.getvalue(),
Raul Tambrea79f0e52019-09-21 07:27:39 +0000529 r'breaking lock.*\.git[/|\\]index\.lock')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000530 self.assertFalse(os.path.exists(file_path))
531 sys.stdout.close()
532
msb@chromium.org5bde4852009-12-14 16:47:12 +0000533 def testUpdateConflict(self):
534 if not self.enabled:
535 return
536 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800537 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
538 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000539 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000540 with open(file_path, 'w') as f:
541 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000542 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000543 scm._AskForData = self._GetAskForDataCallback(
544 'Cannot fast-forward merge, attempt to rebase? '
545 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000546
547 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
548 scm.update(options, (), [])
549 self.assertEqual(
550 e.exception.args[0],
551 'Conflict while rebasing this branch.\n'
552 'Fix the conflict and run gclient again.\n'
553 'See \'man git-rebase\' for details.\n')
554
555 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
556 scm.update(options, (), [])
557 self.assertEqual(
558 e.exception.args[0],
559 '\n____ . at refs/remotes/origin/master\n'
560 '\tYou have unstaged changes.\n'
561 '\tPlease commit, stash, or reset.\n')
562
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000563 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000564
msb@chromium.org0f282062009-11-06 20:14:02 +0000565 def testRevinfo(self):
566 if not self.enabled:
567 return
568 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800569 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
570 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000571 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000572 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000573
msb@chromium.orge28e4982009-09-25 20:51:45 +0000574
Edward Lemur979fa782019-08-13 22:44:05 +0000575class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000576 class OptionsObject(object):
577 def __init__(self, verbose=False, revision=None, force=False):
578 self.verbose = verbose
579 self.revision = revision
580 self.deps_os = None
581 self.force = force
582 self.reset = False
583 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000584 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000585 # TODO(maruel): Test --jobs > 1.
586 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400587 self.patch_ref = None
588 self.patch_repo = None
589 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000590
591 def Options(self, *args, **kwargs):
592 return self.OptionsObject(*args, **kwargs)
593
borenet@google.comb09097a2014-04-09 19:09:08 +0000594 def checkstdout(self, expected):
595 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000596 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000597 # Check that the expected output appears.
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800598 # pylint: disable=no-member
Erik Chene16ffff2019-10-14 20:35:53 +0000599 self.assertIn(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000600
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000601 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000602 self.fake_hash_1 = 't0ta11yf4k3'
603 self.fake_hash_2 = '3v3nf4k3r'
604 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000605 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000606 self.relpath = 'fake'
607 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000608 self.backup_base_path = os.path.join(self.root_dir,
609 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000610 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
611 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
612 mock.patch('gclient_scm.GitWrapper._Fetch').start()
613 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
614 mock.patch('sys.stdout', StringIO()).start()
615 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000616
Edward Lemur979fa782019-08-13 22:44:05 +0000617 @mock.patch('scm.GIT.IsValidRevision')
618 @mock.patch('os.path.isdir', lambda _: True)
619 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800620 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700621 options = self.Options(verbose=True)
622
Edward Lemur979fa782019-08-13 22:44:05 +0000623 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700624
John Budorick0f7b2002018-01-19 15:46:17 -0800625 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
626 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700627 # A [fake] git sha1 with a git repo should work (this is in the case that
628 # the LKGR gets flipped to git sha1's some day).
Edward Lemur979fa782019-08-13 22:44:05 +0000629 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
630 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700631 # An SVN rev with an existing purely git repo should raise an exception.
632 self.assertRaises(gclient_scm.gclient_utils.Error,
633 git_scm.GetUsableRev, '1', options)
634
Edward Lemur979fa782019-08-13 22:44:05 +0000635 @mock.patch('gclient_scm.GitWrapper._Clone')
636 @mock.patch('os.path.isdir')
637 @mock.patch('os.path.exists')
638 @mock.patch('subprocess2.check_output')
639 def testUpdateNoDotGit(
640 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
641 mockIsdir.side_effect = lambda path: path == self.base_path
642 mockExists.side_effect = lambda path: path == self.base_path
643 mockCheckOutput.return_value = b''
644
borenet@google.comb09097a2014-04-09 19:09:08 +0000645 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000646 scm = gclient_scm.GitWrapper(
647 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000648 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000649
650 env = gclient_scm.scm.GIT.ApplyEnvVars({})
651 self.assertEqual(
652 mockCheckOutput.mock_calls,
653 [
654 mock.call(
655 ['git', '-c', 'core.quotePath=false', 'ls-files'],
656 cwd=self.base_path, env=env, stderr=-1),
657 mock.call(
658 ['git', 'rev-parse', '--verify', 'HEAD'],
659 cwd=self.base_path, env=env, stderr=-1),
660 ])
661 mockClone.assert_called_with(
662 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000663 self.checkstdout('\n')
664
Edward Lemur979fa782019-08-13 22:44:05 +0000665 @mock.patch('gclient_scm.GitWrapper._Clone')
666 @mock.patch('os.path.isdir')
667 @mock.patch('os.path.exists')
668 @mock.patch('subprocess2.check_output')
669 def testUpdateConflict(
670 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
671 mockIsdir.side_effect = lambda path: path == self.base_path
672 mockExists.side_effect = lambda path: path == self.base_path
673 mockCheckOutput.return_value = b''
674 mockClone.side_effect = [
675 gclient_scm.subprocess2.CalledProcessError(
676 None, None, None, None, None),
677 None,
678 ]
679
borenet@google.com90fe58b2014-05-01 18:22:00 +0000680 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000681 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800682 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000683 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000684
685 env = gclient_scm.scm.GIT.ApplyEnvVars({})
686 self.assertEqual(
687 mockCheckOutput.mock_calls,
688 [
689 mock.call(
690 ['git', '-c', 'core.quotePath=false', 'ls-files'],
691 cwd=self.base_path, env=env, stderr=-1),
692 mock.call(
693 ['git', 'rev-parse', '--verify', 'HEAD'],
694 cwd=self.base_path, env=env, stderr=-1),
695 ])
696 mockClone.assert_called_with(
697 'refs/remotes/origin/master', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000698 self.checkstdout('\n')
699
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000700
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000701class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000702 def checkInStdout(self, expected):
703 value = sys.stdout.getvalue()
704 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800705 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000706 self.assertIn(expected, value)
707
708 def checkNotInStdout(self, expected):
709 value = sys.stdout.getvalue()
710 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800711 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000712 self.assertNotIn(expected, value)
713
smut@google.com27c9c8a2014-09-11 19:57:55 +0000714 def getCurrentBranch(self):
715 # Returns name of current branch or HEAD for detached HEAD
716 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
717 cwd=self.base_path)
718 if branch == 'HEAD':
719 return None
720 return branch
721
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000722 def testUpdateClone(self):
723 if not self.enabled:
724 return
725 options = self.Options()
726
727 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000728 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
729
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000730 self.root_dir = tempfile.mkdtemp()
731 self.relpath = '.'
732 self.base_path = join(self.root_dir, self.relpath)
733
John Budorick0f7b2002018-01-19 15:46:17 -0800734 scm = gclient_scm.GitWrapper(origin_root_dir,
735 self.root_dir,
736 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000737
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000738 expected_file_list = [join(self.base_path, "a"),
739 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000740 file_list = []
741 options.revision = 'unmanaged'
742 scm.update(options, (), file_list)
743
Edward Lemur979fa782019-08-13 22:44:05 +0000744 self.assertEqual(file_list, expected_file_list)
745 self.assertEqual(scm.revinfo(options, (), None),
746 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000747 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000748 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000749 self.checkInStdout(
750 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000751
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000752
753 def testUpdateCloneOnCommit(self):
754 if not self.enabled:
755 return
756 options = self.Options()
757
758 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000759 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
760
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000761 self.root_dir = tempfile.mkdtemp()
762 self.relpath = '.'
763 self.base_path = join(self.root_dir, self.relpath)
764 url_with_commit_ref = origin_root_dir +\
765 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
766
John Budorick0f7b2002018-01-19 15:46:17 -0800767 scm = gclient_scm.GitWrapper(url_with_commit_ref,
768 self.root_dir,
769 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000770
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000771 expected_file_list = [join(self.base_path, "a"),
772 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000773 file_list = []
774 options.revision = 'unmanaged'
775 scm.update(options, (), file_list)
776
Edward Lemur979fa782019-08-13 22:44:05 +0000777 self.assertEqual(file_list, expected_file_list)
778 self.assertEqual(scm.revinfo(options, (), None),
779 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000780 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000781 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000782 self.checkInStdout(
783 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000784
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000785 def testUpdateCloneOnBranch(self):
786 if not self.enabled:
787 return
788 options = self.Options()
789
790 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000791 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
792
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000793 self.root_dir = tempfile.mkdtemp()
794 self.relpath = '.'
795 self.base_path = join(self.root_dir, self.relpath)
796 url_with_branch_ref = origin_root_dir + '@feature'
797
John Budorick0f7b2002018-01-19 15:46:17 -0800798 scm = gclient_scm.GitWrapper(url_with_branch_ref,
799 self.root_dir,
800 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000801
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000802 expected_file_list = [join(self.base_path, "a"),
803 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000804 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000805 file_list = []
806 options.revision = 'unmanaged'
807 scm.update(options, (), file_list)
808
Edward Lemur979fa782019-08-13 22:44:05 +0000809 self.assertEqual(file_list, expected_file_list)
810 self.assertEqual(scm.revinfo(options, (), None),
811 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200812 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000813 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200814 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000815 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200816 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000817
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000818 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000819 if not self.enabled:
820 return
821 options = self.Options()
822
823 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000824 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
825
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000826 self.root_dir = tempfile.mkdtemp()
827 self.relpath = '.'
828 self.base_path = join(self.root_dir, self.relpath)
829 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
830
John Budorick0f7b2002018-01-19 15:46:17 -0800831 scm = gclient_scm.GitWrapper(url_with_branch_ref,
832 self.root_dir,
833 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000834
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000835 expected_file_list = [join(self.base_path, "a"),
836 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000837 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000838 file_list = []
839 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000840 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000841
Edward Lemur979fa782019-08-13 22:44:05 +0000842 self.assertEqual(file_list, expected_file_list)
843 self.assertEqual(scm.revinfo(options, (), None),
844 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000845 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000846 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000847 self.checkInStdout(
848 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000849
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000850 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000851 if not self.enabled:
852 return
853 options = self.Options()
854
855 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000856 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
857
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000858 self.root_dir = tempfile.mkdtemp()
859 self.relpath = '.'
860 self.base_path = join(self.root_dir, self.relpath)
861 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
862
John Budorick0f7b2002018-01-19 15:46:17 -0800863 scm = gclient_scm.GitWrapper(url_with_branch_ref,
864 self.root_dir,
865 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000866
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000867 expected_file_list = [join(self.base_path, "a"),
868 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000869 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000870 file_list = []
871 options.revision = 'unmanaged'
872 scm.update(options, (), file_list)
873
Edward Lemur979fa782019-08-13 22:44:05 +0000874 self.assertEqual(file_list, expected_file_list)
875 self.assertEqual(scm.revinfo(options, (), None),
876 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000877 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000878 # should be treated as such by gclient.
879 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
880 # as they are known in the upstream repo, since the mapping into the local
881 # repo can be modified by users (or we might even want to change the gclient
882 # defaults at some point). But that will take more work to stop using
883 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
884 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000885 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000886 self.checkInStdout(
887 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000888
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000889 def testUpdateUpdate(self):
890 if not self.enabled:
891 return
892 options = self.Options()
893 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800894 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
895 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000896 file_list = []
897 options.revision = 'unmanaged'
898 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000899 self.assertEqual(file_list, expected_file_list)
900 self.assertEqual(scm.revinfo(options, (), None),
901 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000902 self.checkstdout('________ unmanaged solution; skipping .\n')
903
904
Edward Lemur979fa782019-08-13 22:44:05 +0000905class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800906
907 def setUp(self):
908 # Create this before setting up mocks.
909 self._cipd_root_dir = tempfile.mkdtemp()
910 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800911
912 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
913 self._cipd_root = gclient_scm.CipdRoot(
914 self._cipd_root_dir,
915 self._cipd_instance_url)
916 self._cipd_packages = [
917 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
918 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
919 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
920 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000921 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
922 mock.patch('gclient_scm.CipdRoot.add_package').start()
923 mock.patch('gclient_scm.CipdRoot.clobber').start()
924 mock.patch('gclient_scm.CipdRoot.ensure').start()
925 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000926 self.addCleanup(gclient_utils.rmtree, self._cipd_root_dir)
927 self.addCleanup(gclient_utils.rmtree, self._workdir)
John Budorick0f7b2002018-01-19 15:46:17 -0800928
929 def createScmWithPackageThatSatisfies(self, condition):
930 return gclient_scm.CipdWrapper(
931 url=self._cipd_instance_url,
932 root_dir=self._cipd_root_dir,
933 relpath='fake_relpath',
934 root=self._cipd_root,
935 package=self.getPackageThatSatisfies(condition))
936
937 def getPackageThatSatisfies(self, condition):
938 for p in self._cipd_packages:
939 if condition(p):
940 return p
941
942 self.fail('Unable to find a satisfactory package.')
943
John Budorick0f7b2002018-01-19 15:46:17 -0800944 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700945 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000946 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800947 scm.revert(None, (), [])
948
Edward Lemur979fa782019-08-13 22:44:05 +0000949 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
950 @mock.patch('gclient_scm.gclient_utils.rmtree')
951 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -0800952 """Checks that revinfo uses the JSON from cipd describe."""
953 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
954
955 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
956 json_contents = {
957 'result': {
958 'pin': {
959 'instance_id': expected_revinfo,
960 }
961 }
962 }
963 describe_json_path = join(self._workdir, 'describe.json')
964 with open(describe_json_path, 'w') as describe_json:
965 json.dump(json_contents, describe_json)
966
Edward Lemur979fa782019-08-13 22:44:05 +0000967 revinfo = scm.revinfo(None, (), [])
968 self.assertEqual(revinfo, expected_revinfo)
969
970 mockRmtree.assert_called_with(self._workdir)
971 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -0800972 'cipd', 'describe', 'foo_package',
973 '-log-level', 'error',
974 '-version', 'foo_version',
975 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +0000976 ])
John Budorick0f7b2002018-01-19 15:46:17 -0800977
978 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700979 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000980 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800981 scm.update(None, (), [])
982
983
Edward Lemurd64781e2018-07-11 23:09:55 +0000984class GerritChangesFakeRepo(fake_repos.FakeReposBase):
985 def populateGit(self):
986 # Creates a tree that looks like this:
987 #
Edward Lemurca7d8812018-07-24 17:42:45 +0000988 # 6 refs/changes/35/1235/1
989 # |
990 # 5 refs/changes/34/1234/1
991 # |
Edward Lemurd64781e2018-07-11 23:09:55 +0000992 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +0000993 # | |
994 # | 11(5)--12 refs/heads/master-with-5
995 # |
996 # 7--8--9 refs/heads/feature
997 # |
998 # 10 refs/changes/36/1236/1
999 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001000
1001 self._commit_git('repo_1', {'commit 1': 'touched'})
1002 self._commit_git('repo_1', {'commit 2': 'touched'})
1003 self._commit_git('repo_1', {'commit 3': 'touched'})
1004 self._commit_git('repo_1', {'commit 4': 'touched'})
1005 self._create_ref('repo_1', 'refs/heads/master', 4)
1006
1007 # Create a change on top of commit 3 that consists of two commits.
1008 self._commit_git('repo_1',
1009 {'commit 5': 'touched',
1010 'change': '1234'},
1011 base=3)
1012 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1013 self._commit_git('repo_1',
1014 {'commit 6': 'touched',
1015 'change': '1235'})
1016 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1017
Edward Lemurca7d8812018-07-24 17:42:45 +00001018 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1019 # commits.
1020 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1021 self._commit_git('repo_1', {'commit 8': 'touched'})
1022 self._commit_git('repo_1', {'commit 9': 'touched'})
1023 self._create_ref('repo_1', 'refs/heads/feature', 9)
1024
1025 # Create a change of top of commit 8.
1026 self._commit_git('repo_1',
1027 {'commit 10': 'touched',
1028 'change': '1236'},
1029 base=8)
1030 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1031
1032 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1033 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1034 self._commit_git('repo_1',
1035 # This is really commit 11, but has the changes of commit 5
1036 {'commit 5': 'touched',
1037 'change': '1234'},
1038 base=3)
1039 self._commit_git('repo_1', {'commit 12': 'touched'})
1040 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1041
Edward Lemurd64781e2018-07-11 23:09:55 +00001042
1043class GerritChangesTest(fake_repos.FakeReposTestBase):
1044 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1045
1046 def setUp(self):
1047 super(GerritChangesTest, self).setUp()
1048 self.enabled = self.FAKE_REPOS.set_up_git()
1049 self.options = BaseGitWrapperTestCase.OptionsObject()
1050 self.url = self.git_base + 'repo_1'
1051 self.mirror = None
Edward Lesmese79107e2019-10-25 22:47:33 +00001052 mock.patch('sys.stdout').start()
1053 self.addCleanup(mock.patch.stopall)
Edward Lemurd64781e2018-07-11 23:09:55 +00001054
1055 def setUpMirror(self):
1056 self.mirror = tempfile.mkdtemp()
1057 git_cache.Mirror.SetCachePath(self.mirror)
Edward Lesmese79107e2019-10-25 22:47:33 +00001058 self.addCleanup(gclient_utils.rmtree, self.mirror)
Edward Lemurd64781e2018-07-11 23:09:55 +00001059 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1060
Edward Lemurca7d8812018-07-24 17:42:45 +00001061 def assertCommits(self, commits):
1062 """Check that all, and only |commits| are present in the current checkout.
1063 """
1064 for i in commits:
1065 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001066 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001067
1068 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1069 for i in all_commits - set(commits):
1070 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001071 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001072
Edward Lemurd64781e2018-07-11 23:09:55 +00001073 def testCanCloneGerritChange(self):
1074 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1075 file_list = []
1076
1077 self.options.revision = 'refs/changes/35/1235/1'
1078 scm.update(self.options, None, file_list)
1079 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1080
1081 def testCanSyncToGerritChange(self):
1082 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1083 file_list = []
1084
1085 self.options.revision = self.githash('repo_1', 1)
1086 scm.update(self.options, None, file_list)
1087 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1088
1089 self.options.revision = 'refs/changes/35/1235/1'
1090 scm.update(self.options, None, file_list)
1091 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1092
1093 def testCanCloneGerritChangeMirror(self):
1094 self.setUpMirror()
1095 self.testCanCloneGerritChange()
1096
1097 def testCanSyncToGerritChangeMirror(self):
1098 self.setUpMirror()
1099 self.testCanSyncToGerritChange()
1100
Edward Lesmese79107e2019-10-25 22:47:33 +00001101 def testMirrorPushUrl(self):
1102 self.setUpMirror()
1103
1104 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1105 file_list = []
1106 self.assertIsNotNone(scm._GetMirror(self.url, self.options))
1107
1108 scm.update(self.options, None, file_list)
1109
1110 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
1111 self.assertTrue(
1112 fetch_url.startswith(self.mirror),
1113 msg='\n'.join([
1114 'Repository fetch url should be in the git cache mirror directory.',
1115 ' fetch_url: %s' % fetch_url,
1116 ' mirror: %s' % self.mirror]))
1117 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
1118 self.assertEqual(push_url, self.url)
1119
Edward Lemurca7d8812018-07-24 17:42:45 +00001120 def testAppliesPatchOnTopOfMasterByDefault(self):
1121 """Test the default case, where we apply a patch on top of master."""
1122 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1123 file_list = []
1124
1125 # Make sure we don't specify a revision.
1126 self.options.revision = None
1127 scm.update(self.options, None, file_list)
1128 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1129
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001130 scm.apply_patch_ref(
1131 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1132 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001133
1134 self.assertCommits([1, 2, 3, 4, 5, 6])
1135 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1136
1137 def testCheckoutOlderThanPatchBase(self):
1138 """Test applying a patch on an old checkout.
1139
1140 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1141 contains commits 5 and 6, and is based on top of commit 3.
1142 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1143 """
1144 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1145 file_list = []
1146
1147 # Sync to commit 1
1148 self.options.revision = self.githash('repo_1', 1)
1149 scm.update(self.options, None, file_list)
1150 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1151
1152 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001153 scm.apply_patch_ref(
1154 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1155 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001156
1157 self.assertCommits([1, 5, 6])
1158 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1159
1160 def testCheckoutOriginFeature(self):
1161 """Tests that we can apply a patch on a branch other than master."""
1162 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1163 file_list = []
1164
Edward Lemur8c665652019-05-08 20:23:33 +00001165 # Sync to remote's refs/heads/feature
1166 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001167 scm.update(self.options, None, file_list)
1168 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1169
1170 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001171 scm.apply_patch_ref(
1172 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1173 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001174
1175 self.assertCommits([1, 2, 7, 8, 9, 10])
1176 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1177
1178 def testCheckoutOriginFeatureOnOldRevision(self):
1179 """Tests that we can apply a patch on an old checkout, on a branch other
1180 than master."""
1181 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1182 file_list = []
1183
Edward Lemur8c665652019-05-08 20:23:33 +00001184 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001185 self.options.revision = self.githash('repo_1', 7)
1186 scm.update(self.options, None, file_list)
1187 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1188
1189 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001190 scm.apply_patch_ref(
1191 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1192 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001193
1194 # We shouldn't have rebased on top of 2 (which is the merge base between
Edward Lemur8c665652019-05-08 20:23:33 +00001195 # remote's master branch and the change) but on top of 7 (which is the
1196 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001197 self.assertCommits([1, 2, 7, 10])
1198 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1199
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001200 def testCheckoutOriginFeaturePatchBranch(self):
1201 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1202 file_list = []
1203
Edward Lemur8c665652019-05-08 20:23:33 +00001204 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001205 self.options.revision = self.githash('repo_1', 9)
1206 scm.update(self.options, None, file_list)
1207 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1208
Edward Lemur8c665652019-05-08 20:23:33 +00001209 # Apply refs/changes/34/1234/1, created for remote's master branch on top of
1210 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001211 scm.apply_patch_ref(
1212 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1213 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001214
1215 # 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 +00001216 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001217 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1218 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001219
1220 def testDoesntRebasePatchMaster(self):
1221 """Tests that we can apply a patch without rebasing it.
1222 """
1223 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1224 file_list = []
1225
1226 self.options.rebase_patch_ref = False
1227 scm.update(self.options, None, file_list)
1228 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1229
1230 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001231 scm.apply_patch_ref(
1232 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1233 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001234
1235 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001236 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001237
1238 def testDoesntRebasePatchOldCheckout(self):
1239 """Tests that we can apply a patch without rebasing it on an old checkout.
1240 """
1241 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1242 file_list = []
1243
1244 # Sync to commit 1
1245 self.options.revision = self.githash('repo_1', 1)
1246 self.options.rebase_patch_ref = False
1247 scm.update(self.options, None, file_list)
1248 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1249
1250 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001251 scm.apply_patch_ref(
1252 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1253 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001254
1255 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001256 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001257
1258 def testDoesntSoftResetIfNotAskedTo(self):
1259 """Test that we can apply a patch without doing a soft reset."""
1260 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1261 file_list = []
1262
1263 self.options.reset_patch_ref = False
1264 scm.update(self.options, None, file_list)
1265 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1266
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001267 scm.apply_patch_ref(
1268 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1269 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001270
1271 self.assertCommits([1, 2, 3, 4, 5, 6])
1272 # The commit hash after cherry-picking is not known, but it must be
1273 # different from what the repo was synced at before patching.
1274 self.assertNotEqual(self.githash('repo_1', 4),
1275 self.gitrevparse(self.root_dir))
1276
1277 def testRecoversAfterPatchFailure(self):
1278 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1279 file_list = []
1280
1281 self.options.revision = 'refs/changes/34/1234/1'
1282 scm.update(self.options, None, file_list)
1283 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1284
1285 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1286 # patch 'refs/changes/36/1236/1' creates a patch failure.
1287 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001288 scm.apply_patch_ref(
1289 self.url, 'refs/changes/36/1236/1', 'refs/heads/master', self.options,
1290 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001291 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001292 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001293
1294 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1295 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001296 scm.apply_patch_ref(
1297 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1298 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001299 self.assertCommits([1, 2, 3, 5, 6])
1300 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1301
1302 def testIgnoresAlreadyMergedCommits(self):
1303 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1304 file_list = []
1305
1306 self.options.revision = 'refs/heads/master-with-5'
1307 scm.update(self.options, None, file_list)
1308 self.assertEqual(self.githash('repo_1', 12),
1309 self.gitrevparse(self.root_dir))
1310
1311 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1312 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1313 # already present in the tree as commit 11.
1314 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001315 scm.apply_patch_ref(
1316 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1317 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001318 self.assertCommits([1, 2, 3, 5, 6, 12])
1319 self.assertEqual(self.githash('repo_1', 12),
1320 self.gitrevparse(self.root_dir))
1321
1322 def testRecoversFromExistingCherryPick(self):
1323 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1324 file_list = []
1325
1326 self.options.revision = 'refs/changes/34/1234/1'
1327 scm.update(self.options, None, file_list)
1328 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1329
1330 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1331 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1332 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1333 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1334 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1335 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1336
1337 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1338 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001339 scm.apply_patch_ref(
1340 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1341 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001342 self.assertCommits([1, 2, 3, 5, 6])
1343 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1344
Edward Lemurd64781e2018-07-11 23:09:55 +00001345
msb@chromium.orge28e4982009-09-25 20:51:45 +00001346if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001347 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1348 logging.basicConfig(
1349 level=level,
1350 format='%(asctime).19s %(levelname)s %(filename)s:'
1351 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001352 unittest.main()
1353
1354# vim: ts=2:sw=2:tw=80:et: