| 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 |
| 13 | 14 |
| 14 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | 15 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 15 | 16 |
| 16 from testing_support.auto_stub import TestCase | 17 from testing_support.auto_stub import TestCase |
| 17 | 18 |
| 18 import git_cl | 19 import git_cl |
| 19 import git_common | 20 import git_common |
| 20 import git_footers | 21 import git_footers |
| 21 import subprocess2 | 22 import subprocess2 |
| 22 | 23 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 "GERRIT_HOST: True\n") | 67 "GERRIT_HOST: True\n") |
| 67 | 68 |
| 68 | 69 |
| 69 class AuthenticatorMock(object): | 70 class AuthenticatorMock(object): |
| 70 def __init__(self, *_args): | 71 def __init__(self, *_args): |
| 71 pass | 72 pass |
| 72 def has_cached_credentials(self): | 73 def has_cached_credentials(self): |
| 73 return True | 74 return True |
| 74 | 75 |
| 75 | 76 |
| 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 |
| 76 class TestGitCl(TestCase): | 171 class TestGitCl(TestCase): |
| 77 def setUp(self): | 172 def setUp(self): |
| 78 super(TestGitCl, self).setUp() | 173 super(TestGitCl, self).setUp() |
| 79 self.calls = [] | 174 self.calls = [] |
| 80 self._calls_done = 0 | 175 self._calls_done = 0 |
| 81 self.mock(subprocess2, 'call', self._mocked_call) | 176 self.mock(subprocess2, 'call', self._mocked_call) |
| 82 self.mock(subprocess2, 'check_call', self._mocked_call) | 177 self.mock(subprocess2, 'check_call', self._mocked_call) |
| 83 self.mock(subprocess2, 'check_output', self._mocked_call) | 178 self.mock(subprocess2, 'check_output', self._mocked_call) |
| 84 self.mock(subprocess2, 'communicate', self._mocked_call) | 179 self.mock(subprocess2, 'communicate', self._mocked_call) |
| 85 self.mock(git_cl.gclient_utils, 'CheckCallAndFilter', self._mocked_call) | 180 self.mock(git_cl.gclient_utils, 'CheckCallAndFilter', self._mocked_call) |
| 86 self.mock(git_common, 'is_dirty_git_tree', lambda x: False) | 181 self.mock(git_common, 'is_dirty_git_tree', lambda x: False) |
| 87 self.mock(git_common, 'get_or_create_merge_base', | 182 self.mock(git_common, 'get_or_create_merge_base', |
| 88 lambda *a: ( | 183 lambda *a: ( |
| 89 self._mocked_call(['get_or_create_merge_base']+list(a)))) | 184 self._mocked_call(['get_or_create_merge_base']+list(a)))) |
| 90 self.mock(git_cl, 'BranchExists', lambda _: True) | 185 self.mock(git_cl, 'BranchExists', lambda _: True) |
| 91 self.mock(git_cl, 'FindCodereviewSettingsFile', lambda: '') | 186 self.mock(git_cl, 'FindCodereviewSettingsFile', lambda: '') |
| 92 self.mock(git_cl, 'ask_for_data', self._mocked_call) | 187 self.mock(git_cl, 'ask_for_data', self._mocked_call) |
| 93 self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock) | 188 self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock) |
| 94 self.mock(git_cl.rietveld, 'Rietveld', RietveldMock) | 189 self.mock(git_cl.rietveld, 'Rietveld', RietveldMock) |
| 95 self.mock(git_cl.rietveld, 'CachingRietveld', RietveldMock) | 190 self.mock(git_cl.rietveld, 'CachingRietveld', RietveldMock) |
| 96 self.mock(git_cl.upload, 'RealMain', self.fail) | 191 self.mock(git_cl.upload, 'RealMain', self.fail) |
| 97 self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock) | 192 self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock) |
| 98 self.mock(git_cl.auth, 'get_authenticator_for_host', AuthenticatorMock) | 193 self.mock(git_cl.auth, 'get_authenticator_for_host', AuthenticatorMock) |
| 99 # It's important to reset settings to not have inter-tests interference. | 194 # It's important to reset settings to not have inter-tests interference. |
| 100 git_cl.settings = None | 195 git_cl.settings = None |
| 101 | 196 |
| 197 |
| 102 def tearDown(self): | 198 def tearDown(self): |
| 103 try: | 199 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. |
| 104 if not self.has_failed(): | 203 if not self.has_failed(): |
| 105 self.assertEquals([], self.calls) | 204 self.assertEquals([], self.calls) |
| 106 finally: | 205 finally: |
| 107 super(TestGitCl, self).tearDown() | 206 super(TestGitCl, self).tearDown() |
| 108 | 207 |
| 109 def _mocked_call(self, *args, **_kwargs): | 208 def _mocked_call(self, *args, **_kwargs): |
| 110 self.assertTrue( | 209 self.assertTrue( |
| 111 self.calls, | 210 self.calls, |
| 112 '@%d Expected: <Missing> Actual: %r' % (self._calls_done, args)) | 211 '@%d Expected: <Missing> Actual: %r' % (self._calls_done, args)) |
| 113 top = self.calls.pop(0) | 212 top = self.calls.pop(0) |
| (...skipping 795 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 909 branch, None)) | 1008 branch, None)) |
| 910 | 1009 |
| 911 # Check target refs for pending prefix. | 1010 # Check target refs for pending prefix. |
| 912 self.assertEqual('prefix/heads/master', | 1011 self.assertEqual('prefix/heads/master', |
| 913 git_cl.GetTargetRef('origin', 'refs/remotes/origin/master', | 1012 git_cl.GetTargetRef('origin', 'refs/remotes/origin/master', |
| 914 None, 'prefix/')) | 1013 None, 'prefix/')) |
| 915 | 1014 |
| 916 def test_patch_when_dirty(self): | 1015 def test_patch_when_dirty(self): |
| 917 # Patch when local tree is dirty | 1016 # Patch when local tree is dirty |
| 918 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) | 1017 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 ] |
| 919 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) | 1026 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) |
| 920 | 1027 |
| 921 def test_diff_when_dirty(self): | 1028 def test_diff_when_dirty(self): |
| 922 # Do 'git cl diff' when local tree is dirty | 1029 # Do 'git cl diff' when local tree is dirty |
| 923 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) | 1030 self.mock(git_common, 'is_dirty_git_tree', lambda x: True) |
| 924 self.assertNotEqual(git_cl.main(['diff']), 0) | 1031 self.assertNotEqual(git_cl.main(['diff']), 0) |
| 925 | 1032 |
| 926 def _patch_common(self): | 1033 def _patch_common(self, is_gerrit=False): |
| 927 self.mock(git_cl._RietveldChangelistImpl, 'GetMostRecentPatchset', | 1034 self.mock(git_cl._RietveldChangelistImpl, 'GetMostRecentPatchset', |
| 928 lambda x: '60001') | 1035 lambda x: '60001') |
| 929 self.mock(git_cl._RietveldChangelistImpl, 'GetPatchSetDiff', | 1036 self.mock(git_cl._RietveldChangelistImpl, 'GetPatchSetDiff', |
| 930 lambda *args: None) | 1037 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 }) |
| 931 self.mock(git_cl.Changelist, 'GetDescription', | 1058 self.mock(git_cl.Changelist, 'GetDescription', |
| 932 lambda *args: 'Description') | 1059 lambda *args: 'Description') |
| 933 self.mock(git_cl.Changelist, 'SetIssue', lambda *args: None) | |
| 934 self.mock(git_cl.Changelist, 'SetPatchset', lambda *args: None) | |
| 935 self.mock(git_cl, 'IsGitVersionAtLeast', lambda *args: True) | 1060 self.mock(git_cl, 'IsGitVersionAtLeast', lambda *args: True) |
| 936 | 1061 |
| 937 self.calls = [ | 1062 self.calls = [ |
| 1063 ((['git', 'symbolic-ref', 'HEAD'],), 'master'), |
| 1064 ((['git', 'config', 'branch.master.rietveldissue'],), ''), |
| 1065 ((['git', 'config', 'branch.master.gerritissue'],), ''), |
| 938 ((['git', 'config', 'rietveld.autoupdate'],), ''), | 1066 ((['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/|+++ |'],), ''), | |
| 942 ] | 1067 ] |
| 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 ] |
| 943 | 1079 |
| 944 def test_patch_successful(self): | 1080 def test_patch_successful(self): |
| 945 self._patch_common() | 1081 self._patch_common() |
| 946 self.calls += [ | 1082 self.calls += [ |
| 947 ((['git', 'apply', '--index', '-p0', '--3way'],), ''), | 1083 ((['git', 'apply', '--index', '-p0', '--3way'],), ''), |
| 948 ((['git', 'commit', '-m', | 1084 ((['git', 'commit', '-m', |
| 949 'Description\n\n' + | 1085 'Description\n\n' + |
| 950 'patch from issue 123456 at patchset 60001 ' + | 1086 'patch from issue 123456 at patchset 60001 ' + |
| 951 '(http://crrev.com/123456#ps60001)'],), ''), | 1087 '(http://crrev.com/123456#ps60001)'],), ''), |
| 952 ((['git', 'symbolic-ref', 'HEAD'],), 'master'), | 1088 ((['git', 'config', 'branch.master.rietveldissue', '123456'],), ''), |
| 953 ((['git', 'config', 'branch.master.rietveldserver'],), ''), | 1089 ((['git', 'config', 'branch.master.rietveldserver'],), ''), |
| 1090 ((['git', 'config', 'branch.master.rietveldserver', |
| 1091 'https://codereview.example.com'],), ''), |
| 1092 ((['git', 'config', 'branch.master.rietveldpatchset', '60001'],), ''), |
| 954 ] | 1093 ] |
| 955 self.assertEqual(git_cl.main(['patch', '123456']), 0) | 1094 self.assertEqual(git_cl.main(['patch', '123456']), 0) |
| 956 | 1095 |
| 957 def test_patch_conflict(self): | 1096 def test_patch_conflict(self): |
| 958 self._patch_common() | 1097 self._patch_common() |
| 959 self.calls += [ | 1098 self.calls += [ |
| 960 ((['git', 'apply', '--index', '-p0', '--3way'],), '', | 1099 ((['git', 'apply', '--index', '-p0', '--3way'],), '', |
| 961 subprocess2.CalledProcessError(1, '', '', '', '')), | 1100 subprocess2.CalledProcessError(1, '', '', '', '')), |
| 962 ] | 1101 ] |
| 963 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) | 1102 self.assertNotEqual(git_cl.main(['patch', '123456']), 0) |
| 964 | 1103 |
| 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 |
| 965 if __name__ == '__main__': | 1155 if __name__ == '__main__': |
| 966 git_cl.logging.basicConfig( | 1156 git_cl.logging.basicConfig( |
| 967 level=git_cl.logging.DEBUG if '-v' in sys.argv else git_cl.logging.ERROR) | 1157 level=git_cl.logging.DEBUG if '-v' in sys.argv else git_cl.logging.ERROR) |
| 968 unittest.main() | 1158 unittest.main() |
| OLD | NEW |