1
# Copyright (C) 2006-2012, 2016 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 io import BytesIO
34
branch as _mod_branch,
42
from breezy.bzr import (
43
branch as _mod_bzrbranch,
47
from breezy.bzr.smart import (
48
branch as smart_branch,
50
repository as smart_repo,
51
packrepository as smart_packrepo,
56
from breezy.testament import Testament
57
from breezy.tests import test_server
58
from breezy.transport import (
64
def load_tests(loader, standard_tests, pattern):
65
"""Multiply tests version and protocol consistency."""
66
# FindRepository tests.
69
"_request_class": smart_dir.SmartServerRequestFindRepositoryV1}),
70
("find_repositoryV2", {
71
"_request_class": smart_dir.SmartServerRequestFindRepositoryV2}),
72
("find_repositoryV3", {
73
"_request_class": smart_dir.SmartServerRequestFindRepositoryV3}),
75
to_adapt, result = tests.split_suite_by_re(
76
standard_tests, "TestSmartServerRequestFindRepository")
77
v2_only, v1_and_2 = tests.split_suite_by_re(to_adapt, "_v2")
78
tests.multiply_tests(v1_and_2, scenarios, result)
79
# The first scenario is only applicable to v1 protocols, it is deleted
81
tests.multiply_tests(v2_only, scenarios[1:], result)
85
class TestCaseWithChrootedTransport(tests.TestCaseWithTransport):
88
self.vfs_transport_factory = memory.MemoryServer
89
super(TestCaseWithChrootedTransport, self).setUp()
90
self._chroot_server = None
92
def get_transport(self, relpath=None):
93
if self._chroot_server is None:
94
backing_transport = tests.TestCaseWithTransport.get_transport(self)
95
self._chroot_server = chroot.ChrootServer(backing_transport)
96
self.start_server(self._chroot_server)
97
t = transport.get_transport_from_url(self._chroot_server.get_url())
98
if relpath is not None:
103
class TestCaseWithSmartMedium(tests.TestCaseWithMemoryTransport):
106
super(TestCaseWithSmartMedium, self).setUp()
107
# We're allowed to set the transport class here, so that we don't use
108
# the default or a parameterized class, but rather use the
109
# TestCaseWithTransport infrastructure to set up a smart server and
111
self.overrideAttr(self, "transport_server", self.make_transport_server)
113
def make_transport_server(self):
114
return test_server.SmartTCPServer_for_testing('-' + self.id())
116
def get_smart_medium(self):
117
"""Get a smart medium to use in tests."""
118
return self.get_transport().get_smart_medium()
121
class TestByteStreamToStream(tests.TestCase):
123
def test_repeated_substreams_same_kind_are_one_stream(self):
124
# Make a stream - an iterable of bytestrings.
126
('text', [versionedfile.FulltextContentFactory((b'k1',), None,
128
('text', [versionedfile.FulltextContentFactory((b'k2',), None,
130
fmt = controldir.format_registry.get('pack-0.92')().repository_format
131
bytes = smart_repo._stream_to_byte_stream(stream, fmt)
133
# Iterate the resulting iterable; checking that we get only one stream
135
fmt, stream = smart_repo._byte_stream_to_stream(bytes)
136
for kind, substream in stream:
137
streams.append((kind, list(substream)))
138
self.assertLength(1, streams)
139
self.assertLength(2, streams[0][1])
142
class TestSmartServerResponse(tests.TestCase):
144
def test__eq__(self):
145
self.assertEqual(smart_req.SmartServerResponse((b'ok', )),
146
smart_req.SmartServerResponse((b'ok', )))
147
self.assertEqual(smart_req.SmartServerResponse((b'ok', ), b'body'),
148
smart_req.SmartServerResponse((b'ok', ), b'body'))
149
self.assertNotEqual(smart_req.SmartServerResponse((b'ok', )),
150
smart_req.SmartServerResponse((b'notok', )))
151
self.assertNotEqual(smart_req.SmartServerResponse((b'ok', ), b'body'),
152
smart_req.SmartServerResponse((b'ok', )))
153
self.assertNotEqual(None,
154
smart_req.SmartServerResponse((b'ok', )))
156
def test__str__(self):
157
"""SmartServerResponses can be stringified."""
159
str(smart_req.SuccessfulSmartServerResponse((b'args',), b'body')),
160
("<SuccessfulSmartServerResponse args=(b'args',) body=b'body'>",
161
"<SuccessfulSmartServerResponse args=('args',) body='body'>"))
163
str(smart_req.FailedSmartServerResponse((b'args',), b'body')),
164
("<FailedSmartServerResponse args=(b'args',) body=b'body'>",
165
"<FailedSmartServerResponse args=('args',) body='body'>"))
168
class TestSmartServerRequest(tests.TestCaseWithMemoryTransport):
170
def test_translate_client_path(self):
171
transport = self.get_transport()
172
request = smart_req.SmartServerRequest(transport, 'foo/')
173
self.assertEqual('./', request.translate_client_path(b'foo/'))
175
urlutils.InvalidURLJoin, request.translate_client_path, b'foo/..')
177
errors.PathNotChild, request.translate_client_path, b'/')
179
errors.PathNotChild, request.translate_client_path, b'bar/')
180
self.assertEqual('./baz', request.translate_client_path(b'foo/baz'))
181
e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'
183
u'./' + urlutils.escape(e_acute),
184
request.translate_client_path(b'foo/' + e_acute.encode('utf-8')))
186
def test_translate_client_path_vfs(self):
187
"""VfsRequests receive escaped paths rather than raw UTF-8."""
188
transport = self.get_transport()
189
request = vfs.VfsRequest(transport, 'foo/')
190
e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'
191
escaped = urlutils.escape(u'foo/' + e_acute)
193
'./' + urlutils.escape(e_acute),
194
request.translate_client_path(escaped.encode('ascii')))
196
def test_transport_from_client_path(self):
197
transport = self.get_transport()
198
request = smart_req.SmartServerRequest(transport, 'foo/')
201
request.transport_from_client_path(b'foo/').base)
204
class TestSmartServerBzrDirRequestCloningMetaDir(
205
tests.TestCaseWithMemoryTransport):
206
"""Tests for BzrDir.cloning_metadir."""
208
def test_cloning_metadir(self):
209
"""When there is a bzrdir present, the call succeeds."""
210
backing = self.get_transport()
211
dir = self.make_controldir('.')
212
local_result = dir.cloning_metadir()
213
request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
214
request = request_class(backing)
215
expected = smart_req.SuccessfulSmartServerResponse(
216
(local_result.network_name(),
217
local_result.repository_format.network_name(),
218
(b'branch', local_result.get_branch_format().network_name())))
219
self.assertEqual(expected, request.execute(b'', b'False'))
221
def test_cloning_metadir_reference(self):
222
"""The request fails when bzrdir contains a branch reference."""
223
backing = self.get_transport()
224
referenced_branch = self.make_branch('referenced')
225
dir = self.make_controldir('.')
226
dir.cloning_metadir()
227
_mod_bzrbranch.BranchReferenceFormat().initialize(
228
dir, target_branch=referenced_branch)
229
_mod_bzrbranch.BranchReferenceFormat().get_reference(dir)
230
# The server shouldn't try to follow the branch reference, so it's fine
231
# if the referenced branch isn't reachable.
232
backing.rename('referenced', 'moved')
233
request_class = smart_dir.SmartServerBzrDirRequestCloningMetaDir
234
request = request_class(backing)
235
expected = smart_req.FailedSmartServerResponse((b'BranchReference',))
236
self.assertEqual(expected, request.execute(b'', b'False'))
239
class TestSmartServerBzrDirRequestCheckoutMetaDir(
240
tests.TestCaseWithMemoryTransport):
241
"""Tests for BzrDir.checkout_metadir."""
243
def test_checkout_metadir(self):
244
backing = self.get_transport()
245
request = smart_dir.SmartServerBzrDirRequestCheckoutMetaDir(
247
self.make_branch('.', format='2a')
248
response = request.execute(b'')
250
smart_req.SmartServerResponse(
251
(b'Bazaar-NG meta directory, format 1\n',
252
b'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
253
b'Bazaar Branch Format 7 (needs bzr 1.6)\n')),
257
class TestSmartServerBzrDirRequestDestroyBranch(
258
tests.TestCaseWithMemoryTransport):
259
"""Tests for BzrDir.destroy_branch."""
261
def test_destroy_branch_default(self):
262
"""The default branch can be removed."""
263
backing = self.get_transport()
264
self.make_branch('.')
265
request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
266
request = request_class(backing)
267
expected = smart_req.SuccessfulSmartServerResponse((b'ok',))
268
self.assertEqual(expected, request.execute(b'', None))
270
def test_destroy_branch_named(self):
271
"""A named branch can be removed."""
272
backing = self.get_transport()
273
dir = self.make_repository('.', format="development-colo").controldir
274
dir.create_branch(name="branchname")
275
request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
276
request = request_class(backing)
277
expected = smart_req.SuccessfulSmartServerResponse((b'ok',))
278
self.assertEqual(expected, request.execute(b'', b"branchname"))
280
def test_destroy_branch_missing(self):
281
"""An error is raised if the branch didn't exist."""
282
backing = self.get_transport()
283
self.make_controldir('.', format="development-colo")
284
request_class = smart_dir.SmartServerBzrDirRequestDestroyBranch
285
request = request_class(backing)
286
expected = smart_req.FailedSmartServerResponse((b'nobranch',), None)
287
self.assertEqual(expected, request.execute(b'', b"branchname"))
290
class TestSmartServerBzrDirRequestHasWorkingTree(
291
tests.TestCaseWithTransport):
292
"""Tests for BzrDir.has_workingtree."""
294
def test_has_workingtree_yes(self):
295
"""A working tree is present."""
296
backing = self.get_transport()
297
self.make_branch_and_tree('.')
298
request_class = smart_dir.SmartServerBzrDirRequestHasWorkingTree
299
request = request_class(backing)
300
expected = smart_req.SuccessfulSmartServerResponse((b'yes',))
301
self.assertEqual(expected, request.execute(b''))
303
def test_has_workingtree_no(self):
304
"""A working tree is missing."""
305
backing = self.get_transport()
306
self.make_controldir('.')
307
request_class = smart_dir.SmartServerBzrDirRequestHasWorkingTree
308
request = request_class(backing)
309
expected = smart_req.SuccessfulSmartServerResponse((b'no',))
310
self.assertEqual(expected, request.execute(b''))
313
class TestSmartServerBzrDirRequestDestroyRepository(
314
tests.TestCaseWithMemoryTransport):
315
"""Tests for BzrDir.destroy_repository."""
317
def test_destroy_repository_default(self):
318
"""The repository can be removed."""
319
backing = self.get_transport()
320
self.make_repository('.')
321
request_class = smart_dir.SmartServerBzrDirRequestDestroyRepository
322
request = request_class(backing)
323
expected = smart_req.SuccessfulSmartServerResponse((b'ok',))
324
self.assertEqual(expected, request.execute(b''))
326
def test_destroy_repository_missing(self):
327
"""An error is raised if the repository didn't exist."""
328
backing = self.get_transport()
329
self.make_controldir('.')
330
request_class = smart_dir.SmartServerBzrDirRequestDestroyRepository
331
request = request_class(backing)
332
expected = smart_req.FailedSmartServerResponse(
333
(b'norepository',), None)
334
self.assertEqual(expected, request.execute(b''))
337
class TestSmartServerRequestCreateRepository(
338
tests.TestCaseWithMemoryTransport):
339
"""Tests for BzrDir.create_repository."""
341
def test_makes_repository(self):
342
"""When there is a bzrdir present, the call succeeds."""
343
backing = self.get_transport()
344
self.make_controldir('.')
345
request_class = smart_dir.SmartServerRequestCreateRepository
346
request = request_class(backing)
347
reference_bzrdir_format = controldir.format_registry.get('pack-0.92')()
348
reference_format = reference_bzrdir_format.repository_format
349
network_name = reference_format.network_name()
350
expected = smart_req.SuccessfulSmartServerResponse(
351
(b'ok', b'no', b'no', b'no', network_name))
352
self.assertEqual(expected, request.execute(b'', network_name, b'True'))
355
class TestSmartServerRequestFindRepository(tests.TestCaseWithMemoryTransport):
356
"""Tests for BzrDir.find_repository."""
358
def test_no_repository(self):
359
"""If no repository is found, ('norepository', ) is returned."""
360
backing = self.get_transport()
361
request = self._request_class(backing)
362
self.make_controldir('.')
363
self.assertEqual(smart_req.SmartServerResponse((b'norepository', )),
364
request.execute(b''))
366
def test_nonshared_repository(self):
367
# nonshared repositorys only allow 'find' to return a handle when the
368
# path the repository is being searched on is the same as that that
369
# the repository is at.
370
backing = self.get_transport()
371
request = self._request_class(backing)
372
result = self._make_repository_and_result()
373
self.assertEqual(result, request.execute(b''))
374
self.make_controldir('subdir')
375
self.assertEqual(smart_req.SmartServerResponse((b'norepository', )),
376
request.execute(b'subdir'))
378
def _make_repository_and_result(self, shared=False, format=None):
379
"""Convenience function to setup a repository.
381
:result: The SmartServerResponse to expect when opening it.
383
repo = self.make_repository('.', shared=shared, format=format)
384
if repo.supports_rich_root():
388
if repo._format.supports_tree_reference:
392
if repo._format.supports_external_lookups:
396
if (smart_dir.SmartServerRequestFindRepositoryV3 ==
397
self._request_class):
398
return smart_req.SuccessfulSmartServerResponse(
399
(b'ok', b'', rich_root, subtrees, external,
400
repo._format.network_name()))
401
elif (smart_dir.SmartServerRequestFindRepositoryV2 ==
402
self._request_class):
403
# All tests so far are on formats, and for non-external
405
return smart_req.SuccessfulSmartServerResponse(
406
(b'ok', b'', rich_root, subtrees, external))
408
return smart_req.SuccessfulSmartServerResponse(
409
(b'ok', b'', rich_root, subtrees))
411
def test_shared_repository(self):
412
"""for a shared repository, we get 'ok', 'relpath-to-repo'."""
413
backing = self.get_transport()
414
request = self._request_class(backing)
415
result = self._make_repository_and_result(shared=True)
416
self.assertEqual(result, request.execute(b''))
417
self.make_controldir('subdir')
418
result2 = smart_req.SmartServerResponse(
419
result.args[0:1] + (b'..', ) + result.args[2:])
420
self.assertEqual(result2,
421
request.execute(b'subdir'))
422
self.make_controldir('subdir/deeper')
423
result3 = smart_req.SmartServerResponse(
424
result.args[0:1] + (b'../..', ) + result.args[2:])
425
self.assertEqual(result3,
426
request.execute(b'subdir/deeper'))
428
def test_rich_root_and_subtree_encoding(self):
429
"""Test for the format attributes for rich root and subtree support."""
430
backing = self.get_transport()
431
request = self._request_class(backing)
432
result = self._make_repository_and_result(
433
format='development-subtree')
434
# check the test will be valid
435
self.assertEqual(b'yes', result.args[2])
436
self.assertEqual(b'yes', result.args[3])
437
self.assertEqual(result, request.execute(b''))
439
def test_supports_external_lookups_no_v2(self):
440
"""Test for the supports_external_lookups attribute."""
441
backing = self.get_transport()
442
request = self._request_class(backing)
443
result = self._make_repository_and_result(
444
format='development-subtree')
445
# check the test will be valid
446
self.assertEqual(b'yes', result.args[4])
447
self.assertEqual(result, request.execute(b''))
450
class TestSmartServerBzrDirRequestGetConfigFile(
451
tests.TestCaseWithMemoryTransport):
452
"""Tests for BzrDir.get_config_file."""
454
def test_present(self):
455
backing = self.get_transport()
456
dir = self.make_controldir('.')
457
dir.get_config().set_default_stack_on("/")
458
local_result = dir._get_config()._get_config_file().read()
459
request_class = smart_dir.SmartServerBzrDirRequestConfigFile
460
request = request_class(backing)
461
expected = smart_req.SuccessfulSmartServerResponse((), local_result)
462
self.assertEqual(expected, request.execute(b''))
464
def test_missing(self):
465
backing = self.get_transport()
466
self.make_controldir('.')
467
request_class = smart_dir.SmartServerBzrDirRequestConfigFile
468
request = request_class(backing)
469
expected = smart_req.SuccessfulSmartServerResponse((), b'')
470
self.assertEqual(expected, request.execute(b''))
473
class TestSmartServerBzrDirRequestGetBranches(
474
tests.TestCaseWithMemoryTransport):
475
"""Tests for BzrDir.get_branches."""
477
def test_simple(self):
478
backing = self.get_transport()
479
branch = self.make_branch('.')
480
request_class = smart_dir.SmartServerBzrDirRequestGetBranches
481
request = request_class(backing)
482
local_result = bencode.bencode(
483
{b"": (b"branch", branch._format.network_name())})
484
expected = smart_req.SuccessfulSmartServerResponse(
485
(b"success", ), local_result)
486
self.assertEqual(expected, request.execute(b''))
488
def test_empty(self):
489
backing = self.get_transport()
490
self.make_controldir('.')
491
request_class = smart_dir.SmartServerBzrDirRequestGetBranches
492
request = request_class(backing)
493
local_result = bencode.bencode({})
494
expected = smart_req.SuccessfulSmartServerResponse(
495
(b'success',), local_result)
496
self.assertEqual(expected, request.execute(b''))
499
class TestSmartServerRequestInitializeBzrDir(
500
tests.TestCaseWithMemoryTransport):
502
def test_empty_dir(self):
503
"""Initializing an empty dir should succeed and do it."""
504
backing = self.get_transport()
505
request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
506
self.assertEqual(smart_req.SmartServerResponse((b'ok', )),
507
request.execute(b''))
508
made_dir = controldir.ControlDir.open_from_transport(backing)
509
# no branch, tree or repository is expected with the current
511
self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
512
self.assertRaises(errors.NotBranchError, made_dir.open_branch)
513
self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
515
def test_missing_dir(self):
516
"""Initializing a missing directory should fail like the bzrdir api."""
517
backing = self.get_transport()
518
request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
519
self.assertRaises(errors.NoSuchFile,
520
request.execute, b'subdir')
522
def test_initialized_dir(self):
523
"""Initializing an extant bzrdir should fail like the bzrdir api."""
524
backing = self.get_transport()
525
request = smart_dir.SmartServerRequestInitializeBzrDir(backing)
526
self.make_controldir('subdir')
527
self.assertRaises(errors.AlreadyControlDirError,
528
request.execute, b'subdir')
531
class TestSmartServerRequestBzrDirInitializeEx(
532
tests.TestCaseWithMemoryTransport):
533
"""Basic tests for BzrDir.initialize_ex_1.16 in the smart server.
535
The main unit tests in test_bzrdir exercise the API comprehensively.
538
def test_empty_dir(self):
539
"""Initializing an empty dir should succeed and do it."""
540
backing = self.get_transport()
541
name = self.make_controldir('reference')._format.network_name()
542
request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
544
smart_req.SmartServerResponse((b'', b'', b'', b'', b'', b'', name,
545
b'False', b'', b'', b'')),
546
request.execute(name, b'', b'True', b'False', b'False', b'', b'',
548
made_dir = controldir.ControlDir.open_from_transport(backing)
549
# no branch, tree or repository is expected with the current
551
self.assertRaises(errors.NoWorkingTree, made_dir.open_workingtree)
552
self.assertRaises(errors.NotBranchError, made_dir.open_branch)
553
self.assertRaises(errors.NoRepositoryPresent, made_dir.open_repository)
555
def test_missing_dir(self):
556
"""Initializing a missing directory should fail like the bzrdir api."""
557
backing = self.get_transport()
558
name = self.make_controldir('reference')._format.network_name()
559
request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
560
self.assertRaises(errors.NoSuchFile, request.execute, name,
561
b'subdir/dir', b'False', b'False', b'False', b'',
562
b'', b'', b'', b'False')
564
def test_initialized_dir(self):
565
"""Initializing an extant directory should fail like the bzrdir api."""
566
backing = self.get_transport()
567
name = self.make_controldir('reference')._format.network_name()
568
request = smart_dir.SmartServerRequestBzrDirInitializeEx(backing)
569
self.make_controldir('subdir')
570
self.assertRaises(errors.FileExists, request.execute, name, b'subdir',
571
b'False', b'False', b'False', b'', b'', b'', b'',
575
class TestSmartServerRequestOpenBzrDir(tests.TestCaseWithMemoryTransport):
577
def test_no_directory(self):
578
backing = self.get_transport()
579
request = smart_dir.SmartServerRequestOpenBzrDir(backing)
580
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
581
request.execute(b'does-not-exist'))
583
def test_empty_directory(self):
584
backing = self.get_transport()
585
backing.mkdir('empty')
586
request = smart_dir.SmartServerRequestOpenBzrDir(backing)
587
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
588
request.execute(b'empty'))
590
def test_outside_root_client_path(self):
591
backing = self.get_transport()
592
request = smart_dir.SmartServerRequestOpenBzrDir(
593
backing, root_client_path='root')
594
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
595
request.execute(b'not-root'))
598
class TestSmartServerRequestOpenBzrDir_2_1(tests.TestCaseWithMemoryTransport):
600
def test_no_directory(self):
601
backing = self.get_transport()
602
request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
603
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
604
request.execute(b'does-not-exist'))
606
def test_empty_directory(self):
607
backing = self.get_transport()
608
backing.mkdir('empty')
609
request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
610
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
611
request.execute(b'empty'))
613
def test_present_without_workingtree(self):
614
backing = self.get_transport()
615
request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
616
self.make_controldir('.')
617
self.assertEqual(smart_req.SmartServerResponse((b'yes', b'no')),
618
request.execute(b''))
620
def test_outside_root_client_path(self):
621
backing = self.get_transport()
622
request = smart_dir.SmartServerRequestOpenBzrDir_2_1(
623
backing, root_client_path='root')
624
self.assertEqual(smart_req.SmartServerResponse((b'no',)),
625
request.execute(b'not-root'))
628
class TestSmartServerRequestOpenBzrDir_2_1_disk(TestCaseWithChrootedTransport):
630
def test_present_with_workingtree(self):
631
self.vfs_transport_factory = test_server.LocalURLServer
632
backing = self.get_transport()
633
request = smart_dir.SmartServerRequestOpenBzrDir_2_1(backing)
634
bd = self.make_controldir('.')
635
bd.create_repository()
637
bd.create_workingtree()
638
self.assertEqual(smart_req.SmartServerResponse((b'yes', b'yes')),
639
request.execute(b''))
642
class TestSmartServerRequestOpenBranch(TestCaseWithChrootedTransport):
644
def test_no_branch(self):
645
"""When there is no branch, ('nobranch', ) is returned."""
646
backing = self.get_transport()
647
request = smart_dir.SmartServerRequestOpenBranch(backing)
648
self.make_controldir('.')
649
self.assertEqual(smart_req.SmartServerResponse((b'nobranch', )),
650
request.execute(b''))
652
def test_branch(self):
653
"""When there is a branch, 'ok' is returned."""
654
backing = self.get_transport()
655
request = smart_dir.SmartServerRequestOpenBranch(backing)
656
self.make_branch('.')
657
self.assertEqual(smart_req.SmartServerResponse((b'ok', b'')),
658
request.execute(b''))
660
def test_branch_reference(self):
661
"""When there is a branch reference, the reference URL is returned."""
662
self.vfs_transport_factory = test_server.LocalURLServer
663
backing = self.get_transport()
664
request = smart_dir.SmartServerRequestOpenBranch(backing)
665
branch = self.make_branch('branch')
666
checkout = branch.create_checkout('reference', lightweight=True)
667
reference_url = _mod_bzrbranch.BranchReferenceFormat().get_reference(
668
checkout.controldir).encode('utf-8')
669
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
670
self.assertEqual(smart_req.SmartServerResponse((b'ok', reference_url)),
671
request.execute(b'reference'))
673
def test_notification_on_branch_from_repository(self):
674
"""When there is a repository, the error should return details."""
675
backing = self.get_transport()
676
request = smart_dir.SmartServerRequestOpenBranch(backing)
677
self.make_repository('.')
678
self.assertEqual(smart_req.SmartServerResponse((b'nobranch',)),
679
request.execute(b''))
682
class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport):
684
def test_no_branch(self):
685
"""When there is no branch, ('nobranch', ) is returned."""
686
backing = self.get_transport()
687
self.make_controldir('.')
688
request = smart_dir.SmartServerRequestOpenBranchV2(backing)
689
self.assertEqual(smart_req.SmartServerResponse((b'nobranch', )),
690
request.execute(b''))
692
def test_branch(self):
693
"""When there is a branch, 'ok' is returned."""
694
backing = self.get_transport()
695
expected = self.make_branch('.')._format.network_name()
696
request = smart_dir.SmartServerRequestOpenBranchV2(backing)
697
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
698
(b'branch', expected)),
699
request.execute(b''))
701
def test_branch_reference(self):
702
"""When there is a branch reference, the reference URL is returned."""
703
self.vfs_transport_factory = test_server.LocalURLServer
704
backing = self.get_transport()
705
request = smart_dir.SmartServerRequestOpenBranchV2(backing)
706
branch = self.make_branch('branch')
707
checkout = branch.create_checkout('reference', lightweight=True)
708
reference_url = _mod_bzrbranch.BranchReferenceFormat().get_reference(
709
checkout.controldir).encode('utf-8')
710
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
711
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
712
(b'ref', reference_url)),
713
request.execute(b'reference'))
715
def test_stacked_branch(self):
716
"""Opening a stacked branch does not open the stacked-on branch."""
717
trunk = self.make_branch('trunk')
718
feature = self.make_branch('feature')
719
feature.set_stacked_on_url(trunk.base)
721
_mod_branch.Branch.hooks.install_named_hook(
722
'open', opened_branches.append, None)
723
backing = self.get_transport()
724
request = smart_dir.SmartServerRequestOpenBranchV2(backing)
727
response = request.execute(b'feature')
729
request.teardown_jail()
730
expected_format = feature._format.network_name()
731
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
732
(b'branch', expected_format)),
734
self.assertLength(1, opened_branches)
736
def test_notification_on_branch_from_repository(self):
737
"""When there is a repository, the error should return details."""
738
backing = self.get_transport()
739
request = smart_dir.SmartServerRequestOpenBranchV2(backing)
740
self.make_repository('.')
741
self.assertEqual(smart_req.SmartServerResponse((b'nobranch',)),
742
request.execute(b''))
745
class TestSmartServerRequestOpenBranchV3(TestCaseWithChrootedTransport):
747
def test_no_branch(self):
748
"""When there is no branch, ('nobranch', ) is returned."""
749
backing = self.get_transport()
750
self.make_controldir('.')
751
request = smart_dir.SmartServerRequestOpenBranchV3(backing)
752
self.assertEqual(smart_req.SmartServerResponse((b'nobranch',)),
753
request.execute(b''))
755
def test_branch(self):
756
"""When there is a branch, 'ok' is returned."""
757
backing = self.get_transport()
758
expected = self.make_branch('.')._format.network_name()
759
request = smart_dir.SmartServerRequestOpenBranchV3(backing)
760
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
761
(b'branch', expected)),
762
request.execute(b''))
764
def test_branch_reference(self):
765
"""When there is a branch reference, the reference URL is returned."""
766
self.vfs_transport_factory = test_server.LocalURLServer
767
backing = self.get_transport()
768
request = smart_dir.SmartServerRequestOpenBranchV3(backing)
769
branch = self.make_branch('branch')
770
checkout = branch.create_checkout('reference', lightweight=True)
771
reference_url = _mod_bzrbranch.BranchReferenceFormat().get_reference(
772
checkout.controldir).encode('utf-8')
773
self.assertFileEqual(reference_url, 'reference/.bzr/branch/location')
774
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
775
(b'ref', reference_url)),
776
request.execute(b'reference'))
778
def test_stacked_branch(self):
779
"""Opening a stacked branch does not open the stacked-on branch."""
780
trunk = self.make_branch('trunk')
781
feature = self.make_branch('feature')
782
feature.set_stacked_on_url(trunk.base)
784
_mod_branch.Branch.hooks.install_named_hook(
785
'open', opened_branches.append, None)
786
backing = self.get_transport()
787
request = smart_dir.SmartServerRequestOpenBranchV3(backing)
790
response = request.execute(b'feature')
792
request.teardown_jail()
793
expected_format = feature._format.network_name()
794
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
795
(b'branch', expected_format)),
797
self.assertLength(1, opened_branches)
799
def test_notification_on_branch_from_repository(self):
800
"""When there is a repository, the error should return details."""
801
backing = self.get_transport()
802
request = smart_dir.SmartServerRequestOpenBranchV3(backing)
803
self.make_repository('.')
804
self.assertEqual(smart_req.SmartServerResponse(
805
(b'nobranch', b'location is a repository')),
806
request.execute(b''))
809
class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport):
811
def test_empty(self):
812
"""For an empty branch, the body is empty."""
813
backing = self.get_transport()
814
request = smart_branch.SmartServerRequestRevisionHistory(backing)
815
self.make_branch('.')
816
self.assertEqual(smart_req.SmartServerResponse((b'ok', ), b''),
817
request.execute(b''))
819
def test_not_empty(self):
820
"""For a non-empty branch, the body is empty."""
821
backing = self.get_transport()
822
request = smart_branch.SmartServerRequestRevisionHistory(backing)
823
tree = self.make_branch_and_memory_tree('.')
826
r1 = tree.commit('1st commit')
827
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
830
smart_req.SmartServerResponse((b'ok', ), (b'\x00'.join([r1, r2]))),
831
request.execute(b''))
834
class TestSmartServerBranchRequest(tests.TestCaseWithMemoryTransport):
836
def test_no_branch(self):
837
"""When there is a bzrdir and no branch, NotBranchError is raised."""
838
backing = self.get_transport()
839
request = smart_branch.SmartServerBranchRequest(backing)
840
self.make_controldir('.')
841
self.assertRaises(errors.NotBranchError,
842
request.execute, b'')
844
def test_branch_reference(self):
845
"""When there is a branch reference, NotBranchError is raised."""
846
backing = self.get_transport()
847
request = smart_branch.SmartServerBranchRequest(backing)
848
branch = self.make_branch('branch')
849
branch.create_checkout('reference', lightweight=True)
850
self.assertRaises(errors.NotBranchError,
851
request.execute, b'checkout')
854
class TestSmartServerBranchRequestLastRevisionInfo(
855
tests.TestCaseWithMemoryTransport):
857
def test_empty(self):
858
"""For an empty branch, the result is ('ok', '0', b'null:')."""
859
backing = self.get_transport()
860
request = smart_branch.SmartServerBranchRequestLastRevisionInfo(
862
self.make_branch('.')
864
smart_req.SmartServerResponse((b'ok', b'0', b'null:')),
865
request.execute(b''))
867
def test_ghost(self):
868
"""For an empty branch, the result is ('ok', '0', b'null:')."""
869
backing = self.get_transport()
870
request = smart_branch.SmartServerBranchRequestLastRevisionInfo(
872
branch = self.make_branch('.')
874
def last_revision_info():
875
raise errors.GhostRevisionsHaveNoRevno(b'revid1', b'revid2')
876
self.overrideAttr(branch, 'last_revision_info', last_revision_info)
877
self.assertRaises(errors.GhostRevisionsHaveNoRevno,
878
request.do_with_branch, branch)
880
def test_not_empty(self):
881
"""For a non-empty branch, the result is ('ok', 'revno', 'revid')."""
882
backing = self.get_transport()
883
request = smart_branch.SmartServerBranchRequestLastRevisionInfo(
885
tree = self.make_branch_and_memory_tree('.')
888
rev_id_utf8 = u'\xc8'.encode('utf-8')
889
tree.commit('1st commit')
890
tree.commit('2nd commit', rev_id=rev_id_utf8)
893
smart_req.SmartServerResponse((b'ok', b'2', rev_id_utf8)),
894
request.execute(b''))
897
class TestSmartServerBranchRequestRevisionIdToRevno(
898
tests.TestCaseWithMemoryTransport):
901
backing = self.get_transport()
902
request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
904
self.make_branch('.')
905
self.assertEqual(smart_req.SmartServerResponse((b'ok', b'0')),
906
request.execute(b'', b'null:'))
908
def test_simple(self):
909
backing = self.get_transport()
910
request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
912
tree = self.make_branch_and_memory_tree('.')
915
r1 = tree.commit('1st commit')
918
smart_req.SmartServerResponse((b'ok', b'1')),
919
request.execute(b'', r1))
921
def test_not_found(self):
922
backing = self.get_transport()
923
request = smart_branch.SmartServerBranchRequestRevisionIdToRevno(
925
self.make_branch('.')
927
smart_req.FailedSmartServerResponse(
928
(b'NoSuchRevision', b'idontexist')),
929
request.execute(b'', b'idontexist'))
932
class TestSmartServerBranchRequestGetConfigFile(
933
tests.TestCaseWithMemoryTransport):
935
def test_default(self):
936
"""With no file, we get empty content."""
937
backing = self.get_transport()
938
request = smart_branch.SmartServerBranchGetConfigFile(backing)
939
self.make_branch('.')
940
# there should be no file by default
942
self.assertEqual(smart_req.SmartServerResponse((b'ok', ), content),
943
request.execute(b''))
945
def test_with_content(self):
946
# SmartServerBranchGetConfigFile should return the content from
947
# branch.control_files.get('branch.conf') for now - in the future it
948
# may perform more complex processing.
949
backing = self.get_transport()
950
request = smart_branch.SmartServerBranchGetConfigFile(backing)
951
branch = self.make_branch('.')
952
branch._transport.put_bytes('branch.conf', b'foo bar baz')
954
smart_req.SmartServerResponse((b'ok', ), b'foo bar baz'),
955
request.execute(b''))
958
class TestLockedBranch(tests.TestCaseWithMemoryTransport):
960
def get_lock_tokens(self, branch):
961
branch_token = branch.lock_write().token
962
repo_token = branch.repository.lock_write().repository_token
963
branch.repository.unlock()
964
return branch_token, repo_token
967
class TestSmartServerBranchRequestPutConfigFile(TestLockedBranch):
969
def test_with_content(self):
970
backing = self.get_transport()
971
request = smart_branch.SmartServerBranchPutConfigFile(backing)
972
branch = self.make_branch('.')
973
branch_token, repo_token = self.get_lock_tokens(branch)
974
self.assertIs(None, request.execute(b'', branch_token, repo_token))
976
smart_req.SmartServerResponse((b'ok', )),
977
request.do_body(b'foo bar baz'))
979
branch.control_transport.get_bytes('branch.conf'),
984
class TestSmartServerBranchRequestSetConfigOption(TestLockedBranch):
986
def test_value_name(self):
987
branch = self.make_branch('.')
988
request = smart_branch.SmartServerBranchRequestSetConfigOption(
989
branch.controldir.root_transport)
990
branch_token, repo_token = self.get_lock_tokens(branch)
991
config = branch._get_config()
992
result = request.execute(b'', branch_token, repo_token, b'bar', b'foo',
994
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
995
self.assertEqual('bar', config.get_option('foo'))
999
def test_value_name_section(self):
1000
branch = self.make_branch('.')
1001
request = smart_branch.SmartServerBranchRequestSetConfigOption(
1002
branch.controldir.root_transport)
1003
branch_token, repo_token = self.get_lock_tokens(branch)
1004
config = branch._get_config()
1005
result = request.execute(b'', branch_token, repo_token, b'bar', b'foo',
1007
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
1008
self.assertEqual('bar', config.get_option('foo', 'gam'))
1013
class TestSmartServerBranchRequestSetConfigOptionDict(TestLockedBranch):
1016
TestLockedBranch.setUp(self)
1017
# A dict with non-ascii keys and values to exercise unicode
1019
self.encoded_value_dict = (
1020
b'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde')
1022
'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
1024
def test_value_name(self):
1025
branch = self.make_branch('.')
1026
request = smart_branch.SmartServerBranchRequestSetConfigOptionDict(
1027
branch.controldir.root_transport)
1028
branch_token, repo_token = self.get_lock_tokens(branch)
1029
config = branch._get_config()
1030
result = request.execute(b'', branch_token, repo_token,
1031
self.encoded_value_dict, b'foo', b'')
1032
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
1033
self.assertEqual(self.value_dict, config.get_option('foo'))
1037
def test_value_name_section(self):
1038
branch = self.make_branch('.')
1039
request = smart_branch.SmartServerBranchRequestSetConfigOptionDict(
1040
branch.controldir.root_transport)
1041
branch_token, repo_token = self.get_lock_tokens(branch)
1042
config = branch._get_config()
1043
result = request.execute(b'', branch_token, repo_token,
1044
self.encoded_value_dict, b'foo', b'gam')
1045
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), result)
1046
self.assertEqual(self.value_dict, config.get_option('foo', 'gam'))
1051
class TestSmartServerBranchRequestSetTagsBytes(TestLockedBranch):
1052
# Only called when the branch format and tags match [yay factory
1053
# methods] so only need to test straight forward cases.
1055
def test_set_bytes(self):
1056
base_branch = self.make_branch('base')
1057
tag_bytes = base_branch._get_tags_bytes()
1058
# get_lock_tokens takes out a lock.
1059
branch_token, repo_token = self.get_lock_tokens(base_branch)
1060
request = smart_branch.SmartServerBranchSetTagsBytes(
1061
self.get_transport())
1062
response = request.execute(b'base', branch_token, repo_token)
1063
self.assertEqual(None, response)
1064
response = request.do_chunk(tag_bytes)
1065
self.assertEqual(None, response)
1066
response = request.do_end()
1068
smart_req.SuccessfulSmartServerResponse(()), response)
1069
base_branch.unlock()
1071
def test_lock_failed(self):
1072
base_branch = self.make_branch('base')
1073
base_branch.lock_write()
1074
tag_bytes = base_branch._get_tags_bytes()
1075
request = smart_branch.SmartServerBranchSetTagsBytes(
1076
self.get_transport())
1077
self.assertRaises(errors.TokenMismatch, request.execute,
1078
b'base', b'wrong token', b'wrong token')
1079
# The request handler will keep processing the message parts, so even
1080
# if the request fails immediately do_chunk and do_end are still
1082
request.do_chunk(tag_bytes)
1084
base_branch.unlock()
1087
class SetLastRevisionTestBase(TestLockedBranch):
1088
"""Base test case for verbs that implement set_last_revision."""
1091
super(SetLastRevisionTestBase, self).setUp()
1092
backing_transport = self.get_transport()
1093
self.request = self.request_class(backing_transport)
1094
self.tree = self.make_branch_and_memory_tree('.')
1096
def lock_branch(self):
1097
return self.get_lock_tokens(self.tree.branch)
1099
def unlock_branch(self):
1100
self.tree.branch.unlock()
1102
def set_last_revision(self, revision_id, revno):
1103
branch_token, repo_token = self.lock_branch()
1104
response = self._set_last_revision(
1105
revision_id, revno, branch_token, repo_token)
1106
self.unlock_branch()
1109
def assertRequestSucceeds(self, revision_id, revno):
1110
response = self.set_last_revision(revision_id, revno)
1111
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
1115
class TestSetLastRevisionVerbMixin(object):
1116
"""Mixin test case for verbs that implement set_last_revision."""
1118
def test_set_null_to_null(self):
1119
"""An empty branch can have its last revision set to b'null:'."""
1120
self.assertRequestSucceeds(b'null:', 0)
1122
def test_NoSuchRevision(self):
1123
"""If the revision_id is not present, the verb returns NoSuchRevision.
1125
revision_id = b'non-existent revision'
1127
smart_req.FailedSmartServerResponse(
1128
(b'NoSuchRevision', revision_id)),
1129
self.set_last_revision(revision_id, 1))
1131
def make_tree_with_two_commits(self):
1132
self.tree.lock_write()
1134
rev_id_utf8 = u'\xc8'.encode('utf-8')
1135
self.tree.commit('1st commit', rev_id=rev_id_utf8)
1136
self.tree.commit('2nd commit', rev_id=b'rev-2')
1139
def test_branch_last_revision_info_is_updated(self):
1140
"""A branch's tip can be set to a revision that is present in its
1143
# Make a branch with an empty revision history, but two revisions in
1145
self.make_tree_with_two_commits()
1146
rev_id_utf8 = u'\xc8'.encode('utf-8')
1147
self.tree.branch.set_last_revision_info(0, b'null:')
1149
(0, b'null:'), self.tree.branch.last_revision_info())
1150
# We can update the branch to a revision that is present in the
1152
self.assertRequestSucceeds(rev_id_utf8, 1)
1154
(1, rev_id_utf8), self.tree.branch.last_revision_info())
1156
def test_branch_last_revision_info_rewind(self):
1157
"""A branch's tip can be set to a revision that is an ancestor of the
1160
self.make_tree_with_two_commits()
1161
rev_id_utf8 = u'\xc8'.encode('utf-8')
1163
(2, b'rev-2'), self.tree.branch.last_revision_info())
1164
self.assertRequestSucceeds(rev_id_utf8, 1)
1166
(1, rev_id_utf8), self.tree.branch.last_revision_info())
1168
def test_TipChangeRejected(self):
1169
"""If a pre_change_branch_tip hook raises TipChangeRejected, the verb
1170
returns TipChangeRejected.
1172
rejection_message = u'rejection message\N{INTERROBANG}'
1174
def hook_that_rejects(params):
1175
raise errors.TipChangeRejected(rejection_message)
1176
_mod_branch.Branch.hooks.install_named_hook(
1177
'pre_change_branch_tip', hook_that_rejects, None)
1179
smart_req.FailedSmartServerResponse(
1180
(b'TipChangeRejected', rejection_message.encode('utf-8'))),
1181
self.set_last_revision(b'null:', 0))
1184
class TestSmartServerBranchRequestSetLastRevision(
1185
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
1186
"""Tests for Branch.set_last_revision verb."""
1188
request_class = smart_branch.SmartServerBranchRequestSetLastRevision
1190
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
1191
return self.request.execute(
1192
b'', branch_token, repo_token, revision_id)
1195
class TestSmartServerBranchRequestSetLastRevisionInfo(
1196
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
1197
"""Tests for Branch.set_last_revision_info verb."""
1199
request_class = smart_branch.SmartServerBranchRequestSetLastRevisionInfo
1201
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
1202
return self.request.execute(
1203
b'', branch_token, repo_token, revno, revision_id)
1205
def test_NoSuchRevision(self):
1206
"""Branch.set_last_revision_info does not have to return
1207
NoSuchRevision if the revision_id is absent.
1209
raise tests.TestNotApplicable()
1212
class TestSmartServerBranchRequestSetLastRevisionEx(
1213
SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
1214
"""Tests for Branch.set_last_revision_ex verb."""
1216
request_class = smart_branch.SmartServerBranchRequestSetLastRevisionEx
1218
def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
1219
return self.request.execute(
1220
b'', branch_token, repo_token, revision_id, 0, 0)
1222
def assertRequestSucceeds(self, revision_id, revno):
1223
response = self.set_last_revision(revision_id, revno)
1225
smart_req.SuccessfulSmartServerResponse(
1226
(b'ok', revno, revision_id)),
1229
def test_branch_last_revision_info_rewind(self):
1230
"""A branch's tip can be set to a revision that is an ancestor of the
1231
current tip, but only if allow_overwrite_descendant is passed.
1233
self.make_tree_with_two_commits()
1234
rev_id_utf8 = u'\xc8'.encode('utf-8')
1236
(2, b'rev-2'), self.tree.branch.last_revision_info())
1237
# If allow_overwrite_descendant flag is 0, then trying to set the tip
1238
# to an older revision ID has no effect.
1239
branch_token, repo_token = self.lock_branch()
1240
response = self.request.execute(
1241
b'', branch_token, repo_token, rev_id_utf8, 0, 0)
1243
smart_req.SuccessfulSmartServerResponse((b'ok', 2, b'rev-2')),
1246
(2, b'rev-2'), self.tree.branch.last_revision_info())
1248
# If allow_overwrite_descendant flag is 1, then setting the tip to an
1250
response = self.request.execute(
1251
b'', branch_token, repo_token, rev_id_utf8, 0, 1)
1253
smart_req.SuccessfulSmartServerResponse((b'ok', 1, rev_id_utf8)),
1255
self.unlock_branch()
1257
(1, rev_id_utf8), self.tree.branch.last_revision_info())
1259
def make_branch_with_divergent_history(self):
1260
"""Make a branch with divergent history in its repo.
1262
The branch's tip will be 'child-2', and the repo will also contain
1263
'child-1', which diverges from a common base revision.
1265
self.tree.lock_write()
1267
self.tree.commit('1st commit')
1268
revno_1, revid_1 = self.tree.branch.last_revision_info()
1269
self.tree.commit('2nd commit', rev_id=b'child-1')
1270
# Undo the second commit
1271
self.tree.branch.set_last_revision_info(revno_1, revid_1)
1272
self.tree.set_parent_ids([revid_1])
1273
# Make a new second commit, child-2. child-2 has diverged from
1275
self.tree.commit('2nd commit', rev_id=b'child-2')
1278
def test_not_allow_diverged(self):
1279
"""If allow_diverged is not passed, then setting a divergent history
1280
returns a Diverged error.
1282
self.make_branch_with_divergent_history()
1284
smart_req.FailedSmartServerResponse((b'Diverged',)),
1285
self.set_last_revision(b'child-1', 2))
1286
# The branch tip was not changed.
1287
self.assertEqual(b'child-2', self.tree.branch.last_revision())
1289
def test_allow_diverged(self):
1290
"""If allow_diverged is passed, then setting a divergent history
1293
self.make_branch_with_divergent_history()
1294
branch_token, repo_token = self.lock_branch()
1295
response = self.request.execute(
1296
b'', branch_token, repo_token, b'child-1', 1, 0)
1298
smart_req.SuccessfulSmartServerResponse((b'ok', 2, b'child-1')),
1300
self.unlock_branch()
1301
# The branch tip was changed.
1302
self.assertEqual(b'child-1', self.tree.branch.last_revision())
1305
class TestSmartServerBranchBreakLock(tests.TestCaseWithMemoryTransport):
1307
def test_lock_to_break(self):
1308
base_branch = self.make_branch('base')
1309
request = smart_branch.SmartServerBranchBreakLock(
1310
self.get_transport())
1311
base_branch.lock_write()
1313
smart_req.SuccessfulSmartServerResponse((b'ok', ), None),
1314
request.execute(b'base'))
1316
def test_nothing_to_break(self):
1317
self.make_branch('base')
1318
request = smart_branch.SmartServerBranchBreakLock(
1319
self.get_transport())
1321
smart_req.SuccessfulSmartServerResponse((b'ok', ), None),
1322
request.execute(b'base'))
1325
class TestSmartServerBranchRequestGetParent(tests.TestCaseWithMemoryTransport):
1327
def test_get_parent_none(self):
1328
self.make_branch('base')
1329
request = smart_branch.SmartServerBranchGetParent(self.get_transport())
1330
response = request.execute(b'base')
1332
smart_req.SuccessfulSmartServerResponse((b'',)), response)
1334
def test_get_parent_something(self):
1335
base_branch = self.make_branch('base')
1336
base_branch.set_parent(self.get_url('foo'))
1337
request = smart_branch.SmartServerBranchGetParent(self.get_transport())
1338
response = request.execute(b'base')
1340
smart_req.SuccessfulSmartServerResponse((b"../foo",)),
1344
class TestSmartServerBranchRequestSetParent(TestLockedBranch):
1346
def test_set_parent_none(self):
1347
branch = self.make_branch('base', format="1.9")
1349
branch._set_parent_location('foo')
1351
request = smart_branch.SmartServerBranchRequestSetParentLocation(
1352
self.get_transport())
1353
branch_token, repo_token = self.get_lock_tokens(branch)
1355
response = request.execute(b'base', branch_token, repo_token, b'')
1358
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
1359
# Refresh branch as SetParentLocation modified it
1360
branch = branch.controldir.open_branch()
1361
self.assertEqual(None, branch.get_parent())
1363
def test_set_parent_something(self):
1364
branch = self.make_branch('base', format="1.9")
1365
request = smart_branch.SmartServerBranchRequestSetParentLocation(
1366
self.get_transport())
1367
branch_token, repo_token = self.get_lock_tokens(branch)
1369
response = request.execute(b'base', branch_token, repo_token,
1373
self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
1374
refreshed = _mod_branch.Branch.open(branch.base)
1375
self.assertEqual('http://bar/', refreshed.get_parent())
1378
class TestSmartServerBranchRequestGetTagsBytes(
1379
tests.TestCaseWithMemoryTransport):
1380
# Only called when the branch format and tags match [yay factory
1381
# methods] so only need to test straight forward cases.
1383
def test_get_bytes(self):
1384
self.make_branch('base')
1385
request = smart_branch.SmartServerBranchGetTagsBytes(
1386
self.get_transport())
1387
response = request.execute(b'base')
1389
smart_req.SuccessfulSmartServerResponse((b'',)), response)
1392
class TestSmartServerBranchRequestGetStackedOnURL(
1393
tests.TestCaseWithMemoryTransport):
1395
def test_get_stacked_on_url(self):
1396
self.make_branch('base', format='1.6')
1397
stacked_branch = self.make_branch('stacked', format='1.6')
1398
# typically should be relative
1399
stacked_branch.set_stacked_on_url('../base')
1400
request = smart_branch.SmartServerBranchRequestGetStackedOnURL(
1401
self.get_transport())
1402
response = request.execute(b'stacked')
1404
smart_req.SmartServerResponse((b'ok', b'../base')),
1408
class TestSmartServerBranchRequestLockWrite(TestLockedBranch):
1410
def test_lock_write_on_unlocked_branch(self):
1411
backing = self.get_transport()
1412
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1413
branch = self.make_branch('.', format='knit')
1414
repository = branch.repository
1415
response = request.execute(b'')
1416
branch_nonce = branch.control_files._lock.peek().get('nonce')
1417
repository_nonce = repository.control_files._lock.peek().get('nonce')
1418
self.assertEqual(smart_req.SmartServerResponse(
1419
(b'ok', branch_nonce, repository_nonce)),
1421
# The branch (and associated repository) is now locked. Verify that
1422
# with a new branch object.
1423
new_branch = repository.controldir.open_branch()
1424
self.assertRaises(errors.LockContention, new_branch.lock_write)
1426
request = smart_branch.SmartServerBranchRequestUnlock(backing)
1427
response = request.execute(b'', branch_nonce, repository_nonce)
1429
def test_lock_write_on_locked_branch(self):
1430
backing = self.get_transport()
1431
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1432
branch = self.make_branch('.')
1433
branch_token = branch.lock_write().token
1434
branch.leave_lock_in_place()
1436
response = request.execute(b'')
1438
smart_req.SmartServerResponse((b'LockContention',)), response)
1440
branch.lock_write(branch_token)
1441
branch.dont_leave_lock_in_place()
1444
def test_lock_write_with_tokens_on_locked_branch(self):
1445
backing = self.get_transport()
1446
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1447
branch = self.make_branch('.', format='knit')
1448
branch_token, repo_token = self.get_lock_tokens(branch)
1449
branch.leave_lock_in_place()
1450
branch.repository.leave_lock_in_place()
1452
response = request.execute(b'',
1453
branch_token, repo_token)
1455
smart_req.SmartServerResponse((b'ok', branch_token, repo_token)),
1458
branch.repository.lock_write(repo_token)
1459
branch.repository.dont_leave_lock_in_place()
1460
branch.repository.unlock()
1461
branch.lock_write(branch_token)
1462
branch.dont_leave_lock_in_place()
1465
def test_lock_write_with_mismatched_tokens_on_locked_branch(self):
1466
backing = self.get_transport()
1467
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1468
branch = self.make_branch('.', format='knit')
1469
branch_token, repo_token = self.get_lock_tokens(branch)
1470
branch.leave_lock_in_place()
1471
branch.repository.leave_lock_in_place()
1473
response = request.execute(b'',
1474
branch_token + b'xxx', repo_token)
1476
smart_req.SmartServerResponse((b'TokenMismatch',)), response)
1478
branch.repository.lock_write(repo_token)
1479
branch.repository.dont_leave_lock_in_place()
1480
branch.repository.unlock()
1481
branch.lock_write(branch_token)
1482
branch.dont_leave_lock_in_place()
1485
def test_lock_write_on_locked_repo(self):
1486
backing = self.get_transport()
1487
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1488
branch = self.make_branch('.', format='knit')
1489
repo = branch.repository
1490
repo_token = repo.lock_write().repository_token
1491
repo.leave_lock_in_place()
1493
response = request.execute(b'')
1495
smart_req.SmartServerResponse((b'LockContention',)), response)
1497
repo.lock_write(repo_token)
1498
repo.dont_leave_lock_in_place()
1501
def test_lock_write_on_readonly_transport(self):
1502
backing = self.get_readonly_transport()
1503
request = smart_branch.SmartServerBranchRequestLockWrite(backing)
1504
self.make_branch('.')
1505
root = self.get_transport().clone('/')
1506
path = urlutils.relative_url(root.base, self.get_transport().base)
1507
response = request.execute(path.encode('utf-8'))
1508
error_name, lock_str, why_str = response.args
1509
self.assertFalse(response.is_successful())
1510
self.assertEqual(b'LockFailed', error_name)
1513
class TestSmartServerBranchRequestGetPhysicalLockStatus(TestLockedBranch):
1515
def test_true(self):
1516
backing = self.get_transport()
1517
request = smart_branch.SmartServerBranchRequestGetPhysicalLockStatus(
1519
branch = self.make_branch('.')
1520
branch_token, repo_token = self.get_lock_tokens(branch)
1521
self.assertEqual(True, branch.get_physical_lock_status())
1522
response = request.execute(b'')
1524
smart_req.SmartServerResponse((b'yes',)), response)
1527
def test_false(self):
1528
backing = self.get_transport()
1529
request = smart_branch.SmartServerBranchRequestGetPhysicalLockStatus(
1531
branch = self.make_branch('.')
1532
self.assertEqual(False, branch.get_physical_lock_status())
1533
response = request.execute(b'')
1535
smart_req.SmartServerResponse((b'no',)), response)
1538
class TestSmartServerBranchRequestUnlock(TestLockedBranch):
1540
def test_unlock_on_locked_branch_and_repo(self):
1541
backing = self.get_transport()
1542
request = smart_branch.SmartServerBranchRequestUnlock(backing)
1543
branch = self.make_branch('.', format='knit')
1545
branch_token, repo_token = self.get_lock_tokens(branch)
1546
# Unlock the branch (and repo) object, leaving the physical locks
1548
branch.leave_lock_in_place()
1549
branch.repository.leave_lock_in_place()
1551
response = request.execute(b'',
1552
branch_token, repo_token)
1554
smart_req.SmartServerResponse((b'ok',)), response)
1555
# The branch is now unlocked. Verify that with a new branch
1557
new_branch = branch.controldir.open_branch()
1558
new_branch.lock_write()
1561
def test_unlock_on_unlocked_branch_unlocked_repo(self):
1562
backing = self.get_transport()
1563
request = smart_branch.SmartServerBranchRequestUnlock(backing)
1564
self.make_branch('.', format='knit')
1565
response = request.execute(
1566
b'', b'branch token', b'repo token')
1568
smart_req.SmartServerResponse((b'TokenMismatch',)), response)
1570
def test_unlock_on_unlocked_branch_locked_repo(self):
1571
backing = self.get_transport()
1572
request = smart_branch.SmartServerBranchRequestUnlock(backing)
1573
branch = self.make_branch('.', format='knit')
1574
# Lock the repository.
1575
repo_token = branch.repository.lock_write().repository_token
1576
branch.repository.leave_lock_in_place()
1577
branch.repository.unlock()
1578
# Issue branch lock_write request on the unlocked branch (with locked
1580
response = request.execute(b'', b'branch token', repo_token)
1582
smart_req.SmartServerResponse((b'TokenMismatch',)), response)
1584
branch.repository.lock_write(repo_token)
1585
branch.repository.dont_leave_lock_in_place()
1586
branch.repository.unlock()
1589
class TestSmartServerRepositoryRequest(tests.TestCaseWithMemoryTransport):
1591
def test_no_repository(self):
1592
"""Raise NoRepositoryPresent when there is a bzrdir and no repo."""
1593
# we test this using a shared repository above the named path,
1594
# thus checking the right search logic is used - that is, that
1595
# its the exact path being looked at and the server is not
1597
backing = self.get_transport()
1598
request = smart_repo.SmartServerRepositoryRequest(backing)
1599
self.make_repository('.', shared=True)
1600
self.make_controldir('subdir')
1601
self.assertRaises(errors.NoRepositoryPresent,
1602
request.execute, b'subdir')
1605
class TestSmartServerRepositoryAddSignatureText(
1606
tests.TestCaseWithMemoryTransport):
1608
def test_add_text(self):
1609
backing = self.get_transport()
1610
request = smart_repo.SmartServerRepositoryAddSignatureText(backing)
1611
tree = self.make_branch_and_memory_tree('.')
1612
write_token = tree.lock_write()
1613
self.addCleanup(tree.unlock)
1615
tree.commit("Message", rev_id=b'rev1')
1616
tree.branch.repository.start_write_group()
1617
write_group_tokens = tree.branch.repository.suspend_write_group()
1619
None, request.execute(
1620
b'', write_token, b'rev1',
1621
*[token.encode('utf-8') for token in write_group_tokens]))
1622
response = request.do_body(b'somesignature')
1623
self.assertTrue(response.is_successful())
1624
self.assertEqual(response.args[0], b'ok')
1625
write_group_tokens = [token.decode('utf-8')
1626
for token in response.args[1:]]
1627
tree.branch.repository.resume_write_group(write_group_tokens)
1628
tree.branch.repository.commit_write_group()
1630
self.assertEqual(b"somesignature",
1631
tree.branch.repository.get_signature_text(b"rev1"))
1634
class TestSmartServerRepositoryAllRevisionIds(
1635
tests.TestCaseWithMemoryTransport):
1637
def test_empty(self):
1638
"""An empty body should be returned for an empty repository."""
1639
backing = self.get_transport()
1640
request = smart_repo.SmartServerRepositoryAllRevisionIds(backing)
1641
self.make_repository('.')
1643
smart_req.SuccessfulSmartServerResponse((b"ok", ), b""),
1644
request.execute(b''))
1646
def test_some_revisions(self):
1647
"""An empty body should be returned for an empty repository."""
1648
backing = self.get_transport()
1649
request = smart_repo.SmartServerRepositoryAllRevisionIds(backing)
1650
tree = self.make_branch_and_memory_tree('.')
1653
tree.commit(rev_id=b'origineel', message="message")
1654
tree.commit(rev_id=b'nog-een-revisie', message="message")
1657
request.execute(b''),
1658
[smart_req.SuccessfulSmartServerResponse(
1659
(b"ok", ), b"origineel\nnog-een-revisie"),
1660
smart_req.SuccessfulSmartServerResponse(
1661
(b"ok", ), b"nog-een-revisie\norigineel")])
1664
class TestSmartServerRepositoryBreakLock(tests.TestCaseWithMemoryTransport):
1666
def test_lock_to_break(self):
1667
backing = self.get_transport()
1668
request = smart_repo.SmartServerRepositoryBreakLock(backing)
1669
tree = self.make_branch_and_memory_tree('.')
1670
tree.branch.repository.lock_write()
1672
smart_req.SuccessfulSmartServerResponse((b'ok', ), None),
1673
request.execute(b''))
1675
def test_nothing_to_break(self):
1676
backing = self.get_transport()
1677
request = smart_repo.SmartServerRepositoryBreakLock(backing)
1678
self.make_branch_and_memory_tree('.')
1680
smart_req.SuccessfulSmartServerResponse((b'ok', ), None),
1681
request.execute(b''))
1684
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
1686
def test_trivial_bzipped(self):
1687
# This tests that the wire encoding is actually bzipped
1688
backing = self.get_transport()
1689
request = smart_repo.SmartServerRepositoryGetParentMap(backing)
1690
self.make_branch_and_memory_tree('.')
1692
self.assertEqual(None,
1693
request.execute(b'', b'missing-id'))
1694
# Note that it returns a body that is bzipped.
1696
smart_req.SuccessfulSmartServerResponse(
1697
(b'ok', ), bz2.compress(b'')),
1698
request.do_body(b'\n\n0\n'))
1700
def test_trivial_include_missing(self):
1701
backing = self.get_transport()
1702
request = smart_repo.SmartServerRepositoryGetParentMap(backing)
1703
self.make_branch_and_memory_tree('.')
1706
None, request.execute(b'', b'missing-id', b'include-missing:'))
1708
smart_req.SuccessfulSmartServerResponse(
1709
(b'ok', ), bz2.compress(b'missing:missing-id')),
1710
request.do_body(b'\n\n0\n'))
1713
class TestSmartServerRepositoryGetRevisionGraph(
1714
tests.TestCaseWithMemoryTransport):
1716
def test_none_argument(self):
1717
backing = self.get_transport()
1718
request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
1719
tree = self.make_branch_and_memory_tree('.')
1722
r1 = tree.commit('1st commit')
1723
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1726
# the lines of revision_id->revision_parent_list has no guaranteed
1727
# order coming out of a dict, so sort both our test and response
1728
lines = sorted([b' '.join([r2, r1]), r1])
1729
response = request.execute(b'', b'')
1730
response.body = b'\n'.join(sorted(response.body.split(b'\n')))
1733
smart_req.SmartServerResponse((b'ok', ), b'\n'.join(lines)),
1736
def test_specific_revision_argument(self):
1737
backing = self.get_transport()
1738
request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
1739
tree = self.make_branch_and_memory_tree('.')
1742
rev_id_utf8 = u'\xc9'.encode('utf-8')
1743
tree.commit('1st commit', rev_id=rev_id_utf8)
1744
tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1747
self.assertEqual(smart_req.SmartServerResponse((b'ok', ), rev_id_utf8),
1748
request.execute(b'', rev_id_utf8))
1750
def test_no_such_revision(self):
1751
backing = self.get_transport()
1752
request = smart_repo.SmartServerRepositoryGetRevisionGraph(backing)
1753
tree = self.make_branch_and_memory_tree('.')
1756
tree.commit('1st commit')
1759
# Note that it still returns body (of zero bytes).
1760
self.assertEqual(smart_req.SmartServerResponse(
1761
(b'nosuchrevision', b'missingrevision', ), b''),
1762
request.execute(b'', b'missingrevision'))
1765
class TestSmartServerRepositoryGetRevIdForRevno(
1766
tests.TestCaseWithMemoryTransport):
1768
def test_revno_found(self):
1769
backing = self.get_transport()
1770
request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
1771
tree = self.make_branch_and_memory_tree('.')
1774
rev1_id_utf8 = u'\xc8'.encode('utf-8')
1775
rev2_id_utf8 = u'\xc9'.encode('utf-8')
1776
tree.commit('1st commit', rev_id=rev1_id_utf8)
1777
tree.commit('2nd commit', rev_id=rev2_id_utf8)
1780
self.assertEqual(smart_req.SmartServerResponse((b'ok', rev1_id_utf8)),
1781
request.execute(b'', 1, (2, rev2_id_utf8)))
1783
def test_known_revid_missing(self):
1784
backing = self.get_transport()
1785
request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
1786
self.make_repository('.')
1788
smart_req.FailedSmartServerResponse((b'nosuchrevision', b'ghost')),
1789
request.execute(b'', 1, (2, b'ghost')))
1791
def test_history_incomplete(self):
1792
backing = self.get_transport()
1793
request = smart_repo.SmartServerRepositoryGetRevIdForRevno(backing)
1794
parent = self.make_branch_and_memory_tree('parent', format='1.9')
1796
parent.add([''], [b'TREE_ROOT'])
1797
parent.commit(message='first commit')
1798
r2 = parent.commit(message='second commit')
1800
local = self.make_branch_and_memory_tree('local', format='1.9')
1801
local.branch.pull(parent.branch)
1802
local.set_parent_ids([r2])
1803
r3 = local.commit(message='local commit')
1804
local.branch.create_clone_on_transport(
1805
self.get_transport('stacked'), stacked_on=self.get_url('parent'))
1807
smart_req.SmartServerResponse((b'history-incomplete', 2, r2)),
1808
request.execute(b'stacked', 1, (3, r3)))
1811
class TestSmartServerRepositoryIterRevisions(
1812
tests.TestCaseWithMemoryTransport):
1814
def test_basic(self):
1815
backing = self.get_transport()
1816
request = smart_repo.SmartServerRepositoryIterRevisions(backing)
1817
tree = self.make_branch_and_memory_tree('.', format='2a')
1820
tree.commit('1st commit', rev_id=b"rev1")
1821
tree.commit('2nd commit', rev_id=b"rev2")
1824
self.assertIs(None, request.execute(b''))
1825
response = request.do_body(b"rev1\nrev2")
1826
self.assertTrue(response.is_successful())
1827
# Format 2a uses serializer format 10
1828
self.assertEqual(response.args, (b"ok", b"10"))
1830
self.addCleanup(tree.branch.lock_read().unlock)
1831
entries = [zlib.compress(record.get_bytes_as("fulltext")) for record in
1832
tree.branch.repository.revisions.get_record_stream(
1833
[(b"rev1", ), (b"rev2", )], "unordered", True)]
1835
contents = b"".join(response.body_stream)
1836
self.assertTrue(contents in (
1837
b"".join([entries[0], entries[1]]),
1838
b"".join([entries[1], entries[0]])))
1840
def test_missing(self):
1841
backing = self.get_transport()
1842
request = smart_repo.SmartServerRepositoryIterRevisions(backing)
1843
self.make_branch_and_memory_tree('.', format='2a')
1845
self.assertIs(None, request.execute(b''))
1846
response = request.do_body(b"rev1\nrev2")
1847
self.assertTrue(response.is_successful())
1848
# Format 2a uses serializer format 10
1849
self.assertEqual(response.args, (b"ok", b"10"))
1851
contents = b"".join(response.body_stream)
1852
self.assertEqual(contents, b"")
1855
class GetStreamTestBase(tests.TestCaseWithMemoryTransport):
1857
def make_two_commit_repo(self):
1858
tree = self.make_branch_and_memory_tree('.')
1861
r1 = tree.commit('1st commit')
1862
r2 = tree.commit('2nd commit', rev_id=u'\xc8'.encode('utf-8'))
1864
repo = tree.branch.repository
1868
class TestSmartServerRepositoryGetStream(GetStreamTestBase):
1870
def test_ancestry_of(self):
1871
"""The search argument may be a 'ancestry-of' some heads'."""
1872
backing = self.get_transport()
1873
request = smart_repo.SmartServerRepositoryGetStream(backing)
1874
repo, r1, r2 = self.make_two_commit_repo()
1875
fetch_spec = [b'ancestry-of', r2]
1876
lines = b'\n'.join(fetch_spec)
1877
request.execute(b'', repo._format.network_name())
1878
response = request.do_body(lines)
1879
self.assertEqual((b'ok',), response.args)
1880
stream_bytes = b''.join(response.body_stream)
1881
self.assertStartsWith(stream_bytes, b'Bazaar pack format 1')
1883
def test_search(self):
1884
"""The search argument may be a 'search' of some explicit keys."""
1885
backing = self.get_transport()
1886
request = smart_repo.SmartServerRepositoryGetStream(backing)
1887
repo, r1, r2 = self.make_two_commit_repo()
1888
fetch_spec = [b'search', r1 + b' ' + r2, b'null:', b'2']
1889
lines = b'\n'.join(fetch_spec)
1890
request.execute(b'', repo._format.network_name())
1891
response = request.do_body(lines)
1892
self.assertEqual((b'ok',), response.args)
1893
stream_bytes = b''.join(response.body_stream)
1894
self.assertStartsWith(stream_bytes, b'Bazaar pack format 1')
1896
def test_search_everything(self):
1897
"""A search of 'everything' returns a stream."""
1898
backing = self.get_transport()
1899
request = smart_repo.SmartServerRepositoryGetStream_1_19(backing)
1900
repo, r1, r2 = self.make_two_commit_repo()
1901
serialised_fetch_spec = b'everything'
1902
request.execute(b'', repo._format.network_name())
1903
response = request.do_body(serialised_fetch_spec)
1904
self.assertEqual((b'ok',), response.args)
1905
stream_bytes = b''.join(response.body_stream)
1906
self.assertStartsWith(stream_bytes, b'Bazaar pack format 1')
1909
class TestSmartServerRequestHasRevision(tests.TestCaseWithMemoryTransport):
1911
def test_missing_revision(self):
1912
"""For a missing revision, ('no', ) is returned."""
1913
backing = self.get_transport()
1914
request = smart_repo.SmartServerRequestHasRevision(backing)
1915
self.make_repository('.')
1916
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
1917
request.execute(b'', b'revid'))
1919
def test_present_revision(self):
1920
"""For a present revision, ('yes', ) is returned."""
1921
backing = self.get_transport()
1922
request = smart_repo.SmartServerRequestHasRevision(backing)
1923
tree = self.make_branch_and_memory_tree('.')
1926
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
1927
tree.commit('a commit', rev_id=rev_id_utf8)
1929
self.assertTrue(tree.branch.repository.has_revision(rev_id_utf8))
1930
self.assertEqual(smart_req.SmartServerResponse((b'yes', )),
1931
request.execute(b'', rev_id_utf8))
1934
class TestSmartServerRepositoryIterFilesBytes(tests.TestCaseWithTransport):
1936
def test_single(self):
1937
backing = self.get_transport()
1938
request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
1939
t = self.make_branch_and_tree('.')
1940
self.addCleanup(t.lock_write().unlock)
1941
self.build_tree_contents([("file", b"somecontents")])
1942
t.add(["file"], [b"thefileid"])
1943
t.commit(rev_id=b'somerev', message="add file")
1944
self.assertIs(None, request.execute(b''))
1945
response = request.do_body(b"thefileid\0somerev\n")
1946
self.assertTrue(response.is_successful())
1947
self.assertEqual(response.args, (b"ok", ))
1948
self.assertEqual(b"".join(response.body_stream),
1949
b"ok\x000\n" + zlib.compress(b"somecontents"))
1951
def test_missing(self):
1952
backing = self.get_transport()
1953
request = smart_repo.SmartServerRepositoryIterFilesBytes(backing)
1954
t = self.make_branch_and_tree('.')
1955
self.addCleanup(t.lock_write().unlock)
1956
self.assertIs(None, request.execute(b''))
1957
response = request.do_body(b"thefileid\0revision\n")
1958
self.assertTrue(response.is_successful())
1959
self.assertEqual(response.args, (b"ok", ))
1960
self.assertEqual(b"".join(response.body_stream),
1961
b"absent\x00thefileid\x00revision\x000\n")
1964
class TestSmartServerRequestHasSignatureForRevisionId(
1965
tests.TestCaseWithMemoryTransport):
1967
def test_missing_revision(self):
1968
"""For a missing revision, NoSuchRevision is returned."""
1969
backing = self.get_transport()
1970
request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
1972
self.make_repository('.')
1974
smart_req.FailedSmartServerResponse(
1975
(b'nosuchrevision', b'revid'), None),
1976
request.execute(b'', b'revid'))
1978
def test_missing_signature(self):
1979
"""For a missing signature, ('no', ) is returned."""
1980
backing = self.get_transport()
1981
request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
1983
tree = self.make_branch_and_memory_tree('.')
1986
tree.commit('a commit', rev_id=b'A')
1988
self.assertTrue(tree.branch.repository.has_revision(b'A'))
1989
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
1990
request.execute(b'', b'A'))
1992
def test_present_signature(self):
1993
"""For a present signature, ('yes', ) is returned."""
1994
backing = self.get_transport()
1995
request = smart_repo.SmartServerRequestHasSignatureForRevisionId(
1997
strategy = gpg.LoopbackGPGStrategy(None)
1998
tree = self.make_branch_and_memory_tree('.')
2001
tree.commit('a commit', rev_id=b'A')
2002
tree.branch.repository.start_write_group()
2003
tree.branch.repository.sign_revision(b'A', strategy)
2004
tree.branch.repository.commit_write_group()
2006
self.assertTrue(tree.branch.repository.has_revision(b'A'))
2007
self.assertEqual(smart_req.SmartServerResponse((b'yes', )),
2008
request.execute(b'', b'A'))
2011
class TestSmartServerRepositoryGatherStats(tests.TestCaseWithMemoryTransport):
2013
def test_empty_revid(self):
2014
"""With an empty revid, we get only size an number and revisions"""
2015
backing = self.get_transport()
2016
request = smart_repo.SmartServerRepositoryGatherStats(backing)
2017
repository = self.make_repository('.')
2018
repository.gather_stats()
2019
expected_body = b'revisions: 0\n'
2021
smart_req.SmartServerResponse((b'ok', ), expected_body),
2022
request.execute(b'', b'', b'no'))
2024
def test_revid_with_committers(self):
2025
"""For a revid we get more infos."""
2026
backing = self.get_transport()
2027
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
2028
request = smart_repo.SmartServerRepositoryGatherStats(backing)
2029
tree = self.make_branch_and_memory_tree('.')
2032
# Let's build a predictable result
2033
tree.commit('a commit', timestamp=123456.2, timezone=3600)
2034
tree.commit('a commit', timestamp=654321.4, timezone=0,
2038
tree.branch.repository.gather_stats()
2039
expected_body = (b'firstrev: 123456.200 3600\n'
2040
b'latestrev: 654321.400 0\n'
2043
smart_req.SmartServerResponse((b'ok', ), expected_body),
2044
request.execute(b'', rev_id_utf8, b'no'))
2046
def test_not_empty_repository_with_committers(self):
2047
"""For a revid and requesting committers we get the whole thing."""
2048
backing = self.get_transport()
2049
rev_id_utf8 = u'\xc8abc'.encode('utf-8')
2050
request = smart_repo.SmartServerRepositoryGatherStats(backing)
2051
tree = self.make_branch_and_memory_tree('.')
2054
# Let's build a predictable result
2055
tree.commit('a commit', timestamp=123456.2, timezone=3600,
2057
tree.commit('a commit', timestamp=654321.4, timezone=0,
2058
committer='bar', rev_id=rev_id_utf8)
2060
tree.branch.repository.gather_stats()
2062
expected_body = (b'committers: 2\n'
2063
b'firstrev: 123456.200 3600\n'
2064
b'latestrev: 654321.400 0\n'
2067
smart_req.SmartServerResponse((b'ok', ), expected_body),
2068
request.execute(b'', rev_id_utf8, b'yes'))
2070
def test_unknown_revid(self):
2071
"""An unknown revision id causes a 'nosuchrevision' error."""
2072
backing = self.get_transport()
2073
request = smart_repo.SmartServerRepositoryGatherStats(backing)
2074
self.make_repository('.')
2076
smart_req.FailedSmartServerResponse(
2077
(b'nosuchrevision', b'mia'), None),
2078
request.execute(b'', b'mia', b'yes'))
2081
class TestSmartServerRepositoryIsShared(tests.TestCaseWithMemoryTransport):
2083
def test_is_shared(self):
2084
"""For a shared repository, ('yes', ) is returned."""
2085
backing = self.get_transport()
2086
request = smart_repo.SmartServerRepositoryIsShared(backing)
2087
self.make_repository('.', shared=True)
2088
self.assertEqual(smart_req.SmartServerResponse((b'yes', )),
2089
request.execute(b'', ))
2091
def test_is_not_shared(self):
2092
"""For a shared repository, ('no', ) is returned."""
2093
backing = self.get_transport()
2094
request = smart_repo.SmartServerRepositoryIsShared(backing)
2095
self.make_repository('.', shared=False)
2096
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
2097
request.execute(b'', ))
2100
class TestSmartServerRepositoryGetRevisionSignatureText(
2101
tests.TestCaseWithMemoryTransport):
2103
def test_get_signature(self):
2104
backing = self.get_transport()
2105
request = smart_repo.SmartServerRepositoryGetRevisionSignatureText(
2107
bb = self.make_branch_builder('.')
2108
bb.build_commit(rev_id=b'A')
2109
repo = bb.get_branch().repository
2110
strategy = gpg.LoopbackGPGStrategy(None)
2111
self.addCleanup(repo.lock_write().unlock)
2112
repo.start_write_group()
2113
repo.sign_revision(b'A', strategy)
2114
repo.commit_write_group()
2116
b'-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
2117
Testament.from_revision(repo, b'A').as_short_text() +
2118
b'-----END PSEUDO-SIGNED CONTENT-----\n')
2120
smart_req.SmartServerResponse((b'ok', ), expected_body),
2121
request.execute(b'', b'A'))
2124
class TestSmartServerRepositoryMakeWorkingTrees(
2125
tests.TestCaseWithMemoryTransport):
2127
def test_make_working_trees(self):
2128
"""For a repository with working trees, ('yes', ) is returned."""
2129
backing = self.get_transport()
2130
request = smart_repo.SmartServerRepositoryMakeWorkingTrees(backing)
2131
r = self.make_repository('.')
2132
r.set_make_working_trees(True)
2133
self.assertEqual(smart_req.SmartServerResponse((b'yes', )),
2134
request.execute(b'', ))
2136
def test_is_not_shared(self):
2137
"""For a repository with working trees, ('no', ) is returned."""
2138
backing = self.get_transport()
2139
request = smart_repo.SmartServerRepositoryMakeWorkingTrees(backing)
2140
r = self.make_repository('.')
2141
r.set_make_working_trees(False)
2142
self.assertEqual(smart_req.SmartServerResponse((b'no', )),
2143
request.execute(b'', ))
2146
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
2148
def test_lock_write_on_unlocked_repo(self):
2149
backing = self.get_transport()
2150
request = smart_repo.SmartServerRepositoryLockWrite(backing)
2151
repository = self.make_repository('.', format='knit')
2152
response = request.execute(b'')
2153
nonce = repository.control_files._lock.peek().get('nonce')
2154
self.assertEqual(smart_req.SmartServerResponse(
2155
(b'ok', nonce)), response)
2156
# The repository is now locked. Verify that with a new repository
2158
new_repo = repository.controldir.open_repository()
2159
self.assertRaises(errors.LockContention, new_repo.lock_write)
2161
request = smart_repo.SmartServerRepositoryUnlock(backing)
2162
response = request.execute(b'', nonce)
2164
def test_lock_write_on_locked_repo(self):
2165
backing = self.get_transport()
2166
request = smart_repo.SmartServerRepositoryLockWrite(backing)
2167
repository = self.make_repository('.', format='knit')
2168
repo_token = repository.lock_write().repository_token
2169
repository.leave_lock_in_place()
2171
response = request.execute(b'')
2173
smart_req.SmartServerResponse((b'LockContention',)), response)
2175
repository.lock_write(repo_token)
2176
repository.dont_leave_lock_in_place()
2179
def test_lock_write_on_readonly_transport(self):
2180
backing = self.get_readonly_transport()
2181
request = smart_repo.SmartServerRepositoryLockWrite(backing)
2182
self.make_repository('.', format='knit')
2183
response = request.execute(b'')
2184
self.assertFalse(response.is_successful())
2185
self.assertEqual(b'LockFailed', response.args[0])
2188
class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
2190
def make_empty_byte_stream(self, repo):
2191
byte_stream = smart_repo._stream_to_byte_stream([], repo._format)
2192
return b''.join(byte_stream)
2195
class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
2197
def test_insert_stream_empty(self):
2198
backing = self.get_transport()
2199
request = smart_repo.SmartServerRepositoryInsertStream(backing)
2200
repository = self.make_repository('.')
2201
response = request.execute(b'', b'')
2202
self.assertEqual(None, response)
2203
response = request.do_chunk(self.make_empty_byte_stream(repository))
2204
self.assertEqual(None, response)
2205
response = request.do_end()
2206
self.assertEqual(smart_req.SmartServerResponse((b'ok', )), response)
2209
class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
2211
def test_insert_stream_empty(self):
2212
backing = self.get_transport()
2213
request = smart_repo.SmartServerRepositoryInsertStreamLocked(
2215
repository = self.make_repository('.', format='knit')
2216
lock_token = repository.lock_write().repository_token
2217
response = request.execute(b'', b'', lock_token)
2218
self.assertEqual(None, response)
2219
response = request.do_chunk(self.make_empty_byte_stream(repository))
2220
self.assertEqual(None, response)
2221
response = request.do_end()
2222
self.assertEqual(smart_req.SmartServerResponse((b'ok', )), response)
2225
def test_insert_stream_with_wrong_lock_token(self):
2226
backing = self.get_transport()
2227
request = smart_repo.SmartServerRepositoryInsertStreamLocked(
2229
repository = self.make_repository('.', format='knit')
2230
with repository.lock_write():
2232
errors.TokenMismatch, request.execute, b'', b'',
2236
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
2238
def test_unlock_on_locked_repo(self):
2239
backing = self.get_transport()
2240
request = smart_repo.SmartServerRepositoryUnlock(backing)
2241
repository = self.make_repository('.', format='knit')
2242
token = repository.lock_write().repository_token
2243
repository.leave_lock_in_place()
2245
response = request.execute(b'', token)
2247
smart_req.SmartServerResponse((b'ok',)), response)
2248
# The repository is now unlocked. Verify that with a new repository
2250
new_repo = repository.controldir.open_repository()
2251
new_repo.lock_write()
2254
def test_unlock_on_unlocked_repo(self):
2255
backing = self.get_transport()
2256
request = smart_repo.SmartServerRepositoryUnlock(backing)
2257
self.make_repository('.', format='knit')
2258
response = request.execute(b'', b'some token')
2260
smart_req.SmartServerResponse((b'TokenMismatch',)), response)
2263
class TestSmartServerRepositoryGetPhysicalLockStatus(
2264
tests.TestCaseWithTransport):
2266
def test_with_write_lock(self):
2267
backing = self.get_transport()
2268
repo = self.make_repository('.')
2269
self.addCleanup(repo.lock_write().unlock)
2270
# lock_write() doesn't necessarily actually take a physical
2272
if repo.get_physical_lock_status():
2276
request_class = smart_repo.SmartServerRepositoryGetPhysicalLockStatus
2277
request = request_class(backing)
2278
self.assertEqual(smart_req.SuccessfulSmartServerResponse((expected,)),
2279
request.execute(b'', ))
2281
def test_without_write_lock(self):
2282
backing = self.get_transport()
2283
repo = self.make_repository('.')
2284
self.assertEqual(False, repo.get_physical_lock_status())
2285
request_class = smart_repo.SmartServerRepositoryGetPhysicalLockStatus
2286
request = request_class(backing)
2287
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'no',)),
2288
request.execute(b'', ))
2291
class TestSmartServerRepositoryReconcile(tests.TestCaseWithTransport):
2293
def test_reconcile(self):
2294
backing = self.get_transport()
2295
repo = self.make_repository('.')
2296
token = repo.lock_write().repository_token
2297
self.addCleanup(repo.unlock)
2298
request_class = smart_repo.SmartServerRepositoryReconcile
2299
request = request_class(backing)
2300
self.assertEqual(smart_req.SuccessfulSmartServerResponse(
2302
b'garbage_inventories: 0\n'
2303
b'inconsistent_parents: 0\n'),
2304
request.execute(b'', token))
2307
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
2309
def test_is_readonly_no(self):
2310
backing = self.get_transport()
2311
request = smart_req.SmartServerIsReadonly(backing)
2312
response = request.execute()
2314
smart_req.SmartServerResponse((b'no',)), response)
2316
def test_is_readonly_yes(self):
2317
backing = self.get_readonly_transport()
2318
request = smart_req.SmartServerIsReadonly(backing)
2319
response = request.execute()
2321
smart_req.SmartServerResponse((b'yes',)), response)
2324
class TestSmartServerRepositorySetMakeWorkingTrees(
2325
tests.TestCaseWithMemoryTransport):
2327
def test_set_false(self):
2328
backing = self.get_transport()
2329
repo = self.make_repository('.', shared=True)
2330
repo.set_make_working_trees(True)
2331
request_class = smart_repo.SmartServerRepositorySetMakeWorkingTrees
2332
request = request_class(backing)
2333
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
2334
request.execute(b'', b'False'))
2335
repo = repo.controldir.open_repository()
2336
self.assertFalse(repo.make_working_trees())
2338
def test_set_true(self):
2339
backing = self.get_transport()
2340
repo = self.make_repository('.', shared=True)
2341
repo.set_make_working_trees(False)
2342
request_class = smart_repo.SmartServerRepositorySetMakeWorkingTrees
2343
request = request_class(backing)
2344
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
2345
request.execute(b'', b'True'))
2346
repo = repo.controldir.open_repository()
2347
self.assertTrue(repo.make_working_trees())
2350
class TestSmartServerRepositoryGetSerializerFormat(
2351
tests.TestCaseWithMemoryTransport):
2353
def test_get_serializer_format(self):
2354
backing = self.get_transport()
2355
repo = self.make_repository('.', format='2a')
2356
request_class = smart_repo.SmartServerRepositoryGetSerializerFormat
2357
request = request_class(backing)
2359
smart_req.SuccessfulSmartServerResponse((b'ok', b'10')),
2360
request.execute(b''))
2363
class TestSmartServerRepositoryWriteGroup(
2364
tests.TestCaseWithMemoryTransport):
2366
def test_start_write_group(self):
2367
backing = self.get_transport()
2368
repo = self.make_repository('.')
2369
lock_token = repo.lock_write().repository_token
2370
self.addCleanup(repo.unlock)
2371
request_class = smart_repo.SmartServerRepositoryStartWriteGroup
2372
request = request_class(backing)
2373
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok', [])),
2374
request.execute(b'', lock_token))
2376
def test_start_write_group_unsuspendable(self):
2377
backing = self.get_transport()
2378
repo = self.make_repository('.', format='knit')
2379
lock_token = repo.lock_write().repository_token
2380
self.addCleanup(repo.unlock)
2381
request_class = smart_repo.SmartServerRepositoryStartWriteGroup
2382
request = request_class(backing)
2384
smart_req.FailedSmartServerResponse((b'UnsuspendableWriteGroup',)),
2385
request.execute(b'', lock_token))
2387
def test_commit_write_group(self):
2388
backing = self.get_transport()
2389
repo = self.make_repository('.')
2390
lock_token = repo.lock_write().repository_token
2391
self.addCleanup(repo.unlock)
2392
repo.start_write_group()
2393
tokens = repo.suspend_write_group()
2394
request_class = smart_repo.SmartServerRepositoryCommitWriteGroup
2395
request = request_class(backing)
2396
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
2397
request.execute(b'', lock_token, tokens))
2399
def test_abort_write_group(self):
2400
backing = self.get_transport()
2401
repo = self.make_repository('.')
2402
lock_token = repo.lock_write().repository_token
2403
repo.start_write_group()
2404
tokens = repo.suspend_write_group()
2405
self.addCleanup(repo.unlock)
2406
request_class = smart_repo.SmartServerRepositoryAbortWriteGroup
2407
request = request_class(backing)
2408
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
2409
request.execute(b'', lock_token, tokens))
2411
def test_check_write_group(self):
2412
backing = self.get_transport()
2413
repo = self.make_repository('.')
2414
lock_token = repo.lock_write().repository_token
2415
repo.start_write_group()
2416
tokens = repo.suspend_write_group()
2417
self.addCleanup(repo.unlock)
2418
request_class = smart_repo.SmartServerRepositoryCheckWriteGroup
2419
request = request_class(backing)
2420
self.assertEqual(smart_req.SuccessfulSmartServerResponse((b'ok',)),
2421
request.execute(b'', lock_token, tokens))
2423
def test_check_write_group_invalid(self):
2424
backing = self.get_transport()
2425
repo = self.make_repository('.')
2426
lock_token = repo.lock_write().repository_token
2427
self.addCleanup(repo.unlock)
2428
request_class = smart_repo.SmartServerRepositoryCheckWriteGroup
2429
request = request_class(backing)
2430
self.assertEqual(smart_req.FailedSmartServerResponse(
2431
(b'UnresumableWriteGroup', [b'random'],
2432
b'Malformed write group token')),
2433
request.execute(b'', lock_token, [b"random"]))
2436
class TestSmartServerPackRepositoryAutopack(tests.TestCaseWithTransport):
2438
def make_repo_needing_autopacking(self, path='.'):
2439
# Make a repo in need of autopacking.
2440
tree = self.make_branch_and_tree('.', format='pack-0.92')
2441
repo = tree.branch.repository
2442
# monkey-patch the pack collection to disable autopacking
2443
repo._pack_collection._max_pack_count = lambda count: count
2445
tree.commit('commit %s' % x)
2446
self.assertEqual(10, len(repo._pack_collection.names()))
2447
del repo._pack_collection._max_pack_count
2450
def test_autopack_needed(self):
2451
repo = self.make_repo_needing_autopacking()
2453
self.addCleanup(repo.unlock)
2454
backing = self.get_transport()
2455
request = smart_packrepo.SmartServerPackRepositoryAutopack(
2457
response = request.execute(b'')
2458
self.assertEqual(smart_req.SmartServerResponse((b'ok',)), response)
2459
repo._pack_collection.reload_pack_names()
2460
self.assertEqual(1, len(repo._pack_collection.names()))
2462
def test_autopack_not_needed(self):
2463
tree = self.make_branch_and_tree('.', format='pack-0.92')
2464
repo = tree.branch.repository
2466
self.addCleanup(repo.unlock)
2468
tree.commit('commit %s' % x)
2469
backing = self.get_transport()
2470
request = smart_packrepo.SmartServerPackRepositoryAutopack(
2472
response = request.execute(b'')
2473
self.assertEqual(smart_req.SmartServerResponse((b'ok',)), response)
2474
repo._pack_collection.reload_pack_names()
2475
self.assertEqual(9, len(repo._pack_collection.names()))
2477
def test_autopack_on_nonpack_format(self):
2478
"""A request to autopack a non-pack repo is a no-op."""
2479
repo = self.make_repository('.', format='knit')
2480
backing = self.get_transport()
2481
request = smart_packrepo.SmartServerPackRepositoryAutopack(
2483
response = request.execute(b'')
2484
self.assertEqual(smart_req.SmartServerResponse((b'ok',)), response)
2487
class TestSmartServerVfsGet(tests.TestCaseWithMemoryTransport):
2489
def test_unicode_path(self):
2490
"""VFS requests expect unicode paths to be escaped."""
2491
filename = u'foo\N{INTERROBANG}'
2492
filename_escaped = urlutils.escape(filename)
2493
backing = self.get_transport()
2494
request = vfs.GetRequest(backing)
2495
backing.put_bytes_non_atomic(filename_escaped, b'contents')
2496
self.assertEqual(smart_req.SmartServerResponse((b'ok', ), b'contents'),
2497
request.execute(filename_escaped.encode('ascii')))
2500
class TestHandlers(tests.TestCase):
2501
"""Tests for the request.request_handlers object."""
2503
def test_all_registrations_exist(self):
2504
"""All registered request_handlers can be found."""
2505
# If there's a typo in a register_lazy call, this loop will fail with
2506
# an AttributeError.
2507
for key in smart_req.request_handlers.keys():
2509
item = smart_req.request_handlers.get(key)
2510
except AttributeError as e:
2511
raise AttributeError('failed to get %s: %s' % (key, e))
2513
def assertHandlerEqual(self, verb, handler):
2514
self.assertEqual(smart_req.request_handlers.get(verb), handler)
2516
def test_registered_methods(self):
2517
"""Test that known methods are registered to the correct object."""
2518
self.assertHandlerEqual(b'Branch.break_lock',
2519
smart_branch.SmartServerBranchBreakLock)
2520
self.assertHandlerEqual(b'Branch.get_config_file',
2521
smart_branch.SmartServerBranchGetConfigFile)
2522
self.assertHandlerEqual(b'Branch.put_config_file',
2523
smart_branch.SmartServerBranchPutConfigFile)
2524
self.assertHandlerEqual(b'Branch.get_parent',
2525
smart_branch.SmartServerBranchGetParent)
2526
self.assertHandlerEqual(b'Branch.get_physical_lock_status',
2527
smart_branch.SmartServerBranchRequestGetPhysicalLockStatus)
2528
self.assertHandlerEqual(b'Branch.get_tags_bytes',
2529
smart_branch.SmartServerBranchGetTagsBytes)
2530
self.assertHandlerEqual(b'Branch.lock_write',
2531
smart_branch.SmartServerBranchRequestLockWrite)
2532
self.assertHandlerEqual(b'Branch.last_revision_info',
2533
smart_branch.SmartServerBranchRequestLastRevisionInfo)
2534
self.assertHandlerEqual(b'Branch.revision_history',
2535
smart_branch.SmartServerRequestRevisionHistory)
2536
self.assertHandlerEqual(b'Branch.revision_id_to_revno',
2537
smart_branch.SmartServerBranchRequestRevisionIdToRevno)
2538
self.assertHandlerEqual(b'Branch.set_config_option',
2539
smart_branch.SmartServerBranchRequestSetConfigOption)
2540
self.assertHandlerEqual(b'Branch.set_last_revision',
2541
smart_branch.SmartServerBranchRequestSetLastRevision)
2542
self.assertHandlerEqual(b'Branch.set_last_revision_info',
2543
smart_branch.SmartServerBranchRequestSetLastRevisionInfo)
2544
self.assertHandlerEqual(b'Branch.set_last_revision_ex',
2545
smart_branch.SmartServerBranchRequestSetLastRevisionEx)
2546
self.assertHandlerEqual(b'Branch.set_parent_location',
2547
smart_branch.SmartServerBranchRequestSetParentLocation)
2548
self.assertHandlerEqual(b'Branch.unlock',
2549
smart_branch.SmartServerBranchRequestUnlock)
2550
self.assertHandlerEqual(b'BzrDir.destroy_branch',
2551
smart_dir.SmartServerBzrDirRequestDestroyBranch)
2552
self.assertHandlerEqual(b'BzrDir.find_repository',
2553
smart_dir.SmartServerRequestFindRepositoryV1)
2554
self.assertHandlerEqual(b'BzrDir.find_repositoryV2',
2555
smart_dir.SmartServerRequestFindRepositoryV2)
2556
self.assertHandlerEqual(b'BzrDirFormat.initialize',
2557
smart_dir.SmartServerRequestInitializeBzrDir)
2558
self.assertHandlerEqual(b'BzrDirFormat.initialize_ex_1.16',
2559
smart_dir.SmartServerRequestBzrDirInitializeEx)
2560
self.assertHandlerEqual(b'BzrDir.checkout_metadir',
2561
smart_dir.SmartServerBzrDirRequestCheckoutMetaDir)
2562
self.assertHandlerEqual(b'BzrDir.cloning_metadir',
2563
smart_dir.SmartServerBzrDirRequestCloningMetaDir)
2564
self.assertHandlerEqual(b'BzrDir.get_branches',
2565
smart_dir.SmartServerBzrDirRequestGetBranches)
2566
self.assertHandlerEqual(b'BzrDir.get_config_file',
2567
smart_dir.SmartServerBzrDirRequestConfigFile)
2568
self.assertHandlerEqual(b'BzrDir.open_branch',
2569
smart_dir.SmartServerRequestOpenBranch)
2570
self.assertHandlerEqual(b'BzrDir.open_branchV2',
2571
smart_dir.SmartServerRequestOpenBranchV2)
2572
self.assertHandlerEqual(b'BzrDir.open_branchV3',
2573
smart_dir.SmartServerRequestOpenBranchV3)
2574
self.assertHandlerEqual(b'PackRepository.autopack',
2575
smart_packrepo.SmartServerPackRepositoryAutopack)
2576
self.assertHandlerEqual(b'Repository.add_signature_text',
2577
smart_repo.SmartServerRepositoryAddSignatureText)
2578
self.assertHandlerEqual(b'Repository.all_revision_ids',
2579
smart_repo.SmartServerRepositoryAllRevisionIds)
2580
self.assertHandlerEqual(b'Repository.break_lock',
2581
smart_repo.SmartServerRepositoryBreakLock)
2582
self.assertHandlerEqual(b'Repository.gather_stats',
2583
smart_repo.SmartServerRepositoryGatherStats)
2584
self.assertHandlerEqual(b'Repository.get_parent_map',
2585
smart_repo.SmartServerRepositoryGetParentMap)
2586
self.assertHandlerEqual(b'Repository.get_physical_lock_status',
2587
smart_repo.SmartServerRepositoryGetPhysicalLockStatus)
2588
self.assertHandlerEqual(b'Repository.get_rev_id_for_revno',
2589
smart_repo.SmartServerRepositoryGetRevIdForRevno)
2590
self.assertHandlerEqual(b'Repository.get_revision_graph',
2591
smart_repo.SmartServerRepositoryGetRevisionGraph)
2592
self.assertHandlerEqual(b'Repository.get_revision_signature_text',
2593
smart_repo.SmartServerRepositoryGetRevisionSignatureText)
2594
self.assertHandlerEqual(b'Repository.get_stream',
2595
smart_repo.SmartServerRepositoryGetStream)
2596
self.assertHandlerEqual(b'Repository.get_stream_1.19',
2597
smart_repo.SmartServerRepositoryGetStream_1_19)
2598
self.assertHandlerEqual(b'Repository.iter_revisions',
2599
smart_repo.SmartServerRepositoryIterRevisions)
2600
self.assertHandlerEqual(b'Repository.has_revision',
2601
smart_repo.SmartServerRequestHasRevision)
2602
self.assertHandlerEqual(b'Repository.insert_stream',
2603
smart_repo.SmartServerRepositoryInsertStream)
2604
self.assertHandlerEqual(b'Repository.insert_stream_locked',
2605
smart_repo.SmartServerRepositoryInsertStreamLocked)
2606
self.assertHandlerEqual(b'Repository.is_shared',
2607
smart_repo.SmartServerRepositoryIsShared)
2608
self.assertHandlerEqual(b'Repository.iter_files_bytes',
2609
smart_repo.SmartServerRepositoryIterFilesBytes)
2610
self.assertHandlerEqual(b'Repository.lock_write',
2611
smart_repo.SmartServerRepositoryLockWrite)
2612
self.assertHandlerEqual(b'Repository.make_working_trees',
2613
smart_repo.SmartServerRepositoryMakeWorkingTrees)
2614
self.assertHandlerEqual(b'Repository.pack',
2615
smart_repo.SmartServerRepositoryPack)
2616
self.assertHandlerEqual(b'Repository.reconcile',
2617
smart_repo.SmartServerRepositoryReconcile)
2618
self.assertHandlerEqual(b'Repository.tarball',
2619
smart_repo.SmartServerRepositoryTarball)
2620
self.assertHandlerEqual(b'Repository.unlock',
2621
smart_repo.SmartServerRepositoryUnlock)
2622
self.assertHandlerEqual(b'Repository.start_write_group',
2623
smart_repo.SmartServerRepositoryStartWriteGroup)
2624
self.assertHandlerEqual(b'Repository.check_write_group',
2625
smart_repo.SmartServerRepositoryCheckWriteGroup)
2626
self.assertHandlerEqual(b'Repository.commit_write_group',
2627
smart_repo.SmartServerRepositoryCommitWriteGroup)
2628
self.assertHandlerEqual(b'Repository.abort_write_group',
2629
smart_repo.SmartServerRepositoryAbortWriteGroup)
2630
self.assertHandlerEqual(b'VersionedFileRepository.get_serializer_format',
2631
smart_repo.SmartServerRepositoryGetSerializerFormat)
2632
self.assertHandlerEqual(b'VersionedFileRepository.get_inventories',
2633
smart_repo.SmartServerRepositoryGetInventories)
2634
self.assertHandlerEqual(b'Transport.is_readonly',
2635
smart_req.SmartServerIsReadonly)
2638
class SmartTCPServerHookTests(tests.TestCaseWithMemoryTransport):
2639
"""Tests for SmartTCPServer hooks."""
2642
super(SmartTCPServerHookTests, self).setUp()
2643
self.server = server.SmartTCPServer(self.get_transport())
2645
def test_run_server_started_hooks(self):
2646
"""Test the server started hooks get fired properly."""
2648
server.SmartTCPServer.hooks.install_named_hook('server_started',
2649
lambda backing_urls, url: started_calls.append(
2650
(backing_urls, url)),
2652
started_ex_calls = []
2653
server.SmartTCPServer.hooks.install_named_hook('server_started_ex',
2654
lambda backing_urls, url: started_ex_calls.append(
2655
(backing_urls, url)),
2657
self.server._sockname = ('example.com', 42)
2658
self.server.run_server_started_hooks()
2659
self.assertEqual(started_calls,
2660
[([self.get_transport().base], 'bzr://example.com:42/')])
2661
self.assertEqual(started_ex_calls,
2662
[([self.get_transport().base], self.server)])
2664
def test_run_server_started_hooks_ipv6(self):
2665
"""Test that socknames can contain 4-tuples."""
2666
self.server._sockname = ('::', 42, 0, 0)
2668
server.SmartTCPServer.hooks.install_named_hook('server_started',
2669
lambda backing_urls, url: started_calls.append(
2670
(backing_urls, url)),
2672
self.server.run_server_started_hooks()
2673
self.assertEqual(started_calls,
2674
[([self.get_transport().base], 'bzr://:::42/')])
2676
def test_run_server_stopped_hooks(self):
2677
"""Test the server stopped hooks."""
2678
self.server._sockname = ('example.com', 42)
2680
server.SmartTCPServer.hooks.install_named_hook('server_stopped',
2681
lambda backing_urls, url: stopped_calls.append(
2682
(backing_urls, url)),
2684
self.server.run_server_stopped_hooks()
2685
self.assertEqual(stopped_calls,
2686
[([self.get_transport().base], 'bzr://example.com:42/')])
2689
class TestSmartServerRepositoryPack(tests.TestCaseWithMemoryTransport):
2691
def test_pack(self):
2692
backing = self.get_transport()
2693
request = smart_repo.SmartServerRepositoryPack(backing)
2694
tree = self.make_branch_and_memory_tree('.')
2695
repo_token = tree.branch.repository.lock_write().repository_token
2697
self.assertIs(None, request.execute(b'', repo_token, False))
2700
smart_req.SuccessfulSmartServerResponse((b'ok', ), ),
2701
request.do_body(b''))
2704
class TestSmartServerRepositoryGetInventories(tests.TestCaseWithTransport):
2706
def _get_serialized_inventory_delta(self, repository, base_revid, revid):
2707
base_inv = repository.revision_tree(base_revid).root_inventory
2708
inv = repository.revision_tree(revid).root_inventory
2709
inv_delta = inv._make_delta(base_inv)
2710
serializer = inventory_delta.InventoryDeltaSerializer(True, False)
2711
return b"".join(serializer.delta_to_lines(base_revid, revid, inv_delta))
2713
def test_single(self):
2714
backing = self.get_transport()
2715
request = smart_repo.SmartServerRepositoryGetInventories(backing)
2716
t = self.make_branch_and_tree('.', format='2a')
2717
self.addCleanup(t.lock_write().unlock)
2718
self.build_tree_contents([("file", b"somecontents")])
2719
t.add(["file"], [b"thefileid"])
2720
t.commit(rev_id=b'somerev', message="add file")
2721
self.assertIs(None, request.execute(b'', b'unordered'))
2722
response = request.do_body(b"somerev\n")
2723
self.assertTrue(response.is_successful())
2724
self.assertEqual(response.args, (b"ok", ))
2725
stream = [('inventory-deltas', [
2726
versionedfile.FulltextContentFactory(b'somerev', None, None,
2727
self._get_serialized_inventory_delta(
2728
t.branch.repository, b'null:', b'somerev'))])]
2729
fmt = controldir.format_registry.get('2a')().repository_format
2731
b"".join(response.body_stream),
2732
b"".join(smart_repo._stream_to_byte_stream(stream, fmt)))
2734
def test_empty(self):
2735
backing = self.get_transport()
2736
request = smart_repo.SmartServerRepositoryGetInventories(backing)
2737
t = self.make_branch_and_tree('.', format='2a')
2738
self.addCleanup(t.lock_write().unlock)
2739
self.build_tree_contents([("file", b"somecontents")])
2740
t.add(["file"], [b"thefileid"])
2741
t.commit(rev_id=b'somerev', message="add file")
2742
self.assertIs(None, request.execute(b'', b'unordered'))
2743
response = request.do_body(b"")
2744
self.assertTrue(response.is_successful())
2745
self.assertEqual(response.args, (b"ok", ))
2746
self.assertEqual(b"".join(response.body_stream),
2747
b"Bazaar pack format 1 (introduced in 0.18)\nB54\n\nBazaar repository format 2a (needs bzr 1.16 or later)\nE")
2750
class TestSmartServerRepositoryGetStreamForMissingKeys(GetStreamTestBase):
2752
def test_missing(self):
2753
"""The search argument may be a 'ancestry-of' some heads'."""
2754
backing = self.get_transport()
2755
request = smart_repo.SmartServerRepositoryGetStreamForMissingKeys(
2757
repo, r1, r2 = self.make_two_commit_repo()
2758
request.execute(b'', repo._format.network_name())
2759
lines = b'inventories\t' + r1
2760
response = request.do_body(lines)
2761
self.assertEqual((b'ok',), response.args)
2762
stream_bytes = b''.join(response.body_stream)
2763
self.assertStartsWith(stream_bytes, b'Bazaar pack format 1')
2765
def test_unknown_format(self):
2766
"""The format may not be known by the remote server."""
2767
backing = self.get_transport()
2768
request = smart_repo.SmartServerRepositoryGetStreamForMissingKeys(
2770
repo, r1, r2 = self.make_two_commit_repo()
2771
request.execute(b'', b'yada yada yada')
2772
expected = smart_req.FailedSmartServerResponse(
2773
(b'UnknownFormat', b'repository', b'yada yada yada'))
2776
class TestSmartServerRepositoryRevisionArchive(tests.TestCaseWithTransport):
2778
backing = self.get_transport()
2779
request = smart_repo.SmartServerRepositoryRevisionArchive(backing)
2780
t = self.make_branch_and_tree('.')
2781
self.addCleanup(t.lock_write().unlock)
2782
self.build_tree_contents([("file", b"somecontents")])
2783
t.add(["file"], [b"thefileid"])
2784
t.commit(rev_id=b'somerev', message="add file")
2785
response = request.execute(b'', b"somerev", b"tar", b"foo.tar", b"foo")
2786
self.assertTrue(response.is_successful())
2787
self.assertEqual(response.args, (b"ok", ))
2788
b = BytesIO(b"".join(response.body_stream))
2789
with tarfile.open(mode='r', fileobj=b) as tf:
2790
self.assertEqual(['foo/file'], tf.getnames())
2793
class TestSmartServerRepositoryAnnotateFileRevision(tests.TestCaseWithTransport):
2796
backing = self.get_transport()
2797
request = smart_repo.SmartServerRepositoryAnnotateFileRevision(backing)
2798
t = self.make_branch_and_tree('.')
2799
self.addCleanup(t.lock_write().unlock)
2800
self.build_tree_contents([("file", b"somecontents\nmorecontents\n")])
2801
t.add(["file"], [b"thefileid"])
2802
t.commit(rev_id=b'somerev', message="add file")
2803
response = request.execute(b'', b"somerev", b"file")
2804
self.assertTrue(response.is_successful())
2805
self.assertEqual(response.args, (b"ok", ))
2807
[[b'somerev', b'somecontents\n'], [b'somerev', b'morecontents\n']],
2808
bencode.bdecode(response.body))