/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Matt Nordhoff
  • Date: 2009-04-04 02:50:01 UTC
  • mfrom: (4253 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4256.
  • Revision ID: mnordhoff@mattnordhoff.com-20090404025001-z1403k0tatmc8l91
Merge bzr.dev, fixing conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for remote bzrdir/branch/repo/etc
18
18
 
19
19
These are proxy objects which act on remote objects by sending messages
20
20
through a smart client.  The proxies are to be created when attempting to open
21
 
the object given a transport that supports smartserver rpc operations. 
 
21
the object given a transport that supports smartserver rpc operations.
22
22
 
23
23
These tests correspond to tests.test_smart, which exercises the server side.
24
24
"""
36
36
    repository,
37
37
    smart,
38
38
    tests,
 
39
    treebuilder,
39
40
    urlutils,
40
41
    )
41
42
from bzrlib.branch import Branch
42
43
from bzrlib.bzrdir import BzrDir, BzrDirFormat
43
44
from bzrlib.remote import (
44
45
    RemoteBranch,
 
46
    RemoteBranchFormat,
45
47
    RemoteBzrDir,
46
48
    RemoteBzrDirFormat,
47
49
    RemoteRepository,
 
50
    RemoteRepositoryFormat,
48
51
    )
 
52
from bzrlib.repofmt import pack_repo
49
53
from bzrlib.revision import NULL_REVISION
50
54
from bzrlib.smart import server, medium
51
55
from bzrlib.smart.client import _SmartClient
52
 
from bzrlib.symbol_versioning import one_four
 
56
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
 
57
from bzrlib.tests import (
 
58
    condition_isinstance,
 
59
    split_suite_by_condition,
 
60
    multiply_tests,
 
61
    )
53
62
from bzrlib.transport import get_transport, http
54
63
from bzrlib.transport.memory import MemoryTransport
55
64
from bzrlib.transport.remote import (
58
67
    RemoteTCPTransport,
59
68
)
60
69
 
 
70
def load_tests(standard_tests, module, loader):
 
71
    to_adapt, result = split_suite_by_condition(
 
72
        standard_tests, condition_isinstance(BasicRemoteObjectTests))
 
73
    smart_server_version_scenarios = [
 
74
        ('HPSS-v2',
 
75
            {'transport_server': server.SmartTCPServer_for_testing_v2_only}),
 
76
        ('HPSS-v3',
 
77
            {'transport_server': server.SmartTCPServer_for_testing})]
 
78
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
 
79
 
61
80
 
62
81
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
63
82
 
64
83
    def setUp(self):
65
 
        self.transport_server = server.SmartTCPServer_for_testing
66
84
        super(BasicRemoteObjectTests, self).setUp()
67
85
        self.transport = self.get_transport()
68
86
        # make a branch that can be opened over the smart transport
113
131
        b = BzrDir.open_from_transport(self.transport).open_branch()
114
132
        self.assertStartsWith(str(b), 'RemoteBranch(')
115
133
 
 
134
    def test_remote_branch_format_supports_stacking(self):
 
135
        t = self.transport
 
136
        self.make_branch('unstackable', format='pack-0.92')
 
137
        b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
 
138
        self.assertFalse(b._format.supports_stacking())
 
139
        self.make_branch('stackable', format='1.9')
 
140
        b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
 
141
        self.assertTrue(b._format.supports_stacking())
 
142
 
 
143
    def test_remote_repo_format_supports_external_references(self):
 
144
        t = self.transport
 
145
        bd = self.make_bzrdir('unstackable', format='pack-0.92')
 
146
        r = bd.create_repository()
 
147
        self.assertFalse(r._format.supports_external_lookups)
 
148
        r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
 
149
        self.assertFalse(r._format.supports_external_lookups)
 
150
        bd = self.make_bzrdir('stackable', format='1.9')
 
151
        r = bd.create_repository()
 
152
        self.assertTrue(r._format.supports_external_lookups)
 
153
        r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
 
154
        self.assertTrue(r._format.supports_external_lookups)
 
155
 
116
156
 
117
157
class FakeProtocol(object):
118
158
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
139
179
 
140
180
class FakeClient(_SmartClient):
141
181
    """Lookalike for _SmartClient allowing testing."""
142
 
    
 
182
 
143
183
    def __init__(self, fake_medium_base='fake base'):
144
184
        """Create a FakeClient."""
145
185
        self.responses = []
233
273
        stream = list(stream)
234
274
        self._check_call(args[0], args[1:])
235
275
        self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
236
 
        return self._get_next_response()[1]
 
276
        result = self._get_next_response()
 
277
        # The second value returned from call_with_body_stream is supposed to
 
278
        # be a response_handler object, but so far no tests depend on that.
 
279
        response_handler = None 
 
280
        return result[1], response_handler
237
281
 
238
282
 
239
283
class FakeMedium(medium.SmartClientMedium):
261
305
 
262
306
 
263
307
class TestRemote(tests.TestCaseWithMemoryTransport):
264
 
    
 
308
 
 
309
    def get_branch_format(self):
 
310
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
311
        return reference_bzrdir_format.get_branch_format()
 
312
 
 
313
    def get_repo_format(self):
 
314
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
315
        return reference_bzrdir_format.repository_format
 
316
 
265
317
    def disable_verb(self, verb):
266
318
        """Disable a verb for one test."""
267
319
        request_handlers = smart.request.request_handlers
304
356
        cloned_transport = base_transport.clone(relpath)
305
357
        result = client_medium.remote_path_from_transport(cloned_transport)
306
358
        self.assertEqual(expected, result)
307
 
        
 
359
 
308
360
    def test_remote_path_from_transport_http(self):
309
361
        """Remote paths for HTTP transports are calculated differently to other
310
362
        transports.  They are just relative to the client base, not the root
326
378
        """
327
379
        client_medium = medium.SmartClientMedium('dummy base')
328
380
        self.assertFalse(client_medium._is_remote_before((99, 99)))
329
 
    
 
381
 
330
382
    def test__remember_remote_is_before(self):
331
383
        """Calling _remember_remote_is_before ratchets down the known remote
332
384
        version.
345
397
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
346
398
 
347
399
 
348
 
class TestBzrDirOpenBranch(tests.TestCase):
 
400
class TestBzrDirCloningMetaDir(TestRemote):
 
401
 
 
402
    def test_backwards_compat(self):
 
403
        self.setup_smart_server_with_call_log()
 
404
        a_dir = self.make_bzrdir('.')
 
405
        self.reset_smart_call_log()
 
406
        verb = 'BzrDir.cloning_metadir'
 
407
        self.disable_verb(verb)
 
408
        format = a_dir.cloning_metadir()
 
409
        call_count = len([call for call in self.hpss_calls if
 
410
            call.call.method == verb])
 
411
        self.assertEqual(1, call_count)
 
412
 
 
413
    def test_branch_reference(self):
 
414
        transport = self.get_transport('quack')
 
415
        referenced = self.make_branch('referenced')
 
416
        expected = referenced.bzrdir.cloning_metadir()
 
417
        client = FakeClient(transport.base)
 
418
        client.add_expected_call(
 
419
            'BzrDir.cloning_metadir', ('quack/', 'False'),
 
420
            'error', ('BranchReference',)),
 
421
        client.add_expected_call(
 
422
            'BzrDir.open_branchV2', ('quack/',),
 
423
            'success', ('ref', self.get_url('referenced'))),
 
424
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
425
            _client=client)
 
