1
# Copyright (C) 2006, 2007 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for the smart wire/domain protocol.
19
This module contains tests for the domain-level smart requests and responses,
20
such as the 'Branch.lock_write' request. Many of these use specific disk
21
formats to exercise calls that only make sense for formats with specific
24
Tests for low-level protocol encoding are found in test_smart_transport.
28
from cStringIO import StringIO
41
from bzrlib.branch import Branch, BranchReferenceFormat
42
import bzrlib.smart.branch
43
import bzrlib.smart.bzrdir, bzrlib.smart.bzrdir as smart_dir
44
import bzrlib.smart.packrepository
45
import bzrlib.smart.repository
46
from bzrlib.smart.request import (
47
FailedSmartServerResponse,
50
SuccessfulSmartServerResponse,
52
from bzrlib.tests import (
55
from bzrlib.transport import chroot, get_transport
58
def load_tests(standard_tests, module, loader):
59
"""Multiply tests version and protocol consistency."""
60
# FindRepository tests.
61
bzrdir_mod = bzrlib.smart.bzrdir
64
"_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV1}),
65
("find_repositoryV2", {
66
"_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV2}),
67
("find_repositoryV3", {
68
"_request_class":bzrdir_mod.SmartServerRequestFindRepositoryV3}),
70
to_adapt, result = split_suite_by_re(standard_tests,
71
"TestSmartServerRequestFindRepository")
72
v2_only, v1_and_2 = split_suite_by_re(to_adapt,
74
tests.multiply_tests(v1_and_2, scenarios, result)
75
# The first scenario is only applicable to v1 protocols, it is deleted
77
tests.multiply_tests(v2_only, scenarios[1:], result)
81
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
84
tests.TestCaseWithTransport.setUp(self)
85
self._chroot_server = None
87
def get_transport(self, relpath=None):
88
if self._chroot_server is None:
89
backing_transport = tests.TestCaseWithTransport.get_transport(self)
90
self._chroot_server = chroot.ChrootServer(backing_transport)
91
self._chroot_server.setUp()
92
self.addCleanup(self._chroot_server.tearDown)
93
t = get_transport(self._chroot_server.get_url())
94
if relpath is not None:
99
class TestCaseWithSmartMedium(tests.TestCaseWithTransport):
102
super(TestCaseWithSmartMedium, self).setUp()
103
# We're allowed to set the transport class here, so that we don't use
104
# the default or a parameterized class, but rather use the
105
# TestCaseWithTransport infrastructure to set up a smart server and
107
self.transport_server = self.make_transport_server
109
def make_transport_server(self):
110
return smart.server.SmartTCPServer_for_testing('-' + self.id())
112
def get_smart_medium(self):
113
"""Get a smart medium to use in tests."""
114
return self.get_transport().get_smart_medium()
117
class TestByteStreamToStream(tests.TestCase):
119
def test_repeated_substreams_same_kind_are_one_stream(self):
120
# Make a stream - an iterable of bytestrings.
121
stream = [('text', [versionedfile.FulltextContentFactory(('k1',), None,
122
None, 'foo')]),('text', [
123
versionedfile.FulltextContentFactory(('k2',), None, None, 'bar')])]
124
fmt = bzrdir.format_registry.get('pack-0.92')().repository_format
125
bytes = smart.repository._stream_to_byte_stream(stream, fmt)
127
# Iterate the resulting iterable; checking that we get only one stream
129
fmt, stream = smart.repository._byte_stream_to_stream(bytes)
130
for kind, substream in stream:
131
streams.append((kind, list(substream)))
132
self.assertLength(1, streams)
133
self.assertLength(2, streams[0][1])
136
class TestSmartServerResponse(tests.TestCase):
138
def test__eq__(self):
139
self.assertEqual(SmartServerResponse(('ok', )),
140
SmartServerResponse(('ok', )))
141
self.assertEqual(SmartServerResponse(('ok', ), 'body'),
142
SmartServerResponse(('ok', ), 'body'))
143
self.assertNotEqual(SmartServerResponse(('ok', )),
144
SmartServerResponse(('notok', )))
145
self.assertNotEqual(SmartServerResponse(('ok', ), 'body'),
146
SmartServerResponse(('ok', )))
147
self.assertNotEqual(None,
148
SmartServerResponse(('ok', )))
150
def test__str__(self):
151
"""SmartServerResponses can be stringified."""
153
"<SuccessfulSmartServerResponse args=('args',) body='body'>",
154
str(SuccessfulSmartServerResponse(('args',), 'body')))
156
"<FailedSmartServerResponse args=('args',) body='body'>",
157
str(FailedSmartServerResponse(('args',), 'body')))
160
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
162
def test_translate_client_path(self):
163
transport = self.get_transport()
164
request = SmartServerRequest(transport, 'foo/')
165
self.assertEqual('./', request.translate_client_path('foo/'))
167
errors.InvalidURLJoin, request.translate_client_path, 'foo/..')
169
errors.PathNotChild, request.translate_client_path, '/')
171
errors.PathNotChild, request.translate_client_path, 'bar/')
172
self.assertEqual('./baz', request.translate_client_path('foo/baz'))
174
def test_transport_from_client_path(self):
175
transport = self.get_transport()
176
request = SmartServerRequest(transport, 'foo/')
179
request.transport_from_client_path('foo/').base)
182
class TestSmartServerBzrDirRequestCloningMetaDir(
183
tests.TestCaseWithMemoryTransport):
184
"""Tests for BzrDir.cloning_metadir."""
186
def test_cloning_metadir(self):
187
"""When there is a bzrdir present, the call succeeds."""
188
backing = self.get_transport()
189
dir = self.make_bzrdir('.')
190
local_result = dir.cloning_metadir()
191
request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
192
request = request_class(backing)
193
expected = SuccessfulSmartServerResponse(
194
(local_result.network_name(),
195
local_result.repository_format.network_name(),
196
('branch', local_result.get_branch_format().network_name())))
197
self.assertEqual(expected, request.execute('', 'False'))
199
def test_cloning_metadir_reference(self):
200
"""The request fails when bzrdir contains a branch reference."""
201
backing = self.get_transport()
202
referenced_branch = self.make_branch('referenced')
203
dir = self.make_bzrdir('.')
204
local_result = dir.cloning_metadir()
205
reference = BranchReferenceFormat().initialize(dir, referenced_branch)
206
reference_url = BranchReferenceFormat().get_reference(dir)
207
# The server shouldn't try to follow the branch reference, so it's fine
208
# if the referenced branch isn't reachable.
209
backing.rename('referenced', 'moved')
210
request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
211
request = request_class(backing)
212
expected = FailedSmartServerResponse(('BranchReference',))
213
self.assertEqual(expected, request.execute('', 'False'))
216
class TestSmartServerRequestCreateRepository(tests.TestCaseWithMemoryTransport):
217
"""Tests for BzrDir.create_repository."""
219
def test_makes_repository(self):
220
"""When there is a bzrdir present, the call succeeds."""
221
backing = self.get_transport()
222
self.make_bzrdir('.')
223
request_class = bzrlib.smart.bzrdir.SmartServerRequestCreateRepository
224
request = request_class(backing)
225
reference_bzrdir_format = bzrdir.format_registry.get('pack-0.92')()
226
reference_format = reference_bzrdir_format.repository_format
227
network_name = reference_format.network_name()
228
expected = SuccessfulSmartServerResponse(
229
('ok', 'no', 'no', 'no', network_name))
230
self.assertEqual(expected, request.execute('', network_name, 'True'))
233
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
234
"""Tests for BzrDir.find_repository."""
236
def test_no_repository(self):
237
"""When there is no repository to be found, ('norepository', ) is returned."""
238
backing = self.get_transport()
239
request = self._request_class(backing)
240
self.make_bzrdir('.')
241
self.assertEqual(SmartServerResponse(('norepository', )),
244
def test_nonshared_repository(self):
245
# nonshared repositorys only allow 'find' to return a handle when the
246
# path the repository is being searched on is the same as that that
247
# the repository is at.
248
backing = self.get_transport()
249
request = self._request_class(backing)
250
result = self._make_repository_and_result()
251
self.assertEqual(result, request.execute(''))
252
self.make_bzrdir('subdir')
253
self.assertEqual(SmartServerResponse(('norepository', )),
254
request.execute('subdir'))
256
def _make_repository_and_result(self, shared=False, format=None):
257
"""Convenience function to setup a repository.
259
:result: The SmartServerResponse to expect when opening it.
261
repo = self.make_repository('.', shared=shared, format=format)
262
if repo.supports_rich_root():
266
if repo._format.supports_tree_reference:
270
if repo._format.supports_external_lookups:
274
if (smart.bzrdir.SmartServerRequestFindRepositoryV3 ==
275
self._request_class):
276
return SuccessfulSmartServerResponse(
277
('ok', '', rich_root, subtrees, external,
278
repo._format.network_name()))
279
elif (smart.bzrdir.SmartServerRequestFindRepositoryV2 ==
280
self._request_class):
281
# All tests so far are on formats, and for non-external
283
return SuccessfulSmartServerResponse(
284
('ok', '', rich_root, subtrees, external))
286
return SuccessfulSmartServerResponse(('ok', '', rich_root, subtrees))
288
def test_shared_repository(self):
289
"""When there is a shared repository, we get 'ok', 'relpath-to-repo'."""
290
backing = self.get_transport()
291
request = self._request_class(backing)
292
result = self._make_repository_and_result(shared=True)
293
self.assertEqual(result, request.execute(''))
294
self.make_bzrdir('subdir')
295
result2 = SmartServerResponse(result.args[0:1] + ('..', ) + result.args[2:])
296
self.assertEqual(result2,
297
request.execute('subdir'))
298
self.make_bzrdir('subdir/deeper')
299
result3 = SmartServerResponse(result.args[0:1] + ('../..', ) + result.args[2:])
300
self.assertEqual(result3,
301
request.execute('subdir/deeper'))
303
def test_rich_root_and_subtree_encoding(self):
304
"""Test for the format attributes for rich root and subtree support."""
305
backing = self.get_transport()
306
request = self._request_class(backing)
307
result = self._make_repository_and_result(format='dirstate-with-subtree')
308
# check the test will be valid
309
self.assertEqual('yes', result.args[2])
310
self.assertEqual('yes', result.args[3])
311
self.assertEqual(result, request.execute(''))
313
def test_supports_external_lookups_no_v2(self):
314
"""Test for the supports_external_lookups attribute."""
315
backing = self.get_transport()
316
request = self._request_class(backing)
317
result = self._make_repository_and_result(format='dirstate-with-subtree')
318
# check the test will be valid
319
self.assertEqual('no', result.args[4])
320
self.assertEqual(result, request.execute(''))
323
class TestSmartServerBzrDirRequestGetConfigFile(
324
tests.TestCaseWithMemoryTransport):
325
"""Tests for BzrDir.get_config_file."""
327
def test_present(self):
328
backing = self.get_transport()
329
dir = self.make_bzrdir('.')
330
dir.get_config().set_default_stack_on("/")
331
local_result = dir._get_config()._get_config_file().read()
332
request_class = smart_dir.SmartServerBzrDirRequestConfigFile
333
request = request_class(backing)
334
expected = SuccessfulSmartServerResponse((), local_result)
335
self.assertEqual(expected, request.execute(''))
337
def test_missing(self):
338
backing = self.get_transport()
339
dir = self.make_bzrdir('.')
340
request_class = smart_dir.SmartServerBzrDirRequestConfigFile
341
request = request_class(backing)
342
expected = SuccessfulSmartServerResponse((), '')
343
self.assertEqual(expected, request.execute(''))
346
class TestSmartServerRequestInitializeBzrDir(tests.TestCaseWithMemoryTransport):
348
def test_empty_dir(self):
349
"""Initializing an empty dir should succeed and do it."""
350
backing = self.get_transport()
351
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
352
self.assertEqual(SmartServerResponse(('ok', )),
354
made_dir = bzrdir.BzrDir.open_from_transport(backing)
355
# no branch, tree or repository is expected with the current
357
self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
358
self.assertRaises(errors.NotBranchError, made_dir.open_branch)
359
self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
361
def test_missing_dir(self):
362
"""Initializing a missing directory should fail like the bzrdir api."""
363
backing = self.get_transport()
364
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
365
self.assertRaises(errors.NoSuchFile,
366
request.execute, 'subdir')
368
def test_initialized_dir(self):
369
"""Initializing an extant bzrdir should fail like the bzrdir api."""
370
backing = self.get_transport()
371
request = smart.bzrdir.SmartServerRequestInitializeBzrDir(backing)
372
self.make_bzrdir('subdir')
373
self.assertRaises(errors.FileExists,
374
request.execute, 'subdir')
377
class TestSmartServerRequestBzrDirInitializeEx(tests.TestCaseWithMemoryTransport):
378
"""Basic tests for BzrDir.initialize_ex_1.16 in the smart server.
380
The main unit tests in test_bzrdir exercise the API comprehensively.
383
def test_empty_dir(self):
384
"""Initializing an empty dir should succeed and do it."""
385
backing = self.get_transport()
386
name = self.make_bzrdir('reference')._format.network_name()
387
request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
388
self.assertEqual(SmartServerResponse(('', '', '', '', '', '', name,
389
'False', '', '', '')),
390
request.execute(name, '', 'True', 'False', 'False', '', '', '', '',
392
made_dir = bzrdir.BzrDir.open_from_transport(backing)
393
# no branch, tree or repository is expected with the current
395
self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
396
self.assertRaises(errors.NotBranchError, made_dir.open_branch)
397
self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
399
def test_missing_dir(self):
400
"""Initializing a missing directory should fail like the bzrdir api."""
401
backing = self.get_transport()
402
name = self.make_bzrdir('reference')._format.network_name()
403
request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
404
self.assertRaises(errors.NoSuchFile, request.execute, name,
405
'subdir/dir', 'False', 'False', 'False', '', '', '', '', 'False')
407
def test_initialized_dir(self):
408
"""Initializing an extant directory should fail like the bzrdir api."""
409
backing = self.get_transport()
410
name = self.make_bzrdir('reference')._format.network_name()
411
request = smart.bzrdir.SmartServerRequestBzrDirInitializeEx(backing)
412
self.make_bzrdir('subdir')
413
self.assertRaises(errors.FileExists, request.execute, name, 'subdir',
414
'False', 'False', 'False', '', '', '', '', 'False')
417
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
419
def test_no_branch(self):
420
"""When there is no branch, ('nobranch', ) is returned."""
421
backing = self.get_transport()
422
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
423
self.make_bzrdir('.')
424
self.assertEqual(SmartServerResponse(('nobranch', )),
427
def test_branch(self):
428
"""When there is a branch, 'ok' is returned."""
429
backing = self.get_transport()
430
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
431
self.make_branch('.')
432
self.assertEqual(SmartServerResponse(('ok', '')),
435
def test_branch_reference(self):
436
"""When there is a branch reference, the reference URL is returned."""
437
backing = self.get_transport()
438
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
439
branch = self.make_branch('branch')
440
checkout = branch.create_checkout('reference',lightweight=True)
441
reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
442
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
443
self.assertEqual(SmartServerResponse(('ok', reference_url)),
444
request.execute('reference'))
447
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
449
def test_no_branch(self):
450
"""When there is no branch, ('nobranch', ) is returned."""
451
backing = self.get_transport()
452
self.make_bzrdir('.')
453
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
454
self.assertEqual(SmartServerResponse(('nobranch', )),
457
def test_branch(self):
458
"""When there is a branch, 'ok' is returned."""
459
backing = self.get_transport()
460
expected = self.make_branch('.')._format.network_name()
461
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
462
self.assertEqual(SuccessfulSmartServerResponse(('branch', expected)),
465
def test_branch_reference(self):
466
"""When there is a branch reference, the reference URL is returned."""
467
backing = self.get_transport()
468
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
469
branch = self.make_branch('branch')
470
checkout = branch.create_checkout('reference',lightweight=True)
471
reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
472
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
473
self.assertEqual(SuccessfulSmartServerResponse(('ref', reference_url)),
474
request.execute('reference'))
476
def test_stacked_branch(self):
477
"""Opening a stacked branch does not open the stacked-on branch."""
478
trunk = self.make_branch('trunk')
479
feature = self.make_branch('feature')
480
feature.set_stacked_on_url(trunk.base)
482
Branch.hooks.install_named_hook('open', opened_branches.append, None)
483
backing = self.get_transport()
484
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
487
response = request.execute('feature')
489
request.teardown_jail()
490
expected_format = feature._format.network_name()
492
SuccessfulSmartServerResponse(('branch', expected_format)),
494
self.assertLength(1, opened_branches)
497
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
499
def test_empty(self):
500
"""For an empty branch, the body is empty."""
501
backing = self.get_transport()
502
request = smart.branch.SmartServerRequestRevisionHistory(backing)
503
self.make_branch('.')
504
self.assertEqual(SmartServerResponse(('ok', ), ''),
507
def test_not_empty(self):
508
"""For a non-empty branch, the body is empty."""
509
backing = self.get_transport()
510
request = smart.branch.SmartServerRequestRevisionHistory(backing)
511
tree = self.make_branch_and_memory_tree('.')
514
r1 = tree.commit('1st commit')
515
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
518
SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
522
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
524
def test_no_branch(self):
525
"""When there is a bzrdir and no branch, NotBranchError is raised."""
526
backing = self.get_transport()
527
request = smart.branch.SmartServerBranchRequest(backing)
528
self.make_bzrdir('.')
529
self.assertRaises(errors.NotBranchError,
532
def test_branch_reference(self):
533
"""When there is a branch reference, NotBranchError is raised."""
534
backing = self.get_transport()
535
request = smart.branch.SmartServerBranchRequest(backing)
536
branch = self.make_branch('branch')
537
checkout = branch.create_checkout('reference',lightweight=True)
538
self.assertRaises(errors.NotBranchError,
539
request.execute, 'checkout')
542
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
544
def test_empty(self):
545
"""For an empty branch, the result is ('ok', '0', 'null:')."""
546
backing = self.get_transport()
547
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
548
self.make_branch('.')
549
self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
552
def test_not_empty(self):
553
"""For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
554
backing = self.get_transport()
555
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
556
tree = self.make_branch_and_memory_tree('.')
559
rev_id_utf8 = u'\xc8'.encode('utf-8')
560
r1 = tree.commit('1st commit')
561
r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
564
SmartServerResponse(('ok', '2', rev_id_utf8)),
568
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
570
def test_default(self):
571
"""With no file, we get empty content."""
572
backing = self.get_transport()
573
request = smart.branch.SmartServerBranchGetConfigFile(backing)
574
branch = self.make_branch('.')
575
# there should be no file by default
577
self.assertEqual(SmartServerResponse(('ok', ), content),
580
def test_with_content(self):
581
# SmartServerBranchGetConfigFile should return the content from
582
# branch.control_files.get('branch.conf') for now - in the future it may
583
# perform more complex processing.
584
backing = self.get_transport()
585
request = smart.branch.SmartServerBranchGetConfigFile(backing)
586
branch = self.make_branch('.')
587
branch._transport.put_bytes('branch.conf', 'foo bar baz')
588
self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
592
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
594
def get_lock_tokens(self, branch):
595
branch_token = branch.lock_write()
596
repo_token = branch.repository.lock_write()
597
branch.repository.unlock()
598
return branch_token, repo_token
601
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
603
def test_value_name(self):
604
branch = self.make_branch('.')
605
request = smart.branch.SmartServerBranchRequestSetConfigOption(
606
branch.bzrdir.root_transport)
607
branch_token, repo_token = self.get_lock_tokens(branch)
608
config = branch._get_config()
609
result = request.execute('', branch_token, repo_token, 'bar', 'foo',
611
self.assertEqual(SuccessfulSmartServerResponse(()), result)
612
self.assertEqual('bar', config.get_option('foo'))
616
def test_value_name_section(self):
617
branch = self.make_branch('.')
618
request = smart.branch.SmartServerBranchRequestSetConfigOption(
619
branch.bzrdir.root_transport)
620
branch_token, repo_token = self.get_lock_tokens(branch)
621
config = branch._get_config()
622
result = request.execute('', branch_token, repo_token, 'bar', 'foo',
624
self.assertEqual(SuccessfulSmartServerResponse(()), result)
625
self.assertEqual('bar', config.get_option('foo', 'gam'))
630
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
631
# Only called when the branch format and tags match [yay factory
632
# methods] so only need to test straight forward cases.
634
def test_set_bytes(self):
635
base_branch = self.make_branch('base')
636
tag_bytes = base_branch._get_tags_bytes()
637
# get_lock_tokens takes out a lock.
638
branch_token, repo_token = self.get_lock_tokens(base_branch)
639
request = smart.branch.SmartServerBranchSetTagsBytes(
640
self.get_transport())
641
response = request.execute('base', branch_token, repo_token)
642
self.assertEqual(None, response)
643
response = request.do_chunk(tag_bytes)
644
self.assertEqual(None, response)
645
response = request.do_end()
647
SuccessfulSmartServerResponse(()), response)
650
def test_lock_failed(self):
651
base_branch = self.make_branch('base')
652
base_branch.lock_write()
653
tag_bytes = base_branch._get_tags_bytes()
654
request = smart.branch.SmartServerBranchSetTagsBytes(
655
self.get_transport())
656
self.assertRaises(errors.TokenMismatch, request.execute,
657
'base', 'wrong token', 'wrong token')
658
# The request handler will keep processing the message parts, so even
659
# if the request fails immediately do_chunk and do_end are still
661
request.do_chunk(tag_bytes)
667
class SetLastRevisionTestBase(TestLockedBranch):
668
"""Base test case for verbs that implement set_last_revision."""
671
tests.TestCaseWithMemoryTransport.setUp(self)
672
backing_transport = self.get_transport()
673
self.request = self.request_class(backing_transport)
674
self.tree = self.make_branch_and_memory_tree('.')
676
def lock_branch(self):
677
return self.get_lock_tokens(self.tree.branch)
679
def unlock_branch(self):
680
self.tree.branch.unlock()
682
def set_last_revision(self, revision_id, revno):
683
branch_token, repo_token = self.lock_branch()
684
response = self._set_last_revision(
685
revision_id, revno, branch_token, repo_token)
689
def assertRequestSucceeds(self, revision_id, revno):
690
response = self.set_last_revision(revision_id, revno)
691
self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
694
class TestSetLastRevisionVerbMixin(object):
695
"""Mixin test case for verbs that implement set_last_revision."""
697
def test_set_null_to_null(self):
698
"""An empty branch can have its last revision set to 'null:'."""
699
self.assertRequestSucceeds('null:', 0)
701
def test_NoSuchRevision(self):
702
"""If the revision_id is not present, the verb returns NoSuchRevision.
704
revision_id = 'non-existent revision'
706
FailedSmartServerResponse(('NoSuchRevision', revision_id)),
707
self.set_last_revision(revision_id, 1))
709
def make_tree_with_two_commits(self):
710
self.tree.lock_write()
712
rev_id_utf8 = u'\xc8'.encode('utf-8')
713
r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
714
r2 = self.tree.commit('2nd commit', rev_id='rev-2')
717
def test_branch_last_revision_info_is_updated(self):
718
"""A branch's tip can be set to a revision that is present in its
721
# Make a branch with an empty revision history, but two revisions in
723
self.make_tree_with_two_commits()
724
rev_id_utf8 = u'\xc8'.encode('utf-8')
725
self.tree.branch.set_revision_history([])
727
(0, 'null:'), self.tree.branch.last_revision_info())
728
# We can update the branch to a revision that is present in the
730
self.assertRequestSucceeds(rev_id_utf8, 1)
732
(1, rev_id_utf8), self.tree.branch.last_revision_info())
734
def test_branch_last_revision_info_rewind(self):
735
"""A branch's tip can be set to a revision that is an ancestor of the
738
self.make_tree_with_two_commits()
739
rev_id_utf8 = u'\xc8'.encode('utf-8')
741
(2, 'rev-2'), self.tree.branch.last_revision_info())
742
self.assertRequestSucceeds(rev_id_utf8, 1)
744
(1, rev_id_utf8), self.tree.branch.last_revision_info())
746
def test_TipChangeRejected(self):
747
"""If a pre_change_branch_tip hook raises TipChangeRejected, the verb
748
returns TipChangeRejected.
750
rejection_message = u'rejection message\N{INTERROBANG}'
751
def hook_that_rejects(params):
752
raise errors.TipChangeRejected(rejection_message)
753
Branch.hooks.install_named_hook(
754
'pre_change_branch_tip', hook_that_rejects, None)
756
FailedSmartServerResponse(
757
('TipChangeRejected', rejection_message.encode('utf-8'))),
758
self.set_last_revision('null:', 0))
761
class TestSmartServerBranchRequestSetLastRevision(
762
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
763
"""Tests for Branch.set_last_revision verb."""
765
request_class = smart.branch.SmartServerBranchRequestSetLastRevision
767
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
768
return self.request.execute(
769
'', branch_token, repo_token, revision_id)
772
class TestSmartServerBranchRequestSetLastRevisionInfo(
773
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
774
"""Tests for Branch.set_last_revision_info verb."""
776
request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
778
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
779
return self.request.execute(
780
'', branch_token, repo_token, revno, revision_id)
782
def test_NoSuchRevision(self):
783
"""Branch.set_last_revision_info does not have to return
784
NoSuchRevision if the revision_id is absent.
786
raise tests.TestNotApplicable()
789
class TestSmartServerBranchRequestSetLastRevisionEx(
790
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
791
"""Tests for Branch.set_last_revision_ex verb."""
793
request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
795
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
796
return self.request.execute(
797
'', branch_token, repo_token, revision_id, 0, 0)
799
def assertRequestSucceeds(self, revision_id, revno):
800
response = self.set_last_revision(revision_id, revno)
802
SuccessfulSmartServerResponse(('ok', revno, revision_id)),
805
def test_branch_last_revision_info_rewind(self):
806
"""A branch's tip can be set to a revision that is an ancestor of the
807
current tip, but only if allow_overwrite_descendant is passed.
809
self.make_tree_with_two_commits()
810
rev_id_utf8 = u'\xc8'.encode('utf-8')
812
(2, 'rev-2'), self.tree.branch.last_revision_info())
813
# If allow_overwrite_descendant flag is 0, then trying to set the tip
814
# to an older revision ID has no effect.
815
branch_token, repo_token = self.lock_branch()
816
response = self.request.execute(
817
'', branch_token, repo_token, rev_id_utf8, 0, 0)
819
SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
822
(2, 'rev-2'), self.tree.branch.last_revision_info())
824
# If allow_overwrite_descendant flag is 1, then setting the tip to an
826
response = self.request.execute(
827
'', branch_token, repo_token, rev_id_utf8, 0, 1)
829
SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
833
(1, rev_id_utf8), self.tree.branch.last_revision_info())
835
def make_branch_with_divergent_history(self):
836
"""Make a branch with divergent history in its repo.
838
The branch's tip will be 'child-2', and the repo will also contain
839
'child-1', which diverges from a common base revision.
841
self.tree.lock_write()
843
r1 = self.tree.commit('1st commit')
844
revno_1, revid_1 = self.tree.branch.last_revision_info()
845
r2 = self.tree.commit('2nd commit', rev_id='child-1')
846
# Undo the second commit
847
self.tree.branch.set_last_revision_info(revno_1, revid_1)
848
self.tree.set_parent_ids([revid_1])
849
# Make a new second commit, child-2. child-2 has diverged from
851
new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
854
def test_not_allow_diverged(self):
855
"""If allow_diverged is not passed, then setting a divergent history
856
returns a Diverged error.
858
self.make_branch_with_divergent_history()
860
FailedSmartServerResponse(('Diverged',)),
861
self.set_last_revision('child-1', 2))
862
# The branch tip was not changed.
863
self.assertEqual('child-2', self.tree.branch.last_revision())
865
def test_allow_diverged(self):
866
"""If allow_diverged is passed, then setting a divergent history
869
self.make_branch_with_divergent_history()
870
branch_token, repo_token = self.lock_branch()
871
response = self.request.execute(
872
'', branch_token, repo_token, 'child-1', 1, 0)
874
SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
877
# The branch tip was changed.
878
self.assertEqual('child-1', self.tree.branch.last_revision())
881
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
883
def test_get_parent_none(self):
884
base_branch = self.make_branch('base')
885
request = smart.branch.SmartServerBranchGetParent(self.get_transport())
886
response = request.execute('base')
888
SuccessfulSmartServerResponse(('',)), response)
890
def test_get_parent_something(self):
891
base_branch = self.make_branch('base')
892
base_branch.set_parent(self.get_url('foo'))
893
request = smart.branch.SmartServerBranchGetParent(self.get_transport())
894
response = request.execute('base')
896
SuccessfulSmartServerResponse(("../foo",)),
900
class TestSmartServerBranchRequestSetParent(tests.TestCaseWithMemoryTransport):
902
def test_set_parent_none(self):
903
branch = self.make_branch('base', format="1.9")
905
branch._set_parent_location('foo')
907
request = smart.branch.SmartServerBranchRequestSetParentLocation(
908
self.get_transport())
909
branch_token = branch.lock_write()
910
repo_token = branch.repository.lock_write()
912
response = request.execute('base', branch_token, repo_token, '')
914
branch.repository.unlock()
916
self.assertEqual(SuccessfulSmartServerResponse(()), response)
917
self.assertEqual(None, branch.get_parent())
919
def test_set_parent_something(self):
920
branch = self.make_branch('base', format="1.9")
921
request = smart.branch.SmartServerBranchRequestSetParentLocation(
922
self.get_transport())
923
branch_token = branch.lock_write()
924
repo_token = branch.repository.lock_write()
926
response = request.execute('base', branch_token, repo_token,
929
branch.repository.unlock()
931
self.assertEqual(SuccessfulSmartServerResponse(()), response)
932
self.assertEqual('http://bar/', branch.get_parent())
935
class TestSmartServerBranchRequestGetTagsBytes(tests.TestCaseWithMemoryTransport):
936
# Only called when the branch format and tags match [yay factory
937
# methods] so only need to test straight forward cases.
939
def test_get_bytes(self):
940
base_branch = self.make_branch('base')
941
request = smart.branch.SmartServerBranchGetTagsBytes(
942
self.get_transport())
943
response = request.execute('base')
945
SuccessfulSmartServerResponse(('',)), response)
948
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
950
def test_get_stacked_on_url(self):
951
base_branch = self.make_branch('base', format='1.6')
952
stacked_branch = self.make_branch('stacked', format='1.6')
953
# typically should be relative
954
stacked_branch.set_stacked_on_url('../base')
955
request = smart.branch.SmartServerBranchRequestGetStackedOnURL(
956
self.get_transport())
957
response = request.execute('stacked')
959
SmartServerResponse(('ok', '../base')),
963
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
966
tests.TestCaseWithMemoryTransport.setUp(self)
968
def test_lock_write_on_unlocked_branch(self):
969
backing = self.get_transport()
970
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
971
branch = self.make_branch('.', format='knit')
972
repository = branch.repository
973
response = request.execute('')
974
branch_nonce = branch.control_files._lock.peek().get('nonce')
975
repository_nonce = repository.control_files._lock.peek().get('nonce')
977
SmartServerResponse(('ok', branch_nonce, repository_nonce)),
979
# The branch (and associated repository) is now locked. Verify that
980
# with a new branch object.
981
new_branch = repository.bzrdir.open_branch()
982
self.assertRaises(errors.LockContention, new_branch.lock_write)
984
request = smart.branch.SmartServerBranchRequestUnlock(backing)
985
response = request.execute('', branch_nonce, repository_nonce)
987
def test_lock_write_on_locked_branch(self):
988
backing = self.get_transport()
989
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
990
branch = self.make_branch('.')
991
branch_token = branch.lock_write()
992
branch.leave_lock_in_place()
994
response = request.execute('')
996
SmartServerResponse(('LockContention',)), response)
998
branch.lock_write(branch_token)
999
branch.dont_leave_lock_in_place()
1002
def test_lock_write_with_tokens_on_locked_branch(self):
1003
backing = self.get_transport()
1004
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1005
branch = self.make_branch('.', format='knit')
1006
branch_token = branch.lock_write()
1007
repo_token = branch.repository.lock_write()
1008
branch.repository.unlock()
1009
branch.leave_lock_in_place()
1010
branch.repository.leave_lock_in_place()
1012
response = request.execute('',
1013
branch_token, repo_token)
1015
SmartServerResponse(('ok', branch_token, repo_token)), response)
1017
branch.repository.lock_write(repo_token)
1018
branch.repository.dont_leave_lock_in_place()
1019
branch.repository.unlock()
1020
branch.lock_write(branch_token)
1021
branch.dont_leave_lock_in_place()
1024
def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
1025
backing = self.get_transport()
1026
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1027
branch = self.make_branch('.', format='knit')
1028
branch_token = branch.lock_write()
1029
repo_token = branch.repository.lock_write()
1030
branch.repository.unlock()
1031
branch.leave_lock_in_place()
1032
branch.repository.leave_lock_in_place()
1034
response = request.execute('',
1035
branch_token+'xxx', repo_token)
1037
SmartServerResponse(('TokenMismatch',)), response)
1039
branch.repository.lock_write(repo_token)
1040
branch.repository.dont_leave_lock_in_place()
1041
branch.repository.unlock()
1042
branch.lock_write(branch_token)
1043
branch.dont_leave_lock_in_place()
1046
def test_lock_write_on_locked_repo(self):
1047
backing = self.get_transport()
1048
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1049
branch = self.make_branch('.', format='knit')
1050
repo = branch.repository
1051
repo_token = repo.lock_write()
1052
repo.leave_lock_in_place()
1054
response = request.execute('')
1056
SmartServerResponse(('LockContention',)), response)
1058
repo.lock_write(repo_token)
1059
repo.dont_leave_lock_in_place()
1062
def test_lock_write_on_readonly_transport(self):
1063
backing = self.get_readonly_transport()
1064
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1065
branch = self.make_branch('.')
1066
root = self.get_transport().clone('/')
1067
path = urlutils.relative_url(root.base, self.get_transport().base)
1068
response = request.execute(path)
1069
error_name, lock_str, why_str = response.args
1070
self.assertFalse(response.is_successful())
1071
self.assertEqual('LockFailed', error_name)
1074
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
1077
tests.TestCaseWithMemoryTransport.setUp(self)
1079
def test_unlock_on_locked_branch_and_repo(self):
1080
backing = self.get_transport()
1081
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1082
branch = self.make_branch('.', format='knit')
1084
branch_token = branch.lock_write()
1085
repo_token = branch.repository.lock_write()
1086
branch.repository.unlock()
1087
# Unlock the branch (and repo) object, leaving the physical locks
1089
branch.leave_lock_in_place()
1090
branch.repository.leave_lock_in_place()
1092
response = request.execute('',
1093
branch_token, repo_token)
1095
SmartServerResponse(('ok',)), response)
1096
# The branch is now unlocked. Verify that with a new branch
1098
new_branch = branch.bzrdir.open_branch()
1099
new_branch.lock_write()
1102
def test_unlock_on_unlocked_branch_unlocked_repo(self):
1103
backing = self.get_transport()
1104
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1105
branch = self.make_branch('.', format='knit')
1106
response = request.execute(
1107
'', 'branch token', 'repo token')
1109
SmartServerResponse(('TokenMismatch',)), response)
1111
def test_unlock_on_unlocked_branch_locked_repo(self):
1112
backing = self.get_transport()
1113
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1114
branch = self.make_branch('.', format='knit')
1115
# Lock the repository.
1116
repo_token = branch.repository.lock_write()
1117
branch.repository.leave_lock_in_place()
1118
branch.repository.unlock()
1119
# Issue branch lock_write request on the unlocked branch (with locked
1121
response = request.execute(
1122
'', 'branch token', repo_token)
1124
SmartServerResponse(('TokenMismatch',)), response)
1126
branch.repository.lock_write(repo_token)
1127
branch.repository.dont_leave_lock_in_place()
1128
branch.repository.unlock()
1131
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
1133
def test_no_repository(self):
1134
"""Raise NoRepositoryPresent when there is a bzrdir and no repo."""
1135
# we test this using a shared repository above the named path,
1136
# thus checking the right search logic is used - that is, that
1137
# its the exact path being looked at and the server is not
1139
backing = self.get_transport()
1140
request = smart.repository.SmartServerRepositoryRequest(backing)
1141
self.make_repository('.', shared=True)
1142
self.make_bzrdir('subdir')
1143
self.assertRaises(errors.NoRepositoryPresent,
1144
request.execute, 'subdir')
1147
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
1149
def test_trivial_bzipped(self):
1150
# This tests that the wire encoding is actually bzipped
1151
backing = self.get_transport()
1152
request = smart.repository.SmartServerRepositoryGetParentMap(backing)
1153
tree = self.make_branch_and_memory_tree('.')
1155
self.assertEqual(None,
1156
request.execute('', 'missing-id'))
1157
# Note that it returns a body that is bzipped.
1159
SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
1160
request.do_body('\n\n0\n'))
1162
def test_trivial_include_missing(self):
1163
backing = self.get_transport()
1164
request = smart.repository.SmartServerRepositoryGetParentMap(backing)
1165
tree = self.make_branch_and_memory_tree('.')
1167
self.assertEqual(None,
1168
request.execute('', 'missing-id', 'include-missing:'))
1170
SuccessfulSmartServerResponse(('ok', ),
1171
bz2.compress('missing:missing-id')),
1172
request.do_body('\n\n0\n'))
1175
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
1177
def test_none_argument(self):
1178
backing = self.get_transport()
1179
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1180
tree = self.make_branch_and_memory_tree('.')
1183
r1 = tree.commit('1st commit')
1184
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1187
# the lines of revision_id->revision_parent_list has no guaranteed
1188
# order coming out of a dict, so sort both our test and response
1189
lines = sorted([' '.join([r2, r1]), r1])
1190
response = request.execute('', '')
1191
response.body = '\n'.join(sorted(response.body.split('\n')))
1194
SmartServerResponse(('ok', ), '\n'.join(lines)), response)
1196
def test_specific_revision_argument(self):
1197
backing = self.get_transport()
1198
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1199
tree = self.make_branch_and_memory_tree('.')
1202
rev_id_utf8 = u'\xc9'.encode('utf-8')
1203
r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
1204
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1207
self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
1208
request.execute('', rev_id_utf8))
1210
def test_no_such_revision(self):
1211
backing = self.get_transport()
1212
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1213
tree = self.make_branch_and_memory_tree('.')
1216
r1 = tree.commit('1st commit')
1219
# Note that it still returns body (of zero bytes).
1221
SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
1222
request.execute('', 'missingrevision'))
1225
class TestSmartServerRepositoryGetRevIdForRevno(tests.TestCaseWithMemoryTransport):
1227
def test_revno_found(self):
1228
backing = self.get_transport()
1229
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1230
tree = self.make_branch_and_memory_tree('.')
1233
rev1_id_utf8 = u'\xc8'.encode('utf-8')
1234
rev2_id_utf8 = u'\xc9'.encode('utf-8')
1235
tree.commit('1st commit', rev_id=rev1_id_utf8)
1236
tree.commit('2nd commit', rev_id=rev2_id_utf8)
1239
self.assertEqual(SmartServerResponse(('ok', rev1_id_utf8)),
1240
request.execute('', 1, (2, rev2_id_utf8)))
1242
def test_known_revid_missing(self):
1243
backing = self.get_transport()
1244
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1245
repo = self.make_repository('.')
1247
FailedSmartServerResponse(('nosuchrevision', 'ghost')),
1248
request.execute('', 1, (2, 'ghost')))
1250
def test_history_incomplete(self):
1251
backing = self.get_transport()
1252
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1253
parent = self.make_branch_and_memory_tree('parent', format='1.9')
1255
parent.add([''], ['TREE_ROOT'])
1256
r1 = parent.commit(message='first commit')
1257
r2 = parent.commit(message='second commit')
1259
local = self.make_branch_and_memory_tree('local', format='1.9')
1260
local.branch.pull(parent.branch)
1261
local.set_parent_ids([r2])
1262
r3 = local.commit(message='local commit')
1263
local.branch.create_clone_on_transport(
1264
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
1266
SmartServerResponse(('history-incomplete', 2, r2)),
1267
request.execute('stacked', 1, (3, r3)))
1270
class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport):
1272
def make_two_commit_repo(self):
1273
tree = self.make_branch_and_memory_tree('.')
1276
r1 = tree.commit('1st commit')
1277
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1279
repo = tree.branch.repository
1282
def test_ancestry_of(self):
1283
"""The search argument may be a 'ancestry-of' some heads'."""
1284
backing = self.get_transport()
1285
request = smart.repository.SmartServerRepositoryGetStream(backing)
1286
repo, r1, r2 = self.make_two_commit_repo()
1287
fetch_spec = ['ancestry-of', r2]
1288
lines = '\n'.join(fetch_spec)
1289
request.execute('', repo._format.network_name())
1290
response = request.do_body(lines)
1291
self.assertEqual(('ok',), response.args)
1292
stream_bytes = ''.join(response.body_stream)
1293
self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
1295
def test_search(self):
1296
"""The search argument may be a 'search' of some explicit keys."""
1297
backing = self.get_transport()
1298
request = smart.repository.SmartServerRepositoryGetStream(backing)
1299
repo, r1, r2 = self.make_two_commit_repo()
1300
fetch_spec = ['search', '%s %s' % (r1, r2), 'null:', '2']
1301
lines = '\n'.join(fetch_spec)
1302
request.execute('', repo._format.network_name())
1303
response = request.do_body(lines)
1304
self.assertEqual(('ok',), response.args)
1305
stream_bytes = ''.join(response.body_stream)
1306
self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
1309
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
1311
def test_missing_revision(self):
1312
"""For a missing revision, ('no', ) is returned."""
1313
backing = self.get_transport()
1314
request = smart.repository.SmartServerRequestHasRevision(backing)
1315
self.make_repository('.')
1316
self.assertEqual(SmartServerResponse(('no', )),
1317
request.execute('', 'revid'))
1319
def test_present_revision(self):
1320
"""For a present revision, ('yes', ) is returned."""
1321
backing = self.get_transport()
1322
request = smart.repository.SmartServerRequestHasRevision(backing)
1323
tree = self.make_branch_and_memory_tree('.')
1326
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1327
r1 = tree.commit('a commit', rev_id=rev_id_utf8)
1329
self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
1330
self.assertEqual(SmartServerResponse(('yes', )),
1331
request.execute('', rev_id_utf8))
1334
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
1336
def test_empty_revid(self):
1337
"""With an empty revid, we get only size an number and revisions"""
1338
backing = self.get_transport()
1339
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1340
repository = self.make_repository('.')
1341
stats = repository.gather_stats()
1342
expected_body = 'revisions: 0\n'
1343
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1344
request.execute('', '', 'no'))
1346
def test_revid_with_committers(self):
1347
"""For a revid we get more infos."""
1348
backing = self.get_transport()
1349
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1350
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1351
tree = self.make_branch_and_memory_tree('.')
1354
# Let's build a predictable result
1355
tree.commit('a commit', timestamp=123456.2, timezone=3600)
1356
tree.commit('a commit', timestamp=654321.4, timezone=0,
1360
stats = tree.branch.repository.gather_stats()
1361
expected_body = ('firstrev: 123456.200 3600\n'
1362
'latestrev: 654321.400 0\n'
1364
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1368
def test_not_empty_repository_with_committers(self):
1369
"""For a revid and requesting committers we get the whole thing."""
1370
backing = self.get_transport()
1371
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1372
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1373
tree = self.make_branch_and_memory_tree('.')
1376
# Let's build a predictable result
1377
tree.commit('a commit', timestamp=123456.2, timezone=3600,
1379
tree.commit('a commit', timestamp=654321.4, timezone=0,
1380
committer='bar', rev_id=rev_id_utf8)
1382
stats = tree.branch.repository.gather_stats()
1384
expected_body = ('committers: 2\n'
1385
'firstrev: 123456.200 3600\n'
1386
'latestrev: 654321.400 0\n'
1388
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1390
rev_id_utf8, 'yes'))
1393
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
1395
def test_is_shared(self):
1396
"""For a shared repository, ('yes', ) is returned."""
1397
backing = self.get_transport()
1398
request = smart.repository.SmartServerRepositoryIsShared(backing)
1399
self.make_repository('.', shared=True)
1400
self.assertEqual(SmartServerResponse(('yes', )),
1401
request.execute('', ))
1403
def test_is_not_shared(self):
1404
"""For a shared repository, ('no', ) is returned."""
1405
backing = self.get_transport()
1406
request = smart.repository.SmartServerRepositoryIsShared(backing)
1407
self.make_repository('.', shared=False)
1408
self.assertEqual(SmartServerResponse(('no', )),
1409
request.execute('', ))
1412
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
1414
def test_lock_write_on_unlocked_repo(self):
1415
backing = self.get_transport()
1416
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1417
repository = self.make_repository('.', format='knit')
1418
response = request.execute('')
1419
nonce = repository.control_files._lock.peek().get('nonce')
1420
self.assertEqual(SmartServerResponse(('ok', nonce)), response)
1421
# The repository is now locked. Verify that with a new repository
1423
new_repo = repository.bzrdir.open_repository()
1424
self.assertRaises(errors.LockContention, new_repo.lock_write)
1426
request = smart.repository.SmartServerRepositoryUnlock(backing)
1427
response = request.execute('', nonce)
1429
def test_lock_write_on_locked_repo(self):
1430
backing = self.get_transport()
1431
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1432
repository = self.make_repository('.', format='knit')
1433
repo_token = repository.lock_write()
1434
repository.leave_lock_in_place()
1436
response = request.execute('')
1438
SmartServerResponse(('LockContention',)), response)
1440
repository.lock_write(repo_token)
1441
repository.dont_leave_lock_in_place()
1444
def test_lock_write_on_readonly_transport(self):
1445
backing = self.get_readonly_transport()
1446
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1447
repository = self.make_repository('.', format='knit')
1448
response = request.execute('')
1449
self.assertFalse(response.is_successful())
1450
self.assertEqual('LockFailed', response.args[0])
1453
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
1455
def make_empty_byte_stream(self, repo):
1456
byte_stream = smart.repository._stream_to_byte_stream([], repo._format)
1457
return ''.join(byte_stream)
1460
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
1462
def test_insert_stream_empty(self):
1463
backing = self.get_transport()
1464
request = smart.repository.SmartServerRepositoryInsertStream(backing)
1465
repository = self.make_repository('.')
1466
response = request.execute('', '')
1467
self.assertEqual(None, response)
1468
response = request.do_chunk(self.make_empty_byte_stream(repository))
1469
self.assertEqual(None, response)
1470
response = request.do_end()
1471
self.assertEqual(SmartServerResponse(('ok', )), response)
1474
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
1476
def test_insert_stream_empty(self):
1477
backing = self.get_transport()
1478
request = smart.repository.SmartServerRepositoryInsertStreamLocked(
1480
repository = self.make_repository('.', format='knit')
1481
lock_token = repository.lock_write()
1482
response = request.execute('', '', lock_token)
1483
self.assertEqual(None, response)
1484
response = request.do_chunk(self.make_empty_byte_stream(repository))
1485
self.assertEqual(None, response)
1486
response = request.do_end()
1487
self.assertEqual(SmartServerResponse(('ok', )), response)
1490
def test_insert_stream_with_wrong_lock_token(self):
1491
backing = self.get_transport()
1492
request = smart.repository.SmartServerRepositoryInsertStreamLocked(
1494
repository = self.make_repository('.', format='knit')
1495
lock_token = repository.lock_write()
1497
errors.TokenMismatch, request.execute, '', '', 'wrong-token')
1501
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
1504
tests.TestCaseWithMemoryTransport.setUp(self)
1506
def test_unlock_on_locked_repo(self):
1507
backing = self.get_transport()
1508
request = smart.repository.SmartServerRepositoryUnlock(backing)
1509
repository = self.make_repository('.', format='knit')
1510
token = repository.lock_write()
1511
repository.leave_lock_in_place()
1513
response = request.execute('', token)
1515
SmartServerResponse(('ok',)), response)
1516
# The repository is now unlocked. Verify that with a new repository
1518
new_repo = repository.bzrdir.open_repository()
1519
new_repo.lock_write()
1522
def test_unlock_on_unlocked_repo(self):
1523
backing = self.get_transport()
1524
request = smart.repository.SmartServerRepositoryUnlock(backing)
1525
repository = self.make_repository('.', format='knit')
1526
response = request.execute('', 'some token')
1528
SmartServerResponse(('TokenMismatch',)), response)
1531
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
1533
def test_is_readonly_no(self):
1534
backing = self.get_transport()
1535
request = smart.request.SmartServerIsReadonly(backing)
1536
response = request.execute()
1538
SmartServerResponse(('no',)), response)
1540
def test_is_readonly_yes(self):
1541
backing = self.get_readonly_transport()
1542
request = smart.request.SmartServerIsReadonly(backing)
1543
response = request.execute()
1545
SmartServerResponse(('yes',)), response)
1548
class TestSmartServerRepositorySetMakeWorkingTrees(tests.TestCaseWithMemoryTransport):
1550
def test_set_false(self):
1551
backing = self.get_transport()
1552
repo = self.make_repository('.', shared=True)
1553
repo.set_make_working_trees(True)
1554
request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
1555
request = request_class(backing)
1556
self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
1557
request.execute('', 'False'))
1558
repo = repo.bzrdir.open_repository()
1559
self.assertFalse(repo.make_working_trees())
1561
def test_set_true(self):
1562
backing = self.get_transport()
1563
repo = self.make_repository('.', shared=True)
1564
repo.set_make_working_trees(False)
1565
request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
1566
request = request_class(backing)
1567
self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
1568
request.execute('', 'True'))
1569
repo = repo.bzrdir.open_repository()
1570
self.assertTrue(repo.make_working_trees())
1573
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
1575
def make_repo_needing_autopacking(self, path='.'):
1576
# Make a repo in need of autopacking.
1577
tree = self.make_branch_and_tree('.', format='pack-0.92')
1578
repo = tree.branch.repository
1579
# monkey-patch the pack collection to disable autopacking
1580
repo._pack_collection._max_pack_count = lambda count: count
1582
tree.commit('commit %s' % x)
1583
self.assertEqual(10, len(repo._pack_collection.names()))
1584
del repo._pack_collection._max_pack_count
1587
def test_autopack_needed(self):
1588
repo = self.make_repo_needing_autopacking()
1590
self.addCleanup(repo.unlock)
1591
backing = self.get_transport()
1592
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1594
response = request.execute('')
1595
self.assertEqual(SmartServerResponse(('ok',)), response)
1596
repo._pack_collection.reload_pack_names()
1597
self.assertEqual(1, len(repo._pack_collection.names()))
1599
def test_autopack_not_needed(self):
1600
tree = self.make_branch_and_tree('.', format='pack-0.92')
1601
repo = tree.branch.repository
1603
self.addCleanup(repo.unlock)
1605
tree.commit('commit %s' % x)
1606
backing = self.get_transport()
1607
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1609
response = request.execute('')
1610
self.assertEqual(SmartServerResponse(('ok',)), response)
1611
repo._pack_collection.reload_pack_names()
1612
self.assertEqual(9, len(repo._pack_collection.names()))
1614
def test_autopack_on_nonpack_format(self):
1615
"""A request to autopack a non-pack repo is a no-op."""
1616
repo = self.make_repository('.', format='knit')
1617
backing = self.get_transport()
1618
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1620
response = request.execute('')
1621
self.assertEqual(SmartServerResponse(('ok',)), response)
1624
class TestHandlers(tests.TestCase):
1625
"""Tests for the request.request_handlers object."""
1627
def test_all_registrations_exist(self):
1628
"""All registered request_handlers can be found."""
1629
# If there's a typo in a register_lazy call, this loop will fail with
1630
# an AttributeError.
1631
for key, item in smart.request.request_handlers.iteritems():
1634
def assertHandlerEqual(self, verb, handler):
1635
self.assertEqual(smart.request.request_handlers.get(verb), handler)
1637
def test_registered_methods(self):
1638
"""Test that known methods are registered to the correct object."""
1639
self.assertHandlerEqual('Branch.get_config_file',
1640
smart.branch.SmartServerBranchGetConfigFile)
1641
self.assertHandlerEqual('Branch.get_parent',
1642
smart.branch.SmartServerBranchGetParent)
1643
self.assertHandlerEqual('Branch.get_tags_bytes',
1644
smart.branch.SmartServerBranchGetTagsBytes)
1645
self.assertHandlerEqual('Branch.lock_write',
1646
smart.branch.SmartServerBranchRequestLockWrite)
1647
self.assertHandlerEqual('Branch.last_revision_info',
1648
smart.branch.SmartServerBranchRequestLastRevisionInfo)
1649
self.assertHandlerEqual('Branch.revision_history',
1650
smart.branch.SmartServerRequestRevisionHistory)
1651
self.assertHandlerEqual('Branch.set_config_option',
1652
smart.branch.SmartServerBranchRequestSetConfigOption)
1653
self.assertHandlerEqual('Branch.set_last_revision',
1654
smart.branch.SmartServerBranchRequestSetLastRevision)
1655
self.assertHandlerEqual('Branch.set_last_revision_info',
1656
smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
1657
self.assertHandlerEqual('Branch.set_last_revision_ex',
1658
smart.branch.SmartServerBranchRequestSetLastRevisionEx)
1659
self.assertHandlerEqual('Branch.set_parent_location',
1660
smart.branch.SmartServerBranchRequestSetParentLocation)
1661
self.assertHandlerEqual('Branch.unlock',
1662
smart.branch.SmartServerBranchRequestUnlock)
1663
self.assertHandlerEqual('BzrDir.find_repository',
1664
smart.bzrdir.SmartServerRequestFindRepositoryV1)
1665
self.assertHandlerEqual('BzrDir.find_repositoryV2',
1666
smart.bzrdir.SmartServerRequestFindRepositoryV2)
1667
self.assertHandlerEqual('BzrDirFormat.initialize',
1668
smart.bzrdir.SmartServerRequestInitializeBzrDir)
1669
self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16',
1670
smart.bzrdir.SmartServerRequestBzrDirInitializeEx)
1671
self.assertHandlerEqual('BzrDir.cloning_metadir',
1672
smart.bzrdir.SmartServerBzrDirRequestCloningMetaDir)
1673
self.assertHandlerEqual('BzrDir.get_config_file',
1674
smart.bzrdir.SmartServerBzrDirRequestConfigFile)
1675
self.assertHandlerEqual('BzrDir.open_branch',
1676
smart.bzrdir.SmartServerRequestOpenBranch)
1677
self.assertHandlerEqual('BzrDir.open_branchV2',
1678
smart.bzrdir.SmartServerRequestOpenBranchV2)
1679
self.assertHandlerEqual('PackRepository.autopack',
1680
smart.packrepository.SmartServerPackRepositoryAutopack)
1681
self.assertHandlerEqual('Repository.gather_stats',
1682
smart.repository.SmartServerRepositoryGatherStats)
1683
self.assertHandlerEqual('Repository.get_parent_map',
1684
smart.repository.SmartServerRepositoryGetParentMap)
1685
self.assertHandlerEqual('Repository.get_rev_id_for_revno',
1686
smart.repository.SmartServerRepositoryGetRevIdForRevno)
1687
self.assertHandlerEqual('Repository.get_revision_graph',
1688
smart.repository.SmartServerRepositoryGetRevisionGraph)
1689
self.assertHandlerEqual('Repository.get_stream',
1690
smart.repository.SmartServerRepositoryGetStream)
1691
self.assertHandlerEqual('Repository.has_revision',
1692
smart.repository.SmartServerRequestHasRevision)
1693
self.assertHandlerEqual('Repository.insert_stream',
1694
smart.repository.SmartServerRepositoryInsertStream)
1695
self.assertHandlerEqual('Repository.insert_stream_locked',
1696
smart.repository.SmartServerRepositoryInsertStreamLocked)
1697
self.assertHandlerEqual('Repository.is_shared',
1698
smart.repository.SmartServerRepositoryIsShared)
1699
self.assertHandlerEqual('Repository.lock_write',
1700
smart.repository.SmartServerRepositoryLockWrite)
1701
self.assertHandlerEqual('Repository.tarball',
1702
smart.repository.SmartServerRepositoryTarball)
1703
self.assertHandlerEqual('Repository.unlock',
1704
smart.repository.SmartServerRepositoryUnlock)
1705
self.assertHandlerEqual('Transport.is_readonly',
1706
smart.request.SmartServerIsReadonly)