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