20
20
from bzrlib.errors import HookFailed, TipChangeRejected
21
21
from bzrlib.remote import RemoteBranch
22
22
from bzrlib.revision import NULL_REVISION
23
from bzrlib.smart import server
23
24
from bzrlib.tests import TestCaseWithMemoryTransport
76
77
def capture_set_rh_hook(self, branch, rev_history):
77
78
"""Capture post set-rh hook calls to self.hook_calls.
79
80
The call is logged, as is some state of the branch.
81
82
self.hook_calls.append(
147
148
b = self.make_branch('.')
148
149
if isinstance(b, RemoteBranch):
149
150
# RemoteBranch creation:
150
# - creates the branch via the VFS
151
# - creates the branch via the VFS (for older servers)
151
152
# - does a branch open (by creating a RemoteBranch object)
152
# - this has the same behaviour as simple branch opening, with an
153
# additional VFS open at the front.
154
self.assertEqual(b._real_branch, self.hook_calls[0])
155
self.assertOpenedRemoteBranch(self.hook_calls[1:], b)
153
# - this has the nearly the same behaviour as simple branch opening
154
if (self.transport_readonly_server ==
155
server.ReadonlySmartTCPServer_for_testing_v2_only):
157
self.assertEqual(b._real_branch, self.hook_calls[0])
158
self.assertOpenedRemoteBranch(self.hook_calls[1:], b)
160
self.assertOpenedRemoteBranch(self.hook_calls, b,
157
163
self.assertEqual([b], self.hook_calls)
166
172
self.assertEqual([b], self.hook_calls)
168
def assertOpenedRemoteBranch(self, hook_calls, b):
169
"""Assert that the expected calls were recorded for opening 'b'."""
174
def assertOpenedRemoteBranch(self, hook_calls, b, remote_first=False):
175
"""Assert that the expected calls were recorded for opening 'b'.
177
:param remote_first: If True expect the server side operation to open
178
the branch object first.
170
180
# RemoteBranch open always opens the backing branch to get stacking
171
181
# details. As that is done remotely we can't see the branch object
172
182
# nor even compare base url's etc. So we just assert that the first
173
183
# branch returned is the RemoteBranch, and that the second is a
174
184
# Branch but not a RemoteBranch.
186
# RemoteBranch *creation* on the other hand creates the branch object
187
# on the server, and then creates the local proxy object in the client,
188
# so it sees the reverse order.
175
189
self.assertEqual(2, len(hook_calls))
176
self.assertEqual(b, hook_calls[0])
177
self.assertIsInstance(hook_calls[1], Branch)
178
self.assertFalse(isinstance(hook_calls[1], RemoteBranch))
196
self.assertEqual(b, hook_calls[remote_index])
197
self.assertIsInstance(hook_calls[real_index], Branch)
198
self.assertFalse(isinstance(hook_calls[real_index], RemoteBranch))
181
201
class TestPreChangeBranchTip(ChangeBranchTipTestCase):
182
202
"""Tests for pre_change_branch_tip hook.
184
204
Most of these tests are very similar to the tests in
185
205
TestPostChangeBranchTip.
199
219
def test_hook_failure_prevents_change(self):
200
220
"""If a hook raises an exception, the change does not take effect.
202
222
Also, a HookFailed exception will be raised.
204
224
branch = self.make_branch_with_revision_ids(
214
234
self.assertIsInstance(hook_failed_exc.exc_value, PearShapedError)
215
235
# The revision info is unchanged.
216
236
self.assertEqual((2, 'two-\xc2\xb5'), branch.last_revision_info())
218
238
def test_empty_history(self):
219
239
branch = self.make_branch('source')
220
240
hook_calls = self.install_logging_hook('pre')
263
283
def test_explicit_reject_by_hook(self):
264
284
"""If a hook raises TipChangeRejected, the change does not take effect.
266
286
TipChangeRejected exceptions are propagated, not wrapped in HookFailed.
268
288
branch = self.make_branch_with_revision_ids(
275
295
TipChangeRejected, branch.set_last_revision_info, 0, NULL_REVISION)
276
296
# The revision info is unchanged.
277
297
self.assertEqual((2, 'two-\xc2\xb5'), branch.last_revision_info())
280
300
class TestPostChangeBranchTip(ChangeBranchTipTestCase):
281
301
"""Tests for post_change_branch_tip hook.
351
371
ChangeBranchTipTestCase.setUp(self)
352
372
self.installPreAndPostHooks()
354
374
def installPreAndPostHooks(self):
355
375
self.pre_hook_calls = self.install_logging_hook('pre')
356
376
self.post_hook_calls = self.install_logging_hook('post')