OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Unit tests for git_cl.py.""" | 6 """Unit tests for git_cl.py.""" |
7 | 7 |
8 import os | 8 import os |
9 import StringIO | 9 import StringIO |
10 import stat | 10 import stat |
11 import sys | 11 import sys |
12 import unittest | 12 import unittest |
13 import urlparse | |
14 | 13 |
15 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | 14 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
16 | 15 |
17 from testing_support.auto_stub import TestCase | 16 from testing_support.auto_stub import TestCase |
18 | 17 |
19 import git_cl | 18 import git_cl |
20 import git_common | 19 import git_common |
21 import git_footers | 20 import git_footers |
22 import subprocess2 | 21 import subprocess2 |
23 | 22 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 "GERRIT_HOST: True\n") | 66 "GERRIT_HOST: True\n") |
68 | 67 |
69 | 68 |
70 class AuthenticatorMock(object): | 69 class AuthenticatorMock(object): |
71 def __init__(self, *_args): | 70 def __init__(self, *_args): |
72 pass | 71 pass |
73 def has_cached_credentials(self): | 72 def has_cached_credentials(self): |
74 return True | 73 return True |
75 | 74 |
76 | 75 |
77 class TestGitClBasic(unittest.TestCase): | |
78 def _test_ParseIssueUrl(self, func, url, issue, patchset, hostname, fail): | |
79 parsed = urlparse.urlparse(url) | |
80 result = func(parsed) | |
81 if fail: | |
82 self.assertIsNone(result) | |
83 return None | |
84 self.assertIsNotNone(result) | |
85 self.assertEqual(result.issue, issue) | |
86 self.assertEqual(result.patchset, patchset) | |
87 self.assertEqual(result.hostname, hostname) | |
88 return result | |
89 | |
90 def test_ParseIssueURL_rietveld(self): | |
91 def test(url, issue=None, patchset=None, hostname=None, patch_url=None, | |
92 fail=None): | |
93 result = self._test_ParseIssueUrl( | |
94 git_cl._RietveldChangelistImpl.ParseIssueURL, | |
95 url, issue, patchset, hostname, fail) | |
96 if not fail: | |
97 self.assertEqual(result.patch_url, patch_url) | |
98 | |
99 test('http://codereview.chromium.org/123', | |
100 123, None, 'codereview.chromium.org') | |
101 test('https://codereview.chromium.org/123', | |
102 123, None, 'codereview.chromium.org') | |
103 test('https://codereview.chromium.org/123/', | |
104 123, None, 'codereview.chromium.org') | |
105 test('https://codereview.chromium.org/123/whatever', | |
106 123, None, 'codereview.chromium.org') | |
107 test('http://codereview.chromium.org/download/issue123_4.diff', | |
108 123, 4, 'codereview.chromium.org', | |
109 patch_url='https://codereview.chromium.org/download/issue123_4.diff') | |
110 # This looks like bad Gerrit, but is actually valid Rietveld. | |
111 test('https://chrome-review.source.com/123/4/', | |
112 123, None, 'chrome-review.source.com') | |
113 | |
114 test('https://codereview.chromium.org/deadbeaf', fail=True) | |
115 test('https://codereview.chromium.org/api/123', fail=True) | |
116 test('bad://codereview.chromium.org/123', fail=True) | |
117 test('http://codereview.chromium.org/download/issue123_4.diffff', fail=True) | |
118 | |
119 def test_ParseIssueURL_gerrit(self): | |
120 def test(url, issue=None, patchset=None, hostname=None, fail=None): | |
121 self._test_ParseIssueUrl( | |
122 git_cl._GerritChangelistImpl.ParseIssueURL, | |
123 url, issue, patchset, hostname, fail) | |
124 | |
125 test('http://chrome-review.source.com/c/123', | |
126 123, None, 'chrome-review.source.com') | |
127 test('https://chrome-review.source.com/c/123/', | |
128 123, None, 'chrome-review.source.com') | |
129 test('https://chrome-review.source.com/c/123/4', | |
130 123, 4, 'chrome-review.source.com') | |
131 test('https://chrome-review.source.com/#/c/123/4', | |
132 123, 4, 'chrome-review.source.com') | |
133 test('https://chrome-review.source.com/c/123/4', | |
134 123, 4, 'chrome-review.source.com') | |
135 test('https://chrome-review.source.com/123', | |
136 123, None, 'chrome-review.source.com') | |
137 test('https://chrome-review.source.com/123/4', | |
138 123, 4, 'chrome-review.source.com') | |
139 | |
140 test('https://chrome-review.source.com/c/123/1/whatisthis', fail=True) | |
141 test('https://chrome-review.source.com/c/abc/', fail=True) | |
142 test('ssh://chrome-review.source.com/c/123/1/', fail=True) | |
143 | |
144 def test_ParseIssueNumberArgument(self): | |
145 def test(arg, issue=None, patchset=None, hostname=None, fail=False): | |
146 result = git_cl.ParseIssueNumberArgument(arg) | |
147 self.assertIsNotNone(result) | |
148 if fail: | |
149 self.assertFalse(result.valid) | |
150 else: | |
151 self.assertEqual(result.issue, issue) | |
152 self.assertEqual(result.patchset, patchset) | |
153 self.assertEqual(result.hostname, hostname) | |
154 | |
155 test('123', 123) | |
156 test('', fail=True) | |
157 test('abc', fail=True) | |
158 test('123/1', fail=True) | |
159 test('123a', fail=True) | |
160 test('ssh://chrome-review.source.com/#/c/123/4/', fail=True) | |
161 # Rietveld. | |
162 test('https://codereview.source.com/123', | |
163 123, None, 'codereview.source.com') | |
164 test('https://codereview.source.com/www123', fail=True) | |
165 # Gerrrit. | |
166 test('https://chrome-review.source.com/c/123/4', | |
167 123, 4, 'chrome-review.source.com') | |
168 test('https://chrome-review.source.com/bad/123/4', fail=True) | |
169 | |
170 | |
171 class TestGitCl(TestCase): | 76 class TestGitCl(TestCase): |
172 def setUp(self): | 77 def setUp(self): |
173 super(TestGitCl, self).setUp() | 78 super(TestGitCl, self).setUp() |
174 self.calls = [] | 79 self.calls = [] |
175 self._calls_done = 0 | 80 self._calls_done = 0 |
176 self.mock(subprocess2, 'call', self._mocked_call) | 81 self.mock(subprocess2, 'call', self._mocked_call) |
177 self.mock(subprocess2, 'check_call', self._mocked_call) | 82 self.mock(subprocess2, 'check_call', self._mocked_call) |
178 self.mock(subprocess2, 'check_output', self._mocked_call) | 83 self.mock(subprocess2, 'check_output', self._mocked_call) |
179 self.mock(subprocess2, 'communicate', self._mocked_call) | 84 self.mock(subprocess2, 'communicate', self._mocked_call) |
180 self.mock(git_cl.gclient_utils, 'CheckCallAndFilter', self._mocked_call) | 85 self.mock(git_cl.gclient_utils, 'CheckCallAndFilter', self._mocked_call) |
181 self.mock(git_common, 'is_dirty_git_tree', lambda x: False) | 86 self.mock(git_common, 'is_dirty_git_tree', lambda x: False) |
182 self.mock(git_common, 'get_or_create_merge_base', | 87 self.mock(git_common, 'get_or_create_merge_base', |
183 lambda *a: ( | 88 lambda *a: ( |
184 self._mocked_call(['get_or_create_merge_base']+list(a)))) | 89 self._mocked_call(['get_or_create_merge_base']+list(a)))) |
185 self.mock(git_cl, 'BranchExists', lambda _: True) | 90 self.mock(git_cl, 'BranchExists', lambda _: True) |
186 self.mock(git_cl, 'FindCodereviewSettingsFile', lambda: '') | 91 self.mock(git_cl, 'FindCodereviewSettingsFile', lambda: '') |
187 self.mock(git_cl, 'ask_for_data', self._mocked_call) | 92 self.mock(git_cl, 'ask_for_data', self._mocked_call) |
188 self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock) | 93 self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock) |
189 self.mock(git_cl.rietveld, 'Rietveld', RietveldMock) | 94 self.mock(git_cl.rietveld, 'Rietveld', RietveldMock) |
190 self.mock(git_cl.rietveld, 'CachingRietveld', RietveldMock) | 95 self.mock(git_cl.rietveld, 'CachingRietveld', RietveldMock) |
191 self.mock(git_cl.upload, 'RealMain', self.fail) | 96 self.mock(git_cl.upload, 'RealMain', self.fail) |
192 self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock) | 97 self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock) |
193 self.mock(git_cl.auth, 'get_authenticator_for_host', AuthenticatorMock) | 98 self.mock(git_cl.auth, 'get_authenticator_for_host', AuthenticatorMock) |
194 # It's important to reset settings to not have inter-tests interference. | 99 # It's important to reset settings to not have inter-tests interference. |
195 git_cl.settings = None | 100 git_cl.settings = None |
196 | 101 |
197 | |
198 def tearDown(self): | 102 def tearDown(self): |
199 try: | 103 try: |
200 # Note: has_failed returns True if at least 1 test ran so far, current | |
201 # included, has failed. That means current test may have actually ran | |
202 # fine, and the check for no leftover calls would be skipped. | |
203 if not self.has_failed(): | 104 if not self.has_failed(): |
204 self.assertEquals([], self.calls) | 105 self.assertEquals([], self.calls) |
205 finally: | 106 finally: |
206 super(TestGitCl, self).tearDown() | 107 super(TestGitCl, self).tearDown() |
207 | 108 |
208 def _mocked_call(self, *args, **_kwargs): | 109 def _mocked_call(self, *args, **_kwargs): |
209 self.assertTrue( | 110 self.assertTrue( |
210 self.calls, | 111 self.calls, |
211 '@%d Expected: <Missing> Actual: %r' % (self._calls_done, args)) | 112 '@%d Expected: <Missing> Actual: %r' % (self._calls_done, args)) |
212 top = self.calls.pop(0) | 113 top = self.calls.pop(0) |
(...skipping 795 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1008 branch, None)) | 909 branch, None)) |
1009 | 910 |
1010 # Check target refs for pending prefix. | 911 # Check target refs for pending prefix. |
1011 self.assertEqual('prefix/heads/master', | 912 self.assertEqual('prefix/heads/master', |
1012 git_cl.GetTargetRef('origin', 'refs/remotes/origin/master', | 913 git_cl.GetTargetRef('origin', 'refs/remotes/origin/master', |
1013 None, 'prefix/')) | 914 None, 'prefix/')) |
1014 | 915 |
1015 def test_patch_when_dirty(self): | 916 def test_patch_when_dirty(self): |
1016 # Patch when local tree is dirty | 917 # Patch when local tree is dirty |
1017 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) | 918 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) |
1018 self.calls = [ | |
1019 ((['git', 'symbolic-ref', 'HEAD'],), 'master'), | |
1020 ((['git', 'config', 'branch.master.rietveldissue'],), ''), | |
1021 ((['git', 'config', 'branch.master.gerritissue'],), ''), | |
1022 ((['git', 'config', 'rietveld.autoupdate'],), ''), | |
1023 ((['git', 'config', 'gerrit.host'],), ''), | |
1024 ((['git', 'config', 'rietveld.server'],), 'codereview.example.com'), | |
1025 ] | |
1026 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) | 919 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) |
1027 | 920 |
1028 def test_diff_when_dirty(self): | 921 def test_diff_when_dirty(self): |
1029 # Do 'git cl diff' when local tree is dirty | 922 # Do 'git cl diff' when local tree is dirty |
1030 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) | 923 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) |
1031 self.assertNotEqual(git_cl.main(['diff']), 0) | 924 self.assertNotEqual(git_cl.main(['diff']), 0) |
1032 | 925 |
1033 def _patch_common(self, is_gerrit=False): | 926 def _patch_common(self): |
1034 self.mock(git_cl._RietveldChangelistImpl, 'GetMostRecentPatchset', | 927 self.mock(git_cl._RietveldChangelistImpl, 'GetMostRecentPatchset', |
1035 lambda x: '60001') | 928 lambda x: '60001') |
1036 self.mock(git_cl._RietveldChangelistImpl, 'GetPatchSetDiff', | 929 self.mock(git_cl._RietveldChangelistImpl, 'GetPatchSetDiff', |
1037 lambda *args: None) | 930 lambda *args: None) |
1038 self.mock(git_cl._GerritChangelistImpl, '_GetChangeDetail', | |
1039 lambda *args: { | |
1040 'current_revision': '7777777777', | |
1041 'revisions': { | |
1042 '1111111111': { | |
1043 '_number': 1, | |
1044 'fetch': {'http': { | |
1045 'url': 'https://chromium.googlesource.com/my/repo', | |
1046 'ref': 'refs/changes/56/123456/1', | |
1047 }}, | |
1048 }, | |
1049 '7777777777': { | |
1050 '_number': 7, | |
1051 'fetch': {'http': { | |
1052 'url': 'https://chromium.googlesource.com/my/repo', | |
1053 'ref': 'refs/changes/56/123456/7', | |
1054 }}, | |
1055 }, | |
1056 }, | |
1057 }) | |
1058 self.mock(git_cl.Changelist, 'GetDescription', | 931 self.mock(git_cl.Changelist, 'GetDescription', |
1059 lambda *args: 'Description') | 932 lambda *args: 'Description') |
| 933 self.mock(git_cl.Changelist, 'SetIssue', lambda *args: None) |
| 934 self.mock(git_cl.Changelist, 'SetPatchset', lambda *args: None) |
1060 self.mock(git_cl, 'IsGitVersionAtLeast', lambda *args: True) | 935 self.mock(git_cl, 'IsGitVersionAtLeast', lambda *args: True) |
1061 | 936 |
1062 self.calls = [ | 937 self.calls = [ |
1063 ((['git', 'symbolic-ref', 'HEAD'],), 'master'), | |
1064 ((['git', 'config', 'branch.master.rietveldissue'],), ''), | |
1065 ((['git', 'config', 'branch.master.gerritissue'],), ''), | |
1066 ((['git', 'config', 'rietveld.autoupdate'],), ''), | 938 ((['git', 'config', 'rietveld.autoupdate'],), ''), |
| 939 ((['git', 'config', 'rietveld.server'],), 'codereview.example.com'), |
| 940 ((['git', 'rev-parse', '--show-cdup'],), ''), |
| 941 ((['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'],), ''), |
1067 ] | 942 ] |
1068 if is_gerrit: | |
1069 self.calls += [ | |
1070 ((['git', 'config', 'gerrit.host'],), 'true'), | |
1071 ] | |
1072 else: | |
1073 self.calls += [ | |
1074 ((['git', 'config', 'gerrit.host'],), ''), | |
1075 ((['git', 'config', 'rietveld.server'],), 'codereview.example.com'), | |
1076 ((['git', 'rev-parse', '--show-cdup'],), ''), | |
1077 ((['sed', '-e', 's|^--- a/|--- |; s|^+++ b/|+++ |'],), ''), | |
1078 ] | |
1079 | 943 |
1080 def test_patch_successful(self): | 944 def test_patch_successful(self): |
1081 self._patch_common() | 945 self._patch_common() |
1082 self.calls += [ | 946 self.calls += [ |
1083 ((['git', 'apply', '--index', '-p0', '--3way'],), ''), | 947 ((['git', 'apply', '--index', '-p0', '--3way'],), ''), |
1084 ((['git', 'commit', '-m', | 948 ((['git', 'commit', '-m', |
1085 'Description\n\n' + | 949 'Description\n\n' + |
1086 'patch from issue 123456 at patchset 60001 ' + | 950 'patch from issue 123456 at patchset 60001 ' + |
1087 '(http://crrev.com/123456#ps60001)'],), ''), | 951 '(http://crrev.com/123456#ps60001)'],), ''), |
1088 ((['git', 'config', 'branch.master.rietveldissue', '123456'],), ''), | 952 ((['git', 'symbolic-ref', 'HEAD'],), 'master'), |
1089 ((['git', 'config', 'branch.master.rietveldserver'],), ''), | 953 ((['git', 'config', 'branch.master.rietveldserver'],), ''), |
1090 ((['git', 'config', 'branch.master.rietveldserver', | |
1091 'https://codereview.example.com'],), ''), | |
1092 ((['git', 'config', 'branch.master.rietveldpatchset', '60001'],), ''), | |
1093 ] | 954 ] |
1094 self.assertEqual(git_cl.main(['patch', '123456']), 0) | 955 self.assertEqual(git_cl.main(['patch', '123456']), 0) |
1095 | 956 |
1096 def test_patch_conflict(self): | 957 def test_patch_conflict(self): |
1097 self._patch_common() | 958 self._patch_common() |
1098 self.calls += [ | 959 self.calls += [ |
1099 ((['git', 'apply', '--index', '-p0', '--3way'],), '', | 960 ((['git', 'apply', '--index', '-p0', '--3way'],), '', |
1100 subprocess2.CalledProcessError(1, '', '', '', '')), | 961 subprocess2.CalledProcessError(1, '', '', '', '')), |
1101 ] | 962 ] |
1102 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) | 963 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) |
1103 | 964 |
1104 def test_gerrit_patch_successful(self): | |
1105 self._patch_common(is_gerrit=True) | |
1106 self.calls += [ | |
1107 ((['git', 'fetch', 'https://chromium.googlesource.com/my/repo', | |
1108 'refs/changes/56/123456/7'],), ''), | |
1109 ((['git', 'cherry-pick', 'FETCH_HEAD'],), ''), | |
1110 ((['git', 'config', 'branch.master.gerritissue', '123456'],), ''), | |
1111 ((['git', 'config', 'branch.master.gerritserver'],), ''), | |
1112 ((['git', 'config', 'branch.master.merge'],), 'master'), | |
1113 ((['git', 'config', 'branch.master.remote'],), 'origin'), | |
1114 ((['git', 'config', 'remote.origin.url'],), | |
1115 'https://chromium.googlesource.com/my/repo'), | |
1116 ((['git', 'config', 'branch.master.gerritserver', | |
1117 'https://chromium-review.googlesource.com'],), ''), | |
1118 ((['git', 'config', 'branch.master.gerritpatchset', '7'],), ''), | |
1119 ] | |
1120 self.assertEqual(git_cl.main(['patch', '123456']), 0) | |
1121 | |
1122 def test_gerrit_patch_url_successful(self): | |
1123 self._patch_common(is_gerrit=True) | |
1124 self.calls += [ | |
1125 ((['git', 'fetch', 'https://chromium.googlesource.com/my/repo', | |
1126 'refs/changes/56/123456/1'],), ''), | |
1127 ((['git', 'cherry-pick', 'FETCH_HEAD'],), ''), | |
1128 ((['git', 'config', 'branch.master.gerritissue', '123456'],), ''), | |
1129 ((['git', 'config', 'branch.master.gerritserver', | |
1130 'https://chromium-review.googlesource.com'],), ''), | |
1131 ((['git', 'config', 'branch.master.gerritpatchset', '1'],), ''), | |
1132 ] | |
1133 self.assertEqual(git_cl.main( | |
1134 ['patch', 'https://chromium-review.googlesource.com/#/c/123456/1']), 0) | |
1135 | |
1136 def test_gerrit_patch_conflict(self): | |
1137 self._patch_common(is_gerrit=True) | |
1138 self.mock(git_cl, 'DieWithError', | |
1139 lambda msg: self._mocked_call(['DieWithError', msg])) | |
1140 class SystemExitMock(Exception): | |
1141 pass | |
1142 self.calls += [ | |
1143 ((['git', 'fetch', 'https://chromium.googlesource.com/my/repo', | |
1144 'refs/changes/56/123456/1'],), ''), | |
1145 ((['git', 'cherry-pick', 'FETCH_HEAD'],), | |
1146 '', subprocess2.CalledProcessError(1, '', '', '', '')), | |
1147 ((['DieWithError', 'git cherry-pick FETCH_HEAD" failed.\n'],), | |
1148 '', SystemExitMock()), | |
1149 ] | |
1150 with self.assertRaises(SystemExitMock): | |
1151 git_cl.main(['patch', | |
1152 'https://chromium-review.googlesource.com/#/c/123456/1']) | |
1153 | |
1154 | |
1155 if __name__ == '__main__': | 965 if __name__ == '__main__': |
1156 git_cl.logging.basicConfig( | 966 git_cl.logging.basicConfig( |
1157 level=git_cl.logging.DEBUG if '-v' in sys.argv else git_cl.logging.ERROR) | 967 level=git_cl.logging.DEBUG if '-v' in sys.argv else git_cl.logging.ERROR) |
1158 unittest.main() | 968 unittest.main() |
OLD | NEW |