blob: 6fa3bd52e9ec89654e81597ffac111fea89ccf9c [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()
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000199 # Set HEAD back to master
200 Popen([GIT, 'checkout', 'master', '-q'],
201 stdout=PIPE,
202 stderr=STDOUT,
203 cwd=path).communicate()
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000204 return True
msb@chromium.orge28e4982009-09-25 20:51:45 +0000205
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000206 def _GetAskForDataCallback(self, expected_prompt, return_value):
207 def AskForData(prompt, options):
Edward Lemur979fa782019-08-13 22:44:05 +0000208 self.assertEqual(prompt, expected_prompt)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000209 return return_value
210 return AskForData
211
msb@chromium.orge28e4982009-09-25 20:51:45 +0000212 def setUp(self):
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000213 unittest.TestCase.setUp(self)
Edward Lemur9cafbf42019-08-15 22:03:35 +0000214 test_case_utils.TestCaseUtils.setUp(self)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000215 self.url = 'git://foo'
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000216 # The .git suffix allows gclient_scm to recognize the dir as a git repo
217 # when cloning it locally
218 self.root_dir = tempfile.mkdtemp('.git')
msb@chromium.orge28e4982009-09-25 20:51:45 +0000219 self.relpath = '.'
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000220 self.base_path = join(self.root_dir, self.relpath)
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000221 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000222 mock.patch('sys.stdout', StringIO()).start()
223 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000224 self.addCleanup(gclient_utils.rmtree, self.root_dir)
maruel@chromium.org1a60dca2013-11-26 14:06:26 +0000225
msb@chromium.orge28e4982009-09-25 20:51:45 +0000226
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000227class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
msb@chromium.orge28e4982009-09-25 20:51:45 +0000228
229 def testRevertMissing(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000230 if not self.enabled:
231 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000232 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000233 file_path = join(self.base_path, 'a')
John Budorick0f7b2002018-01-19 15:46:17 -0800234 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
235 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000236 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000237 scm.update(options, None, file_list)
238 gclient_scm.os.remove(file_path)
239 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000240 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000241 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000242 file_list = []
243 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000244 self.assertEqual(file_list, [])
szager@google.com85d3e3a2011-10-07 17:12:00 +0000245 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000246
247 def testRevertNone(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000248 if not self.enabled:
249 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000250 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800251 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
252 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000253 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000254 scm.update(options, None, file_list)
255 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000256 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000257 self.assertEqual(file_list, [])
258 self.assertEqual(scm.revinfo(options, self.args, None),
259 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000260 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000261
262 def testRevertModified(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000263 if not self.enabled:
264 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000265 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800266 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
267 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000268 file_list = []
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000269 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000270 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000271 with open(file_path, 'a') as f:
272 f.writelines('touched\n')
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000273 file_list = []
msb@chromium.orge28e4982009-09-25 20:51:45 +0000274 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000275 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000276 file_list = []
277 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000278 self.assertEqual(file_list, [])
279 self.assertEqual(scm.revinfo(options, self.args, None),
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000280 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000281 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000282
283 def testRevertNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000284 if not self.enabled:
285 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000286 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800287 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
288 self.relpath)
nasser@codeaurora.orgb2b46312010-04-30 20:58:03 +0000289 file_list = []
290 scm.update(options, None, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000291 file_path = join(self.base_path, 'c')
Edward Lemur979fa782019-08-13 22:44:05 +0000292 with open(file_path, 'w') as f:
293 f.writelines('new\n')
Edward Lesmese79107e2019-10-25 22:47:33 +0000294 Popen([GIT, 'add', 'c'], stdout=PIPE,
maruel@chromium.org8ef5f542009-11-12 02:05:02 +0000295 stderr=STDOUT, cwd=self.base_path).communicate()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000296 file_list = []
297 scm.revert(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000298 self.assertEqual(file_list, [file_path])
msb@chromium.orge28e4982009-09-25 20:51:45 +0000299 file_list = []
300 scm.diff(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000301 self.assertEqual(file_list, [])
302 self.assertEqual(scm.revinfo(options, self.args, None),
303 'a7142dc9f0009350b96a11f372b6ea658592aa95')
szager@google.com85d3e3a2011-10-07 17:12:00 +0000304 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000305
306 def testStatusNew(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000307 if not self.enabled:
308 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000309 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000310 file_path = join(self.base_path, 'a')
Edward Lemur979fa782019-08-13 22:44:05 +0000311 with open(file_path, 'a') as f:
312 f.writelines('touched\n')
Anthony Politobb457342019-11-15 22:26:01 +0000313 scm = gclient_scm.GitWrapper(
314 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
315 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000316 file_list = []
317 scm.status(options, self.args, file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000318 self.assertEqual(file_list, [file_path])
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000319 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800320 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000321 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\n') %
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000322 join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000323
Anthony Politobb457342019-11-15 22:26:01 +0000324
325 def testStatusNewNoBaseRev(self):
326 if not self.enabled:
327 return
328 options = self.Options()
329 file_path = join(self.base_path, 'a')
330 with open(file_path, 'a') as f:
331 f.writelines('touched\n')
332 scm = gclient_scm.GitWrapper(self.url, self.root_dir, self.relpath)
333 file_list = []
334 scm.status(options, self.args, file_list)
335 self.assertEqual(file_list, [file_path])
336 self.checkstdout(
337 ('\n________ running \'git -c core.quotePath=false diff --name-status'
338 '\' in \'%s\'\n\nM\ta\n') % join(self.root_dir, '.'))
339
msb@chromium.orge28e4982009-09-25 20:51:45 +0000340 def testStatus2New(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000341 if not self.enabled:
342 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000343 options = self.Options()
344 expected_file_list = []
345 for f in ['a', 'b']:
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000346 file_path = join(self.base_path, f)
Edward Lemur979fa782019-08-13 22:44:05 +0000347 with open(file_path, 'a') as f:
348 f.writelines('touched\n')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000349 expected_file_list.extend([file_path])
Anthony Politobb457342019-11-15 22:26:01 +0000350 scm = gclient_scm.GitWrapper(
351 self.url + '@069c602044c5388d2d15c3f875b057c852003458', self.root_dir,
352 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000353 file_list = []
354 scm.status(options, self.args, file_list)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000355 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
Edward Lemur979fa782019-08-13 22:44:05 +0000356 self.assertEqual(sorted(file_list), expected_file_list)
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000357 self.checkstdout(
Aaron Gablef4068aa2017-12-12 15:14:09 -0800358 ('\n________ running \'git -c core.quotePath=false diff --name-status '
Edward Lemur24146be2019-08-01 21:44:52 +0000359 '069c602044c5388d2d15c3f875b057c852003458\' in \'%s\'\n\nM\ta\nM\tb\n')
360 % join(self.root_dir, '.'))
msb@chromium.orge28e4982009-09-25 20:51:45 +0000361
msb@chromium.orge28e4982009-09-25 20:51:45 +0000362 def testUpdateUpdate(self):
maruel@chromium.orgea6c2c52009-10-09 20:38:14 +0000363 if not self.enabled:
364 return
msb@chromium.orge28e4982009-09-25 20:51:45 +0000365 options = self.Options()
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000366 expected_file_list = [join(self.base_path, x) for x in ['a', 'b']]
John Budorick0f7b2002018-01-19 15:46:17 -0800367 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
368 self.relpath)
msb@chromium.orge28e4982009-09-25 20:51:45 +0000369 file_list = []
370 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000371 self.assertEqual(file_list, expected_file_list)
372 self.assertEqual(scm.revinfo(options, (), None),
msb@chromium.orge28e4982009-09-25 20:51:45 +0000373 'a7142dc9f0009350b96a11f372b6ea658592aa95')
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000374 sys.stdout.close()
msb@chromium.orge28e4982009-09-25 20:51:45 +0000375
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000376 def testUpdateMerge(self):
377 if not self.enabled:
378 return
379 options = self.Options()
380 options.merge = True
John Budorick0f7b2002018-01-19 15:46:17 -0800381 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
382 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000383 scm._Run(['checkout', '-q', 'feature'], options)
384 rev = scm.revinfo(options, (), None)
385 file_list = []
386 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000387 self.assertEqual(file_list, [join(self.base_path, x)
388 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000389 # The actual commit that is created is unstable, so we verify its tree and
390 # parents instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000391 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
392 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000393 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
394 self.assertEqual(scm._Capture(['rev-parse', parent + '1']), rev)
395 self.assertEqual(scm._Capture(['rev-parse', parent + '2']),
Edward Lemur979fa782019-08-13 22:44:05 +0000396 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000397 sys.stdout.close()
398
399 def testUpdateRebase(self):
400 if not self.enabled:
401 return
402 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800403 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
404 self.relpath)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000405 scm._Run(['checkout', '-q', 'feature'], options)
406 file_list = []
407 # Fake a 'y' key press.
408 scm._AskForData = self._GetAskForDataCallback(
409 'Cannot fast-forward merge, attempt to rebase? '
410 '(y)es / (q)uit / (s)kip : ', 'y')
411 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000412 self.assertEqual(file_list, [join(self.base_path, x)
413 for x in ['a', 'b', 'c']])
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000414 # The actual commit that is created is unstable, so we verify its tree and
415 # parent instead.
Edward Lemur979fa782019-08-13 22:44:05 +0000416 self.assertEqual(scm._Capture(['rev-parse', 'HEAD:']),
417 'd2e35c10ac24d6c621e14a1fcadceb533155627d')
Edward Lesmese79107e2019-10-25 22:47:33 +0000418 parent = 'HEAD^' if sys.platform != 'win32' else 'HEAD^^'
419 self.assertEqual(scm._Capture(['rev-parse', parent + '1']),
Edward Lemur979fa782019-08-13 22:44:05 +0000420 scm._Capture(['rev-parse', 'origin/master']))
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000421 sys.stdout.close()
422
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000423 def testUpdateReset(self):
424 if not self.enabled:
425 return
426 options = self.Options()
427 options.reset = True
428
429 dir_path = join(self.base_path, 'c')
430 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000431 with open(join(dir_path, 'nested'), 'w') as f:
432 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000433
434 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000435 with open(file_path, 'w') as f:
436 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000437
John Budorick0f7b2002018-01-19 15:46:17 -0800438 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
439 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000440 file_list = []
441 scm.update(options, (), file_list)
442 self.assert_(gclient_scm.os.path.isdir(dir_path))
443 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000444 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000445
Edward Lemur579c9862018-07-13 23:17:51 +0000446 def testUpdateResetUnsetsFetchConfig(self):
447 if not self.enabled:
448 return
449 options = self.Options()
450 options.reset = True
451
452 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
453 self.relpath)
454 scm._Run(['config', 'remote.origin.fetch',
455 '+refs/heads/bad/ref:refs/remotes/origin/bad/ref'], options)
456
457 file_list = []
458 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000459 self.assertEqual(scm.revinfo(options, (), None),
460 '069c602044c5388d2d15c3f875b057c852003458')
Edward Lemur579c9862018-07-13 23:17:51 +0000461 sys.stdout.close()
462
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000463 def testUpdateResetDeleteUnversionedTrees(self):
464 if not self.enabled:
465 return
466 options = self.Options()
467 options.reset = True
468 options.delete_unversioned_trees = True
469
470 dir_path = join(self.base_path, 'dir')
471 os.mkdir(dir_path)
Edward Lemur979fa782019-08-13 22:44:05 +0000472 with open(join(dir_path, 'nested'), 'w') as f:
473 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000474
475 file_path = join(self.base_path, 'file')
Edward Lemur979fa782019-08-13 22:44:05 +0000476 with open(file_path, 'w') as f:
477 f.writelines('new\n')
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000478
John Budorick0f7b2002018-01-19 15:46:17 -0800479 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
480 self.relpath)
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000481 file_list = []
482 scm.update(options, (), file_list)
483 self.assert_(not gclient_scm.os.path.isdir(dir_path))
484 self.assert_(gclient_scm.os.path.isfile(file_path))
maruel@chromium.orgc6ca3a12012-06-20 14:46:02 +0000485 sys.stdout.close()
steveblock@chromium.org98e69452012-02-16 16:36:43 +0000486
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000487 def testUpdateUnstagedConflict(self):
488 if not self.enabled:
489 return
490 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800491 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
492 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000493 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000494 with open(file_path, 'w') as f:
495 f.writelines('conflict\n')
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000496 try:
497 scm.update(options, (), [])
498 self.fail()
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000499 except (gclient_scm.gclient_utils.Error, subprocess2.CalledProcessError):
maruel@chromium.orgcdbecc42011-02-09 04:31:47 +0000500 # The exact exception text varies across git versions so it's not worth
501 # verifying it. It's fine as long as it throws.
502 pass
503 # Manually flush stdout since we can't verify it's content accurately across
504 # git versions.
505 sys.stdout.getvalue()
506 sys.stdout.close()
nasser@codeaurora.orgd90ba3f2010-02-23 14:42:57 +0000507
Mike Stipicevice992b612016-12-02 15:32:55 -0800508 @unittest.skip('Skipping until crbug.com/670884 is resolved.')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000509 def testUpdateLocked(self):
510 if not self.enabled:
511 return
512 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800513 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
514 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000515 file_path = join(self.base_path, '.git', 'index.lock')
516 with open(file_path, 'w'):
517 pass
Robert Iannucci53f35552016-12-15 19:09:16 -0800518 with self.assertRaises(subprocess2.CalledProcessError):
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000519 scm.update(options, (), [])
520 sys.stdout.close()
521
522 def testUpdateLockedBreak(self):
523 if not self.enabled:
524 return
525 options = self.Options()
526 options.break_repo_locks = True
John Budorick0f7b2002018-01-19 15:46:17 -0800527 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
528 self.relpath)
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000529 file_path = join(self.base_path, '.git', 'index.lock')
530 with open(file_path, 'w'):
531 pass
532 scm.update(options, (), [])
533 self.assertRegexpMatches(sys.stdout.getvalue(),
Raul Tambrea79f0e52019-09-21 07:27:39 +0000534 r'breaking lock.*\.git[/|\\]index\.lock')
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000535 self.assertFalse(os.path.exists(file_path))
536 sys.stdout.close()
537
msb@chromium.org5bde4852009-12-14 16:47:12 +0000538 def testUpdateConflict(self):
539 if not self.enabled:
540 return
541 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800542 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
543 self.relpath)
maruel@chromium.org795a8c12010-10-05 19:54:29 +0000544 file_path = join(self.base_path, 'b')
Edward Lemur979fa782019-08-13 22:44:05 +0000545 with open(file_path, 'w') as f:
546 f.writelines('conflict\n')
maruel@chromium.org389d6de2010-09-09 14:14:37 +0000547 scm._Run(['commit', '-am', 'test'], options)
bauerb@chromium.org30c46d62014-01-23 12:11:56 +0000548 scm._AskForData = self._GetAskForDataCallback(
549 'Cannot fast-forward merge, attempt to rebase? '
550 '(y)es / (q)uit / (s)kip : ', 'y')
Edward Lemur979fa782019-08-13 22:44:05 +0000551
552 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
553 scm.update(options, (), [])
554 self.assertEqual(
555 e.exception.args[0],
556 'Conflict while rebasing this branch.\n'
557 'Fix the conflict and run gclient again.\n'
558 'See \'man git-rebase\' for details.\n')
559
560 with self.assertRaises(gclient_scm.gclient_utils.Error) as e:
561 scm.update(options, (), [])
562 self.assertEqual(
563 e.exception.args[0],
564 '\n____ . at refs/remotes/origin/master\n'
565 '\tYou have unstaged changes.\n'
566 '\tPlease commit, stash, or reset.\n')
567
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000568 sys.stdout.close()
msb@chromium.org5bde4852009-12-14 16:47:12 +0000569
msb@chromium.org0f282062009-11-06 20:14:02 +0000570 def testRevinfo(self):
571 if not self.enabled:
572 return
573 options = self.Options()
John Budorick0f7b2002018-01-19 15:46:17 -0800574 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
575 self.relpath)
msb@chromium.org0f282062009-11-06 20:14:02 +0000576 rev_info = scm.revinfo(options, (), None)
Edward Lemur979fa782019-08-13 22:44:05 +0000577 self.assertEqual(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
msb@chromium.org0f282062009-11-06 20:14:02 +0000578
msb@chromium.orge28e4982009-09-25 20:51:45 +0000579
Edward Lemur979fa782019-08-13 22:44:05 +0000580class ManagedGitWrapperTestCaseMock(unittest.TestCase):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000581 class OptionsObject(object):
582 def __init__(self, verbose=False, revision=None, force=False):
583 self.verbose = verbose
584 self.revision = revision
585 self.deps_os = None
586 self.force = force
587 self.reset = False
588 self.nohooks = False
iannucci@chromium.org30a07982016-04-07 21:35:19 +0000589 self.break_repo_locks = False
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000590 # TODO(maruel): Test --jobs > 1.
591 self.jobs = 1
Edward Lesmesc621b212018-03-21 20:26:56 -0400592 self.patch_ref = None
593 self.patch_repo = None
594 self.rebase_patch_ref = True
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000595
596 def Options(self, *args, **kwargs):
597 return self.OptionsObject(*args, **kwargs)
598
borenet@google.comb09097a2014-04-09 19:09:08 +0000599 def checkstdout(self, expected):
600 value = sys.stdout.getvalue()
primiano@chromium.org5439ea52014-08-06 17:18:18 +0000601 sys.stdout.close()
Erik Chene16ffff2019-10-14 20:35:53 +0000602 # Check that the expected output appears.
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800603 # pylint: disable=no-member
Erik Chene16ffff2019-10-14 20:35:53 +0000604 self.assertIn(expected, strip_timestamps(value))
borenet@google.comb09097a2014-04-09 19:09:08 +0000605
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000606 def setUp(self):
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000607 self.fake_hash_1 = 't0ta11yf4k3'
608 self.fake_hash_2 = '3v3nf4k3r'
609 self.url = 'git://foo'
maruel@chromium.org97170132013-05-08 14:58:34 +0000610 self.root_dir = '/tmp' if sys.platform != 'win32' else 't:\\tmp'
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000611 self.relpath = 'fake'
612 self.base_path = os.path.join(self.root_dir, self.relpath)
primiano@chromium.org1c127382015-02-17 11:15:40 +0000613 self.backup_base_path = os.path.join(self.root_dir,
614 'old_%s.git' % self.relpath)
Edward Lemur979fa782019-08-13 22:44:05 +0000615 mock.patch('gclient_scm.scm.GIT.ApplyEnvVars').start()
616 mock.patch('gclient_scm.GitWrapper._CheckMinVersion').start()
617 mock.patch('gclient_scm.GitWrapper._Fetch').start()
618 mock.patch('gclient_scm.GitWrapper._DeleteOrMove').start()
619 mock.patch('sys.stdout', StringIO()).start()
620 self.addCleanup(mock.patch.stopall)
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000621
Edward Lemur979fa782019-08-13 22:44:05 +0000622 @mock.patch('scm.GIT.IsValidRevision')
623 @mock.patch('os.path.isdir', lambda _: True)
624 def testGetUsableRevGit(self, mockIsValidRevision):
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800625 # pylint: disable=no-member
smutae7ea312016-07-18 11:59:41 -0700626 options = self.Options(verbose=True)
627
Edward Lemur979fa782019-08-13 22:44:05 +0000628 mockIsValidRevision.side_effect = lambda cwd, rev: rev != '1'
smutae7ea312016-07-18 11:59:41 -0700629
John Budorick0f7b2002018-01-19 15:46:17 -0800630 git_scm = gclient_scm.GitWrapper(self.url, self.root_dir,
631 self.relpath)
smutae7ea312016-07-18 11:59:41 -0700632 # A [fake] git sha1 with a git repo should work (this is in the case that
633 # the LKGR gets flipped to git sha1's some day).
Edward Lemur979fa782019-08-13 22:44:05 +0000634 self.assertEqual(git_scm.GetUsableRev(self.fake_hash_1, options),
635 self.fake_hash_1)
smutae7ea312016-07-18 11:59:41 -0700636 # An SVN rev with an existing purely git repo should raise an exception.
637 self.assertRaises(gclient_scm.gclient_utils.Error,
638 git_scm.GetUsableRev, '1', options)
639
Edward Lemur979fa782019-08-13 22:44:05 +0000640 @mock.patch('gclient_scm.GitWrapper._Clone')
641 @mock.patch('os.path.isdir')
642 @mock.patch('os.path.exists')
643 @mock.patch('subprocess2.check_output')
644 def testUpdateNoDotGit(
645 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
646 mockIsdir.side_effect = lambda path: path == self.base_path
647 mockExists.side_effect = lambda path: path == self.base_path
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000648 mockCheckOutput.side_effect = [b'refs/remotes/origin/main', b'', b'']
Edward Lemur979fa782019-08-13 22:44:05 +0000649
borenet@google.comb09097a2014-04-09 19:09:08 +0000650 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000651 scm = gclient_scm.GitWrapper(
652 self.url, self.root_dir, self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000653 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000654
655 env = gclient_scm.scm.GIT.ApplyEnvVars({})
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000656 self.assertEqual(mockCheckOutput.mock_calls, [
657 mock.call(['git', 'symbolic-ref', 'refs/remotes/origin/HEAD'],
658 cwd=self.base_path,
659 env=env,
660 stderr=-1),
661 mock.call(['git', '-c', 'core.quotePath=false', 'ls-files'],
662 cwd=self.base_path,
663 env=env,
664 stderr=-1),
665 mock.call(['git', 'rev-parse', '--verify', 'HEAD'],
666 cwd=self.base_path,
667 env=env,
668 stderr=-1),
669 ])
670 mockClone.assert_called_with('refs/remotes/origin/main', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000671 self.checkstdout('\n')
672
Edward Lemur979fa782019-08-13 22:44:05 +0000673 @mock.patch('gclient_scm.GitWrapper._Clone')
674 @mock.patch('os.path.isdir')
675 @mock.patch('os.path.exists')
676 @mock.patch('subprocess2.check_output')
677 def testUpdateConflict(
678 self, mockCheckOutput, mockExists, mockIsdir, mockClone):
679 mockIsdir.side_effect = lambda path: path == self.base_path
680 mockExists.side_effect = lambda path: path == self.base_path
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000681 mockCheckOutput.side_effect = [b'refs/remotes/origin/main', b'', b'']
Edward Lemur979fa782019-08-13 22:44:05 +0000682 mockClone.side_effect = [
683 gclient_scm.subprocess2.CalledProcessError(
684 None, None, None, None, None),
685 None,
686 ]
687
borenet@google.com90fe58b2014-05-01 18:22:00 +0000688 options = self.Options()
Edward Lemur979fa782019-08-13 22:44:05 +0000689 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
John Budorick0f7b2002018-01-19 15:46:17 -0800690 self.relpath)
borenet@google.comb09097a2014-04-09 19:09:08 +0000691 scm.update(options, None, [])
Edward Lemur979fa782019-08-13 22:44:05 +0000692
693 env = gclient_scm.scm.GIT.ApplyEnvVars({})
Josip Sokcevic091f5ac2021-01-14 23:14:21 +0000694 self.assertEqual(mockCheckOutput.mock_calls, [
695 mock.call(['git', 'symbolic-ref', 'refs/remotes/origin/HEAD'],
696 cwd=self.base_path,
697 env=env,
698 stderr=-1),
699 mock.call(['git', '-c', 'core.quotePath=false', 'ls-files'],
700 cwd=self.base_path,
701 env=env,
702 stderr=-1),
703 mock.call(['git', 'rev-parse', '--verify', 'HEAD'],
704 cwd=self.base_path,
705 env=env,
706 stderr=-1),
707 ])
708 mockClone.assert_called_with('refs/remotes/origin/main', self.url, options)
borenet@google.comb09097a2014-04-09 19:09:08 +0000709 self.checkstdout('\n')
710
dbeam@chromium.orge5d1e612011-12-19 19:49:19 +0000711
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000712class UnmanagedGitWrapperTestCase(BaseGitWrapperTestCase):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000713 def checkInStdout(self, expected):
714 value = sys.stdout.getvalue()
715 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800716 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000717 self.assertIn(expected, value)
718
719 def checkNotInStdout(self, expected):
720 value = sys.stdout.getvalue()
721 sys.stdout.close()
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800722 # pylint: disable=no-member
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000723 self.assertNotIn(expected, value)
724
smut@google.com27c9c8a2014-09-11 19:57:55 +0000725 def getCurrentBranch(self):
726 # Returns name of current branch or HEAD for detached HEAD
727 branch = gclient_scm.scm.GIT.Capture(['rev-parse', '--abbrev-ref', 'HEAD'],
728 cwd=self.base_path)
729 if branch == 'HEAD':
730 return None
731 return branch
732
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000733 def testUpdateClone(self):
734 if not self.enabled:
735 return
736 options = self.Options()
737
738 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000739 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
740
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000741 self.root_dir = tempfile.mkdtemp()
742 self.relpath = '.'
743 self.base_path = join(self.root_dir, self.relpath)
744
John Budorick0f7b2002018-01-19 15:46:17 -0800745 scm = gclient_scm.GitWrapper(origin_root_dir,
746 self.root_dir,
747 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000748
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000749 expected_file_list = [join(self.base_path, "a"),
750 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000751 file_list = []
752 options.revision = 'unmanaged'
753 scm.update(options, (), file_list)
754
Edward Lemur979fa782019-08-13 22:44:05 +0000755 self.assertEqual(file_list, expected_file_list)
756 self.assertEqual(scm.revinfo(options, (), None),
757 '069c602044c5388d2d15c3f875b057c852003458')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000758 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000759 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000760 self.checkInStdout(
761 'Checked out refs/remotes/origin/master to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000762
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000763
764 def testUpdateCloneOnCommit(self):
765 if not self.enabled:
766 return
767 options = self.Options()
768
769 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000770 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
771
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000772 self.root_dir = tempfile.mkdtemp()
773 self.relpath = '.'
774 self.base_path = join(self.root_dir, self.relpath)
775 url_with_commit_ref = origin_root_dir +\
776 '@a7142dc9f0009350b96a11f372b6ea658592aa95'
777
John Budorick0f7b2002018-01-19 15:46:17 -0800778 scm = gclient_scm.GitWrapper(url_with_commit_ref,
779 self.root_dir,
780 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000781
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000782 expected_file_list = [join(self.base_path, "a"),
783 join(self.base_path, "b")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000784 file_list = []
785 options.revision = 'unmanaged'
786 scm.update(options, (), file_list)
787
Edward Lemur979fa782019-08-13 22:44:05 +0000788 self.assertEqual(file_list, expected_file_list)
789 self.assertEqual(scm.revinfo(options, (), None),
790 'a7142dc9f0009350b96a11f372b6ea658592aa95')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000791 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000792 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000793 self.checkInStdout(
794 'Checked out a7142dc9f0009350b96a11f372b6ea658592aa95 to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000795
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000796 def testUpdateCloneOnBranch(self):
797 if not self.enabled:
798 return
799 options = self.Options()
800
801 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000802 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
803
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000804 self.root_dir = tempfile.mkdtemp()
805 self.relpath = '.'
806 self.base_path = join(self.root_dir, self.relpath)
807 url_with_branch_ref = origin_root_dir + '@feature'
808
John Budorick0f7b2002018-01-19 15:46:17 -0800809 scm = gclient_scm.GitWrapper(url_with_branch_ref,
810 self.root_dir,
811 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000812
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000813 expected_file_list = [join(self.base_path, "a"),
814 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000815 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000816 file_list = []
817 options.revision = 'unmanaged'
818 scm.update(options, (), file_list)
819
Edward Lemur979fa782019-08-13 22:44:05 +0000820 self.assertEqual(file_list, expected_file_list)
821 self.assertEqual(scm.revinfo(options, (), None),
822 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200823 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000824 self.assertEqual(self.getCurrentBranch(), None)
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200825 self.checkInStdout(
John Budorick882c91e2018-07-12 22:11:41 +0000826 'Checked out 9a51244740b25fa2ded5252ca00a3178d3f665a9 '
Paweł Hajdan, Jr63b8c2a2017-09-05 17:59:08 +0200827 'to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000828
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000829 def testUpdateCloneOnFetchedRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000830 if not self.enabled:
831 return
832 options = self.Options()
833
834 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000835 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
836
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000837 self.root_dir = tempfile.mkdtemp()
838 self.relpath = '.'
839 self.base_path = join(self.root_dir, self.relpath)
840 url_with_branch_ref = origin_root_dir + '@refs/remotes/origin/feature'
841
John Budorick0f7b2002018-01-19 15:46:17 -0800842 scm = gclient_scm.GitWrapper(url_with_branch_ref,
843 self.root_dir,
844 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000845
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000846 expected_file_list = [join(self.base_path, "a"),
847 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000848 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000849 file_list = []
850 options.revision = 'unmanaged'
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000851 scm.update(options, (), file_list)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000852
Edward Lemur979fa782019-08-13 22:44:05 +0000853 self.assertEqual(file_list, expected_file_list)
854 self.assertEqual(scm.revinfo(options, (), None),
855 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000856 # indicates detached HEAD
Edward Lemur979fa782019-08-13 22:44:05 +0000857 self.assertEqual(self.getCurrentBranch(), None)
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000858 self.checkInStdout(
859 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000860
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000861 def testUpdateCloneOnTrueRemoteBranch(self):
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000862 if not self.enabled:
863 return
864 options = self.Options()
865
866 origin_root_dir = self.root_dir
Edward Lesmese79107e2019-10-25 22:47:33 +0000867 self.addCleanup(gclient_utils.rmtree, origin_root_dir)
868
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000869 self.root_dir = tempfile.mkdtemp()
870 self.relpath = '.'
871 self.base_path = join(self.root_dir, self.relpath)
872 url_with_branch_ref = origin_root_dir + '@refs/heads/feature'
873
John Budorick0f7b2002018-01-19 15:46:17 -0800874 scm = gclient_scm.GitWrapper(url_with_branch_ref,
875 self.root_dir,
876 self.relpath)
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000877
szager@chromium.org3dc5cb72014-06-17 15:06:05 +0000878 expected_file_list = [join(self.base_path, "a"),
879 join(self.base_path, "b"),
John Budorick882c91e2018-07-12 22:11:41 +0000880 join(self.base_path, "c")]
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000881 file_list = []
882 options.revision = 'unmanaged'
883 scm.update(options, (), file_list)
884
Edward Lemur979fa782019-08-13 22:44:05 +0000885 self.assertEqual(file_list, expected_file_list)
886 self.assertEqual(scm.revinfo(options, (), None),
887 '9a51244740b25fa2ded5252ca00a3178d3f665a9')
John Budorick882c91e2018-07-12 22:11:41 +0000888 # @refs/heads/feature is AKA @refs/remotes/origin/feature in the clone, so
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000889 # should be treated as such by gclient.
890 # TODO(mmoss): Though really, we should only allow DEPS to specify branches
891 # as they are known in the upstream repo, since the mapping into the local
892 # repo can be modified by users (or we might even want to change the gclient
893 # defaults at some point). But that will take more work to stop using
894 # refs/remotes/ everywhere that we do (and to stop assuming a DEPS ref will
895 # always resolve locally, like when passing them to show-ref or rev-list).
Edward Lemur979fa782019-08-13 22:44:05 +0000896 self.assertEqual(self.getCurrentBranch(), None)
mmoss@chromium.org6e7202b2014-09-09 18:23:39 +0000897 self.checkInStdout(
898 'Checked out refs/remotes/origin/feature to a detached HEAD')
romain.pokrzywka@gmail.com483a0ba2014-05-30 00:06:07 +0000899
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000900 def testUpdateUpdate(self):
901 if not self.enabled:
902 return
903 options = self.Options()
904 expected_file_list = []
John Budorick0f7b2002018-01-19 15:46:17 -0800905 scm = gclient_scm.GitWrapper(self.url, self.root_dir,
906 self.relpath)
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000907 file_list = []
908 options.revision = 'unmanaged'
909 scm.update(options, (), file_list)
Edward Lemur979fa782019-08-13 22:44:05 +0000910 self.assertEqual(file_list, expected_file_list)
911 self.assertEqual(scm.revinfo(options, (), None),
912 '069c602044c5388d2d15c3f875b057c852003458')
cmp@chromium.orgeb2756d2011-09-20 20:17:51 +0000913 self.checkstdout('________ unmanaged solution; skipping .\n')
914
915
Edward Lemur979fa782019-08-13 22:44:05 +0000916class CipdWrapperTestCase(unittest.TestCase):
John Budorick0f7b2002018-01-19 15:46:17 -0800917
918 def setUp(self):
919 # Create this before setting up mocks.
920 self._cipd_root_dir = tempfile.mkdtemp()
921 self._workdir = tempfile.mkdtemp()
John Budorick0f7b2002018-01-19 15:46:17 -0800922
923 self._cipd_instance_url = 'https://chrome-infra-packages.appspot.com'
924 self._cipd_root = gclient_scm.CipdRoot(
925 self._cipd_root_dir,
926 self._cipd_instance_url)
927 self._cipd_packages = [
928 self._cipd_root.add_package('f', 'foo_package', 'foo_version'),
929 self._cipd_root.add_package('b', 'bar_package', 'bar_version'),
930 self._cipd_root.add_package('b', 'baz_package', 'baz_version'),
931 ]
Edward Lemur979fa782019-08-13 22:44:05 +0000932 mock.patch('tempfile.mkdtemp', lambda: self._workdir).start()
933 mock.patch('gclient_scm.CipdRoot.add_package').start()
934 mock.patch('gclient_scm.CipdRoot.clobber').start()
935 mock.patch('gclient_scm.CipdRoot.ensure').start()
936 self.addCleanup(mock.patch.stopall)
Edward Lesmese79107e2019-10-25 22:47:33 +0000937 self.addCleanup(gclient_utils.rmtree, self._cipd_root_dir)
938 self.addCleanup(gclient_utils.rmtree, self._workdir)
John Budorick0f7b2002018-01-19 15:46:17 -0800939
940 def createScmWithPackageThatSatisfies(self, condition):
941 return gclient_scm.CipdWrapper(
942 url=self._cipd_instance_url,
943 root_dir=self._cipd_root_dir,
944 relpath='fake_relpath',
945 root=self._cipd_root,
946 package=self.getPackageThatSatisfies(condition))
947
948 def getPackageThatSatisfies(self, condition):
949 for p in self._cipd_packages:
950 if condition(p):
951 return p
952
953 self.fail('Unable to find a satisfactory package.')
954
John Budorick0f7b2002018-01-19 15:46:17 -0800955 def testRevert(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700956 """Checks that revert does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000957 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800958 scm.revert(None, (), [])
959
Edward Lemur979fa782019-08-13 22:44:05 +0000960 @mock.patch('gclient_scm.gclient_utils.CheckCallAndFilter')
961 @mock.patch('gclient_scm.gclient_utils.rmtree')
962 def testRevinfo(self, mockRmtree, mockCheckCallAndFilter):
John Budorick0f7b2002018-01-19 15:46:17 -0800963 """Checks that revinfo uses the JSON from cipd describe."""
964 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
965
966 expected_revinfo = '0123456789abcdef0123456789abcdef01234567'
967 json_contents = {
968 'result': {
969 'pin': {
970 'instance_id': expected_revinfo,
971 }
972 }
973 }
974 describe_json_path = join(self._workdir, 'describe.json')
975 with open(describe_json_path, 'w') as describe_json:
976 json.dump(json_contents, describe_json)
977
Edward Lemur979fa782019-08-13 22:44:05 +0000978 revinfo = scm.revinfo(None, (), [])
979 self.assertEqual(revinfo, expected_revinfo)
980
981 mockRmtree.assert_called_with(self._workdir)
982 mockCheckCallAndFilter.assert_called_with([
John Budorick0f7b2002018-01-19 15:46:17 -0800983 'cipd', 'describe', 'foo_package',
984 '-log-level', 'error',
985 '-version', 'foo_version',
986 '-json-output', describe_json_path,
Edward Lemur979fa782019-08-13 22:44:05 +0000987 ])
John Budorick0f7b2002018-01-19 15:46:17 -0800988
989 def testUpdate(self):
John Budorickd3ba72b2018-03-20 12:27:42 -0700990 """Checks that update does nothing."""
Edward Lemur979fa782019-08-13 22:44:05 +0000991 scm = self.createScmWithPackageThatSatisfies(lambda _: True)
John Budorick0f7b2002018-01-19 15:46:17 -0800992 scm.update(None, (), [])
993
994
Edward Lesmes8073a502020-04-15 02:11:14 +0000995class BranchHeadsFakeRepo(fake_repos.FakeReposBase):
996 def populateGit(self):
997 # Creates a tree that looks like this:
998 #
999 # 5 refs/branch-heads/5
1000 # |
1001 # 4
1002 # |
1003 # 1--2--3 refs/heads/master
1004 self._commit_git('repo_1', {'commit 1': 'touched'})
1005 self._commit_git('repo_1', {'commit 2': 'touched'})
1006 self._commit_git('repo_1', {'commit 3': 'touched'})
1007 self._create_ref('repo_1', 'refs/heads/master', 3)
1008
1009 self._commit_git('repo_1', {'commit 4': 'touched'}, base=2)
1010 self._commit_git('repo_1', {'commit 5': 'touched'}, base=2)
1011 self._create_ref('repo_1', 'refs/branch-heads/5', 5)
1012
1013
1014class BranchHeadsTest(fake_repos.FakeReposTestBase):
1015 FAKE_REPOS_CLASS = BranchHeadsFakeRepo
1016
1017 def setUp(self):
1018 super(BranchHeadsTest, self).setUp()
1019 self.enabled = self.FAKE_REPOS.set_up_git()
1020 self.options = BaseGitWrapperTestCase.OptionsObject()
1021 self.url = self.git_base + 'repo_1'
1022 self.mirror = None
Ben Pastened410c662020-08-26 17:07:03 +00001023 mock.patch('sys.stdout', StringIO()).start()
Edward Lesmes8073a502020-04-15 02:11:14 +00001024 self.addCleanup(mock.patch.stopall)
1025
1026 def setUpMirror(self):
1027 self.mirror = tempfile.mkdtemp('mirror')
1028 git_cache.Mirror.SetCachePath(self.mirror)
1029 self.addCleanup(gclient_utils.rmtree, self.mirror)
1030 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1031
1032 def testCheckoutBranchHeads(self):
1033 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1034 file_list = []
1035
1036 self.options.revision = 'refs/branch-heads/5'
1037 scm.update(self.options, None, file_list)
1038 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1039
1040 def testCheckoutUpdatedBranchHeads(self):
1041 # Travel back in time, and set refs/branch-heads/5 to its parent.
1042 subprocess2.check_call(
1043 ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 4)],
1044 cwd=self.url)
1045
1046 # Sync to refs/branch-heads/5
1047 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1048 self.options.revision = 'refs/branch-heads/5'
1049 scm.update(self.options, None, [])
1050
1051 # Set refs/branch-heads/5 back to its original value.
1052 subprocess2.check_call(
1053 ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 5)],
1054 cwd=self.url)
1055
1056 # Attempt to sync to refs/branch-heads/5 again.
1057 self.testCheckoutBranchHeads()
1058
1059 def testCheckoutBranchHeadsMirror(self):
1060 self.setUpMirror()
1061 self.testCheckoutBranchHeads()
1062
1063 def testCheckoutUpdatedBranchHeadsMirror(self):
1064 self.setUpMirror()
1065 self.testCheckoutUpdatedBranchHeads()
1066
1067
Edward Lemurd64781e2018-07-11 23:09:55 +00001068class GerritChangesFakeRepo(fake_repos.FakeReposBase):
1069 def populateGit(self):
1070 # Creates a tree that looks like this:
1071 #
Edward Lemurca7d8812018-07-24 17:42:45 +00001072 # 6 refs/changes/35/1235/1
1073 # |
1074 # 5 refs/changes/34/1234/1
1075 # |
Edward Lemurd64781e2018-07-11 23:09:55 +00001076 # 1--2--3--4 refs/heads/master
Edward Lemurca7d8812018-07-24 17:42:45 +00001077 # | |
1078 # | 11(5)--12 refs/heads/master-with-5
1079 # |
1080 # 7--8--9 refs/heads/feature
1081 # |
1082 # 10 refs/changes/36/1236/1
1083 #
Edward Lemurd64781e2018-07-11 23:09:55 +00001084
1085 self._commit_git('repo_1', {'commit 1': 'touched'})
1086 self._commit_git('repo_1', {'commit 2': 'touched'})
1087 self._commit_git('repo_1', {'commit 3': 'touched'})
1088 self._commit_git('repo_1', {'commit 4': 'touched'})
1089 self._create_ref('repo_1', 'refs/heads/master', 4)
1090
1091 # Create a change on top of commit 3 that consists of two commits.
1092 self._commit_git('repo_1',
1093 {'commit 5': 'touched',
1094 'change': '1234'},
1095 base=3)
1096 self._create_ref('repo_1', 'refs/changes/34/1234/1', 5)
1097 self._commit_git('repo_1',
1098 {'commit 6': 'touched',
1099 'change': '1235'})
1100 self._create_ref('repo_1', 'refs/changes/35/1235/1', 6)
1101
Edward Lemurca7d8812018-07-24 17:42:45 +00001102 # Create a refs/heads/feature branch on top of commit 2, consisting of three
1103 # commits.
1104 self._commit_git('repo_1', {'commit 7': 'touched'}, base=2)
1105 self._commit_git('repo_1', {'commit 8': 'touched'})
1106 self._commit_git('repo_1', {'commit 9': 'touched'})
1107 self._create_ref('repo_1', 'refs/heads/feature', 9)
1108
1109 # Create a change of top of commit 8.
1110 self._commit_git('repo_1',
1111 {'commit 10': 'touched',
1112 'change': '1236'},
1113 base=8)
1114 self._create_ref('repo_1', 'refs/changes/36/1236/1', 10)
1115
1116 # Create a refs/heads/master-with-5 on top of commit 3 which is a branch
1117 # where refs/changes/34/1234/1 (commit 5) has already landed as commit 11.
1118 self._commit_git('repo_1',
1119 # This is really commit 11, but has the changes of commit 5
1120 {'commit 5': 'touched',
1121 'change': '1234'},
1122 base=3)
1123 self._commit_git('repo_1', {'commit 12': 'touched'})
1124 self._create_ref('repo_1', 'refs/heads/master-with-5', 12)
1125
Edward Lemurd64781e2018-07-11 23:09:55 +00001126
1127class GerritChangesTest(fake_repos.FakeReposTestBase):
1128 FAKE_REPOS_CLASS = GerritChangesFakeRepo
1129
1130 def setUp(self):
1131 super(GerritChangesTest, self).setUp()
1132 self.enabled = self.FAKE_REPOS.set_up_git()
1133 self.options = BaseGitWrapperTestCase.OptionsObject()
1134 self.url = self.git_base + 'repo_1'
1135 self.mirror = None
Ben Pastened410c662020-08-26 17:07:03 +00001136 mock.patch('sys.stdout', StringIO()).start()
Edward Lesmese79107e2019-10-25 22:47:33 +00001137 self.addCleanup(mock.patch.stopall)
Edward Lemurd64781e2018-07-11 23:09:55 +00001138
1139 def setUpMirror(self):
1140 self.mirror = tempfile.mkdtemp()
1141 git_cache.Mirror.SetCachePath(self.mirror)
Edward Lesmese79107e2019-10-25 22:47:33 +00001142 self.addCleanup(gclient_utils.rmtree, self.mirror)
Edward Lemurd64781e2018-07-11 23:09:55 +00001143 self.addCleanup(git_cache.Mirror.SetCachePath, None)
1144
Edward Lemurca7d8812018-07-24 17:42:45 +00001145 def assertCommits(self, commits):
1146 """Check that all, and only |commits| are present in the current checkout.
1147 """
1148 for i in commits:
1149 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001150 self.assertTrue(os.path.exists(name), 'Commit not found: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001151
1152 all_commits = set(range(1, len(self.FAKE_REPOS.git_hashes['repo_1'])))
1153 for i in all_commits - set(commits):
1154 name = os.path.join(self.root_dir, 'commit ' + str(i))
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001155 self.assertFalse(os.path.exists(name), 'Unexpected commit: %s' % name)
Edward Lemurca7d8812018-07-24 17:42:45 +00001156
Edward Lemurd64781e2018-07-11 23:09:55 +00001157 def testCanCloneGerritChange(self):
1158 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1159 file_list = []
1160
1161 self.options.revision = 'refs/changes/35/1235/1'
1162 scm.update(self.options, None, file_list)
1163 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1164
1165 def testCanSyncToGerritChange(self):
1166 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1167 file_list = []
1168
1169 self.options.revision = self.githash('repo_1', 1)
1170 scm.update(self.options, None, file_list)
1171 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1172
1173 self.options.revision = 'refs/changes/35/1235/1'
1174 scm.update(self.options, None, file_list)
1175 self.assertEqual(self.githash('repo_1', 6), self.gitrevparse(self.root_dir))
1176
1177 def testCanCloneGerritChangeMirror(self):
1178 self.setUpMirror()
1179 self.testCanCloneGerritChange()
1180
1181 def testCanSyncToGerritChangeMirror(self):
1182 self.setUpMirror()
1183 self.testCanSyncToGerritChange()
1184
Edward Lesmese79107e2019-10-25 22:47:33 +00001185 def testMirrorPushUrl(self):
1186 self.setUpMirror()
1187
1188 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1189 file_list = []
1190 self.assertIsNotNone(scm._GetMirror(self.url, self.options))
1191
1192 scm.update(self.options, None, file_list)
1193
1194 fetch_url = scm._Capture(['remote', 'get-url', 'origin'])
1195 self.assertTrue(
1196 fetch_url.startswith(self.mirror),
1197 msg='\n'.join([
1198 'Repository fetch url should be in the git cache mirror directory.',
1199 ' fetch_url: %s' % fetch_url,
1200 ' mirror: %s' % self.mirror]))
1201 push_url = scm._Capture(['remote', 'get-url', '--push', 'origin'])
1202 self.assertEqual(push_url, self.url)
1203
Edward Lemurca7d8812018-07-24 17:42:45 +00001204 def testAppliesPatchOnTopOfMasterByDefault(self):
1205 """Test the default case, where we apply a patch on top of master."""
1206 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1207 file_list = []
1208
1209 # Make sure we don't specify a revision.
1210 self.options.revision = None
1211 scm.update(self.options, None, file_list)
1212 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1213
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001214 scm.apply_patch_ref(
1215 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1216 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001217
1218 self.assertCommits([1, 2, 3, 4, 5, 6])
1219 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1220
1221 def testCheckoutOlderThanPatchBase(self):
1222 """Test applying a patch on an old checkout.
1223
1224 We first checkout commit 1, and try to patch refs/changes/35/1235/1, which
1225 contains commits 5 and 6, and is based on top of commit 3.
1226 The final result should contain commits 1, 5 and 6, but not commits 2 or 3.
1227 """
1228 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1229 file_list = []
1230
1231 # Sync to commit 1
1232 self.options.revision = self.githash('repo_1', 1)
1233 scm.update(self.options, None, file_list)
1234 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1235
1236 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001237 scm.apply_patch_ref(
1238 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1239 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001240
1241 self.assertCommits([1, 5, 6])
1242 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1243
1244 def testCheckoutOriginFeature(self):
1245 """Tests that we can apply a patch on a branch other than master."""
1246 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1247 file_list = []
1248
Edward Lemur8c665652019-05-08 20:23:33 +00001249 # Sync to remote's refs/heads/feature
1250 self.options.revision = 'refs/heads/feature'
Edward Lemurca7d8812018-07-24 17:42:45 +00001251 scm.update(self.options, None, file_list)
1252 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1253
1254 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001255 scm.apply_patch_ref(
1256 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1257 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001258
1259 self.assertCommits([1, 2, 7, 8, 9, 10])
1260 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1261
1262 def testCheckoutOriginFeatureOnOldRevision(self):
1263 """Tests that we can apply a patch on an old checkout, on a branch other
1264 than master."""
1265 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1266 file_list = []
1267
Edward Lemur8c665652019-05-08 20:23:33 +00001268 # Sync to remote's refs/heads/feature on an old revision
Edward Lemurca7d8812018-07-24 17:42:45 +00001269 self.options.revision = self.githash('repo_1', 7)
1270 scm.update(self.options, None, file_list)
1271 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1272
1273 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001274 scm.apply_patch_ref(
1275 self.url, 'refs/changes/36/1236/1', 'refs/heads/feature', self.options,
1276 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001277
1278 # We shouldn't have rebased on top of 2 (which is the merge base between
Edward Lemur8c665652019-05-08 20:23:33 +00001279 # remote's master branch and the change) but on top of 7 (which is the
1280 # merge base between remote's feature branch and the change).
Edward Lemurca7d8812018-07-24 17:42:45 +00001281 self.assertCommits([1, 2, 7, 10])
1282 self.assertEqual(self.githash('repo_1', 7), self.gitrevparse(self.root_dir))
1283
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001284 def testCheckoutOriginFeaturePatchBranch(self):
1285 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1286 file_list = []
1287
Edward Lemur8c665652019-05-08 20:23:33 +00001288 # Sync to the hash instead of remote's refs/heads/feature.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001289 self.options.revision = self.githash('repo_1', 9)
1290 scm.update(self.options, None, file_list)
1291 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
1292
Edward Lemur8c665652019-05-08 20:23:33 +00001293 # Apply refs/changes/34/1234/1, created for remote's master branch on top of
1294 # remote's feature branch.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001295 scm.apply_patch_ref(
1296 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1297 file_list)
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001298
1299 # 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 +00001300 # part of remote's feature branch.
Edward Lemur6a4e31b2018-08-10 19:59:02 +00001301 self.assertCommits([1, 2, 5, 6, 7, 8, 9])
1302 self.assertEqual(self.githash('repo_1', 9), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001303
1304 def testDoesntRebasePatchMaster(self):
1305 """Tests that we can apply a patch without rebasing it.
1306 """
1307 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1308 file_list = []
1309
1310 self.options.rebase_patch_ref = False
1311 scm.update(self.options, None, file_list)
1312 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1313
1314 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001315 scm.apply_patch_ref(
1316 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1317 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001318
1319 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001320 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001321
1322 def testDoesntRebasePatchOldCheckout(self):
1323 """Tests that we can apply a patch without rebasing it on an old checkout.
1324 """
1325 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1326 file_list = []
1327
1328 # Sync to commit 1
1329 self.options.revision = self.githash('repo_1', 1)
1330 self.options.rebase_patch_ref = False
1331 scm.update(self.options, None, file_list)
1332 self.assertEqual(self.githash('repo_1', 1), self.gitrevparse(self.root_dir))
1333
1334 # Apply the change on top of that.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001335 scm.apply_patch_ref(
1336 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1337 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001338
1339 self.assertCommits([1, 2, 3, 5, 6])
Robert Iannuccic39a7782019-11-01 18:30:33 +00001340 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
Edward Lemurca7d8812018-07-24 17:42:45 +00001341
1342 def testDoesntSoftResetIfNotAskedTo(self):
1343 """Test that we can apply a patch without doing a soft reset."""
1344 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1345 file_list = []
1346
1347 self.options.reset_patch_ref = False
1348 scm.update(self.options, None, file_list)
1349 self.assertEqual(self.githash('repo_1', 4), self.gitrevparse(self.root_dir))
1350
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001351 scm.apply_patch_ref(
1352 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1353 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001354
1355 self.assertCommits([1, 2, 3, 4, 5, 6])
1356 # The commit hash after cherry-picking is not known, but it must be
1357 # different from what the repo was synced at before patching.
1358 self.assertNotEqual(self.githash('repo_1', 4),
1359 self.gitrevparse(self.root_dir))
1360
1361 def testRecoversAfterPatchFailure(self):
1362 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1363 file_list = []
1364
1365 self.options.revision = 'refs/changes/34/1234/1'
1366 scm.update(self.options, None, file_list)
1367 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1368
1369 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1370 # patch 'refs/changes/36/1236/1' creates a patch failure.
1371 with self.assertRaises(subprocess2.CalledProcessError) as cm:
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001372 scm.apply_patch_ref(
1373 self.url, 'refs/changes/36/1236/1', 'refs/heads/master', self.options,
1374 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001375 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
Edward Lemur979fa782019-08-13 22:44:05 +00001376 self.assertIn(b'error: could not apply', cm.exception.stderr)
Edward Lemurca7d8812018-07-24 17:42:45 +00001377
1378 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1379 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001380 scm.apply_patch_ref(
1381 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1382 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001383 self.assertCommits([1, 2, 3, 5, 6])
1384 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1385
1386 def testIgnoresAlreadyMergedCommits(self):
1387 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1388 file_list = []
1389
1390 self.options.revision = 'refs/heads/master-with-5'
1391 scm.update(self.options, None, file_list)
1392 self.assertEqual(self.githash('repo_1', 12),
1393 self.gitrevparse(self.root_dir))
1394
1395 # When we try 'refs/changes/35/1235/1' on top of 'refs/heads/feature',
1396 # 'refs/changes/34/1234/1' will be an empty commit, since the changes were
1397 # already present in the tree as commit 11.
1398 # Make sure we deal with this gracefully.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001399 scm.apply_patch_ref(
1400 self.url, 'refs/changes/35/1235/1', 'refs/heads/feature', self.options,
1401 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001402 self.assertCommits([1, 2, 3, 5, 6, 12])
1403 self.assertEqual(self.githash('repo_1', 12),
1404 self.gitrevparse(self.root_dir))
1405
1406 def testRecoversFromExistingCherryPick(self):
1407 scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
1408 file_list = []
1409
1410 self.options.revision = 'refs/changes/34/1234/1'
1411 scm.update(self.options, None, file_list)
1412 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1413
1414 # Checkout 'refs/changes/34/1234/1' modifies the 'change' file, so trying to
1415 # cherry-pick 'refs/changes/36/1236/1' raises an error.
1416 scm._Run(['fetch', 'origin', 'refs/changes/36/1236/1'], self.options)
1417 with self.assertRaises(subprocess2.CalledProcessError) as cm:
1418 scm._Run(['cherry-pick', 'FETCH_HEAD'], self.options)
1419 self.assertEqual(cm.exception.cmd[:2], ['git', 'cherry-pick'])
1420
1421 # Try to apply 'refs/changes/35/1235/1', which doesn't have a merge
1422 # conflict.
Edward Lemur4c5c8ab2019-06-07 15:58:13 +00001423 scm.apply_patch_ref(
1424 self.url, 'refs/changes/35/1235/1', 'refs/heads/master', self.options,
1425 file_list)
Edward Lemurca7d8812018-07-24 17:42:45 +00001426 self.assertCommits([1, 2, 3, 5, 6])
1427 self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
1428
Edward Lemurd64781e2018-07-11 23:09:55 +00001429
msb@chromium.orge28e4982009-09-25 20:51:45 +00001430if __name__ == '__main__':
szager@chromium.orgb0a13a22014-06-18 00:52:25 +00001431 level = logging.DEBUG if '-v' in sys.argv else logging.FATAL
1432 logging.basicConfig(
1433 level=level,
1434 format='%(asctime).19s %(levelname)s %(filename)s:'
1435 '%(lineno)s %(message)s')
msb@chromium.orge28e4982009-09-25 20:51:45 +00001436 unittest.main()
1437
1438# vim: ts=2:sw=2:tw=80:et: