blob: b2f3e19652a4aa9b9172e60735a1d234617ee03d [file] [log] [blame]
maruel@chromium.orgddd59412011-11-30 14:20:38 +00001#!/usr/bin/env python
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Unit tests for git_cl.py."""
7
8import os
maruel@chromium.orga3353652011-11-30 14:26:57 +00009import StringIO
maruel@chromium.orgddd59412011-11-30 14:20:38 +000010import sys
11import unittest
12
13sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14
15from testing_support.auto_stub import TestCase
16
17import git_cl
18import subprocess2
19
20
21class TestGitCl(TestCase):
22 def setUp(self):
23 super(TestGitCl, self).setUp()
24 self.calls = []
25 self._calls_done = 0
26 def mock_call(args, **kwargs):
27 expected_args, result = self.calls.pop(0)
28 self.assertEquals(
29 expected_args,
30 args,
31 '@%d Expected: %r Actual: %r' % (
32 self._calls_done, expected_args, args))
33 self._calls_done += 1
34 return result
35 self.mock(subprocess2, 'call', mock_call)
36 self.mock(subprocess2, 'check_call', mock_call)
37 self.mock(subprocess2, 'check_output', mock_call)
38 self.mock(subprocess2, 'communicate', mock_call)
39 self.mock(subprocess2, 'Popen', mock_call)
40
41 self.mock(git_cl, 'FindCodereviewSettingsFile', lambda: '')
42
43 class PresubmitMock(object):
44 def __init__(self, *args, **kwargs):
45 self.reviewers = []
46 @staticmethod
47 def should_continue():
48 return True
49 self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock)
50
51 class RietveldMock(object):
52 def __init__(self, *args, **kwargs):
53 pass
54 self.mock(git_cl.rietveld, 'Rietveld', RietveldMock)
55
56 self.mock(git_cl.upload, 'RealMain', self.fail)
57
58 class WatchlistsMock(object):
59 def __init__(self, _):
60 pass
61 @staticmethod
62 def GetWatchersForPaths(_):
63 return ['joe@example.com']
64 self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock)
65 # It's important to reset settings to not have inter-tests interference.
66 git_cl.settings = None
67
68 def tearDown(self):
69 if not self.has_failed():
70 self.assertEquals([], self.calls)
71 super(TestGitCl, self).tearDown()
72
maruel@chromium.orga3353652011-11-30 14:26:57 +000073 @classmethod
74 def _upload_calls(cls):
75 return cls._git_base_calls() + cls._git_upload_calls()
76
maruel@chromium.orgddd59412011-11-30 14:20:38 +000077 @staticmethod
maruel@chromium.orga3353652011-11-30 14:26:57 +000078 def _git_base_calls():
maruel@chromium.orgddd59412011-11-30 14:20:38 +000079 return [
80 (['git', 'update-index', '--refresh', '-q'], ''),
81 (['git', 'diff-index', 'HEAD'], ''),
82 (['git', 'config', 'rietveld.server'], 'codereview.example.com'),
83 (['git', 'symbolic-ref', 'HEAD'], 'master'),
84 (['git', 'config', 'branch.master.merge'], 'master'),
85 (['git', 'config', 'branch.master.remote'], 'origin'),
86 (['git', 'rev-parse', '--show-cdup'], ''),
87 (['git', 'rev-parse', 'HEAD'], '12345'),
88 (['git', 'diff', '--name-status', '-r', 'master...', '.'],
89 'M\t.gitignore\n'),
90 (['git', 'rev-parse', '--git-dir'], '.git'),
91 (['git', 'config', 'branch.master.rietveldissue'], ''),
92 (['git', 'config', 'branch.master.rietveldpatchset'], ''),
93 (['git', 'log', '--pretty=format:%s%n%n%b', 'master...'], 'foo'),
94 (['git', 'config', 'user.email'], 'me@example.com'),
95 (['git', 'diff', '--no-ext-diff', '--stat', '-M', 'master...'], '+dat'),
96 (['git', 'log', '--pretty=format:%s\n\n%b', 'master..'], 'desc\n'),
maruel@chromium.orga3353652011-11-30 14:26:57 +000097 ]
98
99 @staticmethod
100 def _git_upload_calls():
101 return [
maruel@chromium.orgddd59412011-11-30 14:20:38 +0000102 (['git', 'config', 'rietveld.cc'], ''),
103 (['git', 'config', '--get-regexp', '^svn-remote\\.'], (('', None), 0)),
104 (['git', 'rev-parse', '--show-cdup'], ''),
105 (['git', 'svn', 'info'], ''),
106 (['git', 'config', 'branch.master.rietveldissue', '1'], ''),
107 (['git', 'config', 'branch.master.rietveldserver',
108 'http://codereview.example.com'], ''),
109 (['git', 'config', 'branch.master.rietveldpatchset', '2'], ''),
110 ]
111
112 @staticmethod
113 def _cmd_line(description, args):
114 """Returns the upload command line passed to upload.RealMain()."""
115 msg = description.split('\n', 1)[0]
116 return [
117 'upload', '--assume_yes', '--server',
118 'http://codereview.example.com',
119 '--message', msg,
120 '--description', description
121 ] + args + [
122 '--cc', 'joe@example.com',
123 'master...'
124 ]
125
126 def _run_reviewer_test(
127 self,
128 upload_args,
129 expected_description,
130 returned_description,
131 final_description,
132 reviewers):
133 """Generic reviewer test framework."""
134 self.calls = self._upload_calls()
135 def RunEditor(desc, _):
136 self.assertEquals(
137 '# Enter a description of the change.\n'
138 '# This will displayed on the codereview site.\n'
139 '# The first line will also be used as the subject of the review.\n' +
140 expected_description,
141 desc)
142 return returned_description
143 self.mock(git_cl.gclient_utils, 'RunEditor', RunEditor)
144 def check_upload(args):
145 self.assertEquals(self._cmd_line(final_description, reviewers), args)
146 return 1, 2
147 self.mock(git_cl.upload, 'RealMain', check_upload)
148 git_cl.main(['upload'] + upload_args)
149
150 def test_no_reviewer(self):
151 self._run_reviewer_test(
152 [],
153 'desc\n\nBUG=\nTEST=\n',
154 '# Blah blah comment.\ndesc\n\nBUG=\nTEST=\n',
155 'desc\n\nBUG=\nTEST=\n',
156 [])
157
158 def test_reviewers_cmd_line(self):
159 # Reviewer is passed as-is
160 description = 'desc\n\nR=foo@example.com\nBUG=\nTEST=\n'
161 self._run_reviewer_test(
162 ['-r' 'foo@example.com'],
163 description,
164 '\n%s\n' % description,
165 description,
166 ['--reviewers', 'foo@example.com'])
167
168 def test_reviewer_tbr_overriden(self):
169 # Reviewer is overriden with TBR
170 # Also verifies the regexp work without a trailing LF
171 description = 'Foo Bar\nTBR=reviewer@example.com\n'
172 self._run_reviewer_test(
173 ['-r' 'foo@example.com'],
174 'desc\n\nR=foo@example.com\nBUG=\nTEST=\n',
175 description.strip('\n'),
176 description,
177 ['--reviewers', 'reviewer@example.com'])
178
179 def test_reviewer_multiple(self):
180 # Handles multiple R= or TBR= lines.
181 description = (
182 'Foo Bar\nTBR=reviewer@example.com\nBUG=\nR=another@example.com\n')
183 self._run_reviewer_test(
184 [],
185 'desc\n\nBUG=\nTEST=\n',
186 description,
187 description,
188 ['--reviewers', 'reviewer@example.com,another@example.com'])
189
maruel@chromium.orga3353652011-11-30 14:26:57 +0000190 def test_reviewer_send_mail(self):
191 # --send-mail can be used without -r if R= is used
192 description = 'Foo Bar\nR=reviewer@example.com\n'
193 self._run_reviewer_test(
194 ['--send-mail'],
195 'desc\n\nBUG=\nTEST=\n',
196 description.strip('\n'),
197 description,
198 ['--reviewers', 'reviewer@example.com', '--send_mail'])
199
200 def test_reviewer_send_mail_no_rev(self):
201 # Fails without a reviewer.
202 class FileMock(object):
203 buf = StringIO.StringIO()
204 def write(self, content):
205 self.buf.write(content)
206
207 mock = FileMock()
208 try:
209 self.calls = self._git_base_calls()
210 def RunEditor(desc, _):
211 return desc
212 self.mock(git_cl.gclient_utils, 'RunEditor', RunEditor)
213 self.mock(sys, 'stderr', mock)
214 git_cl.main(['upload', '--send-mail'])
215 self.fail()
216 except SystemExit:
217 self.assertEquals(
218 'Must specify reviewers to send email.\n', mock.buf.getvalue())
219
maruel@chromium.orgddd59412011-11-30 14:20:38 +0000220
221if __name__ == '__main__':
222 unittest.main()