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, local, memory
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
self.vfs_transport_factory = memory.MemoryServer
85
tests.TestCaseWithTransport.setUp(self)
86
self._chroot_server = None
88
def get_transport(self, relpath=None):
89
if self._chroot_server is None:
90
backing_transport = tests.TestCaseWithTransport.get_transport(self)
91
self._chroot_server = chroot.ChrootServer(backing_transport)
92
self.start_server(self._chroot_server)
93
t = get_transport(self._chroot_server.get_url())
94
if relpath is not None:
99
class TestCaseWithSmartMedium(tests.TestCaseWithMemoryTransport):
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 TestSmartServerRequestOpenBzrDir(tests.TestCaseWithMemoryTransport):
419
def test_no_directory(self):
420
backing = self.get_transport()
421
request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing)
422
self.assertEqual(SmartServerResponse(('no', )),
423
request.execute('does-not-exist'))
425
def test_empty_directory(self):
426
backing = self.get_transport()
427
backing.mkdir('empty')
428
request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing)
429
self.assertEqual(SmartServerResponse(('no', )),
430
request.execute('empty'))
432
def test_outside_root_client_path(self):
433
backing = self.get_transport()
434
request = smart.bzrdir.SmartServerRequestOpenBzrDir(backing,
435
root_client_path='root')
436
self.assertEqual(SmartServerResponse(('no', )),
437
request.execute('not-root'))
440
class TestSmartServerRequestOpenBzrDir_2_1(tests.TestCaseWithMemoryTransport):
442
def test_no_directory(self):
443
backing = self.get_transport()
444
request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
445
self.assertEqual(SmartServerResponse(('no', )),
446
request.execute('does-not-exist'))
448
def test_empty_directory(self):
449
backing = self.get_transport()
450
backing.mkdir('empty')
451
request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
452
self.assertEqual(SmartServerResponse(('no', )),
453
request.execute('empty'))
455
def test_present_without_workingtree(self):
456
backing = self.get_transport()
457
request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
458
self.make_bzrdir('.')
459
self.assertEqual(SmartServerResponse(('yes', 'no')),
462
def test_outside_root_client_path(self):
463
backing = self.get_transport()
464
request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing,
465
root_client_path='root')
466
self.assertEqual(SmartServerResponse(('no',)),
467
request.execute('not-root'))
470
class TestSmartServerRequestOpenBzrDir_2_1_disk(TestCaseWithChrootedTransport):
472
def test_present_with_workingtree(self):
473
self.vfs_transport_factory = local.LocalURLServer
474
backing = self.get_transport()
475
request = smart.bzrdir.SmartServerRequestOpenBzrDir_2_1(backing)
476
bd = self.make_bzrdir('.')
477
bd.create_repository()
479
bd.create_workingtree()
480
self.assertEqual(SmartServerResponse(('yes', 'yes')),
484
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
486
def test_no_branch(self):
487
"""When there is no branch, ('nobranch', ) is returned."""
488
backing = self.get_transport()
489
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
490
self.make_bzrdir('.')
491
self.assertEqual(SmartServerResponse(('nobranch', )),
494
def test_branch(self):
495
"""When there is a branch, 'ok' is returned."""
496
backing = self.get_transport()
497
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
498
self.make_branch('.')
499
self.assertEqual(SmartServerResponse(('ok', '')),
502
def test_branch_reference(self):
503
"""When there is a branch reference, the reference URL is returned."""
504
self.vfs_transport_factory = local.LocalURLServer
505
backing = self.get_transport()
506
request = smart.bzrdir.SmartServerRequestOpenBranch(backing)
507
branch = self.make_branch('branch')
508
checkout = branch.create_checkout('reference',lightweight=True)
509
reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
510
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
511
self.assertEqual(SmartServerResponse(('ok', reference_url)),
512
request.execute('reference'))
515
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
517
def test_no_branch(self):
518
"""When there is no branch, ('nobranch', ) is returned."""
519
backing = self.get_transport()
520
self.make_bzrdir('.')
521
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
522
self.assertEqual(SmartServerResponse(('nobranch', )),
525
def test_branch(self):
526
"""When there is a branch, 'ok' is returned."""
527
backing = self.get_transport()
528
expected = self.make_branch('.')._format.network_name()
529
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
530
self.assertEqual(SuccessfulSmartServerResponse(('branch', expected)),
533
def test_branch_reference(self):
534
"""When there is a branch reference, the reference URL is returned."""
535
self.vfs_transport_factory = local.LocalURLServer
536
backing = self.get_transport()
537
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
538
branch = self.make_branch('branch')
539
checkout = branch.create_checkout('reference',lightweight=True)
540
reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir)
541
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
542
self.assertEqual(SuccessfulSmartServerResponse(('ref', reference_url)),
543
request.execute('reference'))
545
def test_stacked_branch(self):
546
"""Opening a stacked branch does not open the stacked-on branch."""
547
trunk = self.make_branch('trunk')
548
feature = self.make_branch('feature')
549
feature.set_stacked_on_url(trunk.base)
551
Branch.hooks.install_named_hook('open', opened_branches.append, None)
552
backing = self.get_transport()
553
request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing)
556
response = request.execute('feature')
558
request.teardown_jail()
559
expected_format = feature._format.network_name()
561
SuccessfulSmartServerResponse(('branch', expected_format)),
563
self.assertLength(1, opened_branches)
566
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
568
def test_empty(self):
569
"""For an empty branch, the body is empty."""
570
backing = self.get_transport()
571
request = smart.branch.SmartServerRequestRevisionHistory(backing)
572
self.make_branch('.')
573
self.assertEqual(SmartServerResponse(('ok', ), ''),
576
def test_not_empty(self):
577
"""For a non-empty branch, the body is empty."""
578
backing = self.get_transport()
579
request = smart.branch.SmartServerRequestRevisionHistory(backing)
580
tree = self.make_branch_and_memory_tree('.')
583
r1 = tree.commit('1st commit')
584
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
587
SmartServerResponse(('ok', ), ('\x00'.join([r1, r2]))),
591
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
593
def test_no_branch(self):
594
"""When there is a bzrdir and no branch, NotBranchError is raised."""
595
backing = self.get_transport()
596
request = smart.branch.SmartServerBranchRequest(backing)
597
self.make_bzrdir('.')
598
self.assertRaises(errors.NotBranchError,
601
def test_branch_reference(self):
602
"""When there is a branch reference, NotBranchError is raised."""
603
backing = self.get_transport()
604
request = smart.branch.SmartServerBranchRequest(backing)
605
branch = self.make_branch('branch')
606
checkout = branch.create_checkout('reference',lightweight=True)
607
self.assertRaises(errors.NotBranchError,
608
request.execute, 'checkout')
611
class TestSmartServerBranchRequestLastRevisionInfo(tests.TestCaseWithMemoryTransport):
613
def test_empty(self):
614
"""For an empty branch, the result is ('ok', '0', 'null:')."""
615
backing = self.get_transport()
616
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
617
self.make_branch('.')
618
self.assertEqual(SmartServerResponse(('ok', '0', 'null:')),
621
def test_not_empty(self):
622
"""For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
623
backing = self.get_transport()
624
request = smart.branch.SmartServerBranchRequestLastRevisionInfo(backing)
625
tree = self.make_branch_and_memory_tree('.')
628
rev_id_utf8 = u'\xc8'.encode('utf-8')
629
r1 = tree.commit('1st commit')
630
r2 = tree.commit('2nd commit', rev_id=rev_id_utf8)
633
SmartServerResponse(('ok', '2', rev_id_utf8)),
637
class TestSmartServerBranchRequestGetConfigFile(tests.TestCaseWithMemoryTransport):
639
def test_default(self):
640
"""With no file, we get empty content."""
641
backing = self.get_transport()
642
request = smart.branch.SmartServerBranchGetConfigFile(backing)
643
branch = self.make_branch('.')
644
# there should be no file by default
646
self.assertEqual(SmartServerResponse(('ok', ), content),
649
def test_with_content(self):
650
# SmartServerBranchGetConfigFile should return the content from
651
# branch.control_files.get('branch.conf') for now - in the future it may
652
# perform more complex processing.
653
backing = self.get_transport()
654
request = smart.branch.SmartServerBranchGetConfigFile(backing)
655
branch = self.make_branch('.')
656
branch._transport.put_bytes('branch.conf', 'foo bar baz')
657
self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
661
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
663
def get_lock_tokens(self, branch):
664
branch_token = branch.lock_write()
665
repo_token = branch.repository.lock_write()
666
branch.repository.unlock()
667
return branch_token, repo_token
670
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
672
def test_value_name(self):
673
branch = self.make_branch('.')
674
request = smart.branch.SmartServerBranchRequestSetConfigOption(
675
branch.bzrdir.root_transport)
676
branch_token, repo_token = self.get_lock_tokens(branch)
677
config = branch._get_config()
678
result = request.execute('', branch_token, repo_token, 'bar', 'foo',
680
self.assertEqual(SuccessfulSmartServerResponse(()), result)
681
self.assertEqual('bar', config.get_option('foo'))
685
def test_value_name_section(self):
686
branch = self.make_branch('.')
687
request = smart.branch.SmartServerBranchRequestSetConfigOption(
688
branch.bzrdir.root_transport)
689
branch_token, repo_token = self.get_lock_tokens(branch)
690
config = branch._get_config()
691
result = request.execute('', branch_token, repo_token, 'bar', 'foo',
693
self.assertEqual(SuccessfulSmartServerResponse(()), result)
694
self.assertEqual('bar', config.get_option('foo', 'gam'))
699
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
700
# Only called when the branch format and tags match [yay factory
701
# methods] so only need to test straight forward cases.
703
def test_set_bytes(self):
704
base_branch = self.make_branch('base')
705
tag_bytes = base_branch._get_tags_bytes()
706
# get_lock_tokens takes out a lock.
707
branch_token, repo_token = self.get_lock_tokens(base_branch)
708
request = smart.branch.SmartServerBranchSetTagsBytes(
709
self.get_transport())
710
response = request.execute('base', branch_token, repo_token)
711
self.assertEqual(None, response)
712
response = request.do_chunk(tag_bytes)
713
self.assertEqual(None, response)
714
response = request.do_end()
716
SuccessfulSmartServerResponse(()), response)
719
def test_lock_failed(self):
720
base_branch = self.make_branch('base')
721
base_branch.lock_write()
722
tag_bytes = base_branch._get_tags_bytes()
723
request = smart.branch.SmartServerBranchSetTagsBytes(
724
self.get_transport())
725
self.assertRaises(errors.TokenMismatch, request.execute,
726
'base', 'wrong token', 'wrong token')
727
# The request handler will keep processing the message parts, so even
728
# if the request fails immediately do_chunk and do_end are still
730
request.do_chunk(tag_bytes)
736
class SetLastRevisionTestBase(TestLockedBranch):
737
"""Base test case for verbs that implement set_last_revision."""
740
tests.TestCaseWithMemoryTransport.setUp(self)
741
backing_transport = self.get_transport()
742
self.request = self.request_class(backing_transport)
743
self.tree = self.make_branch_and_memory_tree('.')
745
def lock_branch(self):
746
return self.get_lock_tokens(self.tree.branch)
748
def unlock_branch(self):
749
self.tree.branch.unlock()
751
def set_last_revision(self, revision_id, revno):
752
branch_token, repo_token = self.lock_branch()
753
response = self._set_last_revision(
754
revision_id, revno, branch_token, repo_token)
758
def assertRequestSucceeds(self, revision_id, revno):
759
response = self.set_last_revision(revision_id, revno)
760
self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
763
class TestSetLastRevisionVerbMixin(object):
764
"""Mixin test case for verbs that implement set_last_revision."""
766
def test_set_null_to_null(self):
767
"""An empty branch can have its last revision set to 'null:'."""
768
self.assertRequestSucceeds('null:', 0)
770
def test_NoSuchRevision(self):
771
"""If the revision_id is not present, the verb returns NoSuchRevision.
773
revision_id = 'non-existent revision'
775
FailedSmartServerResponse(('NoSuchRevision', revision_id)),
776
self.set_last_revision(revision_id, 1))
778
def make_tree_with_two_commits(self):
779
self.tree.lock_write()
781
rev_id_utf8 = u'\xc8'.encode('utf-8')
782
r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
783
r2 = self.tree.commit('2nd commit', rev_id='rev-2')
786
def test_branch_last_revision_info_is_updated(self):
787
"""A branch's tip can be set to a revision that is present in its
790
# Make a branch with an empty revision history, but two revisions in
792
self.make_tree_with_two_commits()
793
rev_id_utf8 = u'\xc8'.encode('utf-8')
794
self.tree.branch.set_revision_history([])
796
(0, 'null:'), self.tree.branch.last_revision_info())
797
# We can update the branch to a revision that is present in the
799
self.assertRequestSucceeds(rev_id_utf8, 1)
801
(1, rev_id_utf8), self.tree.branch.last_revision_info())
803
def test_branch_last_revision_info_rewind(self):
804
"""A branch's tip can be set to a revision that is an ancestor of the
807
self.make_tree_with_two_commits()
808
rev_id_utf8 = u'\xc8'.encode('utf-8')
810
(2, 'rev-2'), self.tree.branch.last_revision_info())
811
self.assertRequestSucceeds(rev_id_utf8, 1)
813
(1, rev_id_utf8), self.tree.branch.last_revision_info())
815
def test_TipChangeRejected(self):
816
"""If a pre_change_branch_tip hook raises TipChangeRejected, the verb
817
returns TipChangeRejected.
819
rejection_message = u'rejection message\N{INTERROBANG}'
820
def hook_that_rejects(params):
821
raise errors.TipChangeRejected(rejection_message)
822
Branch.hooks.install_named_hook(
823
'pre_change_branch_tip', hook_that_rejects, None)
825
FailedSmartServerResponse(
826
('TipChangeRejected', rejection_message.encode('utf-8'))),
827
self.set_last_revision('null:', 0))
830
class TestSmartServerBranchRequestSetLastRevision(
831
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
832
"""Tests for Branch.set_last_revision verb."""
834
request_class = smart.branch.SmartServerBranchRequestSetLastRevision
836
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
837
return self.request.execute(
838
'', branch_token, repo_token, revision_id)
841
class TestSmartServerBranchRequestSetLastRevisionInfo(
842
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
843
"""Tests for Branch.set_last_revision_info verb."""
845
request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
847
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
848
return self.request.execute(
849
'', branch_token, repo_token, revno, revision_id)
851
def test_NoSuchRevision(self):
852
"""Branch.set_last_revision_info does not have to return
853
NoSuchRevision if the revision_id is absent.
855
raise tests.TestNotApplicable()
858
class TestSmartServerBranchRequestSetLastRevisionEx(
859
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
860
"""Tests for Branch.set_last_revision_ex verb."""
862
request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
864
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
865
return self.request.execute(
866
'', branch_token, repo_token, revision_id, 0, 0)
868
def assertRequestSucceeds(self, revision_id, revno):
869
response = self.set_last_revision(revision_id, revno)
871
SuccessfulSmartServerResponse(('ok', revno, revision_id)),
874
def test_branch_last_revision_info_rewind(self):
875
"""A branch's tip can be set to a revision that is an ancestor of the
876
current tip, but only if allow_overwrite_descendant is passed.
878
self.make_tree_with_two_commits()
879
rev_id_utf8 = u'\xc8'.encode('utf-8')
881
(2, 'rev-2'), self.tree.branch.last_revision_info())
882
# If allow_overwrite_descendant flag is 0, then trying to set the tip
883
# to an older revision ID has no effect.
884
branch_token, repo_token = self.lock_branch()
885
response = self.request.execute(
886
'', branch_token, repo_token, rev_id_utf8, 0, 0)
888
SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
891
(2, 'rev-2'), self.tree.branch.last_revision_info())
893
# If allow_overwrite_descendant flag is 1, then setting the tip to an
895
response = self.request.execute(
896
'', branch_token, repo_token, rev_id_utf8, 0, 1)
898
SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
902
(1, rev_id_utf8), self.tree.branch.last_revision_info())
904
def make_branch_with_divergent_history(self):
905
"""Make a branch with divergent history in its repo.
907
The branch's tip will be 'child-2', and the repo will also contain
908
'child-1', which diverges from a common base revision.
910
self.tree.lock_write()
912
r1 = self.tree.commit('1st commit')
913
revno_1, revid_1 = self.tree.branch.last_revision_info()
914
r2 = self.tree.commit('2nd commit', rev_id='child-1')
915
# Undo the second commit
916
self.tree.branch.set_last_revision_info(revno_1, revid_1)
917
self.tree.set_parent_ids([revid_1])
918
# Make a new second commit, child-2. child-2 has diverged from
920
new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
923
def test_not_allow_diverged(self):
924
"""If allow_diverged is not passed, then setting a divergent history
925
returns a Diverged error.
927
self.make_branch_with_divergent_history()
929
FailedSmartServerResponse(('Diverged',)),
930
self.set_last_revision('child-1', 2))
931
# The branch tip was not changed.
932
self.assertEqual('child-2', self.tree.branch.last_revision())
934
def test_allow_diverged(self):
935
"""If allow_diverged is passed, then setting a divergent history
938
self.make_branch_with_divergent_history()
939
branch_token, repo_token = self.lock_branch()
940
response = self.request.execute(
941
'', branch_token, repo_token, 'child-1', 1, 0)
943
SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
946
# The branch tip was changed.
947
self.assertEqual('child-1', self.tree.branch.last_revision())
950
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
952
def test_get_parent_none(self):
953
base_branch = self.make_branch('base')
954
request = smart.branch.SmartServerBranchGetParent(self.get_transport())
955
response = request.execute('base')
957
SuccessfulSmartServerResponse(('',)), response)
959
def test_get_parent_something(self):
960
base_branch = self.make_branch('base')
961
base_branch.set_parent(self.get_url('foo'))
962
request = smart.branch.SmartServerBranchGetParent(self.get_transport())
963
response = request.execute('base')
965
SuccessfulSmartServerResponse(("../foo",)),
969
class TestSmartServerBranchRequestSetParent(tests.TestCaseWithMemoryTransport):
971
def test_set_parent_none(self):
972
branch = self.make_branch('base', format="1.9")
974
branch._set_parent_location('foo')
976
request = smart.branch.SmartServerBranchRequestSetParentLocation(
977
self.get_transport())
978
branch_token = branch.lock_write()
979
repo_token = branch.repository.lock_write()
981
response = request.execute('base', branch_token, repo_token, '')
983
branch.repository.unlock()
985
self.assertEqual(SuccessfulSmartServerResponse(()), response)
986
self.assertEqual(None, branch.get_parent())
988
def test_set_parent_something(self):
989
branch = self.make_branch('base', format="1.9")
990
request = smart.branch.SmartServerBranchRequestSetParentLocation(
991
self.get_transport())
992
branch_token = branch.lock_write()
993
repo_token = branch.repository.lock_write()
995
response = request.execute('base', branch_token, repo_token,
998
branch.repository.unlock()
1000
self.assertEqual(SuccessfulSmartServerResponse(()), response)
1001
self.assertEqual('http://bar/', branch.get_parent())
1004
class TestSmartServerBranchRequestGetTagsBytes(tests.TestCaseWithMemoryTransport):
1005
# Only called when the branch format and tags match [yay factory
1006
# methods] so only need to test straight forward cases.
1008
def test_get_bytes(self):
1009
base_branch = self.make_branch('base')
1010
request = smart.branch.SmartServerBranchGetTagsBytes(
1011
self.get_transport())
1012
response = request.execute('base')
1014
SuccessfulSmartServerResponse(('',)), response)
1017
class TestSmartServerBranchRequestGetStackedOnURL(tests.TestCaseWithMemoryTransport):
1019
def test_get_stacked_on_url(self):
1020
base_branch = self.make_branch('base', format='1.6')
1021
stacked_branch = self.make_branch('stacked', format='1.6')
1022
# typically should be relative
1023
stacked_branch.set_stacked_on_url('../base')
1024
request = smart.branch.SmartServerBranchRequestGetStackedOnURL(
1025
self.get_transport())
1026
response = request.execute('stacked')
1028
SmartServerResponse(('ok', '../base')),
1032
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
1035
tests.TestCaseWithMemoryTransport.setUp(self)
1037
def test_lock_write_on_unlocked_branch(self):
1038
backing = self.get_transport()
1039
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1040
branch = self.make_branch('.', format='knit')
1041
repository = branch.repository
1042
response = request.execute('')
1043
branch_nonce = branch.control_files._lock.peek().get('nonce')
1044
repository_nonce = repository.control_files._lock.peek().get('nonce')
1046
SmartServerResponse(('ok', branch_nonce, repository_nonce)),
1048
# The branch (and associated repository) is now locked. Verify that
1049
# with a new branch object.
1050
new_branch = repository.bzrdir.open_branch()
1051
self.assertRaises(errors.LockContention, new_branch.lock_write)
1053
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1054
response = request.execute('', branch_nonce, repository_nonce)
1056
def test_lock_write_on_locked_branch(self):
1057
backing = self.get_transport()
1058
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1059
branch = self.make_branch('.')
1060
branch_token = branch.lock_write()
1061
branch.leave_lock_in_place()
1063
response = request.execute('')
1065
SmartServerResponse(('LockContention',)), response)
1067
branch.lock_write(branch_token)
1068
branch.dont_leave_lock_in_place()
1071
def test_lock_write_with_tokens_on_locked_branch(self):
1072
backing = self.get_transport()
1073
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1074
branch = self.make_branch('.', format='knit')
1075
branch_token = branch.lock_write()
1076
repo_token = branch.repository.lock_write()
1077
branch.repository.unlock()
1078
branch.leave_lock_in_place()
1079
branch.repository.leave_lock_in_place()
1081
response = request.execute('',
1082
branch_token, repo_token)
1084
SmartServerResponse(('ok', branch_token, repo_token)), response)
1086
branch.repository.lock_write(repo_token)
1087
branch.repository.dont_leave_lock_in_place()
1088
branch.repository.unlock()
1089
branch.lock_write(branch_token)
1090
branch.dont_leave_lock_in_place()
1093
def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
1094
backing = self.get_transport()
1095
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1096
branch = self.make_branch('.', format='knit')
1097
branch_token = branch.lock_write()
1098
repo_token = branch.repository.lock_write()
1099
branch.repository.unlock()
1100
branch.leave_lock_in_place()
1101
branch.repository.leave_lock_in_place()
1103
response = request.execute('',
1104
branch_token+'xxx', repo_token)
1106
SmartServerResponse(('TokenMismatch',)), response)
1108
branch.repository.lock_write(repo_token)
1109
branch.repository.dont_leave_lock_in_place()
1110
branch.repository.unlock()
1111
branch.lock_write(branch_token)
1112
branch.dont_leave_lock_in_place()
1115
def test_lock_write_on_locked_repo(self):
1116
backing = self.get_transport()
1117
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1118
branch = self.make_branch('.', format='knit')
1119
repo = branch.repository
1120
repo_token = repo.lock_write()
1121
repo.leave_lock_in_place()
1123
response = request.execute('')
1125
SmartServerResponse(('LockContention',)), response)
1127
repo.lock_write(repo_token)
1128
repo.dont_leave_lock_in_place()
1131
def test_lock_write_on_readonly_transport(self):
1132
backing = self.get_readonly_transport()
1133
request = smart.branch.SmartServerBranchRequestLockWrite(backing)
1134
branch = self.make_branch('.')
1135
root = self.get_transport().clone('/')
1136
path = urlutils.relative_url(root.base, self.get_transport().base)
1137
response = request.execute(path)
1138
error_name, lock_str, why_str = response.args
1139
self.assertFalse(response.is_successful())
1140
self.assertEqual('LockFailed', error_name)
1143
class TestSmartServerBranchRequestUnlock(tests.TestCaseWithMemoryTransport):
1146
tests.TestCaseWithMemoryTransport.setUp(self)
1148
def test_unlock_on_locked_branch_and_repo(self):
1149
backing = self.get_transport()
1150
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1151
branch = self.make_branch('.', format='knit')
1153
branch_token = branch.lock_write()
1154
repo_token = branch.repository.lock_write()
1155
branch.repository.unlock()
1156
# Unlock the branch (and repo) object, leaving the physical locks
1158
branch.leave_lock_in_place()
1159
branch.repository.leave_lock_in_place()
1161
response = request.execute('',
1162
branch_token, repo_token)
1164
SmartServerResponse(('ok',)), response)
1165
# The branch is now unlocked. Verify that with a new branch
1167
new_branch = branch.bzrdir.open_branch()
1168
new_branch.lock_write()
1171
def test_unlock_on_unlocked_branch_unlocked_repo(self):
1172
backing = self.get_transport()
1173
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1174
branch = self.make_branch('.', format='knit')
1175
response = request.execute(
1176
'', 'branch token', 'repo token')
1178
SmartServerResponse(('TokenMismatch',)), response)
1180
def test_unlock_on_unlocked_branch_locked_repo(self):
1181
backing = self.get_transport()
1182
request = smart.branch.SmartServerBranchRequestUnlock(backing)
1183
branch = self.make_branch('.', format='knit')
1184
# Lock the repository.
1185
repo_token = branch.repository.lock_write()
1186
branch.repository.leave_lock_in_place()
1187
branch.repository.unlock()
1188
# Issue branch lock_write request on the unlocked branch (with locked
1190
response = request.execute(
1191
'', 'branch token', repo_token)
1193
SmartServerResponse(('TokenMismatch',)), response)
1195
branch.repository.lock_write(repo_token)
1196
branch.repository.dont_leave_lock_in_place()
1197
branch.repository.unlock()
1200
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
1202
def test_no_repository(self):
1203
"""Raise NoRepositoryPresent when there is a bzrdir and no repo."""
1204
# we test this using a shared repository above the named path,
1205
# thus checking the right search logic is used - that is, that
1206
# its the exact path being looked at and the server is not
1208
backing = self.get_transport()
1209
request = smart.repository.SmartServerRepositoryRequest(backing)
1210
self.make_repository('.', shared=True)
1211
self.make_bzrdir('subdir')
1212
self.assertRaises(errors.NoRepositoryPresent,
1213
request.execute, 'subdir')
1216
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
1218
def test_trivial_bzipped(self):
1219
# This tests that the wire encoding is actually bzipped
1220
backing = self.get_transport()
1221
request = smart.repository.SmartServerRepositoryGetParentMap(backing)
1222
tree = self.make_branch_and_memory_tree('.')
1224
self.assertEqual(None,
1225
request.execute('', 'missing-id'))
1226
# Note that it returns a body that is bzipped.
1228
SuccessfulSmartServerResponse(('ok', ), bz2.compress('')),
1229
request.do_body('\n\n0\n'))
1231
def test_trivial_include_missing(self):
1232
backing = self.get_transport()
1233
request = smart.repository.SmartServerRepositoryGetParentMap(backing)
1234
tree = self.make_branch_and_memory_tree('.')
1236
self.assertEqual(None,
1237
request.execute('', 'missing-id', 'include-missing:'))
1239
SuccessfulSmartServerResponse(('ok', ),
1240
bz2.compress('missing:missing-id')),
1241
request.do_body('\n\n0\n'))
1244
class TestSmartServerRepositoryGetRevisionGraph(tests.TestCaseWithMemoryTransport):
1246
def test_none_argument(self):
1247
backing = self.get_transport()
1248
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1249
tree = self.make_branch_and_memory_tree('.')
1252
r1 = tree.commit('1st commit')
1253
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1256
# the lines of revision_id->revision_parent_list has no guaranteed
1257
# order coming out of a dict, so sort both our test and response
1258
lines = sorted([' '.join([r2, r1]), r1])
1259
response = request.execute('', '')
1260
response.body = '\n'.join(sorted(response.body.split('\n')))
1263
SmartServerResponse(('ok', ), '\n'.join(lines)), response)
1265
def test_specific_revision_argument(self):
1266
backing = self.get_transport()
1267
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1268
tree = self.make_branch_and_memory_tree('.')
1271
rev_id_utf8 = u'\xc9'.encode('utf-8')
1272
r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
1273
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1276
self.assertEqual(SmartServerResponse(('ok', ), rev_id_utf8),
1277
request.execute('', rev_id_utf8))
1279
def test_no_such_revision(self):
1280
backing = self.get_transport()
1281
request = smart.repository.SmartServerRepositoryGetRevisionGraph(backing)
1282
tree = self.make_branch_and_memory_tree('.')
1285
r1 = tree.commit('1st commit')
1288
# Note that it still returns body (of zero bytes).
1290
SmartServerResponse(('nosuchrevision', 'missingrevision', ), ''),
1291
request.execute('', 'missingrevision'))
1294
class TestSmartServerRepositoryGetRevIdForRevno(tests.TestCaseWithMemoryTransport):
1296
def test_revno_found(self):
1297
backing = self.get_transport()
1298
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1299
tree = self.make_branch_and_memory_tree('.')
1302
rev1_id_utf8 = u'\xc8'.encode('utf-8')
1303
rev2_id_utf8 = u'\xc9'.encode('utf-8')
1304
tree.commit('1st commit', rev_id=rev1_id_utf8)
1305
tree.commit('2nd commit', rev_id=rev2_id_utf8)
1308
self.assertEqual(SmartServerResponse(('ok', rev1_id_utf8)),
1309
request.execute('', 1, (2, rev2_id_utf8)))
1311
def test_known_revid_missing(self):
1312
backing = self.get_transport()
1313
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1314
repo = self.make_repository('.')
1316
FailedSmartServerResponse(('nosuchrevision', 'ghost')),
1317
request.execute('', 1, (2, 'ghost')))
1319
def test_history_incomplete(self):
1320
backing = self.get_transport()
1321
request = smart.repository.SmartServerRepositoryGetRevIdForRevno(backing)
1322
parent = self.make_branch_and_memory_tree('parent', format='1.9')
1324
parent.add([''], ['TREE_ROOT'])
1325
r1 = parent.commit(message='first commit')
1326
r2 = parent.commit(message='second commit')
1328
local = self.make_branch_and_memory_tree('local', format='1.9')
1329
local.branch.pull(parent.branch)
1330
local.set_parent_ids([r2])
1331
r3 = local.commit(message='local commit')
1332
local.branch.create_clone_on_transport(
1333
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
1335
SmartServerResponse(('history-incomplete', 2, r2)),
1336
request.execute('stacked', 1, (3, r3)))
1339
class TestSmartServerRepositoryGetStream(tests.TestCaseWithMemoryTransport):
1341
def make_two_commit_repo(self):
1342
tree = self.make_branch_and_memory_tree('.')
1345
r1 = tree.commit('1st commit')
1346
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1348
repo = tree.branch.repository
1351
def test_ancestry_of(self):
1352
"""The search argument may be a 'ancestry-of' some heads'."""
1353
backing = self.get_transport()
1354
request = smart.repository.SmartServerRepositoryGetStream(backing)
1355
repo, r1, r2 = self.make_two_commit_repo()
1356
fetch_spec = ['ancestry-of', r2]
1357
lines = '\n'.join(fetch_spec)
1358
request.execute('', repo._format.network_name())
1359
response = request.do_body(lines)
1360
self.assertEqual(('ok',), response.args)
1361
stream_bytes = ''.join(response.body_stream)
1362
self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
1364
def test_search(self):
1365
"""The search argument may be a 'search' of some explicit keys."""
1366
backing = self.get_transport()
1367
request = smart.repository.SmartServerRepositoryGetStream(backing)
1368
repo, r1, r2 = self.make_two_commit_repo()
1369
fetch_spec = ['search', '%s %s' % (r1, r2), 'null:', '2']
1370
lines = '\n'.join(fetch_spec)
1371
request.execute('', repo._format.network_name())
1372
response = request.do_body(lines)
1373
self.assertEqual(('ok',), response.args)
1374
stream_bytes = ''.join(response.body_stream)
1375
self.assertStartsWith(stream_bytes, 'Bazaar pack format 1')
1378
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
1380
def test_missing_revision(self):
1381
"""For a missing revision, ('no', ) is returned."""
1382
backing = self.get_transport()
1383
request = smart.repository.SmartServerRequestHasRevision(backing)
1384
self.make_repository('.')
1385
self.assertEqual(SmartServerResponse(('no', )),
1386
request.execute('', 'revid'))
1388
def test_present_revision(self):
1389
"""For a present revision, ('yes', ) is returned."""
1390
backing = self.get_transport()
1391
request = smart.repository.SmartServerRequestHasRevision(backing)
1392
tree = self.make_branch_and_memory_tree('.')
1395
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1396
r1 = tree.commit('a commit', rev_id=rev_id_utf8)
1398
self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
1399
self.assertEqual(SmartServerResponse(('yes', )),
1400
request.execute('', rev_id_utf8))
1403
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
1405
def test_empty_revid(self):
1406
"""With an empty revid, we get only size an number and revisions"""
1407
backing = self.get_transport()
1408
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1409
repository = self.make_repository('.')
1410
stats = repository.gather_stats()
1411
expected_body = 'revisions: 0\n'
1412
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1413
request.execute('', '', 'no'))
1415
def test_revid_with_committers(self):
1416
"""For a revid we get more infos."""
1417
backing = self.get_transport()
1418
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1419
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1420
tree = self.make_branch_and_memory_tree('.')
1423
# Let's build a predictable result
1424
tree.commit('a commit', timestamp=123456.2, timezone=3600)
1425
tree.commit('a commit', timestamp=654321.4, timezone=0,
1429
stats = tree.branch.repository.gather_stats()
1430
expected_body = ('firstrev: 123456.200 3600\n'
1431
'latestrev: 654321.400 0\n'
1433
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1437
def test_not_empty_repository_with_committers(self):
1438
"""For a revid and requesting committers we get the whole thing."""
1439
backing = self.get_transport()
1440
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1441
request = smart.repository.SmartServerRepositoryGatherStats(backing)
1442
tree = self.make_branch_and_memory_tree('.')
1445
# Let's build a predictable result
1446
tree.commit('a commit', timestamp=123456.2, timezone=3600,
1448
tree.commit('a commit', timestamp=654321.4, timezone=0,
1449
committer='bar', rev_id=rev_id_utf8)
1451
stats = tree.branch.repository.gather_stats()
1453
expected_body = ('committers: 2\n'
1454
'firstrev: 123456.200 3600\n'
1455
'latestrev: 654321.400 0\n'
1457
self.assertEqual(SmartServerResponse(('ok', ), expected_body),
1459
rev_id_utf8, 'yes'))
1462
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
1464
def test_is_shared(self):
1465
"""For a shared repository, ('yes', ) is returned."""
1466
backing = self.get_transport()
1467
request = smart.repository.SmartServerRepositoryIsShared(backing)
1468
self.make_repository('.', shared=True)
1469
self.assertEqual(SmartServerResponse(('yes', )),
1470
request.execute('', ))
1472
def test_is_not_shared(self):
1473
"""For a shared repository, ('no', ) is returned."""
1474
backing = self.get_transport()
1475
request = smart.repository.SmartServerRepositoryIsShared(backing)
1476
self.make_repository('.', shared=False)
1477
self.assertEqual(SmartServerResponse(('no', )),
1478
request.execute('', ))
1481
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
1483
def test_lock_write_on_unlocked_repo(self):
1484
backing = self.get_transport()
1485
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1486
repository = self.make_repository('.', format='knit')
1487
response = request.execute('')
1488
nonce = repository.control_files._lock.peek().get('nonce')
1489
self.assertEqual(SmartServerResponse(('ok', nonce)), response)
1490
# The repository is now locked. Verify that with a new repository
1492
new_repo = repository.bzrdir.open_repository()
1493
self.assertRaises(errors.LockContention, new_repo.lock_write)
1495
request = smart.repository.SmartServerRepositoryUnlock(backing)
1496
response = request.execute('', nonce)
1498
def test_lock_write_on_locked_repo(self):
1499
backing = self.get_transport()
1500
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1501
repository = self.make_repository('.', format='knit')
1502
repo_token = repository.lock_write()
1503
repository.leave_lock_in_place()
1505
response = request.execute('')
1507
SmartServerResponse(('LockContention',)), response)
1509
repository.lock_write(repo_token)
1510
repository.dont_leave_lock_in_place()
1513
def test_lock_write_on_readonly_transport(self):
1514
backing = self.get_readonly_transport()
1515
request = smart.repository.SmartServerRepositoryLockWrite(backing)
1516
repository = self.make_repository('.', format='knit')
1517
response = request.execute('')
1518
self.assertFalse(response.is_successful())
1519
self.assertEqual('LockFailed', response.args[0])
1522
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
1524
def make_empty_byte_stream(self, repo):
1525
byte_stream = smart.repository._stream_to_byte_stream([], repo._format)
1526
return ''.join(byte_stream)
1529
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
1531
def test_insert_stream_empty(self):
1532
backing = self.get_transport()
1533
request = smart.repository.SmartServerRepositoryInsertStream(backing)
1534
repository = self.make_repository('.')
1535
response = request.execute('', '')
1536
self.assertEqual(None, response)
1537
response = request.do_chunk(self.make_empty_byte_stream(repository))
1538
self.assertEqual(None, response)
1539
response = request.do_end()
1540
self.assertEqual(SmartServerResponse(('ok', )), response)
1543
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
1545
def test_insert_stream_empty(self):
1546
backing = self.get_transport()
1547
request = smart.repository.SmartServerRepositoryInsertStreamLocked(
1549
repository = self.make_repository('.', format='knit')
1550
lock_token = repository.lock_write()
1551
response = request.execute('', '', lock_token)
1552
self.assertEqual(None, response)
1553
response = request.do_chunk(self.make_empty_byte_stream(repository))
1554
self.assertEqual(None, response)
1555
response = request.do_end()
1556
self.assertEqual(SmartServerResponse(('ok', )), response)
1559
def test_insert_stream_with_wrong_lock_token(self):
1560
backing = self.get_transport()
1561
request = smart.repository.SmartServerRepositoryInsertStreamLocked(
1563
repository = self.make_repository('.', format='knit')
1564
lock_token = repository.lock_write()
1566
errors.TokenMismatch, request.execute, '', '', 'wrong-token')
1570
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
1573
tests.TestCaseWithMemoryTransport.setUp(self)
1575
def test_unlock_on_locked_repo(self):
1576
backing = self.get_transport()
1577
request = smart.repository.SmartServerRepositoryUnlock(backing)
1578
repository = self.make_repository('.', format='knit')
1579
token = repository.lock_write()
1580
repository.leave_lock_in_place()
1582
response = request.execute('', token)
1584
SmartServerResponse(('ok',)), response)
1585
# The repository is now unlocked. Verify that with a new repository
1587
new_repo = repository.bzrdir.open_repository()
1588
new_repo.lock_write()
1591
def test_unlock_on_unlocked_repo(self):
1592
backing = self.get_transport()
1593
request = smart.repository.SmartServerRepositoryUnlock(backing)
1594
repository = self.make_repository('.', format='knit')
1595
response = request.execute('', 'some token')
1597
SmartServerResponse(('TokenMismatch',)), response)
1600
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
1602
def test_is_readonly_no(self):
1603
backing = self.get_transport()
1604
request = smart.request.SmartServerIsReadonly(backing)
1605
response = request.execute()
1607
SmartServerResponse(('no',)), response)
1609
def test_is_readonly_yes(self):
1610
backing = self.get_readonly_transport()
1611
request = smart.request.SmartServerIsReadonly(backing)
1612
response = request.execute()
1614
SmartServerResponse(('yes',)), response)
1617
class TestSmartServerRepositorySetMakeWorkingTrees(tests.TestCaseWithMemoryTransport):
1619
def test_set_false(self):
1620
backing = self.get_transport()
1621
repo = self.make_repository('.', shared=True)
1622
repo.set_make_working_trees(True)
1623
request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
1624
request = request_class(backing)
1625
self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
1626
request.execute('', 'False'))
1627
repo = repo.bzrdir.open_repository()
1628
self.assertFalse(repo.make_working_trees())
1630
def test_set_true(self):
1631
backing = self.get_transport()
1632
repo = self.make_repository('.', shared=True)
1633
repo.set_make_working_trees(False)
1634
request_class = smart.repository.SmartServerRepositorySetMakeWorkingTrees
1635
request = request_class(backing)
1636
self.assertEqual(SuccessfulSmartServerResponse(('ok',)),
1637
request.execute('', 'True'))
1638
repo = repo.bzrdir.open_repository()
1639
self.assertTrue(repo.make_working_trees())
1642
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
1644
def make_repo_needing_autopacking(self, path='.'):
1645
# Make a repo in need of autopacking.
1646
tree = self.make_branch_and_tree('.', format='pack-0.92')
1647
repo = tree.branch.repository
1648
# monkey-patch the pack collection to disable autopacking
1649
repo._pack_collection._max_pack_count = lambda count: count
1651
tree.commit('commit %s' % x)
1652
self.assertEqual(10, len(repo._pack_collection.names()))
1653
del repo._pack_collection._max_pack_count
1656
def test_autopack_needed(self):
1657
repo = self.make_repo_needing_autopacking()
1659
self.addCleanup(repo.unlock)
1660
backing = self.get_transport()
1661
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1663
response = request.execute('')
1664
self.assertEqual(SmartServerResponse(('ok',)), response)
1665
repo._pack_collection.reload_pack_names()
1666
self.assertEqual(1, len(repo._pack_collection.names()))
1668
def test_autopack_not_needed(self):
1669
tree = self.make_branch_and_tree('.', format='pack-0.92')
1670
repo = tree.branch.repository
1672
self.addCleanup(repo.unlock)
1674
tree.commit('commit %s' % x)
1675
backing = self.get_transport()
1676
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1678
response = request.execute('')
1679
self.assertEqual(SmartServerResponse(('ok',)), response)
1680
repo._pack_collection.reload_pack_names()
1681
self.assertEqual(9, len(repo._pack_collection.names()))
1683
def test_autopack_on_nonpack_format(self):
1684
"""A request to autopack a non-pack repo is a no-op."""
1685
repo = self.make_repository('.', format='knit')
1686
backing = self.get_transport()
1687
request = smart.packrepository.SmartServerPackRepositoryAutopack(
1689
response = request.execute('')
1690
self.assertEqual(SmartServerResponse(('ok',)), response)
1693
class TestHandlers(tests.TestCase):
1694
"""Tests for the request.request_handlers object."""
1696
def test_all_registrations_exist(self):
1697
"""All registered request_handlers can be found."""
1698
# If there's a typo in a register_lazy call, this loop will fail with
1699
# an AttributeError.
1700
for key, item in smart.request.request_handlers.iteritems():
1703
def assertHandlerEqual(self, verb, handler):
1704
self.assertEqual(smart.request.request_handlers.get(verb), handler)
1706
def test_registered_methods(self):
1707
"""Test that known methods are registered to the correct object."""
1708
self.assertHandlerEqual('Branch.get_config_file',
1709
smart.branch.SmartServerBranchGetConfigFile)
1710
self.assertHandlerEqual('Branch.get_parent',
1711
smart.branch.SmartServerBranchGetParent)
1712
self.assertHandlerEqual('Branch.get_tags_bytes',
1713
smart.branch.SmartServerBranchGetTagsBytes)
1714
self.assertHandlerEqual('Branch.lock_write',
1715
smart.branch.SmartServerBranchRequestLockWrite)
1716
self.assertHandlerEqual('Branch.last_revision_info',
1717
smart.branch.SmartServerBranchRequestLastRevisionInfo)
1718
self.assertHandlerEqual('Branch.revision_history',
1719
smart.branch.SmartServerRequestRevisionHistory)
1720
self.assertHandlerEqual('Branch.set_config_option',
1721
smart.branch.SmartServerBranchRequestSetConfigOption)
1722
self.assertHandlerEqual('Branch.set_last_revision',
1723
smart.branch.SmartServerBranchRequestSetLastRevision)
1724
self.assertHandlerEqual('Branch.set_last_revision_info',
1725
smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
1726
self.assertHandlerEqual('Branch.set_last_revision_ex',
1727
smart.branch.SmartServerBranchRequestSetLastRevisionEx)
1728
self.assertHandlerEqual('Branch.set_parent_location',
1729
smart.branch.SmartServerBranchRequestSetParentLocation)
1730
self.assertHandlerEqual('Branch.unlock',
1731
smart.branch.SmartServerBranchRequestUnlock)
1732
self.assertHandlerEqual('BzrDir.find_repository',
1733
smart.bzrdir.SmartServerRequestFindRepositoryV1)
1734
self.assertHandlerEqual('BzrDir.find_repositoryV2',
1735
smart.bzrdir.SmartServerRequestFindRepositoryV2)
1736
self.assertHandlerEqual('BzrDirFormat.initialize',
1737
smart.bzrdir.SmartServerRequestInitializeBzrDir)
1738
self.assertHandlerEqual('BzrDirFormat.initialize_ex_1.16',
1739
smart.bzrdir.SmartServerRequestBzrDirInitializeEx)
1740
self.assertHandlerEqual('BzrDir.cloning_metadir',
1741
smart.bzrdir.SmartServerBzrDirRequestCloningMetaDir)
1742
self.assertHandlerEqual('BzrDir.get_config_file',
1743
smart.bzrdir.SmartServerBzrDirRequestConfigFile)
1744
self.assertHandlerEqual('BzrDir.open_branch',
1745
smart.bzrdir.SmartServerRequestOpenBranch)
1746
self.assertHandlerEqual('BzrDir.open_branchV2',
1747
smart.bzrdir.SmartServerRequestOpenBranchV2)
1748
self.assertHandlerEqual('PackRepository.autopack',
1749
smart.packrepository.SmartServerPackRepositoryAutopack)
1750
self.assertHandlerEqual('Repository.gather_stats',
1751
smart.repository.SmartServerRepositoryGatherStats)
1752
self.assertHandlerEqual('Repository.get_parent_map',
1753
smart.repository.SmartServerRepositoryGetParentMap)
1754
self.assertHandlerEqual('Repository.get_rev_id_for_revno',
1755
smart.repository.SmartServerRepositoryGetRevIdForRevno)
1756
self.assertHandlerEqual('Repository.get_revision_graph',
1757
smart.repository.SmartServerRepositoryGetRevisionGraph)
1758
self.assertHandlerEqual('Repository.get_stream',
1759
smart.repository.SmartServerRepositoryGetStream)
1760
self.assertHandlerEqual('Repository.has_revision',
1761
smart.repository.SmartServerRequestHasRevision)
1762
self.assertHandlerEqual('Repository.insert_stream',
1763
smart.repository.SmartServerRepositoryInsertStream)
1764
self.assertHandlerEqual('Repository.insert_stream_locked',
1765
smart.repository.SmartServerRepositoryInsertStreamLocked)
1766
self.assertHandlerEqual('Repository.is_shared',
1767
smart.repository.SmartServerRepositoryIsShared)
1768
self.assertHandlerEqual('Repository.lock_write',
1769
smart.repository.SmartServerRepositoryLockWrite)
1770
self.assertHandlerEqual('Repository.tarball',
1771
smart.repository.SmartServerRepositoryTarball)
1772
self.assertHandlerEqual('Repository.unlock',
1773
smart.repository.SmartServerRepositoryUnlock)
1774
self.assertHandlerEqual('Transport.is_readonly',
1775
smart.request.SmartServerIsReadonly)