~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2007-2010 Canonical Ltd
2553.1.1 by Robert Collins
Give Hooks names.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2553.1.1 by Robert Collins
Give Hooks names.
16
17
"""Tests for the core Hooks logic."""
18
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
19
from bzrlib import (
20
    branch,
21
    errors,
5691.2.3 by Jelmer Vernooij
Fix tests.
22
    hooks as _mod_hooks,
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
23
    pyutils,
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
24
    tests,
25
    )
2553.1.1 by Robert Collins
Give Hooks names.
26
from bzrlib.hooks import (
4098.2.2 by Robert Collins
Review feedback.
27
    HookPoint,
2553.1.1 by Robert Collins
Give Hooks names.
28
    Hooks,
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
29
    install_lazy_named_hook,
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
30
    known_hooks,
31
    known_hooks_key_to_object,
2553.1.1 by Robert Collins
Give Hooks names.
32
    )
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
33
from bzrlib.symbol_versioning import (
34
    deprecated_in,
35
    )
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
36
37
38
class TestHooks(tests.TestCase):
2553.1.1 by Robert Collins
Give Hooks names.
39
4098.2.1 by Robert Collins
Allow self documenting hooks.
40
    def test_docs(self):
41
        """docs() should return something reasonable about the Hooks."""
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
42
        class MyHooks(Hooks):
43
            pass
5622.3.7 by Jelmer Vernooij
Fix tests.
44
        hooks = MyHooks("bzrlib.tests.test_hooks", "some_hooks")
4098.2.1 by Robert Collins
Allow self documenting hooks.
45
        hooks['legacy'] = []
5622.3.13 by Jelmer Vernooij
Catch deprecation warnings.
46
        hooks.add_hook('post_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
47
            "Invoked after the tip of a branch changes. Called with "
5622.3.13 by Jelmer Vernooij
Catch deprecation warnings.
48
            "a ChangeBranchTipParams object.", (1, 4))
49
        hooks.add_hook('pre_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
50
            "Invoked before the tip of a branch changes. Called with "
51
            "a ChangeBranchTipParams object. Hooks should raise "
52
            "TipChangeRejected to signal that a tip change is not permitted.",
53
            (1, 6), None)
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
54
        self.assertEqualDiff(
55
            "MyHooks\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
56
            "-------\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
57
            "\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
58
            "legacy\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
59
            "~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
60
            "\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
61
            "An old-style hook. For documentation see the __init__ method of 'MyHooks'\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
62
            "\n"
63
            "post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
64
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
65
            "\n"
66
            "Introduced in: 1.4\n"
67
            "\n"
68
            "Invoked after the tip of a branch changes. Called with a\n"
69
            "ChangeBranchTipParams object.\n"
70
            "\n"
71
            "pre_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
72
            "~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
73
            "\n"
74
            "Introduced in: 1.6\n"
75
            "\n"
76
            "Invoked before the tip of a branch changes. Called with a\n"
77
            "ChangeBranchTipParams object. Hooks should raise TipChangeRejected to\n"
78
            "signal that a tip change is not permitted.\n", hooks.docs())
79
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
80
    def test_install_named_hook_raises_unknown_hook(self):
5622.3.7 by Jelmer Vernooij
Fix tests.
81
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
82
        self.assertRaises(errors.UnknownHook, hooks.install_named_hook, 'silly',
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
83
                          None, "")
84
85
    def test_install_named_hook_appends_known_hook(self):
5622.3.7 by Jelmer Vernooij
Fix tests.
86
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
87
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
88
        hooks.install_named_hook('set_rh', None, "demo")
89
        self.assertEqual(hooks['set_rh'], [None])
90
91
    def test_install_named_hook_and_retrieve_name(self):
5622.3.7 by Jelmer Vernooij
Fix tests.
92
        hooks = Hooks("bzrlib.tests.test_hooks", "somehooks")
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
93
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
94
        hooks.install_named_hook('set_rh', None, "demo")
3256.2.1 by Daniel Watkins
Added tests from Hooks.install_hook with an optional name parameter.
95
        self.assertEqual("demo", hooks.get_hook_name(None))
96
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
97
    def test_uninstall_named_hook(self):
98
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
5622.4.3 by Jelmer Vernooij
Remove all callables with specified label, not just the first.
99
        hooks.add_hook('set_rh', "Set revision history", (2, 0))
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
100
        hooks.install_named_hook('set_rh', None, "demo")
101
        self.assertEqual(1, len(hooks["set_rh"]))
102
        hooks.uninstall_named_hook("set_rh", "demo")
103
        self.assertEqual(0, len(hooks["set_rh"]))
104
5622.4.3 by Jelmer Vernooij
Remove all callables with specified label, not just the first.
105
    def test_uninstall_multiple_named_hooks(self):