426
        result = a_bzrdir.cloning_metadir()
 
427
        # We should have got a control dir matching the referenced branch.
 
428
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
 
429
        self.assertEqual(expected._repository_format, result._repository_format)
 
430
        self.assertEqual(expected._branch_format, result._branch_format)
 
431
        client.finished_test()
 
432
 
 
433
    def test_current_server(self):
 
434
        transport = self.get_transport('.')
 
435
        transport = transport.clone('quack')
 
436
        self.make_bzrdir('quack')
 
437
        client = FakeClient(transport.base)
 
438
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
439
        control_name = reference_bzrdir_format.network_name()
 
440
        client.add_expected_call(
 
441
            'BzrDir.cloning_metadir', ('quack/', 'False'),
 
442
            'success', (control_name, '', ('branch', ''))),
 
443
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
444
            _client=client)
 
445
        result = a_bzrdir.cloning_metadir()
 
446
        # We should have got a reference control dir with default branch and
 
447
        # repository formats.
 
448
        # This pokes a little, just to be sure.
 
449
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
 
450
        self.assertEqual(None, result._repository_format)
 
451
        self.assertEqual(None, result._branch_format)
 
452
        client.finished_test()
 
453
 
 
454
 
 
455
class TestBzrDirOpenBranch(TestRemote):
 
456
 
 
457
    def test_backwards_compat(self):
 
458
        self.setup_smart_server_with_call_log()
 
459
        self.make_branch('.')
 
460
        a_dir = BzrDir.open(self.get_url('.'))
 
461
        self.reset_smart_call_log()
 
462
        verb = 'BzrDir.open_branchV2'
 
463
        self.disable_verb(verb)
 
464
        format = a_dir.open_branch()
 
465
        call_count = len([call for call in self.hpss_calls if
 
466
            call.call.method == verb])
 
467
        self.assertEqual(1, call_count)
349
468
 
350
469
    def test_branch_present(self):
 
470
        reference_format = self.get_repo_format()
 
471
        network_name = reference_format.network_name()
 
472
        branch_network_name = self.get_branch_format().network_name()
351
473
        transport = MemoryTransport()
352
474
        transport.mkdir('quack')
353
475
        transport = transport.clone('quack')
354
476
        client = FakeClient(transport.base)
355
477
        client.add_expected_call(
356
 
            'BzrDir.open_branch', ('quack/',),
357
 
            'success', ('ok', ''))
 
478
            'BzrDir.open_branchV2', ('quack/',),
 
479
            'success', ('branch', branch_network_name))
358
480
        client.add_expected_call(
359
 
            'BzrDir.find_repositoryV2', ('quack/',),
360
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
481
            'BzrDir.find_repositoryV3', ('quack/',),
 
482
            'success', ('ok', '', 'no', 'no', 'no', network_name))
361
483
        client.add_expected_call(
362
484
            'Branch.get_stacked_on_url', ('quack/',),
363
485
            'error', ('NotStacked',))
378
500
            _client=client)
379
501
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
380
502
        self.assertEqual(
381
 
            [('call', 'BzrDir.open_branch', ('quack/',))],
 
503
            [('call', 'BzrDir.open_branchV2', ('quack/',))],
382
504
            client._calls)
383
505
 
384
506
    def test__get_tree_branch(self):
404
526
        # transmitted as "~", not "%7E".
405
527
        transport = RemoteTCPTransport('bzr://localhost/~hello/')
406
528
        client = FakeClient(transport.base)
407
 
        client.add_expected_call(
408
 
            'BzrDir.open_branch', ('~hello/',),
409
 
            'success', ('ok', ''))
410
 
        client.add_expected_call(
411
 
            'BzrDir.find_repositoryV2', ('~hello/',),
412
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
529
        reference_format = self.get_repo_format()
 
530
        network_name = reference_format.network_name()
 
531
        branch_network_name = self.get_branch_format().network_name()
 
532
        client.add_expected_call(
 
533
            'BzrDir.open_branchV2', ('~hello/',),
 
534
            'success', ('branch', branch_network_name))
 
535
        client.add_expected_call(
 
536
            'BzrDir.find_repositoryV3', ('~hello/',),
 
537
            'success', ('ok', '', 'no', 'no', 'no', network_name))
413
538
        client.add_expected_call(
414
539
            'Branch.get_stacked_on_url', ('~hello/',),
415
540
            'error', ('NotStacked',))
419
544
        client.finished_test()
420
545
 
421
546
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
 
547
        reference_format = self.get_repo_format()
 
548
        network_name = reference_format.network_name()
422
549
        transport = MemoryTransport()
423
550
        transport.mkdir('quack')
424
551
        transport = transport.clone('quack')
432
559
            subtree_response = 'no'
433
560
        client = FakeClient(transport.base)
434
561
        client.add_success_response(
435
 
            'ok', '', rich_response, subtree_response, external_lookup)
 
562
            'ok', '', rich_response, subtree_response, external_lookup,
 
563
            network_name)
436
564
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
437
565
            _client=client)
438
566
        result = bzrdir.open_repository()
439
567
        self.assertEqual(
440
 
            [('call', 'BzrDir.find_repositoryV2', ('quack/',))],
 
568
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
441
569
            client._calls)
442
570
        self.assertIsInstance(result, RemoteRepository)
443
571
        self.assertEqual(bzrdir, result.bzrdir)
459
587
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
460
588
 
461
589
 
 
590
class TestBzrDirCreateBranch(TestRemote):
 
591
 
 
592
    def test_backwards_compat(self):
 
593
        self.setup_smart_server_with_call_log()
 
594
        repo = self.make_repository('.')
 
595
        self.reset_smart_call_log()
 
596
        self.disable_verb('BzrDir.create_branch')
 
597
        branch = repo.bzrdir.create_branch()
 
598
        create_branch_call_count = len([call for call in self.hpss_calls if
 
599
            call.call.method == 'BzrDir.create_branch'])
 
600
        self.assertEqual(1, create_branch_call_count)
 
601
 
 
602
    def test_current_server(self):
 
603
        transport = self.get_transport('.')
 
604
        transport = transport.clone('quack')
 
605
        self.make_repository('quack')
 
606
        client = FakeClient(transport.base)
 
607
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
608
        reference_format = reference_bzrdir_format.get_branch_format()
 
609
        network_name = reference_format.network_name()
 
610
        reference_repo_fmt = reference_bzrdir_format.repository_format
 
611
        reference_repo_name = reference_repo_fmt.network_name()
 
612
        client.add_expected_call(
 
613
            'BzrDir.create_branch', ('quack/', network_name),
 
614
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
 
615
            reference_repo_name))
 
616
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
617
            _client=client)
 
618
        branch = a_bzrdir.create_branch()
 
619
        # We should have got a remote branch
 
620
        self.assertIsInstance(branch, remote.RemoteBranch)
 
