| 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 | 
|---|