~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/branch_implementations/test_hooks.py

Add pre_change_branch_tip hook. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Tests that branch classes implement hook callouts correctly."""
18
18
 
19
 
from bzrlib.branch import Branch
 
19
from bzrlib.branch import Branch, ChangeBranchTipParams
20
20
from bzrlib.revision import NULL_REVISION
21
21
from bzrlib.tests import TestCaseWithMemoryTransport
22
22
 
80
80
            ])
81
81
 
82
82
 
83
 
class TestPostChangeBranchTip(TestCaseWithMemoryTransport):
84
 
 
85
 
    def setUp(self):
86
 
        self.hook_calls = []
87
 
        TestCaseWithMemoryTransport.setUp(self)
88
 
 
89
 
    def capture_post_change_branch_tip_hook(self, params):
90
 
        """Capture post_change_branch_tip hook calls to self.hook_calls.
91
 
 
92
 
        The call is logged, as is some state of the branch.
 
83
class ChangeBranchTipTestCase(TestCaseWithMemoryTransport):
 
84
    """Base TestCase for testing pre/post_change_branch_tip hooks."""
 
85
 
 
86
    def install_logging_hook(self, prefix):
 
87
        """Add a hook that logs calls made to it.
 
88
        
 
89
        :returns: the list that the calls will be appended to.
93
90
        """
94
 
        self.hook_calls.append((params, params.branch.is_locked()))
95
 
        self.assertEquals(params.branch.last_revision_info(),
96
 
                          (params.new_revno, params.new_revid))
97
 
 
98
 
    def test_post_change_branch_tip_empty_history(self):
99
 
        branch = self.make_branch('source')
 
91
        hook_calls = []
100
92
        Branch.hooks.install_named_hook(
101
 
            'post_change_branch_tip',
102
 
            self.capture_post_change_branch_tip_hook,
103
 
            None)
104
 
        branch.set_last_revision_info(0, NULL_REVISION)
105
 
        self.assertEqual(len(self.hook_calls), 1)
106
 
        self.assertEqual(self.hook_calls[0][0].branch, branch)
107
 
        self.assertEqual(self.hook_calls[0][0].old_revid, NULL_REVISION)
108
 
        self.assertEqual(self.hook_calls[0][0].old_revno, 0)
109
 
        self.assertEqual(self.hook_calls[0][0].new_revid, NULL_REVISION)
110
 
        self.assertEqual(self.hook_calls[0][0].new_revno, 0)
 
93
            'pre_change_branch_tip', hook_calls.append, None)
 
94
        return hook_calls
111
95
 
112
 
    def test_post_change_branch_tip_nonempty_history(self):
 
96
    def make_branch_with_revision_ids(self, *revision_ids):
 
97
        """Makes a branch with the given commits."""
113
98
        tree = self.make_branch_and_memory_tree('source')
114
99
        tree.lock_write()
115
100
        tree.add('')
116
 
        tree.commit('another commit', rev_id='f\xc2\xb5')
117
 
        tree.commit('empty commit', rev_id='foo')
 
101
        for revision_id in revision_ids:
 
102
            tree.commit('Message of ' + revision_id, rev_id=revision_id)
118
103
        tree.unlock()
119
104
        branch = tree.branch
120
 
        Branch.hooks.install_named_hook(
121
 
            'post_change_branch_tip',
122
 
            self.capture_post_change_branch_tip_hook,
123
 
            None)
124
 
        # some branches require that their history be set to a revision in the
125
 
        # repository
126
 
        branch.set_last_revision_info(1, 'f\xc2\xb5')
127
 
        self.assertEqual(len(self.hook_calls), 1)
128
 
        self.assertEqual(self.hook_calls[0][0].branch, branch)
129
 
        self.assertEqual(self.hook_calls[0][0].old_revid, 'foo')
130
 
        self.assertEqual(self.hook_calls[0][0].old_revno, 2)
131
 
        self.assertEqual(self.hook_calls[0][0].new_revid, 'f\xc2\xb5')
132
 
        self.assertEqual(self.hook_calls[0][0].new_revno, 1)
133
 
 
134
 
    def test_post_change_branch_tip_branch_is_locked(self):
135
 
        branch = self.make_branch('source')