621
        # its format should have the settings from the response
 
622
        format = branch._format
 
623
        self.assertEqual(network_name, format.network_name())
 
624
 
 
625
 
462
626
class TestBzrDirCreateRepository(TestRemote):
463
627
 
464
628
    def test_backwards_compat(self):
468
632
        self.disable_verb('BzrDir.create_repository')
469
633
        repo = bzrdir.create_repository()
470
634
        create_repo_call_count = len([call for call in self.hpss_calls if
471
 
            call[0].method == 'BzrDir.create_repository'])
 
635
            call.call.method == 'BzrDir.create_repository'])
472
636
        self.assertEqual(1, create_repo_call_count)
473
637
 
474
638
    def test_current_server(self):
496
660
        self.assertEqual(network_name, format.network_name())
497
661
 
498
662
 
499
 
class TestBzrDirOpenRepository(tests.TestCase):
 
663
class TestBzrDirOpenRepository(TestRemote):
500
664
 
501
 
    def test_backwards_compat_1_2(self):
502
 
        transport = MemoryTransport()
503
 
        transport.mkdir('quack')
504
 
        transport = transport.clone('quack')
505
 
        client = FakeClient(transport.base)
 
665
    def test_backwards_compat_1_2_3(self):
 
666
        # fallback all the way to the first version.
 
667
        reference_format = self.get_repo_format()
 
668
        network_name = reference_format.network_name()
 
669
        client = FakeClient('bzr://example.com/')
 
670
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
506
671
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
507
672
        client.add_success_response('ok', '', 'no', 'no')
 
673
        # A real repository instance will be created to determine the network
 
674
        # name.
 
675
        client.add_success_response_with_body(
 
676
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
677
        client.add_success_response_with_body(
 
678
            reference_format.get_format_string(), 'ok')
 
679
        # PackRepository wants to do a stat
 
680
        client.add_success_response('stat', '0', '65535')
 
681
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
682
            _client=client)
 
683
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
684
            _client=client)
 
685
        repo = bzrdir.open_repository()
 
686
        self.assertEqual(
 
687
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
 
688
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
 
689
             ('call', 'BzrDir.find_repository', ('quack/',)),
 
690
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
 
691
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
 
692
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
693
             ],
 
694
            client._calls)
 
695
        self.assertEqual(network_name, repo._format.network_name())
 
696
 
 
697
    def test_backwards_compat_2(self):
 
698
        # fallback to find_repositoryV2
 
699
        reference_format = self.get_repo_format()
 
700
        network_name = reference_format.network_name()
 
701
        client = FakeClient('bzr://example.com/')
 
702
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
 
703
        client.add_success_response('ok', '', 'no', 'no', 'no')
 
704
        # A real repository instance will be created to determine the network
 
705
        # name.
 
706
        client.add_success_response_with_body(
 
707
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
708
        client.add_success_response_with_body(
 
709
            reference_format.get_format_string(), 'ok')
 
710
        # PackRepository wants to do a stat
 
711
        client.add_success_response('stat', '0', '65535')
 
712
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
713
            _client=client)
 
714
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
715
            _client=client)
 
716
        repo = bzrdir.open_repository()
 
717
        self.assertEqual(
 
718
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
 
719
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
 
720
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
 
721
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
 
722
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
723
             ],
 
724
            client._calls)
 
725
        self.assertEqual(network_name, repo._format.network_name())
 
726
 
 
727
    def test_current_server(self):
 
728
        reference_format = self.get_repo_format()
 
729
        network_name = reference_format.network_name()
 
730
        transport = MemoryTransport()
 
731
        transport.mkdir('quack')
 
732
        transport = transport.clone('quack')
 
733
        client = FakeClient(transport.base)
 
734
        client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
508
735
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
509
736
            _client=client)
510
737
        repo = bzrdir.open_repository()
511
738
        self.assertEqual(
512
 
            [('call', 'BzrDir.find_repositoryV2', ('quack/',)),
513
 
             ('call', 'BzrDir.find_repository', ('quack/',))],
 
739
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
514
740
            client._calls)
 
741
        self.assertEqual(network_name, repo._format.network_name())
515
742
 
516
743
 
517
744
class OldSmartClient(object):
542
769
        return OldSmartClient()
543
770
 
544
771
 
545
 
class RemoteBranchTestCase(tests.TestCase):
 
772
class RemoteBranchTestCase(TestRemote):
546
773
 
547
774
    def make_remote_branch(self, transport, client):
548
775
        """Make a RemoteBranch using 'client' as its _SmartClient.
549
 
        
 
776
 
550
777
        A RemoteBzrDir and RemoteRepository will also be created to fill out
551
778
        the RemoteBranch, albeit with stub values for some of their attributes.
552
779
        """
556
783
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
557
784
            _client=False)
558
785
        repo = RemoteRepository(bzrdir, None, _client=client)
559
 
        return RemoteBranch(bzrdir, repo, _client=client)
 
786
        branch_format = self.get_branch_format()
 
787
        format = RemoteBranchFormat(network_name=branch_format.network_name())
 
788
        return RemoteBranch(bzrdir, repo, _client=client, format=format)
 
789
 
 
790
 
 
791
class TestBranchGetParent(RemoteBranchTestCase):
 
792
 
 
793
    def test_no_parent(self):
 
794
        # in an empty branch we decode the response properly
 
795
        transport = MemoryTransport()
 
796
        client = FakeClient(transport.base)
 
797
        client.add_expected_call(
 
798
            'Branch.get_stacked_on_url', ('quack/',),
 
799
            'error', ('NotStacked',))
 
800
        client.add_expected_call(
 
801
            'Branch.get_parent', ('quack/',),
 
802
            'success', ('',))
 
803
        transport.mkdir('quack')
 
804
        transport = transport.clone('quack')
 
805
        branch = self.make_remote_branch(transport, client)
 
806
        result = branch.get_parent()
 
807
        client.finished_test()
 
808
        self.assertEqual(None, result)
 
809
 
 
810
    def test_parent_relative(self):
 
811
        transport = MemoryTransport()
 
812
        client = FakeClient(transport.base)
 
813
        client.add_expected_call(
 
814
            'Branch.get_stacked_on_url', ('kwaak/',),
 
815
            'error', ('NotStacked',))
 
816
        client.add_expected_call(
 
817
            'Branch.get_parent', ('kwaak/',),
 
818
            'success', ('../foo/',))
 
819
        transport.mkdir('kwaak')
 
820
        transport = transport.clone('kwaak')
 
821
        branch = self.make_remote_branch(transport, client)
 
822
        result = branch.get_parent()
 
823
        self.assertEqual(transport.clone('../foo').base, result)
 
824
 
 
825
    def test_parent_absolute(self):
 
826
        transport = MemoryTransport()
 
827
        client = FakeClient(transport.base)
 
828
        client.add_expected_call(
 
829
            'Branch.get_stacked_on_url', ('kwaak/',),
 
830
            'error', ('NotStacked',))
 
831
        client.add_expected_call(
 
832
            'Branch.get_parent', ('kwaak/',),
 
833
            'success', ('http://foo/',))
 
834
        transport.mkdir('kwaak')
 