106
        # Multiple callbacks with the same label all get removed
107
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
108
        hooks.add_hook('set_rh', "Set revision history", (2, 0))
109
        hooks.install_named_hook('set_rh', 1, "demo")
110
        hooks.install_named_hook('set_rh', 2, "demo")
111
        hooks.install_named_hook('set_rh', 3, "othername")
112
        self.assertEqual(3, len(hooks["set_rh"]))
113
        hooks.uninstall_named_hook("set_rh", "demo")
114
        self.assertEqual(1, len(hooks["set_rh"]))
115
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
116
    def test_uninstall_named_hook_unknown_callable(self):
117
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
118
        hooks.add_hook('set_rh', "Set revision hsitory", (2, 0))
119
        self.assertRaises(KeyError, hooks.uninstall_named_hook, "set_rh",
120
            "demo")
121
122
    def test_uninstall_named_hook_raises_unknown_hook(self):
123
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
124
        self.assertRaises(errors.UnknownHook, hooks.uninstall_named_hook,
125
            'silly', "")
126
127
    def test_uninstall_named_hook_old_style(self):
128
        hooks = Hooks("bzrlib.tests.test_hooks", "some_hooks")
129
        hooks["set_rh"] = []
130
        hooks.install_named_hook('set_rh', None, "demo")
131
        self.assertRaises(errors.UnsupportedOperation,
132
            hooks.uninstall_named_hook, "set_rh", "demo")
133
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
134
    hooks = Hooks("bzrlib.tests.test_hooks", "TestHooks.hooks")
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
135
136
    def test_install_lazy_named_hook(self):
137
        # When the hook points are not yet registered the hook is
138
        # added to the _lazy_hooks dictionary in bzrlib.hooks.
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
139
        self.hooks.add_hook('set_rh', "doc", (0, 15))
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
140
        set_rh = lambda: None
141
        install_lazy_named_hook('bzrlib.tests.test_hooks',
142
            'TestHooks.hooks', 'set_rh', set_rh, "demo")
5691.2.3 by Jelmer Vernooij
Fix tests.
143
        set_rh_lazy_hooks = _mod_hooks._lazy_hooks[
5622.2.4 by Jelmer Vernooij
Fix tests for lazy hooks.
144
            ('bzrlib.tests.test_hooks', 'TestHooks.hooks', 'set_rh')]
145
        self.assertEquals(1, len(set_rh_lazy_hooks))
146
        self.assertEquals(set_rh, set_rh_lazy_hooks[0][0].get_obj())
147
        self.assertEquals("demo", set_rh_lazy_hooks[0][1])
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
148
        self.assertEqual(list(TestHooks.hooks['set_rh']), [set_rh])
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
149
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
150
    set_rh = lambda: None
151
152
    def test_install_named_hook_lazy(self):
5622.3.7 by Jelmer Vernooij
Fix tests.
153
        hooks = Hooks("bzrlib.tests.hooks", "some_hooks")
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
154
        hooks['set_rh'] = HookPoint("set_rh", "doc", (0, 15), None)
155
        hooks.install_named_hook_lazy('set_rh', 'bzrlib.tests.test_hooks',
156
            'TestHooks.set_rh', "demo")
157
        self.assertEqual(list(hooks['set_rh']), [TestHooks.set_rh])
158
159
    def test_install_named_hook_lazy_old(self):
160
        # An exception is raised if a lazy hook is raised for
161
        # an old style hook point.
5622.3.7 by Jelmer Vernooij
Fix tests.
162
        hooks = Hooks("bzrlib.tests.hooks", "some_hooks")
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
163
        hooks['set_rh'] = []
164
        self.assertRaises(errors.UnsupportedOperation,
165
            hooks.install_named_hook_lazy,
166
            'set_rh', 'bzrlib.tests.test_hooks', 'TestHooks.set_rh',
167
            "demo")
168
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
169
    def test_valid_lazy_hooks(self):
170
        # Make sure that all the registered lazy hooks are referring to existing
171
        # hook points which allow lazy registration.
5691.2.3 by Jelmer Vernooij
Fix tests.
172
        for key, callbacks in _mod_hooks._lazy_hooks.iteritems():
5622.2.8 by Jelmer Vernooij
more tests.
173
            (module_name, member_name, hook_name) = key
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
174
            obj = pyutils.get_named_object(module_name, member_name)
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
175
            self.assertEquals(obj._module, module_name)
176
            self.assertEquals(obj._member_name, member_name)
177
            self.assertTrue(hook_name in obj)
5622.2.8 by Jelmer Vernooij
more tests.
178
            self.assertIs(callbacks, obj[hook_name]._callbacks)
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
179
4098.2.1 by Robert Collins
Allow self documenting hooks.
180
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
181
class TestHook(tests.TestCase):
4098.2.1 by Robert Collins
Allow self documenting hooks.
182
183
    def test___init__(self):