136
 
        Branch.hooks.install_named_hook(
137
 
            'post_change_branch_tip',
138
 
            self.capture_post_change_branch_tip_hook,
139
 
            None)
140
 
        branch.set_last_revision_info(0, NULL_REVISION)
141
 
        self.assertEqual(len(self.hook_calls), 1)
142
 
        self.assertEqual(self.hook_calls[0][0].branch, branch)
143
 
        self.assertEqual(self.hook_calls[0][1], True)
144
 
 
145
 
    def test_post_change_branch_tip_calls_all_hooks_no_errors(self):
146
 
        branch = self.make_branch('source')
147
 
        Branch.hooks.install_named_hook(
148
 
            'post_change_branch_tip',
149
 
            self.capture_post_change_branch_tip_hook,
150
 
            None)
151
 
        Branch.hooks.install_named_hook(
152
 
            'post_change_branch_tip',
153
 
            self.capture_post_change_branch_tip_hook,
154
 
            None)
155
 
        branch.set_last_revision_info(0, NULL_REVISION)
156
 
        self.assertEqual(len(self.hook_calls), 2)
157
 
        self.assertEqual(self.hook_calls[0][0].branch, branch)
158
 
        self.assertEqual(self.hook_calls[1][0].branch, branch)
 
105
        return branch
 
106
 
 
107
 
 
108
class TestPreChangeBranchTip(ChangeBranchTipTestCase):
 
109
    """Tests for pre_change_branch_tip hook.
 
110
    
 
111
    Most of these tests are very similar to the tests in
 
112
    TestPostChangeBranchTip.
 
113
    """
 
114
 
 
115
    def test_hook_runs_before_change(self):
 
116
        """The hook runs *before* the branch's last_revision_info has changed.
 
117
        """
 
118
        branch = self.make_branch_with_revision_ids('revid-one')
 
119
        def assertBranchAtRevision1(params):
 
120
            self.assertEquals(
 
121
                (1, 'revid-one'), params.branch.last_revision_info())
 
122
        Branch.hooks.install_named_hook(
 
123
            'pre_change_branch_tip', assertBranchAtRevision1, None)
 
124
        branch.set_last_revision_info(0, NULL_REVISION)
 
125
 
 
126
    def test_reject_by_hook(self):
 
127
        """If a hook raises an exception, the change does not take effect.
 
128
        
 
129
        Also, the exception will be propogated.
 
130
        """
 
131
        branch = self.make_branch_with_revision_ids(
 
132
            'one-\xc2\xb5', 'two-\xc2\xb5')
 
133
        class PearShapedError(Exception):
 
134
            pass
 
135
        def hook_that_raises(params):
 
136
            raise PearShapedError()
 
137
        Branch.hooks.install_named_hook(
 
138
            'pre_change_branch_tip', hook_that_raises, None)
 
139
        self.assertRaises(
 
140
            PearShapedError, branch.set_last_revision_info, 0, NULL_REVISION)
 
141
        # The revision info is unchanged.
 
142
        self.assertEqual((2, 'two-\xc2\xb5'), branch.last_revision_info())
 
143
        
 
144
    def test_empty_history(self):
 
145
        branch = self.make_branch('source')
 
146
        hook_calls = self.install_logging_hook('pre')
 
147
        branch.set_last_revision_info(0, NULL_REVISION)
 
148
        expected_params = ChangeBranchTipParams(
 
149
            branch, 0, 0, NULL_REVISION, NULL_REVISION)
 
150
        self.assertEqual([expected_params], hook_calls)
 
151
 
 
152
    def test_nonempty_history(self):
 
153
        # some branches require that their history be set to a revision in the
 
154
        # repository, so we need to make a branch with non-empty history for
 
155
        # this test.
 
156
        branch = self.make_branch_with_revision_ids(
 
157
            'one-\xc2\xb5', 'two-\xc2\xb5')
 
158
        hook_calls = self.install_logging_hook('pre')
 
159
        branch.set_last_revision_info(1, 'one-\xc2\xb5')
 
160
        expected_params = ChangeBranchTipParams(
 
161
            branch, 2, 1, 'two-\xc2\xb5', 'one-\xc2\xb5')
 
162
        self.assertEqual([expected_params], hook_calls)
 
163
 
 
164
    def test_branch_is_locked(self):
 
