~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,
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
22
    pyutils,
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
23
    tests,
24
    )
2553.1.1 by Robert Collins
Give Hooks names.
25
from bzrlib.hooks import (
4098.2.2 by Robert Collins
Review feedback.
26
    HookPoint,
2553.1.1 by Robert Collins
Give Hooks names.
27
    Hooks,
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
28
    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.
29
    known_hooks,
30
    known_hooks_key_to_object,
31
    known_hooks_key_to_parent_and_attribute,
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
32
    _lazy_hooks,
2553.1.1 by Robert Collins
Give Hooks names.
33
    )
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
34
from bzrlib.symbol_versioning import (
35
    deprecated_in,
36
    )
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
37
38
39
class TestHooks(tests.TestCase):
2553.1.1 by Robert Collins
Give Hooks names.
40
4098.2.1 by Robert Collins
Allow self documenting hooks.
41
    def test_create_hook_first(self):
42
        hooks = Hooks()
43
        doc = ("Invoked after changing the tip of a branch object. Called with"
44
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
45
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
46
        hooks.create_hook(hook)
47
        self.assertEqual(hook, hooks['post_tip_change'])
48
49
    def test_create_hook_name_collision_errors(self):
50
        hooks = Hooks()
51
        doc = ("Invoked after changing the tip of a branch object. Called with"
52
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
53
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
54
        hook2 = HookPoint("post_tip_change", None, None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
55
        hooks.create_hook(hook)
56
        self.assertRaises(errors.DuplicateKey, hooks.create_hook, hook2)
57
        self.assertEqual(hook, hooks['post_tip_change'])
58
59
    def test_docs(self):
60
        """docs() should return something reasonable about the Hooks."""
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
61
        class MyHooks(Hooks):
62
            pass
63
        hooks = MyHooks()
4098.2.1 by Robert Collins
Allow self documenting hooks.
64
        hooks['legacy'] = []
4098.2.2 by Robert Collins
Review feedback.
65
        hook1 = HookPoint('post_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
66
            "Invoked after the tip of a branch changes. Called with "
67
            "a ChangeBranchTipParams object.", (1, 4), None)
4098.2.2 by Robert Collins
Review feedback.
68
        hook2 = HookPoint('pre_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
69
            "Invoked before the tip of a branch changes. Called with "
70
            "a ChangeBranchTipParams object. Hooks should raise "
71
            "TipChangeRejected to signal that a tip change is not permitted.",
72
            (1, 6), None)
73
        hooks.create_hook(hook1)
74
        hooks.create_hook(hook2)
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
75
        self.assertEqualDiff(
76
            "MyHooks\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
77
            "-------\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
78
            "\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
79
            "legacy\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
80
            "~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
81
            "\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
82
            "An old-style hook. For documentation see the __init__ method of 'MyHooks'\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
83
            "\n"
84
            "post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
85
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
86
            "\n"
87
            "Introduced in: 1.4\n"
88
            "\n"
89
            "Invoked after the tip of a branch changes. Called with a\n"
90
            "ChangeBranchTipParams object.\n"
91
            "\n"
92
            "pre_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
93
            "~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
94
            "\n"
95
            "Introduced in: 1.6\n"
96
            "\n"
97
            "Invoked before the tip of a branch changes. Called with a\n"
98
            "ChangeBranchTipParams object. Hooks should raise TipChangeRejected to\n"
99
            "signal that a tip change is not permitted.\n", hooks.docs())
100
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
101
    def test_install_named_hook_raises_unknown_hook(self):
102
        hooks = Hooks()
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
103
        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.
104
                          None, "")
105
106
    def test_install_named_hook_appends_known_hook(self):
107
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
108
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
109
        hooks.install_named_hook('set_rh', None, "demo")
110
        self.assertEqual(hooks['set_rh'], [None])
111
112
    def test_install_named_hook_and_retrieve_name(self):
113
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
114
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
115
        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.
116
        self.assertEqual("demo", hooks.get_hook_name(None))
117
5622.2.6 by Jelmer Vernooij
Put module/member information on Hooks, not individual hook points.
118
    hooks = Hooks("bzrlib.tests.test_hooks", "TestHooks.hooks")
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
119
120
    def test_install_lazy_named_hook(self):
121
        # When the hook points are not yet registered the hook is
122
        # 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.
123
        self.hooks.add_hook('set_rh', "doc", (0, 15))
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
124
        set_rh = lambda: None
125
        install_lazy_named_hook('bzrlib.tests.test_hooks',
126
            'TestHooks.hooks', 'set_rh', set_rh, "demo")
5622.2.4 by Jelmer Vernooij
Fix tests for lazy hooks.
127
        set_rh_lazy_hooks = _lazy_hooks[
128
            ('bzrlib.tests.test_hooks', 'TestHooks.hooks', 'set_rh')]
129
        self.assertEquals(1, len(set_rh_lazy_hooks))
130
        self.assertEquals(set_rh, set_rh_lazy_hooks[0][0].get_obj())
131
        self.assertEquals("demo", set_rh_lazy_hooks[0][1])
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
132
        self.assertEqual(list(TestHooks.hooks['set_rh']), [set_rh])
5622.2.1 by Jelmer Vernooij
Some work on lazy hooks.
133
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
134
    set_rh = lambda: None
135
136
    def test_install_named_hook_lazy(self):
137
        hooks = Hooks()
138
        hooks['set_rh'] = HookPoint("set_rh", "doc", (0, 15), None)
139
        hooks.install_named_hook_lazy('set_rh', 'bzrlib.tests.test_hooks',
140
            'TestHooks.set_rh', "demo")
141
        self.assertEqual(list(hooks['set_rh']), [TestHooks.set_rh])
142
143
    def test_install_named_hook_lazy_old(self):
144
        # An exception is raised if a lazy hook is raised for
145
        # an old style hook point.
146
        hooks = Hooks()
147
        hooks['set_rh'] = []
148
        self.assertRaises(errors.UnsupportedOperation,
149
            hooks.install_named_hook_lazy,
150
            'set_rh', 'bzrlib.tests.test_hooks', 'TestHooks.set_rh',
151
            "demo")
152
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
153
    def test_valid_lazy_hooks(self):
154
        # Make sure that all the registered lazy hooks are referring to existing
155
        # hook points which allow lazy registration.
5622.2.8 by Jelmer Vernooij
more tests.
156
        for key, callbacks in _lazy_hooks.iteritems():
157
            (module_name, member_name, hook_name) = key
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
158
            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.
159
            self.assertEquals(obj._module, module_name)
160
            self.assertEquals(obj._member_name, member_name)
161
            self.assertTrue(hook_name in obj)
5622.2.8 by Jelmer Vernooij
more tests.
162
            self.assertIs(callbacks, obj[hook_name]._callbacks)
5622.2.5 by Jelmer Vernooij
Add more tests for lazy hooks.
163
4098.2.1 by Robert Collins
Allow self documenting hooks.
164
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
165
class TestHook(tests.TestCase):
4098.2.1 by Robert Collins
Allow self documenting hooks.
166
167
    def test___init__(self):
168
        doc = ("Invoked after changing the tip of a branch object. Called with"
169
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
170
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
171
        self.assertEqual(doc, hook.__doc__)
172
        self.assertEqual("post_tip_change", hook.name)
173
        self.assertEqual((0, 15), hook.introduced)
174
        self.assertEqual(None, hook.deprecated)
175
        self.assertEqual([], list(hook))
176
177
    def test_docs(self):
178
        doc = ("Invoked after changing the tip of a branch object. Called with"
179
            " a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
180
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
181
        self.assertEqual("post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
182
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
183
            "\n"
184
            "Introduced in: 0.15\n"
185
            "\n"
186
            "Invoked after changing the tip of a branch object. Called with a\n"
187
            "bzrlib.branch.PostChangeBranchTipParams object\n", hook.docs())
188
189
    def test_hook(self):
4098.2.2 by Robert Collins
Review feedback.
190
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
191
        def callback():
192
            pass
193
        hook.hook(callback, "my callback")
194
        self.assertEqual([callback], list(hook))
195
5622.1.1 by Jelmer Vernooij
Allow lazily loading hook callbacks.
196
    def lazy_callback():
197
        pass
198
199
    def test_lazy_hook(self):
200
        hook = HookPoint("foo", "no docs", None, None)
201
        hook.hook_lazy(
202
            "bzrlib.tests.test_hooks", "TestHook.lazy_callback",
203
            "my callback")
204
        self.assertEqual([TestHook.lazy_callback], list(hook))
205
4098.2.1 by Robert Collins
Allow self documenting hooks.
206
    def test___repr(self):
207
        # The repr should list all the callbacks, with names.
4098.2.2 by Robert Collins
Review feedback.
208
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
209
        def callback():
210
            pass
211
        hook.hook(callback, "my callback")
212
        callback_repr = repr(callback)
213
        self.assertEqual(
4098.2.2 by Robert Collins
Review feedback.
214
            '<HookPoint(foo), callbacks=[%s(my callback)]>' %
4098.2.1 by Robert Collins
Allow self documenting hooks.
215
            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.
216
217
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
218
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.
219
220
    def test_items_are_reasonable_keys(self):
221
        # All the items in the known_hooks registry need to map from
222
        # (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.
223
        # 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.
224
        # suite which needs to generate empty hooks (and HookPoints) to ensure
225
        # isolation and prevent tests failing spuriously.
226
        for key, factory in known_hooks.items():
227
            self.assertTrue(callable(factory),
228
                "The factory(%r) for %r is not callable" % (factory, key))
229
            obj = known_hooks_key_to_object(key)
230
            self.assertIsInstance(obj, Hooks)
231
            new_hooks = factory()
232
            self.assertIsInstance(obj, Hooks)
233
            self.assertEqual(type(obj), type(new_hooks))
4781.1.1 by Vincent Ladeuil
Hooks daughter classes should always call the base constructor
234
            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.
235
236
    def test_known_hooks_key_to_object(self):
237
        self.assertIs(branch.Branch.hooks,
238
            known_hooks_key_to_object(('bzrlib.branch', 'Branch.hooks')))
239
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
240
    def test_known_hooks_key_to_parent_and_attribute_deprecated(self):
241
        self.assertEqual((branch.Branch, 'hooks'),
242
            self.applyDeprecated(deprecated_in((2,3)),
243
                known_hooks_key_to_parent_and_attribute,
244
                ('bzrlib.branch', 'Branch.hooks')))
245
        self.assertEqual((branch, 'Branch'),
246
            self.applyDeprecated(deprecated_in((2,3)),
247
                known_hooks_key_to_parent_and_attribute,
248
                ('bzrlib.branch', 'Branch')))
249
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.
250
    def test_known_hooks_key_to_parent_and_attribute(self):
251
        self.assertEqual((branch.Branch, 'hooks'),
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
252
            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.
253
            ('bzrlib.branch', 'Branch.hooks')))
254
        self.assertEqual((branch, 'Branch'),
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
255
            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.
256
            ('bzrlib.branch', 'Branch')))