835
        transport = transport.clone('kwaak')
 
836
        branch = self.make_remote_branch(transport, client)
 
837
        result = branch.get_parent()
 
838
        self.assertEqual('http://foo/', result)
 
839
 
 
840
 
 
841
class TestBranchGetTagsBytes(RemoteBranchTestCase):
 
842
 
 
843
    def test_backwards_compat(self):
 
844
        self.setup_smart_server_with_call_log()
 
845
        branch = self.make_branch('.')
 
846
        self.reset_smart_call_log()
 
847
        verb = 'Branch.get_tags_bytes'
 
848
        self.disable_verb(verb)
 
849
        branch.tags.get_tag_dict()
 
850
        call_count = len([call for call in self.hpss_calls if
 
851
            call.call.method == verb])
 
852
        self.assertEqual(1, call_count)
 
853
 
 
854
    def test_trivial(self):
 
855
        transport = MemoryTransport()
 
856
        client = FakeClient(transport.base)
 
857
        client.add_expected_call(
 
858
            'Branch.get_stacked_on_url', ('quack/',),
 
859
            'error', ('NotStacked',))
 
860
        client.add_expected_call(
 
861
            'Branch.get_tags_bytes', ('quack/',),
 
862
            'success', ('',))
 
863
        transport.mkdir('quack')
 
864
        transport = transport.clone('quack')
 
865
        branch = self.make_remote_branch(transport, client)
 
866
        result = branch.tags.get_tag_dict()
 
867
        client.finished_test()
 
868
        self.assertEqual({}, result)
560
869
 
561
870
 
562
871
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
596
905
        self.assertEqual((2, revid), result)
597
906
 
598
907
 
599
 
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
 
908
class TestBranch_get_stacked_on_url(TestRemote):
600
909
    """Test Branch._get_stacked_on_url rpc"""
601
910
 
602
911
    def test_get_stacked_on_invalid_url(self):
621
930
            'success', ('ok', vfs_url))
622
931
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
623
932
            _client=client)
624
 
        branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, None),
 
933
        repo_fmt = remote.RemoteRepositoryFormat()
 
934
        repo_fmt._custom_format = stacked_branch.repository._format
 
935
        branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
625
936
            _client=client)
626
937
        result = branch.get_stacked_on_url()
627
938
        self.assertEqual(vfs_url, result)
632
943
        stacked_branch = self.make_branch('stacked', format='1.6')
633
944
        stacked_branch.set_stacked_on_url('../base')
634
945
        client = FakeClient(self.get_url())
635
 
        client.add_expected_call(
636
 
            'BzrDir.open_branch', ('stacked/',),
637
 
            'success', ('ok', ''))