165
        branch = self.make_branch('source')
 
166
        def assertBranchIsLocked(params):
 
167
            self.assertTrue(params.branch.is_locked())
 
168
        Branch.hooks.install_named_hook(
 
169
            'pre_change_branch_tip', assertBranchIsLocked, None)
 
170
        branch.set_last_revision_info(0, NULL_REVISION)
 
171
 
 
172
    def test_calls_all_hooks_no_errors(self):
 
173
        """If multiple hooks are registered, all are called (if none raise
 
174
        errors).
 
175
        """
 
176
        branch = self.make_branch('source')
 
177
        hook_calls_1 = self.install_logging_hook('pre')
 
178
        hook_calls_2 = self.install_logging_hook('pre')
 
179
        self.assertIsNot(hook_calls_1, hook_calls_2)
 
180
        branch.set_last_revision_info(0, NULL_REVISION)
 
181
        # Both hooks are called.
 
182
        self.assertEqual(len(hook_calls_1), 1)
 
183
        self.assertEqual(len(hook_calls_2), 1)
 
184
 
 
185
 
 
186
class TestPostChangeBranchTip(ChangeBranchTipTestCase):
 
187
    """Tests for post_change_branch_tip hook.
 
188
 
 
189
    Most of these tests are very similar to the tests in
 
190
    TestPostChangeBranchTip.
 
191
    """
 
192
 
 
193
    def test_hook_runs_after_change(self):
 
194
        """The hook runs *after* the branch's last_revision_info has changed.
 
195
        """
 
196
        branch = self.make_branch_with_revision_ids('revid-one')
 
197
        def assertBranchAtRevision1(params):
 
198
            self.assertEquals(
 
199
                (0, NULL_REVISION), params.branch.last_revision_info())
 
200
        Branch.hooks.install_named_hook(
 
201
            'post_change_branch_tip', assertBranchAtRevision1, None)
 
202
        branch.set_last_revision_info(0, NULL_REVISION)
 
203
 
 
204
    def test_empty_history(self):
 
205
        branch = self.make_branch('source')
 
206
        hook_calls = self.install_logging_hook('post')
 
207
        branch.set_last_revision_info(0, NULL_REVISION)
 
208
        expected_params = ChangeBranchTipParams(
 
209
            branch, 0, 0, NULL_REVISION, NULL_REVISION)
 
210
        self.assertEqual([expected_params], hook_calls)
 
211
 
 
212
    def test_nonempty_history(self):
 
213
        # some branches require that their history be set to a revision in the
 
214
        # repository, so we need to make a branch with non-empty history for
 
215
        # this test.
 
216
        branch = self.make_branch_with_revision_ids(
 
217
            'one-\xc2\xb5', 'two-\xc2\xb5')
 
218
        hook_calls = self.install_logging_hook('post')
 
219
        branch.set_last_revision_info(1, 'one-\xc2\xb5')
 
220
        expected_params = ChangeBranchTipParams(
 
221
            branch, 2, 1, 'two-\xc2\xb5', 'one-\xc2\xb5')
 
222
        self.assertEqual([expected_params], hook_calls)
 
223
 
 
224
    def test_branch_is_locked(self):
 
225
        """The branch passed to the hook is locked."""
 
226
        branch = self.make_branch('source')
 
227
        def assertBranchIsLocked(params):
 
228
            self.assertTrue(params.branch.is_locked())
 
229
        Branch.hooks.install_named_hook(
 
230
            'post_change_branch_tip', assertBranchIsLocked, None)
 
231
        branch.set_last_revision_info(0, NULL_REVISION)
 
232
 
 
233
    def test_calls_all_hooks_no_errors(self):
 
234
        """If multiple hooks are registered, all are called (if none raise
 
235
        errors).
 
236
        """
 
237
        branch = self.make_branch('source')
 
238
        hook_calls_1 = self.install_logging_hook('post')
 
239
        hook_calls_2 = self.install_logging_hook('post')
 
240
        self.assertIsNot(hook_calls_1, hook_calls_2)
 
241
        branch.set_last_revision_info(0, NULL_REVISION)
 
242
        # Both hooks are called.
 
243
        self.assertEqual(len(hook_calls_1), 1)
 
244
        self.assertEqual(len(hook_calls_2), 1)