Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: sitescripts/hg/test/test_update_issues.py

Issue 30048555: Issue 7334 - Remove handling of "Fixes XXXX - ..." commit messages (Closed) Base URL: https://hg.adblockplus.org/sitescripts
Patch Set: Created April 18, 2019, 3:48 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 # This file is part of the Adblock Plus web scripts, 1 # This file is part of the Adblock Plus web scripts,
2 # Copyright (C) 2006-present eyeo GmbH 2 # Copyright (C) 2006-present eyeo GmbH
3 # 3 #
4 # Adblock Plus is free software: you can redistribute it and/or modify 4 # Adblock Plus is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License version 3 as 5 # it under the terms of the GNU General Public License version 3 as
6 # published by the Free Software Foundation. 6 # published by the Free Software Foundation.
7 # 7 #
8 # Adblock Plus is distributed in the hope that it will be useful, 8 # Adblock Plus is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details. 11 # GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License 13 # You should have received a copy of the GNU General Public License
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
15 15
16 import ConfigParser 16 """Test Mercurial Trac integration hook."""
17
17 import mock 18 import mock
18 import unittest 19 import pytest
19 20
20 import sitescripts.hg.bin.update_issues as update_issues 21 import sitescripts.hg.bin.update_issues as update_issues
21 22
22 23
23 def _issue(component, milestone='', can_resolve=False): 24 class RepoMock(list):
24 issue = { 25 """Very simple mock of a Mercurial repository."""
mathias 2019/05/14 09:32:02 If you call the class "MercurialRepositoryMock" th
Vasily Kuznetsov 2019/05/14 10:29:00 Done.
25 'attrs': {'_ts': 1, 'milestone': milestone, 'component': component}, 26
26 'actions': [['leave', '', '', []]], 27 def __init__(self, message):
27 } 28 change_mock = mock.MagicMock()
28 if can_resolve: 29 change_mock.rev.return_value = 0
29 issue['actions'].append(['resolve', '', '', []]) 30 change_mock.hex.return_value = '000011112222'
mathias 2019/05/14 09:32:02 This magic number string should be a constant.
Vasily Kuznetsov 2019/05/14 10:29:00 Done.
30 return issue 31 change_mock.description.return_value = message
32 list.__init__(self, [change_mock])
33
34 def url(self):
35 return 'repo/mock'
31 36
32 37
33 ISSUES = { 38 @pytest.fixture
34 1337: _issue(component='one', can_resolve=True), 39 def server_proxy_mock(mocker):
35 2448: _issue(component='two'), 40 return mocker.patch('xmlrpclib.ServerProxy')
36 3559: _issue(component='one', milestone='other'),
37 4670: _issue(component='three', can_resolve=True),
38 5781: _issue(component='four', can_resolve=True),
39 }
40
41 MILESTONES = {
42 'completed': {'completed': True, 'name': 'completed'},
43 'current': {'completed': None, 'name': 'current'},
44 'other': {'completed': None, 'name': 'other'},
45 }
46 41
47 42
48 class _TestBase(unittest.TestCase): 43 @pytest.fixture
49 """Base class for hook tests that prepares the environment.""" 44 def ui_mock():
50 45 return mock.MagicMock()
51 def _patchWith(self, target, return_value):
52 patcher = mock.patch(target, return_value=return_value)
53 patcher.start()
54 self.addCleanup(patcher.stop)
55
56 def _create_mock_milestone_multicall(self):
57 ret = []
58 multicall = mock.Mock(return_value=ret)
59 multicall.ticket.milestone.get = lambda i: ret.append(MILESTONES[i])
60 return multicall
61
62 def _mock_trac(self):
63 trac = mock.Mock()
64 trac.ticket.get = lambda i: [i, mock.ANY, mock.ANY, ISSUES[i]['attrs']]
65 trac.ticket.getActions = lambda i: ISSUES[i]['actions']
66 trac.ticket.milestone.getAll = lambda: sorted(MILESTONES.keys())
67 self.trac_proxy_mock = trac
68 self._patchWith('xmlrpclib.ServerProxy', trac)
69 self._patchWith('xmlrpclib.MultiCall',
70 self._create_mock_milestone_multicall())
71
72 def _mock_config(self):
73 config = ConfigParser.ConfigParser()
74 config.add_section('hg')
75 config.set('hg', 'trac_xmlrpc_url', 'foo')
76 config.set('hg', 'issue_url_template', '#{id}')
77 config.add_section('hg_module_milestones')
78 config.set('hg_module_milestones', 'one', '.*')
79 config.set('hg_module_milestones', 'two', 'other')
80 config.set('hg_module_milestones', 'four', 'completed')
81 self._patchWith('sitescripts.hg.bin.update_issues.get_config', config)
82
83 def setUp(self):
84 self.ui = mock.Mock()
85 self._mock_trac()
86 self._mock_config()
87 46
88 47
89 class _MockRepo(list): 48 @pytest.mark.parametrize('message', [
90 def __init__(self, commit_messages): 49 '', 'Issue #1337', 'Tissue 1337', 'Issue 13b',
91 list.__init__(self) 50 ])
92 for i, message in enumerate(commit_messages): 51 def test_invalid_message_format(message, ui_mock, server_proxy_mock):
93 mock_commit = mock.MagicMock() 52 """Check that commits with invalid messages are ignored with a warning."""
94 mock_commit.rev.return_value = i 53 repo_mock = RepoMock(message)
95 mock_commit.hex.return_value = '{:010x}'.format(i) + '0' * 30 54 update_issues.hook(ui_mock, repo_mock, 0)
96 mock_commit.description.return_value = message 55 ui_mock.warn.assert_called_once()
97 self.append(mock_commit) 56 assert not server_proxy_mock.called
98 self.changelog = mock.Mock()
99 self.changelog.findmissingrevs = self._findmissingrevs
100
101 def _findmissingrevs(self, olds, news):
102 return range(olds[0] + 1, news[0] + 1)
103
104 def __getitem__(self, commit_id):
105 if isinstance(commit_id, str):
106 return [commit for commit in self if commit.hex() == commit_id][0]
107 return list.__getitem__(self, commit_id)
108
109 def url(self):
110 return 'mock/repo'
111 57
112 58
113 class TestChangegroupHook(_TestBase): 59 @pytest.mark.parametrize('message', ['Noissue - foobar', 'noissue'])
114 60 def test_noissue(message, ui_mock, server_proxy_mock):
115 def _run_hook(self, commit_messages, warning_count=0, update_count=0): 61 """Check that noissue commits are ignored without warning."""
116 repo = _MockRepo(commit_messages) 62 repo_mock = RepoMock(message)
117 update_issues.changegroup_hook(self.ui, repo, 0) 63 update_issues.hook(ui_mock, repo_mock, 0)
118 warnings = self.ui.warn.call_args_list 64 assert not ui_mock.warn.called
119 updates = self.trac_proxy_mock.ticket.update.call_args_list 65 assert not server_proxy_mock.called
120 self.assertEqual(len(warnings), warning_count)
121 self.assertEqual(len(updates), update_count)
122 return updates
123
124 def test_commits_with_invalid_message_format_ignored(self):
125 self._run_hook([
126 '',
127 'Issue #1337 - Extraneous characters in issue number',
128 'Issue 1337', # No dash, no message.
129 'Issue 1337: Colon instead of dash',
130 'Noissue no dash',
131 'Issue 1337-No space around dash',
132 'Fixes 1337 no dash',
133 ], warning_count=7)
134
135 def test_noissue_commits_ignored(self):
136 self._run_hook(['Noissue - Foo', 'noissue - Bar']) # No updates.
137
138 def test_single_issue_referenced(self):
139 updates = self._run_hook(['Issue 1337 - Foo'], update_count=1)
140 self.assertEqual(updates[0][0][0], 1337)
141
142 def test_multiline_commit_message(self):
143 updates = self._run_hook(['Issue 1337 - Foo\nBar',
144 'Issue 1337 - Bar.\nBaz',
145 'Fixes 2448 - Foo\n\nBar',
146 'Fixes 2448 - Bar\n \nBaz'],
147 update_count=2)
148 comment_1337 = updates[0][0][1]
149 self.assertIn('Issue 1337 - Foo...]', comment_1337)
150 self.assertIn('Issue 1337 - Bar...]', comment_1337)
151 comment_2448 = updates[1][0][1]
152 self.assertIn('Fixes 2448 - Foo]', comment_2448)
153 self.assertIn('Fixes 2448 - Bar]', comment_2448)
154
155 def test_multiline_commit_message_crlf(self):
156 updates = self._run_hook(['Issue 1337 - Foo\r\nBar',
157 'Issue 1337 - Bar.\r\nBaz',
158 'Fixes 2448 - Foo\r\n\r\nBar',
159 'Fixes 2448 - Bar\r\n \r\nBaz'],
160 update_count=2)
161 comment_1337 = updates[0][0][1]
162 self.assertIn('Issue 1337 - Foo...]', comment_1337)
163 self.assertIn('Issue 1337 - Bar...]', comment_1337)
164 comment_2448 = updates[1][0][1]
165 self.assertIn('Fixes 2448 - Foo]', comment_2448)
166 self.assertIn('Fixes 2448 - Bar]', comment_2448)
167
168 def test_missing_issue_referenced(self):
169 self._run_hook(['Issue 42 - Bar'], warning_count=1)
170
171 def test_multiple_issues_referenced(self):
172 updates = self._run_hook(['Issue 1337, fixes 2448 - Foo'],
173 update_count=2)
174 self.assertEqual(updates[0][0][0], 1337)
175 self.assertEqual(updates[1][0][0], 2448)
176
177 def test_multiple_commits_for_issue(self):
178 updates = self._run_hook(['Issue 1337 - Foo', 'Fixes 1337 - Bar'],
179 update_count=1)
180 comment = updates[0][0][1]
181 self.assertIn('000000000000', comment)
182 self.assertIn('000000000100', comment)
183 66
184 67
185 class TestPushkeyHook(_TestBase): 68 @pytest.mark.parametrize('message', ['Issue 1337 - foo', 'issue 1337 - foo'])
186 69 def test_single_issue(message, ui_mock, server_proxy_mock):
187 def _run_hook(self, commit_messages, bookmark='master', 70 """Check that a commit referencing a single issue gets linked."""
188 warning_count=0, update_count=0): 71 server_proxy = server_proxy_mock.return_value
189 repo = _MockRepo(['Base', 'Old'] + commit_messages) 72 repo_mock = RepoMock(message)
190 update_issues.pushkey_hook(self.ui, repo, 73 update_issues.hook(ui_mock, repo_mock, 0)
191 namespace='bookmarks', key=bookmark, 74 assert not ui_mock.warn.called
192 old=1, new=1 + len(commit_messages)) 75 server_proxy.ticket.update.assert_called_once()
193 warnings = self.ui.warn.call_args_list 76 call = server_proxy.ticket.update.call_args
194 updates = self.trac_proxy_mock.ticket.update.call_args_list 77 assert call[0][0] == 1337
195 self.assertEqual(len(warnings), warning_count) 78 comment = call[0][1]
196 self.assertEqual(len(updates), update_count) 79 assert comment.startswith('A commit referencing this issue has landed')
197 return updates 80 assert '000011112222' in comment
mathias 2019/05/14 09:32:02 This magic number string should be a constant.
Vasily Kuznetsov 2019/05/14 10:29:00 Done.
198 81 assert comment.endswith('- foo]')
199 def _check_update(self, update, issue_id, action='resolve',
200 milestone='current'):
201 self.assertEqual(update[0][0], issue_id)
202 changes = update[0][2]
203 self.assertEqual(changes['action'], action)
204 if milestone is None:
205 self.assertNotIn('milestone', changes)
206 else:
207 self.assertEqual(changes['milestone'], milestone)
208
209 def test_move_other_bookmark(self):
210 self._run_hook(['Fixes 1337 - Foo'], bookmark='other') # No updates.
211
212 def test_one_issue_fixed(self):
213 updates = self._run_hook(['Fixes 1337 - Foo'], update_count=1)
214 self._check_update(updates[0], 1337)
215
216 def test_fix_closed_issue(self):
217 updates = self._run_hook(['fixes 2448 - Foo'], update_count=1)
218 self._check_update(updates[0], 2448, action='leave', milestone='other')
219
220 def test_fix_issue_noregexp(self):
221 updates = self._run_hook(['Fixes 4670 - Foo'], update_count=1)
222 self._check_update(updates[0], 4670, milestone=None)
223
224 def test_fix_issue_no_matching_milestones(self):
225 updates = self._run_hook(['Fixes 5781 - Foo'], update_count=1)
226 self._check_update(updates[0], 5781, milestone=None)
227
228 def test_fix_many(self):
229 updates = self._run_hook(['Fixes 1337 - Foo', 'Fixes 2448 - Bar'],
230 update_count=2)
231 self._check_update(updates[0], 1337)
232 self._check_update(updates[1], 2448, action='leave', milestone='other')
233
234 def test_fix_nonexistent(self):
235 self._run_hook(['Fixes 7331 - Foo'], warning_count=1)
236
237 def test_fix_closed_with_assigned_milestone(self):
238 self._run_hook(['fixes 3559 - Foo']) # No updates.
239 82
240 83
241 if __name__ == '__main__': 84 def test_multiple_issues(ui_mock, server_proxy_mock):
242 unittest.main() 85 """Check that a commit referencing two issues gets linked twice."""
86 server_proxy = server_proxy_mock.return_value
87 repo_mock = RepoMock('Issue 1337, issue 2448')
88 update_issues.hook(ui_mock, repo_mock, 0)
89 assert not ui_mock.warn.called
90 calls = server_proxy.ticket.update.call_args_list
91 assert len(calls) == 2
92 assert calls[0][0][0] == 1337
93 assert calls[1][0][0] == 2448
OLDNEW
« sitescripts/hg/bin/update_issues.py ('K') | « sitescripts/hg/bin/update_issues.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld