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