184
        doc = ("Invoked after changing the tip of a branch object. Called with"
185
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
186
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
187
        self.assertEqual(doc, hook.__doc__)
188
        self.assertEqual("post_tip_change", hook.name)
189
        self.assertEqual((0, 15), hook.introduced)
190
        self.assertEqual(None, hook.deprecated)
191
        self.assertEqual([], list(hook))
192
193
    def test_docs(self):
194
        doc = ("Invoked after changing the tip of a branch object. Called with"
195
            " a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
196
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
197
        self.assertEqual("post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
198
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
199
            "\n"
200
            "Introduced in: 0.15\n"
201
            "\n"
202
            "Invoked after changing the tip of a branch object. Called with a\n"
203
            "bzrlib.branch.PostChangeBranchTipParams object\n", hook.docs())
204
205
    def test_hook(self):
4098.2.2 by Robert Collins
Review feedback.
206
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
207
        def callback():
208
            pass
209
        hook.hook(callback, "my callback")
210
        self.assertEqual([callback], list(hook))
211
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
212
    def lazy_callback():
213
        pass
214
215
    def test_lazy_hook(self):
216
        hook = HookPoint("foo", "no docs", None, None)
217
        hook.hook_lazy(
218
            "bzrlib.tests.test_hooks", "TestHook.lazy_callback",
219
            "my callback")
220
        self.assertEqual([TestHook.lazy_callback], list(hook))
221
5622.4.2 by Jelmer Vernooij
Allow uninstalling hooks.
222
    def test_uninstall(self):
223
        hook = HookPoint("foo", "no docs", None, None)
224
        hook.hook_lazy(
225
            "bzrlib.tests.test_hooks", "TestHook.lazy_callback",
226
            "my callback")
227
        self.assertEqual([TestHook.lazy_callback], list(hook))
228
        hook.uninstall("my callback")
229
        self.assertEqual([], list(hook))
230
231
    def test_uninstall_unknown(self):
232
        hook = HookPoint("foo", "no docs", None, None)
233
        self.assertRaises(KeyError, hook.uninstall, "my callback")
234
4098.2.1 by Robert Collins
Allow self documenting hooks.
235
    def test___repr(self):
236
        # The repr should list all the callbacks, with names.
4098.2.2 by Robert Collins
Review feedback.
237
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
238
        def callback():
239
            pass
240
        hook.hook(callback, "my callback")
241
        callback_repr = repr(callback)
242
        self.assertEqual(
4098.2.2 by Robert Collins
Review feedback.
243
            '<HookPoint(foo), callbacks=[%s(my callback)]>' %
4098.2.1 by Robert Collins
Allow self documenting hooks.
244
            callback_repr, repr(hook))
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
245
246
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
247
class TestHookRegistry(tests.TestCase):
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
248
249
    def test_items_are_reasonable_keys(self):
250
        # All the items in the known_hooks registry need to map from
251
        # (module_name, member_name) tuples to the callable used to get an
4230.1.2 by James Westby
Update the comments based on those in the test.
252
        # empty Hooks for that attribute. This is used to support the test
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
253
        # suite which needs to generate empty hooks (and HookPoints) to ensure
254
        # isolation and prevent tests failing spuriously.
255
        for key, factory in known_hooks.items():
256
            self.assertTrue(callable(factory),
257
                "The factory(%r) for %r is not callable" % (factory, key))
258
            obj = known_hooks_key_to_object(key)
259
            self.assertIsInstance(obj, Hooks)
5622.3.11 by Jelmer Vernooij
Revert some unnecessary changes.
260
            new_hooks = factory()
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
261
            self.assertIsInstance(obj, Hooks)
262
            self.assertEqual(type(obj), type(new_hooks))
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
263
            self.assertEqual("No hook name", new_hooks.get_hook_name(None))
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
264
265
    def test_known_hooks_key_to_object(self):
266
        self.assertIs(branch.Branch.hooks,
267
            known_hooks_key_to_object(('bzrlib.branch', 'Branch.hooks')))
268
269
    def test_known_hooks_key_to_parent_and_attribute(self):
270
        self.assertEqual((branch.Branch, 'hooks'),
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
271
            known_hooks.key_to_parent_and_attribute(
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
272
            ('bzrlib.branch', 'Branch.hooks')))
273
        self.assertEqual((branch, 'Branch'),
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
274
            known_hooks.key_to_parent_and_attribute(
4119.3.1 by Robert Collins
Create a single registry of all Hooks classes, removing the test suite knowledge of such hooks and allowing plugins to sensibly and safely define new hooks.
275
            ('bzrlib.branch', 'Branch')))