~bzr-pqm/bzr/bzr.dev

2553.1.1 by Robert Collins
Give Hooks names.
1
# Copyright (C) 2007 Canonical Ltd
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for the core Hooks logic."""
18
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.
19
from bzrlib import branch, errors
2553.1.1 by Robert Collins
Give Hooks names.
20
from bzrlib.hooks import (
4098.2.2 by Robert Collins
Review feedback.
21
    HookPoint,
2553.1.1 by Robert Collins
Give Hooks names.
22
    Hooks,
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.
23
    known_hooks,
24
    known_hooks_key_to_object,
25
    known_hooks_key_to_parent_and_attribute,
2553.1.1 by Robert Collins
Give Hooks names.
26
    )
27
from bzrlib.errors import (
28
    UnknownHook,
29
    )
30
3256.2.30 by Daniel Watkins
Updated deprecation warnings and tests.
31
from bzrlib.symbol_versioning import one_five
2553.1.1 by Robert Collins
Give Hooks names.
32
from bzrlib.tests import TestCase
33
34
35
class TestHooks(TestCase):
36
4098.2.1 by Robert Collins
Allow self documenting hooks.
37
    def test_create_hook_first(self):
38
        hooks = Hooks()
39
        doc = ("Invoked after changing the tip of a branch object. Called with"
40
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
41
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
42
        hooks.create_hook(hook)
43
        self.assertEqual(hook, hooks['post_tip_change'])
44
45
    def test_create_hook_name_collision_errors(self):
46
        hooks = Hooks()
47
        doc = ("Invoked after changing the tip of a branch object. Called with"
48
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
49
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
50
        hook2 = HookPoint("post_tip_change", None, None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
51
        hooks.create_hook(hook)
52
        self.assertRaises(errors.DuplicateKey, hooks.create_hook, hook2)
53
        self.assertEqual(hook, hooks['post_tip_change'])
54
55
    def test_docs(self):
56
        """docs() should return something reasonable about the Hooks."""
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
57
        class MyHooks(Hooks):
58
            pass
59
        hooks = MyHooks()
4098.2.1 by Robert Collins
Allow self documenting hooks.
60
        hooks['legacy'] = []
4098.2.2 by Robert Collins
Review feedback.
61
        hook1 = HookPoint('post_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
62
            "Invoked after the tip of a branch changes. Called with "
63
            "a ChangeBranchTipParams object.", (1, 4), None)
4098.2.2 by Robert Collins
Review feedback.
64
        hook2 = HookPoint('pre_tip_change',
4098.2.1 by Robert Collins
Allow self documenting hooks.
65
            "Invoked before the tip of a branch changes. Called with "
66
            "a ChangeBranchTipParams object. Hooks should raise "
67
            "TipChangeRejected to signal that a tip change is not permitted.",
68
            (1, 6), None)
69
        hooks.create_hook(hook1)
70
        hooks.create_hook(hook2)
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
71
        self.assertEqualDiff(
72
            "MyHooks\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
73
            "-------\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
74
            "\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
75
            "legacy\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
76
            "~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
77
            "\n"
4108.3.1 by Robert Collins
Include the Hooks class in the Hooks docs.
78
            "An old-style hook. For documentation see the __init__ method of 'MyHooks'\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
79
            "\n"
80
            "post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
81
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
82
            "\n"
83
            "Introduced in: 1.4\n"
84
            "Deprecated in: Not deprecated\n"
85
            "\n"
86
            "Invoked after the tip of a branch changes. Called with a\n"
87
            "ChangeBranchTipParams object.\n"
88
            "\n"
89
            "pre_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
90
            "~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
91
            "\n"
92
            "Introduced in: 1.6\n"
93
            "Deprecated in: Not deprecated\n"
94
            "\n"
95
            "Invoked before the tip of a branch changes. Called with a\n"
96
            "ChangeBranchTipParams object. Hooks should raise TipChangeRejected to\n"
97
            "signal that a tip change is not permitted.\n", hooks.docs())
98
2553.1.1 by Robert Collins
Give Hooks names.
99
    def test_install_hook_raises_unknown_hook(self):
100
        """install_hook should raise UnknownHook if a hook is unknown."""
101
        hooks = Hooks()
3256.2.30 by Daniel Watkins
Updated deprecation warnings and tests.
102
        self.assertRaises(UnknownHook, self.applyDeprecated, one_five,
3256.2.20 by Daniel Watkins
Modified Hooks.install_hook tests to use applyDeprecated.
103
                          hooks.install_hook, 'silly', None)
2553.1.1 by Robert Collins
Give Hooks names.
104
105
    def test_install_hook_appends_known_hook(self):
106
        """install_hook should append the callable for known hooks."""
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.30 by Daniel Watkins
Updated deprecation warnings and tests.
109
        self.applyDeprecated(one_five, hooks.install_hook, 'set_rh', None)
2553.1.1 by Robert Collins
Give Hooks names.
110
        self.assertEqual(hooks['set_rh'], [None])
111
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
112
    def test_install_named_hook_raises_unknown_hook(self):
113
        hooks = Hooks()
114
        self.assertRaises(UnknownHook, hooks.install_named_hook, 'silly',
115
                          None, "")
116
117
    def test_install_named_hook_appends_known_hook(self):
118
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
119
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
120
        hooks.install_named_hook('set_rh', None, "demo")
121
        self.assertEqual(hooks['set_rh'], [None])
122
123
    def test_install_named_hook_and_retrieve_name(self):
124
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
125
        hooks['set_rh'] = []
3256.2.4 by Daniel Watkins
Modified tests to reflect install_named_hook becoming a separate method.
126
        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.
127
        self.assertEqual("demo", hooks.get_hook_name(None))
128
2553.1.1 by Robert Collins
Give Hooks names.
129
    def test_name_hook_and_retrieve_name(self):
130
        """name_hook puts the name in the names mapping."""
131
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
132
        hooks['set_rh'] = []
3256.2.30 by Daniel Watkins
Updated deprecation warnings and tests.
133
        self.applyDeprecated(one_five, hooks.install_hook, 'set_rh', None)
2553.1.1 by Robert Collins
Give Hooks names.
134
        hooks.name_hook(None, 'demo')
135
        self.assertEqual("demo", hooks.get_hook_name(None))
136
137
    def test_get_unnamed_hook_name_is_unnamed(self):
138
        hooks = Hooks()
3256.2.32 by Ian Clatworthy
tweak hook tests to init the set_rh test when needed
139
        hooks['set_rh'] = []
3256.2.30 by Daniel Watkins
Updated deprecation warnings and tests.
140
        self.applyDeprecated(one_five, hooks.install_hook, 'set_rh', None)
2553.1.1 by Robert Collins
Give Hooks names.
141
        self.assertEqual("No hook name", hooks.get_hook_name(None))
4098.2.1 by Robert Collins
Allow self documenting hooks.
142
143
144
class TestHook(TestCase):
145
146
    def test___init__(self):
147
        doc = ("Invoked after changing the tip of a branch object. Called with"
148
            "a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
149
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
150
        self.assertEqual(doc, hook.__doc__)
151
        self.assertEqual("post_tip_change", hook.name)
152
        self.assertEqual((0, 15), hook.introduced)
153
        self.assertEqual(None, hook.deprecated)
154
        self.assertEqual([], list(hook))
155
156
    def test_docs(self):
157
        doc = ("Invoked after changing the tip of a branch object. Called with"
158
            " a bzrlib.branch.PostChangeBranchTipParams object")
4098.2.2 by Robert Collins
Review feedback.
159
        hook = HookPoint("post_tip_change", doc, (0, 15), None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
160
        self.assertEqual("post_tip_change\n"
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
161
            "~~~~~~~~~~~~~~~\n"
4098.2.1 by Robert Collins
Allow self documenting hooks.
162
            "\n"
163
            "Introduced in: 0.15\n"
164
            "Deprecated in: Not deprecated\n"
165
            "\n"
166
            "Invoked after changing the tip of a branch object. Called with a\n"
167
            "bzrlib.branch.PostChangeBranchTipParams object\n", hook.docs())
168
169
    def test_hook(self):
4098.2.2 by Robert Collins
Review feedback.
170
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
171
        def callback():
172
            pass
173
        hook.hook(callback, "my callback")
174
        self.assertEqual([callback], list(hook))
175
176
    def test___repr(self):
177
        # The repr should list all the callbacks, with names.
4098.2.2 by Robert Collins
Review feedback.
178
        hook = HookPoint("foo", "no docs", None, None)
4098.2.1 by Robert Collins
Allow self documenting hooks.
179
        def callback():
180
            pass
181
        hook.hook(callback, "my callback")
182
        callback_repr = repr(callback)
183
        self.assertEqual(
4098.2.2 by Robert Collins
Review feedback.
184
            '<HookPoint(foo), callbacks=[%s(my callback)]>' %
4098.2.1 by Robert Collins
Allow self documenting hooks.
185
            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.
186
187
188
class TestHookRegistry(TestCase):
189
190
    def test_items_are_reasonable_keys(self):
191
        # All the items in the known_hooks registry need to map from
192
        # (module_name, member_name) tuples to the callable used to get an
193
        # empty Hooks of for that attribute. This is used to support the test
194
        # suite which needs to generate empty hooks (and HookPoints) to ensure
195
        # isolation and prevent tests failing spuriously.
196
        for key, factory in known_hooks.items():
197
            self.assertTrue(callable(factory),
198
                "The factory(%r) for %r is not callable" % (factory, key))
199
            obj = known_hooks_key_to_object(key)
200
            self.assertIsInstance(obj, Hooks)
201
            new_hooks = factory()
202
            self.assertIsInstance(obj, Hooks)
203
            self.assertEqual(type(obj), type(new_hooks))
204
205
    def test_known_hooks_key_to_object(self):
206
        self.assertIs(branch.Branch.hooks,
207
            known_hooks_key_to_object(('bzrlib.branch', 'Branch.hooks')))
208
209
    def test_known_hooks_key_to_parent_and_attribute(self):
210
        self.assertEqual((branch.Branch, 'hooks'),
211
            known_hooks_key_to_parent_and_attribute(
212
            ('bzrlib.branch', 'Branch.hooks')))
213
        self.assertEqual((branch, 'Branch'),
214
            known_hooks_key_to_parent_and_attribute(
215
            ('bzrlib.branch', 'Branch')))