OLD | NEW |
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-2016 Eyeo GmbH | 2 # Copyright (C) 2006-2016 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 import mock | 17 import mock |
17 import unittest | 18 import unittest |
18 | 19 |
19 import sitescripts.hg.bin.update_issues as update_issues | 20 import sitescripts.hg.bin.update_issues as update_issues |
20 | 21 |
21 | 22 |
22 def _create_mock_repo(message): | 23 def _issue(component, milestone='', can_resolve=False): |
23 mock_repo = mock.MagicMock() | 24 issue = { |
24 mock_repo.__len__.return_value = 1 | 25 'attrs': {'_ts': 1, 'milestone': milestone, 'component': component}, |
25 mock_change = mock.MagicMock() | 26 'actions': [['leave', '', '', []]] |
26 mock_change.rev.return_value = 0 | 27 } |
27 mock_change.description.return_value = message | 28 if can_resolve: |
28 mock_repo.__getitem__.return_value = mock_change | 29 issue['actions'].append(['resolve', '', '', []]) |
29 return mock_repo | 30 return issue |
30 | 31 |
31 | 32 |
32 class TestUpdateIssues(unittest.TestCase): | 33 ISSUES = { |
| 34 1337: _issue(component='one', can_resolve=True), |
| 35 2448: _issue(component='two'), |
| 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 |
| 47 |
| 48 class _TestBase(unittest.TestCase): |
| 49 """Base class for hook tests that prepares the environment.""" |
| 50 |
| 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: 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 |
33 def setUp(self): | 83 def setUp(self): |
34 self.ui = mock.Mock() | 84 self.ui = mock.Mock() |
35 | 85 self._mock_trac() |
36 @mock.patch("xmlrpclib.ServerProxy") | 86 self._mock_config() |
37 def test_commits_with_invalid_message_format_ignored(self, | 87 |
38 mock_server_proxy): | 88 |
39 messages = ["", "Issue #1337", "Tissue 1337", "Issue 13b"] | 89 class _MockRepo(list): |
40 for message in messages: | 90 def __init__(self, commit_messages): |
41 mock_repo = _create_mock_repo(message) | 91 list.__init__(self) |
42 update_issues.hook(self.ui, mock_repo, 0) | 92 for i, message in enumerate(commit_messages): |
43 self.ui.warn.assert_called_once() | 93 mock_commit = mock.MagicMock() |
44 self.assertFalse(mock_server_proxy.called) | 94 mock_commit.rev.return_value = i |
45 self.ui.warn.reset_mock() | 95 mock_commit.hex.return_value = '{:010x}'.format(i) + '0' * 30 |
46 | 96 mock_commit.description.return_value = message |
47 @mock.patch("xmlrpclib.ServerProxy") | 97 self.append(mock_commit) |
48 def test_noissue_commits_ignored(self, mock_server_proxy): | 98 self.changelog = mock.Mock() |
49 messages = ["Noissue", "noissue"] | 99 self.changelog.findmissingrevs = self._findmissingrevs |
50 for message in messages: | 100 |
51 mock_repo = _create_mock_repo(message) | 101 def _findmissingrevs(self, olds, news): |
52 update_issues.hook(self.ui, mock_repo, 0) | 102 return range(olds[0] + 1, news[0] + 1) |
53 self.assertFalse(self.ui.warn.called) | 103 |
54 self.assertFalse(mock_server_proxy.called) | 104 def __getitem__(self, commit_id): |
55 | 105 if isinstance(commit_id, str): |
56 @mock.patch("xmlrpclib.ServerProxy") | 106 return [commit for commit in self if commit.hex() == commit_id][0] |
57 def test_single_issue_referenced(self, mock_server_proxy): | 107 return list.__getitem__(self, commit_id) |
58 server_proxy_instance = mock_server_proxy.return_value | 108 |
59 messages = ["Issue 1337", "issue 1337"] | 109 def url(self): |
60 for message in messages: | 110 return 'mock/repo' |
61 mock_repo = _create_mock_repo(message) | 111 |
62 update_issues.hook(self.ui, mock_repo, 0) | 112 |
63 self.assertFalse(self.ui.warn.called) | 113 class TestChangegroupHook(_TestBase): |
64 server_proxy_instance.ticket.update.assert_called_once() | 114 |
65 self.assertEqual(server_proxy_instance.ticket.update.call_args[0][0]
, | 115 def _run_hook(self, commit_messages, warning_count=0, update_count=0): |
66 1337) | 116 repo = _MockRepo(commit_messages) |
67 server_proxy_instance.reset_mock() | 117 update_issues.changegroup_hook(self.ui, repo, 0) |
68 | 118 warnings = self.ui.warn.call_args_list |
69 @mock.patch("xmlrpclib.ServerProxy") | 119 updates = self.trac_proxy_mock.ticket.update.call_args_list |
70 def test_multiple_issues_referenced(self, mock_server_proxy): | 120 self.assertEqual(len(warnings), warning_count) |
71 server_proxy_instance = mock_server_proxy.return_value | 121 self.assertEqual(len(updates), update_count) |
72 mock_repo = _create_mock_repo("Issue 1337, issue 2448") | 122 return updates |
73 update_issues.hook(self.ui, mock_repo, 0) | 123 |
74 self.assertFalse(self.ui.warn.called) | 124 def test_commits_with_invalid_message_format_ignored(self): |
75 calls = server_proxy_instance.ticket.update.call_args_list | 125 self._run_hook([ |
76 self.assertEqual(len(calls), 2) | 126 '', |
77 self.assertEqual(calls[0][0][0], 1337) | 127 'Issue #1337 - Extraneous characters in issue number', |
78 self.assertEqual(calls[1][0][0], 2448) | 128 'Issue 1337', # No dash, no message. |
79 | 129 'Issue 1337: Colon instead of dash', |
80 if __name__ == "__main__": | 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_missing_issue_referenced(self): |
| 143 self._run_hook(['Issue 42 - Bar'], warning_count=1) |
| 144 |
| 145 def test_multiple_issues_referenced(self): |
| 146 updates = self._run_hook(['Issue 1337, fixes 2448 - Foo'], |
| 147 update_count=2) |
| 148 self.assertEqual(updates[0][0][0], 1337) |
| 149 self.assertEqual(updates[1][0][0], 2448) |
| 150 |
| 151 def test_multiple_commits_for_issue(self): |
| 152 updates = self._run_hook(['Issue 1337 - Foo', 'Fixes 1337 - Bar'], |
| 153 update_count=1) |
| 154 comment = updates[0][0][1] |
| 155 self.assertIn('000000000000', comment) |
| 156 self.assertIn('000000000100', comment) |
| 157 |
| 158 |
| 159 class TestPushkeyHook(_TestBase): |
| 160 |
| 161 def _run_hook(self, commit_messages, bookmark='master', |
| 162 warning_count=0, update_count=0): |
| 163 repo = _MockRepo(['Base', 'Old'] + commit_messages) |
| 164 update_issues.pushkey_hook(self.ui, repo, |
| 165 namespace='bookmarks', key=bookmark, |
| 166 old=1, new=1 + len(commit_messages)) |
| 167 warnings = self.ui.warn.call_args_list |
| 168 updates = self.trac_proxy_mock.ticket.update.call_args_list |
| 169 self.assertEqual(len(warnings), warning_count) |
| 170 self.assertEqual(len(updates), update_count) |
| 171 return updates |
| 172 |
| 173 def _check_update(self, update, issue_id, action='resolve', |
| 174 milestone='current'): |
| 175 self.assertEqual(update[0][0], issue_id) |
| 176 changes = update[0][2] |
| 177 self.assertEqual(changes['action'], action) |
| 178 if milestone is None: |
| 179 self.assertNotIn('milestone', changes) |
| 180 else: |
| 181 self.assertEqual(changes['milestone'], milestone) |
| 182 |
| 183 def test_move_other_bookmark(self): |
| 184 self._run_hook(['Fixes 1337 - Foo'], bookmark='other') # No updates. |
| 185 |
| 186 def test_one_issue_fixed(self): |
| 187 updates = self._run_hook(['Fixes 1337 - Foo'], update_count=1) |
| 188 self._check_update(updates[0], 1337) |
| 189 |
| 190 def test_fix_closed_issue(self): |
| 191 updates = self._run_hook(['fixes 2448 - Foo'], update_count=1) |
| 192 self._check_update(updates[0], 2448, action='leave', milestone='other') |
| 193 |
| 194 def test_fix_issue_noregexp(self): |
| 195 updates = self._run_hook(['Fixes 4670 - Foo'], update_count=1) |
| 196 self._check_update(updates[0], 4670, milestone=None) |
| 197 |
| 198 def test_fix_issue_no_matching_milestones(self): |
| 199 updates = self._run_hook(['Fixes 5781 - Foo'], update_count=1) |
| 200 self._check_update(updates[0], 5781, milestone=None) |
| 201 |
| 202 def test_fix_many(self): |
| 203 updates = self._run_hook(['Fixes 1337 - Foo', 'Fixes 2448 - Bar'], |
| 204 update_count=2) |
| 205 self._check_update(updates[0], 1337) |
| 206 self._check_update(updates[1], 2448, action='leave', milestone='other') |
| 207 |
| 208 def test_fix_nonexistent(self): |
| 209 self._run_hook(['Fixes 7331 - Foo'], warning_count=1) |
| 210 |
| 211 def test_fix_closed_with_assigned_milestone(self): |
| 212 self._run_hook(['fixes 3559 - Foo']) # No updates. |
| 213 |
| 214 |
| 215 if __name__ == '__main__': |
81 unittest.main() | 216 unittest.main() |
OLD | NEW |