638
 
        client.add_expected_call(
639
 
            'BzrDir.find_repositoryV2', ('stacked/',),
640
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
946
        branch_network_name = self.get_branch_format().network_name()
 
947
        client.add_expected_call(
 
948
            'BzrDir.open_branchV2', ('stacked/',),
 
949
            'success', ('branch', branch_network_name))
 
950
        client.add_expected_call(
 
951
            'BzrDir.find_repositoryV3', ('stacked/',),
 
952
            'success', ('ok', '', 'no', 'no', 'yes',
 
953
                stacked_branch.repository._format.network_name()))
641
954
        # called twice, once from constructor and then again by us
642
955
        client.add_expected_call(
643
956
            'Branch.get_stacked_on_url', ('stacked/',),
663
976
        base_branch = self.make_branch('base', format='1.6')
664
977
        stacked_branch = self.make_branch('stacked', format='1.6')
665
978
        stacked_branch.set_stacked_on_url('../base')
 
979
        reference_format = self.get_repo_format()
 
980
        network_name = reference_format.network_name()
666
981
        client = FakeClient(self.get_url())
667
 
        client.add_expected_call(
668
 
            'BzrDir.open_branch', ('stacked/',),
669
 
            'success', ('ok', ''))
670
 
        client.add_expected_call(
671
 
            'BzrDir.find_repositoryV2', ('stacked/',),
672
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
982
        branch_network_name = self.get_branch_format().network_name()
 
983
        client.add_expected_call(
 
984
            'BzrDir.open_branchV2', ('stacked/',),
 
985
            'success', ('branch', branch_network_name))
 
986
        client.add_expected_call(
 
987
            'BzrDir.find_repositoryV3', ('stacked/',),
 
988
            'success', ('ok', '', 'no', 'no', 'yes', network_name))
673
989
        # called twice, once from constructor and then again by us
674
990
        client.add_expected_call(
675
991
            'Branch.get_stacked_on_url', ('stacked/',),
683
999
        result = branch.get_stacked_on_url()
684
1000
        self.assertEqual('../base', result)
685
1001
        client.finished_test()
686
 
        # it's in the fallback list both for the RemoteRepository and its vfs
687
 
        # repository
 
1002
        # it's in the fallback list both for the RemoteRepository.
688
1003
        self.assertEqual(1, len(branch.repository._fallback_repositories))
689
 
        self.assertEqual(1,
690
 
            len(branch.repository._real_repository._fallback_repositories))
 
1004
        # And we haven't had to construct a real repository.
 
1005
        self.assertEqual(None, branch.repository._real_repository)
691
1006
 
692
1007
 
693
1008
class TestBranchSetLastRevision(RemoteBranchTestCase):
831
1146
        branch = self.make_remote_branch(transport, client)
832
1147
        branch._ensure_real = lambda: None
833
1148
        branch.lock_write()
834
 
        self.addCleanup(branch.unlock)
835
1149
        # The 'TipChangeRejected' error response triggered by calling
836
1150
        # set_revision_history causes a TipChangeRejected exception.
837
1151
        err = self.assertRaises(
1018
1332
        self.assertEqual('rejection message', err.msg)
1019
1333
 
1020
1334
 
1021
 
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
1022
 
    """Getting the branch configuration should use an abstract method not vfs.
1023
 
    """
 
1335
class TestBranchGetSetConfig(RemoteBranchTestCase):
1024
1336
 
1025
1337
    def test_get_branch_conf(self):
1026
 
        raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
1027
 
        ## # We should see that branch.get_config() does a single rpc to get the
1028
 
        ## # remote configuration file, abstracting away where that is stored on
1029
 
        ## # the server.  However at the moment it always falls back to using the
1030
 
        ## # vfs, and this would need some changes in config.py.
1031
 
 
1032
 
        ## # in an empty branch we decode the response properly
1033
 
        ## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
1034
 
        ## # we need to make a real branch because the remote_branch.control_files
1035
 
        ## # will trigger _ensure_real.
1036
 
        ## branch = self.make_branch('quack')
1037
 
        ## transport = branch.bzrdir.root_transport
1038
 
        ## # we do not want bzrdir to make any remote calls
1039
 
        ## bzrdir = RemoteBzrDir(transport, _client=False)
1040
 
        ## branch = RemoteBranch(bzrdir, None, _client=client)
1041
 
        ## config = branch.get_config()
1042
 
        ## self.assertEqual(
1043
 
        ##     [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
1044
 
        ##     client._calls)
 
1338
        # in an empty branch we decode the response properly
 
1339
        client = FakeClient()
 
1340
        client.add_expected_call(
 
1341
            'Branch.get_stacked_on_url', ('memory:///',),
 
1342
            'error', ('NotStacked',),)
 
1343
        client.add_success_response_with_body('# config file body', 'ok')
 
1344
        transport = MemoryTransport()
 
1345
        branch = self.make_remote_branch(transport, client)
 
1346
        config = branch.get_config()
 
1347
        config.has_explicit_nickname()
 
1348
        self.assertEqual(
 
1349
            [('call', 'Branch.get_stacked_on_url', ('memory:///',)),
 
1350
             ('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
 
1351
            client._calls)
 
1352
 
 
1353
    def test_get_multi_line_branch_conf(self):
 
1354
        # Make sure that multiple-line branch.conf files are supported
 
1355
        #
 
1356
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
 
1357
        client = FakeClient()
 
1358
        client.add_expected_call(
 
1359
            'Branch.get_stacked_on_url', ('memory:///',),
 
1360
            'error', ('NotStacked',),)
 
1361
        client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
 
1362
        transport = MemoryTransport()
 
1363
        branch = self.make_remote_branch(transport, client)
 
1364
        config = branch.get_config()
 
1365
        self.assertEqual(u'2', config.get_user_option('b'))
 
1366
 
 
1367
    def test_set_option(self):
 
1368
        client = FakeClient()
 
1369
        client.add_expected_call(
 
1370
            'Branch.get_stacked_on_url', ('memory:///',),
 
1371
            'error', ('NotStacked',),)
 
1372
        client.add_expected_call(
 
1373
            'Branch.lock_write', ('memory:///', '', ''),
 
1374
            'success', ('ok', 'branch token', 'repo token'))
 
1375
        client.add_expected_call(
 
1376
            'Branch.set_config_option', ('memory:///', 'branch token',
 
1377
            'repo token', 'foo', 'bar', ''),
 
1378
            'success', ())
 
1379
        client.add_expected_call(
 
1380
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
 
1381
            'success', ('ok',))
 
1382
        transport = MemoryTransport()
 
1383
        branch = self.make_remote_branch(transport, client)
 
1384
        branch.lock_write()
 
1385
        config = branch._get_config()
 
1386
        config.set_option('foo', 'bar')
 
1387
        branch.unlock()
 
1388
        client.finished_test()
 
1389
 
 
1390
    def test_backwards_compat_set_option(self):
 
1391
        self.setup_smart_server_with_call_log()
 
1392
        branch = self.make_branch('.')
 
1393
        verb = 'Branch.set_config_option'
 
1394
        self.disable_verb(verb)
 
1395
        branch.lock_write()
 
1396
        self.addCleanup(branch.unlock)
 
1397
        self.reset_smart_call_log()
 
1398
        branch._get_config().set_option('value', 'name')
 
1399
        self.assertLength(10, self.hpss_calls)
 
1400
        self.assertEqual('value', branch._get_config().get_option('name'))
1045
1401
 
1046
1402
 
1047
1403
class TestBranchLockWrite(RemoteBranchTestCase):
1086
1442
 
1087
1443
    def test_error_from_old_server(self):
1088
1444
        """bzr 0.15 and earlier servers don't recognise the is_readonly verb.
1089
 
        
 
1445
 
1090
1446
        Clients should treat it as a "no" response, because is_readonly is only
1091
1447
        advisory anyway (a transport could be read-write, but then the
1092
1448
        underlying filesystem could be readonly anyway).
1132
1488
 
1133
1489
class TestRemoteRepository(TestRemote):
1134
1490
    """Base for testing RemoteRepository protocol usage.
1135
 
    
1136
 
    These tests contain frozen requests and responses.  We want any changes to 
 
1491
 
 
1492
    These tests contain frozen requests and responses.  We want any changes to
1137
1493
    what is sent or expected to be require a thoughtful update to these tests
1138
1494
    because they might break compatibility with different-versioned servers.
1139
1495
    """
1140
1496
 
1141
1497
    def setup_fake_client_and_repository(self, transport_path):
1142
1498
        """Create the fake client and repository for testing with.
1143
 
        
 
1499
 
1144
1500
        There's no real server here; we just have canned responses sent
1145
1501
        back one by one.
1146
 
        
 
1502
 
1147
1503
        :param transport_path: Path below the root of the MemoryTransport
1148
1504
            where the repository will be created.
1149
1505
        """
1158
1514
        return repo, client
1159
1515
 
1160
1516
 
 
1517
class TestRepositoryFormat(TestRemoteRepository):
 
1518
 
 
1519
    def test_fast_delta(self):
 
1520
        true_name = pack_repo.RepositoryFormatPackDevelopment2().network_name()
 
1521
        true_format = RemoteRepositoryFormat()
 
1522
        true_format._network_name = true_name
 
1523
        self.assertEqual(True, true_format.fast_deltas)
 
1524
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
 
1525
        false_format = RemoteRepositoryFormat()
 
1526
        false_format._network_name = false_name
 
1527
        self.assertEqual(False, false_format.fast_deltas)
 
1528
 
 
1529
 
1161
1530
class TestRepositoryGatherStats(TestRemoteRepository):
1162
1531
 
1163
1532
    def test_revid_none(self):
1251
1620
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1252
1621
        self.assertEqual(
1253
1622
            [('call_with_body_bytes_expecting_body',
1254
 
              'Repository.get_parent_map', ('quack/', r2), '\n\n0')],
 
1623
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
 
1624
              '\n\n0')],
1255
1625
            client._calls)
1256
1626
        repo.unlock()
1257
1627
        # now we call again, and it should use the second response.
1261
1631
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1262
1632
        self.assertEqual(
1263
1633
            [('call_with_body_bytes_expecting_body',
1264
 
              'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
 
1634
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
 
1635
              '\n\n0'),
1265
1636
             ('call_with_body_bytes_expecting_body',
1266
 
              'Repository.get_parent_map', ('quack/', r1), '\n\n0'),
 
1637
              'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
 
1638
              '\n\n0'),
1267
1639
            ],
1268
1640
            client._calls)
1269
1641
        repo.unlock()
1270
1642
 
1271
1643
    def test_get_parent_map_reconnects_if_unknown_method(self):
1272
1644
        transport_path = 'quack'
 
1645
        rev_id = 'revision-id'
1273
1646
        repo, client = self.setup_fake_client_and_repository(transport_path)
1274
 
        client.add_unknown_method_response('Repository,get_parent_map')
1275
 
        client.add_success_response_with_body('', 'ok')
 
1647
        client.add_unknown_method_response('Repository.get_parent_map')
 
1648
        client.add_success_response_with_body(rev_id, 'ok')
1276
1649
        self.assertFalse(client._medium._is_remote_before((1, 2)))
1277
 
        rev_id = 'revision-id'
1278
 
        expected_deprecations = [
1279
 
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1280
 
            'in version 1.4.']
1281
 
        parents = self.callDeprecated(
1282
 
            expected_deprecations, repo.get_parent_map, [rev_id])
 
1650
        parents = repo.get_parent_map([rev_id])
1283
1651
        self.assertEqual(
1284
1652
            [('call_with_body_bytes_expecting_body',
1285
 
              'Repository.get_parent_map', ('quack/', rev_id), '\n\n0'),
 
1653
              'Repository.get_parent_map', ('quack/', 'include-missing:',
 
1654
              rev_id), '\n\n0'),
1286
1655
             ('disconnect medium',),
1287
1656
             ('call_expecting_body', 'Repository.get_revision_graph',
1288
1657
              ('quack/', ''))],
1289
1658
            client._calls)
1290
1659
        # The medium is now marked as being connected to an older server
1291
1660
        self.assertTrue(client._medium._is_remote_before((1, 2)))
 
1661
        self.assertEqual({rev_id: ('null:',)}, parents)
1292
1662
 
1293
1663
    def test_get_parent_map_fallback_parentless_node(self):
1294
1664
        """get_parent_map falls back to get_revision_graph on old servers.  The
1306
1676
        repo, client = self.setup_fake_client_and_repository(transport_path)
1307
1677
        client.add_success_response_with_body(rev_id, 'ok')
1308
1678
        client._medium._remember_remote_is_before((1, 2))
1309
 
        expected_deprecations = [
1310
 
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1311
 
            'in version 1.4.']
1312
 
        parents = self.callDeprecated(
1313
 
            expected_deprecations, repo.get_parent_map, [rev_id])
 
1679
        parents = repo.get_parent_map([rev_id])
1314
1680
        self.assertEqual(
1315
1681
            [('call_expecting_body', 'Repository.get_revision_graph',
1316
1682
             ('quack/', ''))],
1324
1690
            errors.UnexpectedSmartServerResponse,
1325
1691
            repo.get_parent_map, ['a-revision-id'])
1326
1692
 
 
1693
    def test_get_parent_map_negative_caches_missing_keys(self):
 
1694
        self.setup_smart_server_with_call_log()
 
1695
        repo = self.make_repository('foo')
 
1696
        self.assertIsInstance(repo, RemoteRepository)
 
1697
        repo.lock_read()
 
1698
        self.addCleanup(repo.unlock)
 
1699
        self.reset_smart_call_log()
 
1700
        graph = repo.get_graph()
 
1701
        self.assertEqual({},
 
1702
            graph.get_parent_map(['some-missing', 'other-missing']))
 
1703
        self.assertLength(1, self.hpss_calls)
 
1704
        # No call if we repeat this
 
1705
        self.reset_smart_call_log()
 
1706
        graph = repo.get_graph()
 
1707
        self.assertEqual({},
 
1708
            graph.get_parent_map(['some-missing', 'other-missing']))
 
1709
        self.assertLength(0, self.hpss_calls)
 
1710
        # Asking for more unknown keys makes a request.
 
1711
        self.reset_smart_call_log()
 
1712
        graph = repo.get_graph()
 
1713
        self.assertEqual({},
 
1714
            graph.get_parent_map(['some-missing', 'other-missing',
 
1715
                'more-missing']))
 
1716
        self.assertLength(1, self.hpss_calls)
 
1717
 
 
1718
    def disableExtraResults(self):
 
1719
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
 
1720
        SmartServerRepositoryGetParentMap.no_extra_results = True
 
1721
        def reset_values():
 
1722
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
 
1723
        self.addCleanup(reset_values)
 
1724
 
 
1725
    def test_null_cached_missing_and_stop_key(self):
 
1726
        self.setup_smart_server_with_call_log()
 
1727
        # Make a branch with a single revision.
 
1728
        builder = self.make_branch_builder('foo')
 
1729
        builder.start_series()
 
1730
        builder.build_snapshot('first', None, [
 
1731
            ('add', ('', 'root-id', 'directory', ''))])
 
1732
        builder.finish_series()
 
1733
        branch = builder.get_branch()
 
1734
        repo = branch.repository
 
1735
        self.assertIsInstance(repo, RemoteRepository)
 
1736
        # Stop the server from sending extra results.
 
1737
        self.disableExtraResults()
 
1738
        repo.lock_read()
 
1739
        self.addCleanup(repo.unlock)
 
1740
        self.reset_smart_call_log()
 
1741
        graph = repo.get_graph()
 
1742
        # Query for 'first' and 'null:'.  Because 'null:' is a parent of
 
1743
        # 'first' it will be a candidate for the stop_keys of subsequent
 
1744
        # requests, and because 'null:' was queried but not returned it will be
 
1745
        # cached as missing.
 
1746
        self.assertEqual({'first': ('null:',)},
 
1747
            graph.get_parent_map(['first', 'null:']))
 
1748
        # Now query for another key.  This request will pass along a recipe of
 
1749
        # start and stop keys describing the already cached results, and this
 
1750
        # recipe's revision count must be correct (or else it will trigger an
 
1751
        # error from the server).
 
1752
        self.assertEqual({}, graph.get_parent_map(['another-key']))
 
1753
        # This assertion guards against disableExtraResults silently failing to
 
1754
        # work, thus invalidating the test.
 
1755
        self.assertLength(2, self.hpss_calls)
 
1756
 
 
1757
    def test_get_parent_map_gets_ghosts_from_result(self):
 
1758
        # asking for a revision should negatively cache close ghosts in its
 
1759
        # ancestry.
 
1760
        self.setup_smart_server_with_call_log()
 
1761
        tree = self.make_branch_and_memory_tree('foo')
 
1762
        tree.lock_write()
 
1763
        try:
 
1764
            builder = treebuilder.TreeBuilder()
 
1765
            builder.start_tree(tree)
 
1766
            builder.build([])
 
1767
            builder.finish_tree()
 
1768
            tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
 
1769
            rev_id = tree.commit('')
 
1770
        finally:
 
1771
            tree.unlock()
 
1772
        tree.lock_read()
 
1773
        self.addCleanup(tree.unlock)
 
1774
        repo = tree.branch.repository
 
1775
        self.assertIsInstance(repo, RemoteRepository)
 
1776
        # ask for rev_id
 
1777
        repo.get_parent_map([rev_id])
 
1778
        self.reset_smart_call_log()
 
1779
        # Now asking for rev_id's ghost parent should not make calls
 
1780
        self.assertEqual({}, repo.get_parent_map(['non-existant']))
 
1781
        self.assertLength(0, self.hpss_calls)
 
1782
 
1327
1783
 
1328
1784
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1329
1785
 
1346
1802
 
1347
1803
 
1348
1804
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
1349
 
    
 
1805
 
1350
1806
    def test_null_revision(self):
1351
1807
        # a null revision has the predictable result {}, we should have no wire
1352
1808
        # traffic when calling it with this argument
1353
1809
        transport_path = 'empty'
1354
1810
        repo, client = self.setup_fake_client_and_repository(transport_path)
1355
1811
        client.add_success_response('notused')
1356
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph,
1357
 
            NULL_REVISION)
 
1812
        # actual RemoteRepository.get_revision_graph is gone, but there's an
 
1813
        # equivalent private method for testing
 
1814
        result = repo._get_revision_graph(NULL_REVISION)
1358
1815
        self.assertEqual([], client._calls)
1359
1816
        self.assertEqual({}, result)
1360
1817
 
1368
1825
        transport_path = 'sinhala'
1369
1826
        repo, client = self.setup_fake_client_and_repository(transport_path)
1370
1827
        client.add_success_response_with_body(encoded_body, 'ok')
1371
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph)
 
1828
        # actual RemoteRepository.get_revision_graph is gone, but there's an
 
1829
        # equivalent private method for testing
 
1830
        result = repo._get_revision_graph(None)
1372
1831
        self.assertEqual(
1373
1832
            [('call_expecting_body', 'Repository.get_revision_graph',
1374
1833
             ('sinhala/', ''))],
1387
1846
        transport_path = 'sinhala'
1388
1847
        repo, client = self.setup_fake_client_and_repository(transport_path)
1389
1848
        client.add_success_response_with_body(encoded_body, 'ok')
1390
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph, r2)
 
1849
        result = repo._get_revision_graph(r2)
1391
1850
        self.assertEqual(
1392
1851
            [('call_expecting_body', 'Repository.get_revision_graph',
1393
1852
             ('sinhala/', r2))],
1401
1860
        client.add_error_response('nosuchrevision', revid)
1402
1861
        # also check that the right revision is reported in the error
1403
1862
        self.assertRaises(errors.NoSuchRevision,
1404
 
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
 
1863
            repo._get_revision_graph, revid)
1405
1864
        self.assertEqual(
1406
1865
            [('call_expecting_body', 'Repository.get_revision_graph',
1407
1866
             ('sinhala/', revid))],
1413
1872
        repo, client = self.setup_fake_client_and_repository(transport_path)
1414
1873
        client.add_error_response('AnUnexpectedError')
1415
1874
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1416
 
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
 
1875
            repo._get_revision_graph, revid)
1417
1876
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
1418
1877
 
1419
 
        
 
1878
 
1420
1879
class TestRepositoryIsShared(TestRemoteRepository):
1421
1880
 
1422
1881
    def test_is_shared(self):
1483
1942
        self.disable_verb(verb)
1484
1943
        repo.set_make_working_trees(True)
1485
1944
        call_count = len([call for call in self.hpss_calls if
1486
 
            call[0].method == verb])
 
1945
            call.call.method == verb])
1487
1946
        self.assertEqual(1, call_count)
1488
1947
 
1489
1948
    def test_current(self):
1537
1996
        self.assertEqual([], client._calls)
1538
1997
 
1539
1998
 
 
1999
class TestRepositoryInsertStream(TestRemoteRepository):
 
2000
 
 
2001
    def test_unlocked_repo(self):
 
2002
        transport_path = 'quack'
 
2003
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2004
        client.add_expected_call(
 
2005
            'Repository.insert_stream', ('quack/', ''),
 
2006
            'success', ('ok',))
 
2007
        client.add_expected_call(
 
2008
            'Repository.insert_stream', ('quack/', ''),
 
2009
            'success', ('ok',))
 
2010
        sink = repo._get_sink()
 
2011
        fmt = repository.RepositoryFormat.get_default_format()
 
2012
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2013
        self.assertEqual([], resume_tokens)
 
2014
        self.assertEqual(set(), missing_keys)
 
2015
        client.finished_test()
 
2016
 
 
2017
    def test_locked_repo_with_no_lock_token(self):
 
2018
        transport_path = 'quack'
 
2019
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2020
        client.add_expected_call(
 
2021
            'Repository.lock_write', ('quack/', ''),
 
2022
            'success', ('ok', ''))
 
2023
        client.add_expected_call(
 
2024
            'Repository.insert_stream', ('quack/', ''),
 
2025
            'success', ('ok',))
 
2026
        client.add_expected_call(
 
2027
            'Repository.insert_stream', ('quack/', ''),
 
2028
            'success', ('ok',))
 
2029
        repo.lock_write()
 
2030
        sink = repo._get_sink()
 
2031
        fmt = repository.RepositoryFormat.get_default_format()
 
2032
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2033
        self.assertEqual([], resume_tokens)
 
2034
        self.assertEqual(set(), missing_keys)
 
2035
        client.finished_test()
 
2036
 
 
2037
    def test_locked_repo_with_lock_token(self):
 
2038
        transport_path = 'quack'
 
2039
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2040
        client.add_expected_call(
 
2041
            'Repository.lock_write', ('quack/', ''),
 
2042
            'success', ('ok', 'a token'))
 
2043
        client.add_expected_call(
 
2044
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2045
            'success', ('ok',))
 
2046
        client.add_expected_call(
 
2047
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2048
            'success', ('ok',))
 
2049
        repo.lock_write()
 
2050
        sink = repo._get_sink()
 
2051
        fmt = repository.RepositoryFormat.get_default_format()
 
2052
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2053
        self.assertEqual([], resume_tokens)
 
2054
        self.assertEqual(set(), missing_keys)
 
2055
        client.finished_test()
 
2056
 
 
2057
 
1540
2058
class TestRepositoryTarball(TestRemoteRepository):
1541
2059
 
1542
2060
    # This is a canned tarball reponse we can validate against
1594
2112
class _StubRealPackRepository(object):
1595
2113
 
1596
2114
    def __init__(self, calls):
 
2115
        self.calls = calls
1597
2116
        self._pack_collection = _StubPackCollection(calls)
1598
2117
 
 
2118
    def is_in_write_group(self):
 
2119
        return False
 
2120
 
 
2121
    def refresh_data(self):
 
2122
        self.calls.append(('pack collection reload_pack_names',))
 
2123
 
1599
2124
 
1600
2125
class _StubPackCollection(object):
1601
2126
 
1605
2130
    def autopack(self):
1606
2131
        self.calls.append(('pack collection autopack',))
1607
2132
 
1608
 
    def reload_pack_names(self):
1609
 
        self.calls.append(('pack collection reload_pack_names',))
1610
2133
 
1611
 
    
1612
2134
class TestRemotePackRepositoryAutoPack(TestRemoteRepository):
1613
2135
    """Tests for RemoteRepository.autopack implementation."""
1614
2136
 
1638
2160
            [('call', 'PackRepository.autopack', ('quack/',)),
1639
2161
             ('pack collection reload_pack_names',)],
1640
2162
            client._calls)
1641
 
        
 
2163
 
1642
2164
    def test_backwards_compatibility(self):
1643
2165
        """If the server does not recognise the PackRepository.autopack verb,
1644
2166
        fallback to the real_repository's implementation.
1694
2216
 
1695
2217
class TestErrorTranslationSuccess(TestErrorTranslationBase):
1696
2218
    """Unit tests for bzrlib.remote._translate_error.
1697
 
    
 
2219
 
1698
2220
    Given an ErrorFromSmartServer (which has an error tuple from a smart
1699
2221
    server) and some context, _translate_error raises more specific errors from
1700
2222
    bzrlib.errors.
1805
2327
 
1806
2328
class TestErrorTranslationRobustness(TestErrorTranslationBase):
1807
2329
    """Unit tests for bzrlib.remote._translate_error's robustness.
1808
 
    
 
2330
 
1809
2331
    TestErrorTranslationSuccess is for cases where _translate_error can
1810
2332
    translate successfully.  This class about how _translate_err behaves when
1811
2333
    it fails to translate: it re-raises the original error.
1839
2361
        self.assertContainsRe(
1840
2362
            self._get_log(keep_log_file=True),
1841
2363
            "Missing key 'branch' in context")
1842
 
        
 
2364
 
1843
2365
    def test_path_missing(self):
1844
2366
        """Some translations (PermissionDenied, ReadError) can determine the
1845
2367
        'path' variable from either the wire or the local context.  If neither
1857
2379
 
1858
2380
class TestStacking(tests.TestCaseWithTransport):
1859
2381
    """Tests for operations on stacked remote repositories.
1860
 
    
 
2382
 
1861
2383
    The underlying format type must support stacking.
1862
2384
    """
1863
2385
 
1867
2389
        # revision, then open it over hpss - we should be able to see that
1868
2390
        # revision.
1869
2391
        base_transport = self.get_transport()
1870
 
        base_builder = self.make_branch_builder('base', format='1.6')
 
2392
        base_builder = self.make_branch_builder('base', format='1.9')
1871
2393
        base_builder.start_series()
1872
2394
        base_revid = base_builder.build_snapshot('rev-id', None,
1873
2395
            [('add', ('', None, 'directory', None))],
1874
2396
            'message')
1875
2397
        base_builder.finish_series()
1876
 
        stacked_branch = self.make_branch('stacked', format='1.6')
 
2398
        stacked_branch = self.make_branch('stacked', format='1.9')
1877
2399
        stacked_branch.set_stacked_on_url('../base')
1878
2400
        # start a server looking at this
1879
2401
        smart_server = server.SmartTCPServer_for_testing()
1900
2422
            remote_repo.unlock()
1901
2423
 
1902
2424
    def prepare_stacked_remote_branch(self):
1903
 
        smart_server = server.SmartTCPServer_for_testing()
1904
 
        smart_server.setUp()
1905
 
        self.addCleanup(smart_server.tearDown)
1906
 
        tree1 = self.make_branch_and_tree('tree1')
 
2425
        """Get stacked_upon and stacked branches with content in each."""
 
2426
        self.setup_smart_server_with_call_log()
 
2427
        tree1 = self.make_branch_and_tree('tree1', format='1.9')
1907
2428
        tree1.commit('rev1', rev_id='rev1')
1908
 
        tree2 = self.make_branch_and_tree('tree2', format='1.6')
1909
 
        tree2.branch.set_stacked_on_url(tree1.branch.base)
1910
 
        branch2 = Branch.open(smart_server.get_url() + '/tree2')
 
2429
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
 
2430
            ).open_workingtree()
 
2431
        tree2.commit('local changes make me feel good.')
 
2432
        branch2 = Branch.open(self.get_url('tree2'))
1911
2433
        branch2.lock_read()
1912
2434
        self.addCleanup(branch2.unlock)
1913
 
        return branch2
 
2435
        return tree1.branch, branch2
1914
2436
 
1915
2437
    def test_stacked_get_parent_map(self):
1916
2438
        # the public implementation of get_parent_map obeys stacking
1917
 
        branch = self.prepare_stacked_remote_branch()
 
2439
        _, branch = self.prepare_stacked_remote_branch()
1918
2440
        repo = branch.repository
1919
2441
        self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1920
2442
 
1921
2443
    def test_unstacked_get_parent_map(self):
1922
2444
        # _unstacked_provider.get_parent_map ignores stacking
1923
 
        branch = self.prepare_stacked_remote_branch()
 
2445
        _, branch = self.prepare_stacked_remote_branch()
1924
2446
        provider = branch.repository._unstacked_provider
1925
2447
        self.assertEqual([], provider.get_parent_map(['rev1']).keys())
1926
2448
 
 
2449
    def fetch_stream_to_rev_order(self, stream):
 
2450
        result = []
 
2451
        for kind, substream in stream:
 
2452
            if not kind == 'revisions':
 
2453
                list(substream)
 
2454
            else:
 
2455
                for content in substream:
 
2456
                    result.append(content.key[-1])
 
2457
        return result
 
2458
 
 
2459
    def get_ordered_revs(self, format, order):
 
2460
        """Get a list of the revisions in a stream to format format.
 
2461
 
 
2462
        :param format: The format of the target.
 
2463
        :param order: the order that target should have requested.
 
2464
        :result: The revision ids in the stream, in the order seen,
 
2465
            the topological order of revisions in the source.
 
2466
        """
 
2467
        unordered_format = bzrdir.format_registry.get(format)()
 
2468
        target_repository_format = unordered_format.repository_format
 
2469
        # Cross check
 
2470
        self.assertEqual(order, target_repository_format._fetch_order)
 
2471
        trunk, stacked = self.prepare_stacked_remote_branch()
 
2472
        source = stacked.repository._get_source(target_repository_format)
 
2473
        tip = stacked.last_revision()
 
2474
        revs = stacked.repository.get_ancestry(tip)
 
2475
        search = graph.PendingAncestryResult([tip], stacked.repository)
 
2476
        self.reset_smart_call_log()
 
2477
        stream = source.get_stream(search)
 
2478
        if None in revs:
 
2479
            revs.remove(None)
 
2480
        # We trust that if a revision is in the stream the rest of the new
 
2481
        # content for it is too, as per our main fetch tests; here we are
 
2482
        # checking that the revisions are actually included at all, and their
 
2483
        # order.
 
2484
        return self.fetch_stream_to_rev_order(stream), revs
 
2485
 
 
2486
    def test_stacked_get_stream_unordered(self):
 
2487
        # Repository._get_source.get_stream() from a stacked repository with
 
2488
        # unordered yields the full data from both stacked and stacked upon
 
2489
        # sources.
 
2490
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
 
2491
        self.assertEqual(set(expected_revs), set(rev_ord))
 
2492
        # Getting unordered results should have made a streaming data request
 
2493
        # from the server, then one from the backing branch.
 
2494
        self.assertLength(2, self.hpss_calls)
 
2495
 
 
2496
    def test_stacked_get_stream_topological(self):
 
2497
        # Repository._get_source.get_stream() from a stacked repository with
 
2498
        # topological sorting yields the full data from both stacked and
 
2499
        # stacked upon sources in topological order.
 
2500
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
 
2501
        self.assertEqual(expected_revs, rev_ord)
 
2502
        # Getting topological sort requires VFS calls still
 
2503
        self.assertLength(12, self.hpss_calls)
 
2504
 
 
2505
    def test_stacked_get_stream_groupcompress(self):
 
2506
        # Repository._get_source.get_stream() from a stacked repository with
 
2507
        # groupcompress sorting yields the full data from both stacked and
 
2508
        # stacked upon sources in groupcompress order.
 
2509
        raise tests.TestSkipped('No groupcompress ordered format available')
 
2510
        rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
 
2511
        self.assertEqual(expected_revs, reversed(rev_ord))
 
2512
        # Getting unordered results should have made a streaming data request
 
2513
        # from the backing branch, and one from the stacked on branch.
 
2514
        self.assertLength(2, self.hpss_calls)
 
2515
 
1927
2516
 
1928
2517
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
1929
2518