~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-27 05:24:02 UTC
  • mfrom: (4570 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4573.
  • Revision ID: andrew.bennetts@canonical.com-20090727052402-e3vakc2pnq0y66gm
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 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
27
27
from cStringIO import StringIO
28
28
 
29
29
from bzrlib import (
30
 
    branch,
31
30
    bzrdir,
32
31
    config,
33
 
    controldir,
34
32
    errors,
35
 
    graph as _mod_graph,
36
 
    inventory,
37
 
    inventory_delta,
 
33
    graph,
 
34
    pack,
38
35
    remote,
39
36
    repository,
 
37
    smart,
40
38
    tests,
41
 
    transport,
42
39
    treebuilder,
43
 
    versionedfile,
 
40
    urlutils,
44
41
    )
45
42
from bzrlib.branch import Branch
46
 
from bzrlib.bzrdir import (
47
 
    BzrDir,
48
 
    BzrDirFormat,
49
 
    RemoteBzrProber,
50
 
    )
 
43
from bzrlib.bzrdir import BzrDir, BzrDirFormat
51
44
from bzrlib.remote import (
52
45
    RemoteBranch,
53
46
    RemoteBranchFormat,
56
49
    RemoteRepository,
57
50
    RemoteRepositoryFormat,
58
51
    )
59
 
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
 
52
from bzrlib.repofmt import groupcompress_repo, pack_repo
60
53
from bzrlib.revision import NULL_REVISION
61
 
from bzrlib.smart import medium, request
 
54
from bzrlib.smart import server, medium
62
55
from bzrlib.smart.client import _SmartClient
63
 
from bzrlib.smart.repository import (
64
 
    SmartServerRepositoryGetParentMap,
65
 
    SmartServerRepositoryGetStream_1_19,
66
 
    )
 
56
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
67
57
from bzrlib.tests import (
68
 
    test_server,
 
58
    condition_isinstance,
 
59
    split_suite_by_condition,
 
60
    multiply_tests,
 
61
    KnownFailure,
69
62
    )
70
 
from bzrlib.tests.scenarios import load_tests_apply_scenarios
 
63
from bzrlib.transport import get_transport, http
71
64
from bzrlib.transport.memory import MemoryTransport
72
65
from bzrlib.transport.remote import (
73
66
    RemoteTransport,
74
67
    RemoteSSHTransport,
75
68
    RemoteTCPTransport,
76
 
    )
77
 
 
78
 
 
79
 
load_tests = load_tests_apply_scenarios
80
 
 
81
 
 
82
 
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
83
 
 
84
 
    scenarios = [
 
69
)
 
70
 
 
71
def load_tests(standard_tests, module, loader):
 
72
    to_adapt, result = split_suite_by_condition(
 
73
        standard_tests, condition_isinstance(BasicRemoteObjectTests))
 
74
    smart_server_version_scenarios = [
85
75
        ('HPSS-v2',
86
 
            {'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
 
76
            {'transport_server': server.SmartTCPServer_for_testing_v2_only}),
87
77
        ('HPSS-v3',
88
 
            {'transport_server': test_server.SmartTCPServer_for_testing})]
89
 
 
 
78
            {'transport_server': server.SmartTCPServer_for_testing})]
 
79
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
 
80
 
 
81
 
 
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
90
83
 
91
84
    def setUp(self):
92
85
        super(BasicRemoteObjectTests, self).setUp()
93
86
        self.transport = self.get_transport()
94
87
        # make a branch that can be opened over the smart transport
95
88
        self.local_wt = BzrDir.create_standalone_workingtree('.')
96
 
        self.addCleanup(self.transport.disconnect)
 
89
 
 
90
    def tearDown(self):
 
91
        self.transport.disconnect()
 
92
        tests.TestCaseWithTransport.tearDown(self)
97
93
 
98
94
    def test_create_remote_bzrdir(self):
99
 
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
 
95
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
100
96
        self.assertIsInstance(b, BzrDir)
101
97
 
102
98
    def test_open_remote_branch(self):
103
99
        # open a standalone branch in the working directory
104
 
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
 
100
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
105
101
        branch = b.open_branch()
106
102
        self.assertIsInstance(branch, Branch)
107
103
 
123
119
    def test_find_correct_format(self):
124
120
        """Should open a RemoteBzrDir over a RemoteTransport"""
125
121
        fmt = BzrDirFormat.find_format(self.transport)
126
 
        self.assertTrue(bzrdir.RemoteBzrProber
127
 
                        in controldir.ControlDirFormat._server_probers)
128
 
        self.assertIsInstance(fmt, RemoteBzrDirFormat)
 
122
        self.assertTrue(RemoteBzrDirFormat
 
123
                        in BzrDirFormat._control_server_formats)
 
124
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
129
125
 
130
126
    def test_open_detected_smart_format(self):
131
127
        fmt = BzrDirFormat.find_format(self.transport)
136
132
        b = BzrDir.open_from_transport(self.transport).open_branch()
137
133
        self.assertStartsWith(str(b), 'RemoteBranch(')
138
134
 
139
 
    def test_remote_bzrdir_repr(self):
140
 
        b = BzrDir.open_from_transport(self.transport)
141
 
        self.assertStartsWith(str(b), 'RemoteBzrDir(')
142
 
 
143
135
    def test_remote_branch_format_supports_stacking(self):
144
136
        t = self.transport
145
137
        self.make_branch('unstackable', format='pack-0.92')
285
277
        self.expecting_body = True
286
278
        return result[1], FakeProtocol(result[2], self)
287
279
 
288
 
    def call_with_body_bytes(self, method, args, body):
289
 
        self._check_call(method, args)
290
 
        self._calls.append(('call_with_body_bytes', method, args, body))
291
 
        result = self._get_next_response()
292
 
        return result[1], FakeProtocol(result[2], self)
293
 
 
294
280
    def call_with_body_bytes_expecting_body(self, method, args, body):
295
281
        self._check_call(method, args)
296
282
        self._calls.append(('call_with_body_bytes_expecting_body', method,
346
332
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
347
333
        return reference_bzrdir_format.repository_format
348
334
 
 
335
    def disable_verb(self, verb):
 
336
        """Disable a verb for one test."""
 
337
        request_handlers = smart.request.request_handlers
 
338
        orig_method = request_handlers.get(verb)
 
339
        request_handlers.remove(verb)
 
340
        def restoreVerb():
 
341
            request_handlers.register(verb, orig_method)
 
342
        self.addCleanup(restoreVerb)
 
343
 
349
344
    def assertFinished(self, fake_client):
350
345
        """Assert that all of a FakeClient's expected calls have occurred."""
351
346
        fake_client.finished_test()
360
355
        a given client_base and transport_base.
361
356
        """
362
357
        client_medium = medium.SmartClientMedium(client_base)
363
 
        t = transport.get_transport(transport_base)
364
 
        result = client_medium.remote_path_from_transport(t)
 
358
        transport = get_transport(transport_base)
 
359
        result = client_medium.remote_path_from_transport(transport)
365
360
        self.assertEqual(expected, result)
366
361
 
367
362
    def test_remote_path_from_transport(self):
378
373
        a given transport_base and relpath of that transport.  (Note that
379
374
        HttpTransportBase is a subclass of SmartClientMedium)
380
375
        """
381
 
        base_transport = transport.get_transport(transport_base)
 
376
        base_transport = get_transport(transport_base)
382
377
        client_medium = base_transport.get_smart_medium()
383
378
        cloned_transport = base_transport.clone(relpath)
384
379
        result = client_medium.remote_path_from_transport(cloned_transport)
419
414
        # Calling _remember_remote_is_before again with a lower value works.
420
415
        client_medium._remember_remote_is_before((1, 5))
421
416
        self.assertTrue(client_medium._is_remote_before((1, 5)))
422
 
        # If you call _remember_remote_is_before with a higher value it logs a
423
 
        # warning, and continues to remember the lower value.
424
 
        self.assertNotContainsRe(self.get_log(), '_remember_remote_is_before')
425
 
        client_medium._remember_remote_is_before((1, 9))
426
 
        self.assertContainsRe(self.get_log(), '_remember_remote_is_before')
427
 
        self.assertTrue(client_medium._is_remote_before((1, 5)))
 
417
        # You cannot call _remember_remote_is_before with a larger value.
 
418
        self.assertRaises(
 
419
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
428
420
 
429
421
 
430
422
class TestBzrDirCloningMetaDir(TestRemote):
449
441
            'BzrDir.cloning_metadir', ('quack/', 'False'),
450
442
            'error', ('BranchReference',)),
451
443
        client.add_expected_call(
452
 
            'BzrDir.open_branchV3', ('quack/',),
 
444
            'BzrDir.open_branchV2', ('quack/',),
453
445
            'success', ('ref', self.get_url('referenced'))),
454
 
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
446
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
455
447
            _client=client)
456
448
        result = a_bzrdir.cloning_metadir()
457
449
        # We should have got a control dir matching the referenced branch.
470
462
        client.add_expected_call(
471
463
            'BzrDir.cloning_metadir', ('quack/', 'False'),
472
464
            'success', (control_name, '', ('branch', ''))),
473
 
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
465
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
474
466
            _client=client)
475
467
        result = a_bzrdir.cloning_metadir()
476
468
        # We should have got a reference control dir with default branch and
482
474
        self.assertFinished(client)
483
475
 
484
476
 
485
 
class TestBzrDirOpen(TestRemote):
486
 
 
487
 
    def make_fake_client_and_transport(self, path='quack'):
488
 
        transport = MemoryTransport()
489
 
        transport.mkdir(path)
490
 
        transport = transport.clone(path)
491
 
        client = FakeClient(transport.base)
492
 
        return client, transport
493
 
 
494
 
    def test_absent(self):
495
 
        client, transport = self.make_fake_client_and_transport()
496
 
        client.add_expected_call(
497
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
498
 
        self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
499
 
                RemoteBzrDirFormat(), _client=client, _force_probe=True)
500
 
        self.assertFinished(client)
501
 
 
502
 
    def test_present_without_workingtree(self):
503
 
        client, transport = self.make_fake_client_and_transport()
504
 
        client.add_expected_call(
505
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
506
 
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
507
 
            _client=client, _force_probe=True)
508
 
        self.assertIsInstance(bd, RemoteBzrDir)
509
 
        self.assertFalse(bd.has_workingtree())
510
 
        self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
511
 
        self.assertFinished(client)
512
 
 
513
 
    def test_present_with_workingtree(self):
514
 
        client, transport = self.make_fake_client_and_transport()
515
 
        client.add_expected_call(
516
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
517
 
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
518
 
            _client=client, _force_probe=True)
519
 
        self.assertIsInstance(bd, RemoteBzrDir)
520
 
        self.assertTrue(bd.has_workingtree())
521
 
        self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
522
 
        self.assertFinished(client)
523
 
 
524
 
    def test_backwards_compat(self):
525
 
        client, transport = self.make_fake_client_and_transport()
526
 
        client.add_expected_call(
527
 
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
528
 
        client.add_expected_call(
529
 
            'BzrDir.open', ('quack/',), 'success', ('yes',))
530
 
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
531
 
            _client=client, _force_probe=True)
532
 
        self.assertIsInstance(bd, RemoteBzrDir)
533
 
        self.assertFinished(client)
534
 
 
535
 
    def test_backwards_compat_hpss_v2(self):
536
 
        client, transport = self.make_fake_client_and_transport()
537
 
        # Monkey-patch fake client to simulate real-world behaviour with v2
538
 
        # server: upon first RPC call detect the protocol version, and because
539
 
        # the version is 2 also do _remember_remote_is_before((1, 6)) before
540
 
        # continuing with the RPC.
541
 
        orig_check_call = client._check_call
542
 
        def check_call(method, args):
543
 
            client._medium._protocol_version = 2
544
 
            client._medium._remember_remote_is_before((1, 6))
545
 
            client._check_call = orig_check_call
546
 
            client._check_call(method, args)
547
 
        client._check_call = check_call
548
 
        client.add_expected_call(
549
 
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
550
 
        client.add_expected_call(
551
 
            'BzrDir.open', ('quack/',), 'success', ('yes',))
552
 
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
553
 
            _client=client, _force_probe=True)
554
 
        self.assertIsInstance(bd, RemoteBzrDir)
555
 
        self.assertFinished(client)
556
 
 
557
 
 
558
477
class TestBzrDirOpenBranch(TestRemote):
559
478
 
560
479
    def test_backwards_compat(self):
562
481
        self.make_branch('.')
563
482
        a_dir = BzrDir.open(self.get_url('.'))
564
483
        self.reset_smart_call_log()
565
 
        verb = 'BzrDir.open_branchV3'
 
484
        verb = 'BzrDir.open_branchV2'
566
485
        self.disable_verb(verb)
567
486
        format = a_dir.open_branch()
568
487
        call_count = len([call for call in self.hpss_calls if
578
497
        transport = transport.clone('quack')
579
498
        client = FakeClient(transport.base)
580
499
        client.add_expected_call(
581
 
            'BzrDir.open_branchV3', ('quack/',),
 
500
            'BzrDir.open_branchV2', ('quack/',),
582
501
            'success', ('branch', branch_network_name))
583
502
        client.add_expected_call(
584
503
            'BzrDir.find_repositoryV3', ('quack/',),
586
505
        client.add_expected_call(
587
506
            'Branch.get_stacked_on_url', ('quack/',),
588
507
            'error', ('NotStacked',))
589
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
508
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
590
509
            _client=client)
591
510
        result = bzrdir.open_branch()
592
511
        self.assertIsInstance(result, RemoteBranch)
599
518
        transport = transport.clone('quack')
600
519
        client = FakeClient(transport.base)
601
520
        client.add_error_response('nobranch')
602
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
521
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
603
522
            _client=client)
604
523
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
605
524
        self.assertEqual(
606
 
            [('call', 'BzrDir.open_branchV3', ('quack/',))],
 
525
            [('call', 'BzrDir.open_branchV2', ('quack/',))],
607
526
            client._calls)
608
527
 
609
528
    def test__get_tree_branch(self):
610
529
        # _get_tree_branch is a form of open_branch, but it should only ask for
611
530
        # branch opening, not any other network requests.
612
531
        calls = []
613
 
        def open_branch(name=None):
 
532
        def open_branch():
614
533
            calls.append("Called")
615
534
            return "a-branch"
616
535
        transport = MemoryTransport()
617
536
        # no requests on the network - catches other api calls being made.
618
537
        client = FakeClient(transport.base)
619
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
538
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
620
539
            _client=client)
621
540
        # patch the open_branch call to record that it was called.
622
541
        bzrdir.open_branch = open_branch
633
552
        network_name = reference_format.network_name()
634
553
        branch_network_name = self.get_branch_format().network_name()
635
554
        client.add_expected_call(
636
 
            'BzrDir.open_branchV3', ('~hello/',),
 
555
            'BzrDir.open_branchV2', ('~hello/',),
637
556
            'success', ('branch', branch_network_name))
638
557
        client.add_expected_call(
639
558
            'BzrDir.find_repositoryV3', ('~hello/',),
641
560
        client.add_expected_call(
642
561
            'Branch.get_stacked_on_url', ('~hello/',),
643
562
            'error', ('NotStacked',))
644
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
563
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
645
564
            _client=client)
646
565
        result = bzrdir.open_branch()
647
566
        self.assertFinished(client)
664
583
        client.add_success_response(
665
584
            'ok', '', rich_response, subtree_response, external_lookup,
666
585
            network_name)
667
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
586
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
668
587
            _client=client)
669
588
        result = bzrdir.open_repository()
670
589
        self.assertEqual(
687
606
        old.
688
607
        """
689
608
        self.assertRaises(errors.NotBranchError,
690
 
            RemoteBzrProber.probe_transport, OldServerTransport())
 
609
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
691
610
 
692
611
 
693
612
class TestBzrDirCreateBranch(TestRemote):
716
635
            'BzrDir.create_branch', ('quack/', network_name),
717
636
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
718
637
            reference_repo_name))
719
 
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
638
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
720
639
            _client=client)
721
640
        branch = a_bzrdir.create_branch()
722
641
        # We should have got a remote branch
725
644
        format = branch._format
726
645
        self.assertEqual(network_name, format.network_name())
727
646
 
728
 
    def test_already_open_repo_and_reused_medium(self):
729
 
        """Bug 726584: create_branch(..., repository=repo) should work
730
 
        regardless of what the smart medium's base URL is.
731
 
        """
732
 
        self.transport_server = test_server.SmartTCPServer_for_testing
733
 
        transport = self.get_transport('.')
734
 
        repo = self.make_repository('quack')
735
 
        # Client's medium rooted a transport root (not at the bzrdir)
736
 
        client = FakeClient(transport.base)
737
 
        transport = transport.clone('quack')
738
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
739
 
        reference_format = reference_bzrdir_format.get_branch_format()
740
 
        network_name = reference_format.network_name()
741
 
        reference_repo_fmt = reference_bzrdir_format.repository_format
742
 
        reference_repo_name = reference_repo_fmt.network_name()
743
 
        client.add_expected_call(
744
 
            'BzrDir.create_branch', ('extra/quack/', network_name),
745
 
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
746
 
            reference_repo_name))
747
 
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
748
 
            _client=client)
749
 
        branch = a_bzrdir.create_branch(repository=repo)
750
 
        # We should have got a remote branch
751
 
        self.assertIsInstance(branch, remote.RemoteBranch)
752
 
        # its format should have the settings from the response
753
 
        format = branch._format
754
 
        self.assertEqual(network_name, format.network_name())
755
 
 
756
647
 
757
648
class TestBzrDirCreateRepository(TestRemote):
758
649
 
776
667
        network_name = reference_format.network_name()
777
668
        client.add_expected_call(
778
669
            'BzrDir.create_repository', ('quack/',
779
 
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
780
 
                'False'),
781
 
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
782
 
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
670
                'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
 
671
            'success', ('ok', 'no', 'no', 'no', network_name))
 
672
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
783
673
            _client=client)
784
674
        repo = a_bzrdir.create_repository()
785
675
        # We should have got a remote repository
786
676
        self.assertIsInstance(repo, remote.RemoteRepository)
787
677
        # its format should have the settings from the response
788
678
        format = repo._format
789
 
        self.assertTrue(format.rich_root_data)
790
 
        self.assertTrue(format.supports_tree_reference)
791
 
        self.assertTrue(format.supports_external_lookups)
 
679
        self.assertFalse(format.rich_root_data)
 
680
        self.assertFalse(format.supports_tree_reference)
 
681
        self.assertFalse(format.supports_external_lookups)
792
682
        self.assertEqual(network_name, format.network_name())
793
683
 
794
684
 
798
688
        # fallback all the way to the first version.
799
689
        reference_format = self.get_repo_format()
800
690
        network_name = reference_format.network_name()
801
 
        server_url = 'bzr://example.com/'
802
 
        self.permit_url(server_url)
803
 
        client = FakeClient(server_url)
 
691
        client = FakeClient('bzr://example.com/')
804
692
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
805
693
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
806
694
        client.add_success_response('ok', '', 'no', 'no')
812
700
            reference_format.get_format_string(), 'ok')
813
701
        # PackRepository wants to do a stat
814
702
        client.add_success_response('stat', '0', '65535')
815
 
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
 
703
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
816
704
            _client=client)
817
 
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
 
705
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
818
706
            _client=client)
819
707
        repo = bzrdir.open_repository()
820
708
        self.assertEqual(
832
720
        # fallback to find_repositoryV2
833
721
        reference_format = self.get_repo_format()
834
722
        network_name = reference_format.network_name()
835
 
        server_url = 'bzr://example.com/'
836
 
        self.permit_url(server_url)
837
 
        client = FakeClient(server_url)
 
723
        client = FakeClient('bzr://example.com/')
838
724
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
839
725
        client.add_success_response('ok', '', 'no', 'no', 'no')
840
726
        # A real repository instance will be created to determine the network
845
731
            reference_format.get_format_string(), 'ok')
846
732
        # PackRepository wants to do a stat
847
733
        client.add_success_response('stat', '0', '65535')
848
 
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
 
734
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
849
735
            _client=client)
850
 
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
 
736
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
851
737
            _client=client)
852
738
        repo = bzrdir.open_repository()
853
739
        self.assertEqual(
868
754
        transport = transport.clone('quack')
869
755
        client = FakeClient(transport.base)
870
756
        client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
871
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
757
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
872
758
            _client=client)
873
759
        repo = bzrdir.open_repository()
874
760
        self.assertEqual(
881
767
 
882
768
    def test_success(self):
883
769
        """Simple test for typical successful call."""
884
 
        fmt = RemoteBzrDirFormat()
 
770
        fmt = bzrdir.RemoteBzrDirFormat()
885
771
        default_format_name = BzrDirFormat.get_default_format().network_name()
886
772
        transport = self.get_transport()
887
773
        client = FakeClient(transport.base)
903
789
        """Error responses are translated, e.g. 'PermissionDenied' raises the
904
790
        corresponding error from the client.
905
791
        """
906
 
        fmt = RemoteBzrDirFormat()
 
792
        fmt = bzrdir.RemoteBzrDirFormat()
907
793
        default_format_name = BzrDirFormat.get_default_format().network_name()
908
794
        transport = self.get_transport()
909
795
        client = FakeClient(transport.base)
927
813
        """Integration test for error translation."""
928
814
        transport = self.make_smart_server('foo')
929
815
        transport = transport.clone('no-such-path')
930
 
        fmt = RemoteBzrDirFormat()
 
816
        fmt = bzrdir.RemoteBzrDirFormat()
931
817
        err = self.assertRaises(errors.NoSuchFile,
932
818
            fmt.initialize_on_transport_ex, transport, create_prefix=False)
933
819
 
964
850
 
965
851
    def make_remote_bzrdir(self, transport, client):
966
852
        """Make a RemotebzrDir using 'client' as the _client."""
967
 
        return RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
853
        return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
968
854
            _client=client)
969
855
 
970
856
 
971
857
class RemoteBranchTestCase(RemoteBzrDirTestCase):
972
858
 
973
 
    def lock_remote_branch(self, branch):
974
 
        """Trick a RemoteBranch into thinking it is locked."""
975
 
        branch._lock_mode = 'w'
976
 
        branch._lock_count = 2
977
 
        branch._lock_token = 'branch token'
978
 
        branch._repo_lock_token = 'repo token'
979
 
        branch.repository._lock_mode = 'w'
980
 
        branch.repository._lock_count = 2
981
 
        branch.repository._lock_token = 'repo token'
982
 
 
983
859
    def make_remote_branch(self, transport, client):
984
860
        """Make a RemoteBranch using 'client' as its _SmartClient.
985
861
 
1124
1000
        self.assertEqual({}, result)
1125
1001
 
1126
1002
 
1127
 
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1128
 
 
1129
 
    def test_trivial(self):
1130
 
        transport = MemoryTransport()
1131
 
        client = FakeClient(transport.base)
1132
 
        client.add_expected_call(
1133
 
            'Branch.get_stacked_on_url', ('quack/',),
1134
 
            'error', ('NotStacked',))
1135
 
        client.add_expected_call(
1136
 
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1137
 
            'success', ('',))
1138
 
        transport.mkdir('quack')
1139
 
        transport = transport.clone('quack')
1140
 
        branch = self.make_remote_branch(transport, client)
1141
 
        self.lock_remote_branch(branch)
1142
 
        branch._set_tags_bytes('tags bytes')
1143
 
        self.assertFinished(client)
1144
 
        self.assertEqual('tags bytes', client._calls[-1][-1])
1145
 
 
1146
 
    def test_backwards_compatible(self):
1147
 
        transport = MemoryTransport()
1148
 
        client = FakeClient(transport.base)
1149
 
        client.add_expected_call(
1150
 
            'Branch.get_stacked_on_url', ('quack/',),
1151
 
            'error', ('NotStacked',))
1152
 
        client.add_expected_call(
1153
 
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1154
 
            'unknown', ('Branch.set_tags_bytes',))
1155
 
        transport.mkdir('quack')
1156
 
        transport = transport.clone('quack')
1157
 
        branch = self.make_remote_branch(transport, client)
1158
 
        self.lock_remote_branch(branch)
1159
 
        class StubRealBranch(object):
1160
 
            def __init__(self):
1161
 
                self.calls = []
1162
 
            def _set_tags_bytes(self, bytes):
1163
 
                self.calls.append(('set_tags_bytes', bytes))
1164
 
        real_branch = StubRealBranch()
1165
 
        branch._real_branch = real_branch
1166
 
        branch._set_tags_bytes('tags bytes')
1167
 
        # Call a second time, to exercise the 'remote version already inferred'
1168
 
        # code path.
1169
 
        branch._set_tags_bytes('tags bytes')
1170
 
        self.assertFinished(client)
1171
 
        self.assertEqual(
1172
 
            [('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1173
 
 
1174
 
 
1175
 
class TestBranchHeadsToFetch(RemoteBranchTestCase):
1176
 
 
1177
 
    def test_uses_last_revision_info_and_tags_by_default(self):
1178
 
        transport = MemoryTransport()
1179
 
        client = FakeClient(transport.base)
1180
 
        client.add_expected_call(
1181
 
            'Branch.get_stacked_on_url', ('quack/',),
1182
 
            'error', ('NotStacked',))
1183
 
        client.add_expected_call(
1184
 
            'Branch.last_revision_info', ('quack/',),
1185
 
            'success', ('ok', '1', 'rev-tip'))
1186
 
        client.add_expected_call(
1187
 
            'Branch.get_config_file', ('quack/',),
1188
 
            'success', ('ok',), '')
1189
 
        transport.mkdir('quack')
1190
 
        transport = transport.clone('quack')
1191
 
        branch = self.make_remote_branch(transport, client)
1192
 
        result = branch.heads_to_fetch()
1193
 
        self.assertFinished(client)
1194
 
        self.assertEqual((set(['rev-tip']), set()), result)
1195
 
 
1196
 
    def test_uses_last_revision_info_and_tags_when_set(self):
1197
 
        transport = MemoryTransport()
1198
 
        client = FakeClient(transport.base)
1199
 
        client.add_expected_call(
1200
 
            'Branch.get_stacked_on_url', ('quack/',),
1201
 
            'error', ('NotStacked',))
1202
 
        client.add_expected_call(
1203
 
            'Branch.last_revision_info', ('quack/',),
1204
 
            'success', ('ok', '1', 'rev-tip'))
1205
 
        client.add_expected_call(
1206
 
            'Branch.get_config_file', ('quack/',),
1207
 
            'success', ('ok',), 'branch.fetch_tags = True')
1208
 
        # XXX: this will break if the default format's serialization of tags
1209
 
        # changes, or if the RPC for fetching tags changes from get_tags_bytes.
1210
 
        client.add_expected_call(
1211
 
            'Branch.get_tags_bytes', ('quack/',),
1212
 
            'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
1213
 
        transport.mkdir('quack')
1214
 
        transport = transport.clone('quack')
1215
 
        branch = self.make_remote_branch(transport, client)
1216
 
        result = branch.heads_to_fetch()
1217
 
        self.assertFinished(client)
1218
 
        self.assertEqual(
1219
 
            (set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
1220
 
 
1221
 
    def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
1222
 
        transport = MemoryTransport()
1223
 
        client = FakeClient(transport.base)
1224
 
        client.add_expected_call(
1225
 
            'Branch.get_stacked_on_url', ('quack/',),
1226
 
            'error', ('NotStacked',))
1227
 
        client.add_expected_call(
1228
 
            'Branch.heads_to_fetch', ('quack/',),
1229
 
            'success', (['tip'], ['tagged-1', 'tagged-2']))
1230
 
        transport.mkdir('quack')
1231
 
        transport = transport.clone('quack')
1232
 
        branch = self.make_remote_branch(transport, client)
1233
 
        branch._format._use_default_local_heads_to_fetch = lambda: False
1234
 
        result = branch.heads_to_fetch()
1235
 
        self.assertFinished(client)
1236
 
        self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
1237
 
 
1238
 
    def make_branch_with_tags(self):
1239
 
        self.setup_smart_server_with_call_log()
1240
 
        # Make a branch with a single revision.
1241
 
        builder = self.make_branch_builder('foo')
1242
 
        builder.start_series()
1243
 
        builder.build_snapshot('tip', None, [
1244
 
            ('add', ('', 'root-id', 'directory', ''))])
1245
 
        builder.finish_series()
1246
 
        branch = builder.get_branch()
1247
 
        # Add two tags to that branch
1248
 
        branch.tags.set_tag('tag-1', 'rev-1')
1249
 
        branch.tags.set_tag('tag-2', 'rev-2')
1250
 
        return branch
1251
 
 
1252
 
    def test_backwards_compatible(self):
1253
 
        branch = self.make_branch_with_tags()
1254
 
        c = branch.get_config()
1255
 
        c.set_user_option('branch.fetch_tags', 'True')
1256
 
        self.addCleanup(branch.lock_read().unlock)
1257
 
        # Disable the heads_to_fetch verb
1258
 
        verb = 'Branch.heads_to_fetch'
1259
 
        self.disable_verb(verb)
1260
 
        self.reset_smart_call_log()
1261
 
        result = branch.heads_to_fetch()
1262
 
        self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1263
 
        self.assertEqual(
1264
 
            ['Branch.last_revision_info', 'Branch.get_config_file',
1265
 
             'Branch.get_tags_bytes'],
1266
 
            [call.call.method for call in self.hpss_calls])
1267
 
 
1268
 
    def test_backwards_compatible_no_tags(self):
1269
 
        branch = self.make_branch_with_tags()
1270
 
        c = branch.get_config()
1271
 
        c.set_user_option('branch.fetch_tags', 'False')
1272
 
        self.addCleanup(branch.lock_read().unlock)
1273
 
        # Disable the heads_to_fetch verb
1274
 
        verb = 'Branch.heads_to_fetch'
1275
 
        self.disable_verb(verb)
1276
 
        self.reset_smart_call_log()
1277
 
        result = branch.heads_to_fetch()
1278
 
        self.assertEqual((set(['tip']), set()), result)
1279
 
        self.assertEqual(
1280
 
            ['Branch.last_revision_info', 'Branch.get_config_file'],
1281
 
            [call.call.method for call in self.hpss_calls])
1282
 
 
1283
 
 
1284
1003
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1285
1004
 
1286
1005
    def test_empty_branch(self):
1341
1060
        client.add_expected_call(
1342
1061
            'Branch.get_stacked_on_url', ('stacked/',),
1343
1062
            'success', ('ok', vfs_url))
1344
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
1063
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1345
1064
            _client=client)
1346
1065
        repo_fmt = remote.RemoteRepositoryFormat()
1347
1066
        repo_fmt._custom_format = stacked_branch.repository._format
1358
1077
        client = FakeClient(self.get_url())
1359
1078
        branch_network_name = self.get_branch_format().network_name()
1360
1079
        client.add_expected_call(
1361
 
            'BzrDir.open_branchV3', ('stacked/',),
 
1080
            'BzrDir.open_branchV2', ('stacked/',),
1362
1081
            'success', ('branch', branch_network_name))
1363
1082
        client.add_expected_call(
1364
1083
            'BzrDir.find_repositoryV3', ('stacked/',),
1374
1093
        # this will also do vfs access, but that goes direct to the transport
1375
1094
        # and isn't seen by the FakeClient.
1376
1095
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1377
 
            RemoteBzrDirFormat(), _client=client)
 
1096
            remote.RemoteBzrDirFormat(), _client=client)
1378
1097
        branch = bzrdir.open_branch()
1379
1098
        result = branch.get_stacked_on_url()
1380
1099
        self.assertEqual('../base', result)
1386
1105
            len(branch.repository._real_repository._fallback_repositories))
1387
1106
 
1388
1107
    def test_get_stacked_on_real_branch(self):
1389
 
        base_branch = self.make_branch('base')
1390
 
        stacked_branch = self.make_branch('stacked')
 
1108
        base_branch = self.make_branch('base', format='1.6')
 
1109
        stacked_branch = self.make_branch('stacked', format='1.6')
1391
1110
        stacked_branch.set_stacked_on_url('../base')
1392
1111
        reference_format = self.get_repo_format()
1393
1112
        network_name = reference_format.network_name()
1394
1113
        client = FakeClient(self.get_url())
1395
1114
        branch_network_name = self.get_branch_format().network_name()
1396
1115
        client.add_expected_call(
1397
 
            'BzrDir.open_branchV3', ('stacked/',),
 
1116
            'BzrDir.open_branchV2', ('stacked/',),
1398
1117
            'success', ('branch', branch_network_name))
1399
1118
        client.add_expected_call(
1400
1119
            'BzrDir.find_repositoryV3', ('stacked/',),
1401
 
            'success', ('ok', '', 'yes', 'no', 'yes', network_name))
 
1120
            'success', ('ok', '', 'no', 'no', 'yes', network_name))
1402
1121
        # called twice, once from constructor and then again by us
1403
1122
        client.add_expected_call(
1404
1123
            'Branch.get_stacked_on_url', ('stacked/',),
1407
1126
            'Branch.get_stacked_on_url', ('stacked/',),
1408
1127
            'success', ('ok', '../base'))
1409
1128
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1410
 
            RemoteBzrDirFormat(), _client=client)
 
1129
            remote.RemoteBzrDirFormat(), _client=client)
1411
1130
        branch = bzrdir.open_branch()
1412
1131
        result = branch.get_stacked_on_url()
1413
1132
        self.assertEqual('../base', result)
1421
1140
class TestBranchSetLastRevision(RemoteBranchTestCase):
1422
1141
 
1423
1142
    def test_set_empty(self):
1424
 
        # _set_last_revision_info('null:') is translated to calling
 
1143
        # set_revision_history([]) is translated to calling
1425
1144
        # Branch.set_last_revision(path, '') on the wire.
1426
1145
        transport = MemoryTransport()
1427
1146
        transport.mkdir('branch')
1449
1168
        # unnecessarily invokes _ensure_real upon a call to lock_write.
1450
1169
        branch._ensure_real = lambda: None
1451
1170
        branch.lock_write()
1452
 
        result = branch._set_last_revision(NULL_REVISION)
 
1171
        result = branch.set_revision_history([])
1453
1172
        branch.unlock()
1454
1173
        self.assertEqual(None, result)
1455
1174
        self.assertFinished(client)
1456
1175
 
1457
1176
    def test_set_nonempty(self):
1458
 
        # set_last_revision_info(N, rev-idN) is translated to calling
 
1177
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
1459
1178
        # Branch.set_last_revision(path, rev-idN) on the wire.
1460
1179
        transport = MemoryTransport()
1461
1180
        transport.mkdir('branch')
1487
1206
        branch._ensure_real = lambda: None
1488
1207
        # Lock the branch, reset the record of remote calls.
1489
1208
        branch.lock_write()
1490
 
        result = branch._set_last_revision('rev-id2')
 
1209
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
1491
1210
        branch.unlock()
1492
1211
        self.assertEqual(None, result)
1493
1212
        self.assertFinished(client)
1523
1242
        branch = self.make_remote_branch(transport, client)
1524
1243
        branch.lock_write()
1525
1244
        self.assertRaises(
1526
 
            errors.NoSuchRevision, branch._set_last_revision, 'rev-id')
 
1245
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
1527
1246
        branch.unlock()
1528
1247
        self.assertFinished(client)
1529
1248
 
1560
1279
        branch._ensure_real = lambda: None
1561
1280
        branch.lock_write()
1562
1281
        # The 'TipChangeRejected' error response triggered by calling
1563
 
        # set_last_revision_info causes a TipChangeRejected exception.
 
1282
        # set_revision_history causes a TipChangeRejected exception.
1564
1283
        err = self.assertRaises(
1565
 
            errors.TipChangeRejected,
1566
 
            branch._set_last_revision, 'rev-id')
 
1284
            errors.TipChangeRejected, branch.set_revision_history, ['rev-id'])
1567
1285
        # The UTF-8 message from the response has been decoded into a unicode
1568
1286
        # object.
1569
1287
        self.assertIsInstance(err.msg, unicode)
1629
1347
            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
1630
1348
        branch.unlock()
1631
1349
 
 
1350
    def lock_remote_branch(self, branch):
 
1351
        """Trick a RemoteBranch into thinking it is locked."""
 
1352
        branch._lock_mode = 'w'
 
1353
        branch._lock_count = 2
 
1354
        branch._lock_token = 'branch token'
 
1355
        branch._repo_lock_token = 'repo token'
 
1356
        branch.repository._lock_mode = 'w'
 
1357
        branch.repository._lock_count = 2
 
1358
        branch.repository._lock_token = 'repo token'
 
1359
 
1632
1360
    def test_backwards_compatibility(self):
1633
1361
        """If the server does not support the Branch.set_last_revision_info
1634
1362
        verb (which is new in 1.4), then the client falls back to VFS methods.
1757
1485
    def test_get_multi_line_branch_conf(self):
1758
1486
        # Make sure that multiple-line branch.conf files are supported
1759
1487
        #
1760
 
        # https://bugs.launchpad.net/bzr/+bug/354075
 
1488
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
1761
1489
        client = FakeClient()
1762
1490
        client.add_expected_call(
1763
1491
            'Branch.get_stacked_on_url', ('memory:///',),
1791
1519
        branch.unlock()
1792
1520
        self.assertFinished(client)
1793
1521
 
1794
 
    def test_set_option_with_dict(self):
1795
 
        client = FakeClient()
1796
 
        client.add_expected_call(
1797
 
            'Branch.get_stacked_on_url', ('memory:///',),
1798
 
            'error', ('NotStacked',),)
1799
 
        client.add_expected_call(
1800
 
            'Branch.lock_write', ('memory:///', '', ''),
1801
 
            'success', ('ok', 'branch token', 'repo token'))
1802
 
        encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
1803
 
        client.add_expected_call(
1804
 
            'Branch.set_config_option_dict', ('memory:///', 'branch token',
1805
 
            'repo token', encoded_dict_value, 'foo', ''),
1806
 
            'success', ())
1807
 
        client.add_expected_call(
1808
 
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1809
 
            'success', ('ok',))
1810
 
        transport = MemoryTransport()
1811
 
        branch = self.make_remote_branch(transport, client)
1812
 
        branch.lock_write()
1813
 
        config = branch._get_config()
1814
 
        config.set_option(
1815
 
            {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
1816
 
            'foo')
1817
 
        branch.unlock()
1818
 
        self.assertFinished(client)
1819
 
 
1820
1522
    def test_backwards_compat_set_option(self):
1821
1523
        self.setup_smart_server_with_call_log()
1822
1524
        branch = self.make_branch('.')
1829
1531
        self.assertLength(10, self.hpss_calls)
1830
1532
        self.assertEqual('value', branch._get_config().get_option('name'))
1831
1533
 
1832
 
    def test_backwards_compat_set_option_with_dict(self):
1833
 
        self.setup_smart_server_with_call_log()
1834
 
        branch = self.make_branch('.')
1835
 
        verb = 'Branch.set_config_option_dict'
1836
 
        self.disable_verb(verb)
1837
 
        branch.lock_write()
1838
 
        self.addCleanup(branch.unlock)
1839
 
        self.reset_smart_call_log()
1840
 
        config = branch._get_config()
1841
 
        value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
1842
 
        config.set_option(value_dict, 'name')
1843
 
        self.assertLength(10, self.hpss_calls)
1844
 
        self.assertEqual(value_dict, branch._get_config().get_option('name'))
1845
 
 
1846
1534
 
1847
1535
class TestBranchLockWrite(RemoteBranchTestCase):
1848
1536
 
1984
1672
        client = FakeClient(transport.base)
1985
1673
        transport = transport.clone(transport_path)
1986
1674
        # we do not want bzrdir to make any remote calls
1987
 
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
1675
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1988
1676
            _client=False)
1989
1677
        repo = RemoteRepository(bzrdir, None, _client=client)
1990
1678
        return repo, client
1991
1679
 
1992
1680
 
1993
 
def remoted_description(format):
1994
 
    return 'Remote: ' + format.get_format_description()
1995
 
 
1996
 
 
1997
 
class TestBranchFormat(tests.TestCase):
1998
 
 
1999
 
    def test_get_format_description(self):
2000
 
        remote_format = RemoteBranchFormat()
2001
 
        real_format = branch.format_registry.get_default()
2002
 
        remote_format._network_name = real_format.network_name()
2003
 
        self.assertEqual(remoted_description(real_format),
2004
 
            remote_format.get_format_description())
2005
 
 
2006
 
 
2007
1681
class TestRepositoryFormat(TestRemoteRepository):
2008
1682
 
2009
1683
    def test_fast_delta(self):
2010
 
        true_name = groupcompress_repo.RepositoryFormat2a().network_name()
 
1684
        true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
2011
1685
        true_format = RemoteRepositoryFormat()
2012
1686
        true_format._network_name = true_name
2013
1687
        self.assertEqual(True, true_format.fast_deltas)
2014
 
        false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
 
1688
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
2015
1689
        false_format = RemoteRepositoryFormat()
2016
1690
        false_format._network_name = false_name
2017
1691
        self.assertEqual(False, false_format.fast_deltas)
2018
1692
 
2019
 
    def test_get_format_description(self):
2020
 
        remote_repo_format = RemoteRepositoryFormat()
2021
 
        real_format = repository.format_registry.get_default()
2022
 
        remote_repo_format._network_name = real_format.network_name()
2023
 
        self.assertEqual(remoted_description(real_format),
2024
 
            remote_repo_format.get_format_description())
2025
 
 
2026
1693
 
2027
1694
class TestRepositoryGatherStats(TestRemoteRepository):
2028
1695
 
2213
1880
        self.assertLength(1, self.hpss_calls)
2214
1881
 
2215
1882
    def disableExtraResults(self):
2216
 
        self.overrideAttr(SmartServerRepositoryGetParentMap,
2217
 
                          'no_extra_results', True)
 
1883
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
 
1884
        SmartServerRepositoryGetParentMap.no_extra_results = True
 
1885
        def reset_values():
 
1886
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
 
1887
        self.addCleanup(reset_values)
2218
1888
 
2219
1889
    def test_null_cached_missing_and_stop_key(self):
2220
1890
        self.setup_smart_server_with_call_log()
2279
1949
 
2280
1950
    def test_allows_new_revisions(self):
2281
1951
        """get_parent_map's results can be updated by commit."""
2282
 
        smart_server = test_server.SmartTCPServer_for_testing()
2283
 
        self.start_server(smart_server)
 
1952
        smart_server = server.SmartTCPServer_for_testing()
 
1953
        smart_server.setUp()
 
1954
        self.addCleanup(smart_server.tearDown)
2284
1955
        self.make_branch('branch')
2285
1956
        branch = Branch.open(smart_server.get_url() + '/branch')
2286
1957
        tree = branch.create_checkout('tree', lightweight=True)
2395
2066
        """
2396
2067
        # Make a repo with a fallback repo, both using a FakeClient.
2397
2068
        format = remote.response_tuple_to_repo_format(
2398
 
            ('yes', 'no', 'yes', self.get_repo_format().network_name()))
 
2069
            ('yes', 'no', 'yes', 'fake-network-name'))
2399
2070
        repo, client = self.setup_fake_client_and_repository('quack')
2400
2071
        repo._format = format
2401
2072
        fallback_repo, ignored = self.setup_fake_client_and_repository(
2402
2073
            'fallback')
2403
2074
        fallback_repo._client = client
2404
 
        fallback_repo._format = format
2405
2075
        repo.add_fallback_repository(fallback_repo)
2406
2076
        # First the client should ask the primary repo
2407
2077
        client.add_expected_call(
2428
2098
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2429
2099
        self.assertFinished(client)
2430
2100
 
2431
 
    def test_branch_fallback_locking(self):
2432
 
        """RemoteBranch.get_rev_id takes a read lock, and tries to call the
2433
 
        get_rev_id_for_revno verb.  If the verb is unknown the VFS fallback
2434
 
        will be invoked, which will fail if the repo is unlocked.
2435
 
        """
2436
 
        self.setup_smart_server_with_call_log()
2437
 
        tree = self.make_branch_and_memory_tree('.')
2438
 
        tree.lock_write()
2439
 
        tree.add('')
2440
 
        rev1 = tree.commit('First')
2441
 
        rev2 = tree.commit('Second')
2442
 
        tree.unlock()
2443
 
        branch = tree.branch
2444
 
        self.assertFalse(branch.is_locked())
2445
 
        self.reset_smart_call_log()
2446
 
        verb = 'Repository.get_rev_id_for_revno'
2447
 
        self.disable_verb(verb)
2448
 
        self.assertEqual(rev1, branch.get_rev_id(1))
2449
 
        self.assertLength(1, [call for call in self.hpss_calls if
2450
 
                              call.call.method == verb])
2451
 
 
2452
2101
 
2453
2102
class TestRepositoryIsShared(TestRemoteRepository):
2454
2103
 
2481
2130
        transport_path = 'quack'
2482
2131
        repo, client = self.setup_fake_client_and_repository(transport_path)
2483
2132
        client.add_success_response('ok', 'a token')
2484
 
        token = repo.lock_write().repository_token
 
2133
        result = repo.lock_write()
2485
2134
        self.assertEqual(
2486
2135
            [('call', 'Repository.lock_write', ('quack/', ''))],
2487
2136
            client._calls)
2488
 
        self.assertEqual('a token', token)
 
2137
        self.assertEqual('a token', result)
2489
2138
 
2490
2139
    def test_lock_write_already_locked(self):
2491
2140
        transport_path = 'quack'
2570
2219
        self.assertEqual([], client._calls)
2571
2220
 
2572
2221
 
2573
 
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2574
 
    """Base class for Repository.insert_stream and .insert_stream_1.19
2575
 
    tests.
2576
 
    """
2577
 
    
2578
 
    def checkInsertEmptyStream(self, repo, client):
2579
 
        """Insert an empty stream, checking the result.
2580
 
 
2581
 
        This checks that there are no resume_tokens or missing_keys, and that
2582
 
        the client is finished.
2583
 
        """
2584
 
        sink = repo._get_sink()
2585
 
        fmt = repository.format_registry.get_default()
2586
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2587
 
        self.assertEqual([], resume_tokens)
2588
 
        self.assertEqual(set(), missing_keys)
2589
 
        self.assertFinished(client)
2590
 
 
2591
 
 
2592
 
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2593
 
    """Tests for using Repository.insert_stream verb when the _1.19 variant is
2594
 
    not available.
2595
 
 
2596
 
    This test case is very similar to TestRepositoryInsertStream_1_19.
2597
 
    """
2598
 
 
2599
 
    def setUp(self):
2600
 
        TestRemoteRepository.setUp(self)
2601
 
        self.disable_verb('Repository.insert_stream_1.19')
2602
 
 
2603
 
    def test_unlocked_repo(self):
2604
 
        transport_path = 'quack'
2605
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2606
 
        client.add_expected_call(
2607
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2608
 
            'unknown', ('Repository.insert_stream_1.19',))
2609
 
        client.add_expected_call(
2610
 
            'Repository.insert_stream', ('quack/', ''),
2611
 
            'success', ('ok',))
2612
 
        client.add_expected_call(
2613
 
            'Repository.insert_stream', ('quack/', ''),
2614
 
            'success', ('ok',))
2615
 
        self.checkInsertEmptyStream(repo, client)
2616
 
 
2617
 
    def test_locked_repo_with_no_lock_token(self):
2618
 
        transport_path = 'quack'
2619
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2620
 
        client.add_expected_call(
2621
 
            'Repository.lock_write', ('quack/', ''),
2622
 
            'success', ('ok', ''))
2623
 
        client.add_expected_call(
2624
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2625
 
            'unknown', ('Repository.insert_stream_1.19',))
2626
 
        client.add_expected_call(
2627
 
            'Repository.insert_stream', ('quack/', ''),
2628
 
            'success', ('ok',))
2629
 
        client.add_expected_call(
2630
 
            'Repository.insert_stream', ('quack/', ''),
2631
 
            'success', ('ok',))
2632
 
        repo.lock_write()
2633
 
        self.checkInsertEmptyStream(repo, client)
2634
 
 
2635
 
    def test_locked_repo_with_lock_token(self):
2636
 
        transport_path = 'quack'
2637
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2638
 
        client.add_expected_call(
2639
 
            'Repository.lock_write', ('quack/', ''),
2640
 
            'success', ('ok', 'a token'))
2641
 
        client.add_expected_call(
2642
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2643
 
            'unknown', ('Repository.insert_stream_1.19',))
2644
 
        client.add_expected_call(
2645
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2646
 
            'success', ('ok',))
2647
 
        client.add_expected_call(
2648
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2649
 
            'success', ('ok',))
2650
 
        repo.lock_write()
2651
 
        self.checkInsertEmptyStream(repo, client)
2652
 
 
2653
 
    def test_stream_with_inventory_deltas(self):
2654
 
        """'inventory-deltas' substreams cannot be sent to the
2655
 
        Repository.insert_stream verb, because not all servers that implement
2656
 
        that verb will accept them.  So when one is encountered the RemoteSink
2657
 
        immediately stops using that verb and falls back to VFS insert_stream.
2658
 
        """
2659
 
        transport_path = 'quack'
2660
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2661
 
        client.add_expected_call(
2662
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2663
 
            'unknown', ('Repository.insert_stream_1.19',))
2664
 
        client.add_expected_call(
2665
 
            'Repository.insert_stream', ('quack/', ''),
2666
 
            'success', ('ok',))
2667
 
        client.add_expected_call(
2668
 
            'Repository.insert_stream', ('quack/', ''),
2669
 
            'success', ('ok',))
2670
 
        # Create a fake real repository for insert_stream to fall back on, so
2671
 
        # that we can directly see the records the RemoteSink passes to the
2672
 
        # real sink.
2673
 
        class FakeRealSink:
2674
 
            def __init__(self):
2675
 
                self.records = []
2676
 
            def insert_stream(self, stream, src_format, resume_tokens):
2677
 
                for substream_kind, substream in stream:
2678
 
                    self.records.append(
2679
 
                        (substream_kind, [record.key for record in substream]))
2680
 
                return ['fake tokens'], ['fake missing keys']
2681
 
        fake_real_sink = FakeRealSink()
2682
 
        class FakeRealRepository:
2683
 
            def _get_sink(self):
2684
 
                return fake_real_sink
2685
 
            def is_in_write_group(self):
2686
 
                return False
2687
 
            def refresh_data(self):
2688
 
                return True
2689
 
        repo._real_repository = FakeRealRepository()
2690
 
        sink = repo._get_sink()
2691
 
        fmt = repository.format_registry.get_default()
2692
 
        stream = self.make_stream_with_inv_deltas(fmt)
2693
 
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2694
 
        # Every record from the first inventory delta should have been sent to
2695
 
        # the VFS sink.
2696
 
        expected_records = [
2697
 
            ('inventory-deltas', [('rev2',), ('rev3',)]),
2698
 
            ('texts', [('some-rev', 'some-file')])]
2699
 
        self.assertEqual(expected_records, fake_real_sink.records)
2700
 
        # The return values from the real sink's insert_stream are propagated
2701
 
        # back to the original caller.
2702
 
        self.assertEqual(['fake tokens'], resume_tokens)
2703
 
        self.assertEqual(['fake missing keys'], missing_keys)
2704
 
        self.assertFinished(client)
2705
 
 
2706
 
    def make_stream_with_inv_deltas(self, fmt):
2707
 
        """Make a simple stream with an inventory delta followed by more
2708
 
        records and more substreams to test that all records and substreams
2709
 
        from that point on are used.
2710
 
 
2711
 
        This sends, in order:
2712
 
           * inventories substream: rev1, rev2, rev3.  rev2 and rev3 are
2713
 
             inventory-deltas.
2714
 
           * texts substream: (some-rev, some-file)
2715
 
        """
2716
 
        # Define a stream using generators so that it isn't rewindable.
2717
 
        inv = inventory.Inventory(revision_id='rev1')
2718
 
        inv.root.revision = 'rev1'
2719
 
        def stream_with_inv_delta():
2720
 
            yield ('inventories', inventories_substream())
2721
 
            yield ('inventory-deltas', inventory_delta_substream())
2722
 
            yield ('texts', [
2723
 
                versionedfile.FulltextContentFactory(
2724
 
                    ('some-rev', 'some-file'), (), None, 'content')])
2725
 
        def inventories_substream():
2726
 
            # An empty inventory fulltext.  This will be streamed normally.
2727
 
            text = fmt._serializer.write_inventory_to_string(inv)
2728
 
            yield versionedfile.FulltextContentFactory(
2729
 
                ('rev1',), (), None, text)
2730
 
        def inventory_delta_substream():
2731
 
            # An inventory delta.  This can't be streamed via this verb, so it
2732
 
            # will trigger a fallback to VFS insert_stream.
2733
 
            entry = inv.make_entry(
2734
 
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
2735
 
            entry.revision = 'ghost'
2736
 
            delta = [(None, 'newdir', 'newdir-id', entry)]
2737
 
            serializer = inventory_delta.InventoryDeltaSerializer(
2738
 
                versioned_root=True, tree_references=False)
2739
 
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2740
 
            yield versionedfile.ChunkedContentFactory(
2741
 
                ('rev2',), (('rev1',)), None, lines)
2742
 
            # Another delta.
2743
 
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2744
 
            yield versionedfile.ChunkedContentFactory(
2745
 
                ('rev3',), (('rev1',)), None, lines)
2746
 
        return stream_with_inv_delta()
2747
 
 
2748
 
 
2749
 
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2750
 
 
2751
 
    def test_unlocked_repo(self):
2752
 
        transport_path = 'quack'
2753
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2754
 
        client.add_expected_call(
2755
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2756
 
            'success', ('ok',))
2757
 
        client.add_expected_call(
2758
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2759
 
            'success', ('ok',))
2760
 
        self.checkInsertEmptyStream(repo, client)
2761
 
 
2762
 
    def test_locked_repo_with_no_lock_token(self):
2763
 
        transport_path = 'quack'
2764
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2765
 
        client.add_expected_call(
2766
 
            'Repository.lock_write', ('quack/', ''),
2767
 
            'success', ('ok', ''))
2768
 
        client.add_expected_call(
2769
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2770
 
            'success', ('ok',))
2771
 
        client.add_expected_call(
2772
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2773
 
            'success', ('ok',))
2774
 
        repo.lock_write()
2775
 
        self.checkInsertEmptyStream(repo, client)
2776
 
 
2777
 
    def test_locked_repo_with_lock_token(self):
2778
 
        transport_path = 'quack'
2779
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2780
 
        client.add_expected_call(
2781
 
            'Repository.lock_write', ('quack/', ''),
2782
 
            'success', ('ok', 'a token'))
2783
 
        client.add_expected_call(
2784
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2785
 
            'success', ('ok',))
2786
 
        client.add_expected_call(
2787
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2788
 
            'success', ('ok',))
2789
 
        repo.lock_write()
2790
 
        self.checkInsertEmptyStream(repo, client)
 
2222
class TestRepositoryInsertStream(TestRemoteRepository):
 
2223
 
 
2224
    def test_unlocked_repo(self):
 
2225
        transport_path = 'quack'
 
2226
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2227
        client.add_expected_call(
 
2228
            'Repository.insert_stream', ('quack/', ''),
 
2229
            'success', ('ok',))
 
2230
        client.add_expected_call(
 
2231
            'Repository.insert_stream', ('quack/', ''),
 
2232
            'success', ('ok',))
 
2233
        sink = repo._get_sink()
 
2234
        fmt = repository.RepositoryFormat.get_default_format()
 
2235
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2236
        self.assertEqual([], resume_tokens)
 
2237
        self.assertEqual(set(), missing_keys)
 
2238
        self.assertFinished(client)
 
2239
 
 
2240
    def test_locked_repo_with_no_lock_token(self):
 
2241
        transport_path = 'quack'
 
2242
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2243
        client.add_expected_call(
 
2244
            'Repository.lock_write', ('quack/', ''),
 
2245
            'success', ('ok', ''))
 
2246
        client.add_expected_call(
 
2247
            'Repository.insert_stream', ('quack/', ''),
 
2248
            'success', ('ok',))
 
2249
        client.add_expected_call(
 
2250
            'Repository.insert_stream', ('quack/', ''),
 
2251
            'success', ('ok',))
 
2252
        repo.lock_write()
 
2253
        sink = repo._get_sink()
 
2254
        fmt = repository.RepositoryFormat.get_default_format()
 
2255
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2256
        self.assertEqual([], resume_tokens)
 
2257
        self.assertEqual(set(), missing_keys)
 
2258
        self.assertFinished(client)
 
2259
 
 
2260
    def test_locked_repo_with_lock_token(self):
 
2261
        transport_path = 'quack'
 
2262
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2263
        client.add_expected_call(
 
2264
            'Repository.lock_write', ('quack/', ''),
 
2265
            'success', ('ok', 'a token'))
 
2266
        client.add_expected_call(
 
2267
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2268
            'success', ('ok',))
 
2269
        client.add_expected_call(
 
2270
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2271
            'success', ('ok',))
 
2272
        repo.lock_write()
 
2273
        sink = repo._get_sink()
 
2274
        fmt = repository.RepositoryFormat.get_default_format()
 
2275
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2276
        self.assertEqual([], resume_tokens)
 
2277
        self.assertEqual(set(), missing_keys)
 
2278
        self.assertFinished(client)
2791
2279
 
2792
2280
 
2793
2281
class TestRepositoryTarball(TestRemoteRepository):
2829
2317
    """RemoteRepository.copy_content_into optimizations"""
2830
2318
 
2831
2319
    def test_copy_content_remote_to_local(self):
2832
 
        self.transport_server = test_server.SmartTCPServer_for_testing
 
2320
        self.transport_server = server.SmartTCPServer_for_testing
2833
2321
        src_repo = self.make_repository('repo1')
2834
2322
        src_repo = repository.Repository.open(self.get_url('repo1'))
2835
2323
        # At the moment the tarball-based copy_content_into can't write back
2914
2402
             ('pack collection autopack',)],
2915
2403
            client._calls)
2916
2404
 
2917
 
    def test_oom_error_reporting(self):
2918
 
        """An out-of-memory condition on the server is reported clearly"""
2919
 
        transport_path = 'quack'
2920
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2921
 
        client.add_expected_call(
2922
 
            'PackRepository.autopack', ('quack/',),
2923
 
            'error', ('MemoryError',))
2924
 
        err = self.assertRaises(errors.BzrError, repo.autopack)
2925
 
        self.assertContainsRe(str(err), "^remote server out of mem")
2926
 
 
2927
2405
 
2928
2406
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2929
2407
    """Base class for unit tests for bzrlib.remote._translate_error."""
2993
2471
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
2994
2472
        self.assertEqual(expected_error, translated_error)
2995
2473
 
2996
 
    def test_nobranch_one_arg(self):
2997
 
        bzrdir = self.make_bzrdir('')
2998
 
        translated_error = self.translateTuple(
2999
 
            ('nobranch', 'extra detail'), bzrdir=bzrdir)
3000
 
        expected_error = errors.NotBranchError(
3001
 
            path=bzrdir.root_transport.base,
3002
 
            detail='extra detail')
3003
 
        self.assertEqual(expected_error, translated_error)
3004
 
 
3005
 
    def test_norepository(self):
3006
 
        bzrdir = self.make_bzrdir('')
3007
 
        translated_error = self.translateTuple(('norepository',),
3008
 
            bzrdir=bzrdir)
3009
 
        expected_error = errors.NoRepositoryPresent(bzrdir)
3010
 
        self.assertEqual(expected_error, translated_error)
3011
 
 
3012
2474
    def test_LockContention(self):
3013
2475
        translated_error = self.translateTuple(('LockContention',))
3014
2476
        expected_error = errors.LockContention('(remote lock)')
3042
2504
        expected_error = errors.DivergedBranches(branch, other_branch)
3043
2505
        self.assertEqual(expected_error, translated_error)
3044
2506
 
3045
 
    def test_NotStacked(self):
3046
 
        branch = self.make_branch('')
3047
 
        translated_error = self.translateTuple(('NotStacked',), branch=branch)
3048
 
        expected_error = errors.NotStacked(branch)
3049
 
        self.assertEqual(expected_error, translated_error)
3050
 
 
3051
2507
    def test_ReadError_no_args(self):
3052
2508
        path = 'a path'
3053
2509
        translated_error = self.translateTuple(('ReadError',), path=path)
3060
2516
        expected_error = errors.ReadError(path)
3061
2517
        self.assertEqual(expected_error, translated_error)
3062
2518
 
3063
 
    def test_IncompatibleRepositories(self):
3064
 
        translated_error = self.translateTuple(('IncompatibleRepositories',
3065
 
            "repo1", "repo2", "details here"))
3066
 
        expected_error = errors.IncompatibleRepositories("repo1", "repo2",
3067
 
            "details here")
3068
 
        self.assertEqual(expected_error, translated_error)
3069
 
 
3070
2519
    def test_PermissionDenied_no_args(self):
3071
2520
        path = 'a path'
3072
 
        translated_error = self.translateTuple(('PermissionDenied',),
3073
 
            path=path)
 
2521
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
3074
2522
        expected_error = errors.PermissionDenied(path)
3075
2523
        self.assertEqual(expected_error, translated_error)
3076
2524
 
3099
2547
        expected_error = errors.PermissionDenied(path, extra)
3100
2548
        self.assertEqual(expected_error, translated_error)
3101
2549
 
3102
 
    # GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
3103
 
 
3104
 
    def test_NoSuchFile_context_path(self):
3105
 
        local_path = "local path"
3106
 
        translated_error = self.translateTuple(('ReadError', "remote path"),
3107
 
            path=local_path)
3108
 
        expected_error = errors.ReadError(local_path)
3109
 
        self.assertEqual(expected_error, translated_error)
3110
 
 
3111
 
    def test_NoSuchFile_without_context(self):
3112
 
        remote_path = "remote path"
3113
 
        translated_error = self.translateTuple(('ReadError', remote_path))
3114
 
        expected_error = errors.ReadError(remote_path)
3115
 
        self.assertEqual(expected_error, translated_error)
3116
 
 
3117
 
    def test_ReadOnlyError(self):
3118
 
        translated_error = self.translateTuple(('ReadOnlyError',))
3119
 
        expected_error = errors.TransportNotPossible("readonly transport")
3120
 
        self.assertEqual(expected_error, translated_error)
3121
 
 
3122
 
    def test_MemoryError(self):
3123
 
        translated_error = self.translateTuple(('MemoryError',))
3124
 
        self.assertStartsWith(str(translated_error),
3125
 
            "remote server out of memory")
3126
 
 
3127
 
    def test_generic_IndexError_no_classname(self):
3128
 
        err = errors.ErrorFromSmartServer(('error', "list index out of range"))
3129
 
        translated_error = self.translateErrorFromSmartServer(err)
3130
 
        expected_error = errors.UnknownErrorFromSmartServer(err)
3131
 
        self.assertEqual(expected_error, translated_error)
3132
 
 
3133
 
    # GZ 2011-03-02: TODO test generic non-ascii error string
3134
 
 
3135
 
    def test_generic_KeyError(self):
3136
 
        err = errors.ErrorFromSmartServer(('error', 'KeyError', "1"))
3137
 
        translated_error = self.translateErrorFromSmartServer(err)
3138
 
        expected_error = errors.UnknownErrorFromSmartServer(err)
3139
 
        self.assertEqual(expected_error, translated_error)
3140
 
 
3141
2550
 
3142
2551
class TestErrorTranslationRobustness(TestErrorTranslationBase):
3143
2552
    """Unit tests for bzrlib.remote._translate_error's robustness.
3173
2582
        # In addition to re-raising ErrorFromSmartServer, some debug info has
3174
2583
        # been muttered to the log file for developer to look at.
3175
2584
        self.assertContainsRe(
3176
 
            self.get_log(),
 
2585
            self._get_log(keep_log_file=True),
3177
2586
            "Missing key 'branch' in context")
3178
2587
 
3179
2588
    def test_path_missing(self):
3187
2596
        self.assertEqual(server_error, translated_error)
3188
2597
        # In addition to re-raising ErrorFromSmartServer, some debug info has
3189
2598
        # been muttered to the log file for developer to look at.
3190
 
        self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
 
2599
        self.assertContainsRe(
 
2600
            self._get_log(keep_log_file=True), "Missing key 'path' in context")
3191
2601
 
3192
2602
 
3193
2603
class TestStacking(tests.TestCaseWithTransport):
3211
2621
        stacked_branch = self.make_branch('stacked', format='1.9')
3212
2622
        stacked_branch.set_stacked_on_url('../base')
3213
2623
        # start a server looking at this
3214
 
        smart_server = test_server.SmartTCPServer_for_testing()
3215
 
        self.start_server(smart_server)
 
2624
        smart_server = server.SmartTCPServer_for_testing()
 
2625
        smart_server.setUp()
 
2626
        self.addCleanup(smart_server.tearDown)
3216
2627
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
3217
2628
        # can get its branch and repository
3218
2629
        remote_branch = remote_bzrdir.open_branch()
3240
2651
        tree1.commit('rev1', rev_id='rev1')
3241
2652
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
3242
2653
            ).open_workingtree()
3243
 
        local_tree = tree2.branch.create_checkout('local')
3244
 
        local_tree.commit('local changes make me feel good.')
 
2654
        tree2.commit('local changes make me feel good.')
3245
2655
        branch2 = Branch.open(self.get_url('tree2'))
3246
2656
        branch2.lock_read()
3247
2657
        self.addCleanup(branch2.unlock)
3269
2679
                    result.append(content.key[-1])
3270
2680
        return result
3271
2681
 
3272
 
    def get_ordered_revs(self, format, order, branch_factory=None):
 
2682
    def get_ordered_revs(self, format, order):
3273
2683
        """Get a list of the revisions in a stream to format format.
3274
2684
 
3275
2685
        :param format: The format of the target.
3276
2686
        :param order: the order that target should have requested.
3277
 
        :param branch_factory: A callable to create a trunk and stacked branch
3278
 
            to fetch from. If none, self.prepare_stacked_remote_branch is used.
3279
2687
        :result: The revision ids in the stream, in the order seen,
3280
2688
            the topological order of revisions in the source.
3281
2689
        """
3283
2691
        target_repository_format = unordered_format.repository_format
3284
2692
        # Cross check
3285
2693
        self.assertEqual(order, target_repository_format._fetch_order)
3286
 
        if branch_factory is None:
3287
 
            branch_factory = self.prepare_stacked_remote_branch
3288
 
        _, stacked = branch_factory()
 
2694
        trunk, stacked = self.prepare_stacked_remote_branch()
3289
2695
        source = stacked.repository._get_source(target_repository_format)
3290
2696
        tip = stacked.last_revision()
3291
 
        stacked.repository._ensure_real()
3292
 
        graph = stacked.repository.get_graph()
3293
 
        revs = [r for (r,ps) in graph.iter_ancestry([tip])
3294
 
                if r != NULL_REVISION]
3295
 
        revs.reverse()
3296
 
        search = _mod_graph.PendingAncestryResult([tip], stacked.repository)
 
2697
        revs = stacked.repository.get_ancestry(tip)
 
2698
        search = graph.PendingAncestryResult([tip], stacked.repository)
3297
2699
        self.reset_smart_call_log()
3298
2700
        stream = source.get_stream(search)
 
2701
        if None in revs:
 
2702
            revs.remove(None)
3299
2703
        # We trust that if a revision is in the stream the rest of the new
3300
2704
        # content for it is too, as per our main fetch tests; here we are
3301
2705
        # checking that the revisions are actually included at all, and their
3312
2716
        # from the server, then one from the backing branch.
3313
2717
        self.assertLength(2, self.hpss_calls)
3314
2718
 
3315
 
    def test_stacked_on_stacked_get_stream_unordered(self):
3316
 
        # Repository._get_source.get_stream() from a stacked repository which
3317
 
        # is itself stacked yields the full data from all three sources.
3318
 
        def make_stacked_stacked():
3319
 
            _, stacked = self.prepare_stacked_remote_branch()
3320
 
            tree = stacked.bzrdir.sprout('tree3', stacked=True
3321
 
                ).open_workingtree()
3322
 
            local_tree = tree.branch.create_checkout('local-tree3')
3323
 
            local_tree.commit('more local changes are better')
3324
 
            branch = Branch.open(self.get_url('tree3'))
3325
 
            branch.lock_read()
3326
 
            self.addCleanup(branch.unlock)
3327
 
            return None, branch
3328
 
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3329
 
            branch_factory=make_stacked_stacked)
3330
 
        self.assertEqual(set(expected_revs), set(rev_ord))
3331
 
        # Getting unordered results should have made a streaming data request
3332
 
        # from the server, and one from each backing repo
3333
 
        self.assertLength(3, self.hpss_calls)
3334
 
 
3335
2719
    def test_stacked_get_stream_topological(self):
3336
2720
        # Repository._get_source.get_stream() from a stacked repository with
3337
2721
        # topological sorting yields the full data from both stacked and
3338
2722
        # stacked upon sources in topological order.
3339
2723
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3340
2724
        self.assertEqual(expected_revs, rev_ord)
3341
 
        # Getting topological sort requires VFS calls still - one of which is
3342
 
        # pushing up from the bound branch.
3343
 
        self.assertLength(14, self.hpss_calls)
 
2725
        # Getting topological sort requires VFS calls still
 
2726
        self.assertLength(12, self.hpss_calls)
3344
2727
 
3345
2728
    def test_stacked_get_stream_groupcompress(self):
3346
2729
        # Repository._get_source.get_stream() from a stacked repository with
3375
2758
        super(TestRemoteBranchEffort, self).setUp()
3376
2759
        # Create a smart server that publishes whatever the backing VFS server
3377
2760
        # does.
3378
 
        self.smart_server = test_server.SmartTCPServer_for_testing()
3379
 
        self.start_server(self.smart_server, self.get_server())
 
2761
        self.smart_server = server.SmartTCPServer_for_testing()
 
2762
        self.smart_server.setUp(self.get_server())
 
2763
        self.addCleanup(self.smart_server.tearDown)
3380
2764
        # Log all HPSS calls into self.hpss_calls.
3381
2765
        _SmartClient.hooks.install_named_hook(
3382
2766
            'call', self.capture_hpss_call, None)
3387
2771
 
3388
2772
    def test_copy_content_into_avoids_revision_history(self):
3389
2773
        local = self.make_branch('local')
3390
 
        builder = self.make_branch_builder('remote')
3391
 
        builder.build_commit(message="Commit.")
 
2774
        remote_backing_tree = self.make_branch_and_tree('remote')
 
2775
        remote_backing_tree.commit("Commit.")
3392
2776
        remote_branch_url = self.smart_server.get_url() + 'remote'
3393
2777
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3394
2778
        local.repository.fetch(remote_branch.repository)
3395
2779
        self.hpss_calls = []
3396
2780
        remote_branch.copy_content_into(local)
3397
2781
        self.assertFalse('Branch.revision_history' in self.hpss_calls)
3398
 
 
3399
 
    def test_fetch_everything_needs_just_one_call(self):
3400
 
        local = self.make_branch('local')
3401
 
        builder = self.make_branch_builder('remote')
3402
 
        builder.build_commit(message="Commit.")
3403
 
        remote_branch_url = self.smart_server.get_url() + 'remote'
3404
 
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3405
 
        self.hpss_calls = []
3406
 
        local.repository.fetch(
3407
 
            remote_branch.repository,
3408
 
            fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3409
 
        self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3410
 
 
3411
 
    def override_verb(self, verb_name, verb):
3412
 
        request_handlers = request.request_handlers
3413
 
        orig_verb = request_handlers.get(verb_name)
3414
 
        request_handlers.register(verb_name, verb, override_existing=True)
3415
 
        self.addCleanup(request_handlers.register, verb_name, orig_verb,
3416
 
                override_existing=True)
3417
 
 
3418
 
    def test_fetch_everything_backwards_compat(self):
3419
 
        """Can fetch with EverythingResult even with pre 2.4 servers.
3420
 
        
3421
 
        Pre-2.4 do not support 'everything' searches with the
3422
 
        Repository.get_stream_1.19 verb.
3423
 
        """
3424
 
        verb_log = []
3425
 
        class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3426
 
            """A version of the Repository.get_stream_1.19 verb patched to
3427
 
            reject 'everything' searches the way 2.3 and earlier do.
3428
 
            """
3429
 
            def recreate_search(self, repository, search_bytes,
3430
 
                                discard_excess=False):
3431
 
                verb_log.append(search_bytes.split('\n', 1)[0])
3432
 
                if search_bytes == 'everything':
3433
 
                    return (None,
3434
 
                            request.FailedSmartServerResponse(('BadSearch',)))
3435
 
                return super(OldGetStreamVerb,
3436
 
                        self).recreate_search(repository, search_bytes,
3437
 
                            discard_excess=discard_excess)
3438
 
        self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3439
 
        local = self.make_branch('local')
3440
 
        builder = self.make_branch_builder('remote')
3441
 
        builder.build_commit(message="Commit.")
3442
 
        remote_branch_url = self.smart_server.get_url() + 'remote'
3443
 
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3444
 
        self.hpss_calls = []
3445
 
        local.repository.fetch(
3446
 
            remote_branch.repository,
3447
 
            fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3448
 
        # make sure the overridden verb was used
3449
 
        self.assertLength(1, verb_log)
3450
 
        # more than one HPSS call is needed, but because it's a VFS callback
3451
 
        # its hard to predict exactly how many.
3452
 
        self.assertTrue(len(self.hpss_calls) > 1)
3453
 
 
3454
 
 
3455
 
class TestUpdateBoundBranchWithModifiedBoundLocation(
3456
 
    tests.TestCaseWithTransport):
3457
 
    """Ensure correct handling of bound_location modifications.
3458
 
 
3459
 
    This is tested against a smart server as http://pad.lv/786980 was about a
3460
 
    ReadOnlyError (write attempt during a read-only transaction) which can only
3461
 
    happen in this context.
3462
 
    """
3463
 
 
3464
 
    def setUp(self):
3465
 
        super(TestUpdateBoundBranchWithModifiedBoundLocation, self).setUp()
3466
 
        self.transport_server = test_server.SmartTCPServer_for_testing
3467
 
 
3468
 
    def make_master_and_checkout(self, master_name, checkout_name):
3469
 
        # Create the master branch and its associated checkout
3470
 
        self.master = self.make_branch_and_tree(master_name)
3471
 
        self.checkout = self.master.branch.create_checkout(checkout_name)
3472
 
        # Modify the master branch so there is something to update
3473
 
        self.master.commit('add stuff')
3474
 
        self.last_revid = self.master.commit('even more stuff')
3475
 
        self.bound_location = self.checkout.branch.get_bound_location()
3476
 
 
3477
 
    def assertUpdateSucceeds(self, new_location):
3478
 
        self.checkout.branch.set_bound_location(new_location)
3479
 
        self.checkout.update()
3480
 
        self.assertEquals(self.last_revid, self.checkout.last_revision())
3481
 
 
3482
 
    def test_without_final_slash(self):
3483
 
        self.make_master_and_checkout('master', 'checkout')
3484
 
        # For unclear reasons some users have a bound_location without a final
3485
 
        # '/', simulate that by forcing such a value
3486
 
        self.assertEndsWith(self.bound_location, '/')
3487
 
        self.assertUpdateSucceeds(self.bound_location.rstrip('/'))
3488
 
 
3489
 
    def test_plus_sign(self):
3490
 
        self.make_master_and_checkout('+master', 'checkout')
3491
 
        self.assertUpdateSucceeds(self.bound_location.replace('%2B', '+', 1))
3492
 
 
3493
 
    def test_tilda(self):
3494
 
        # Embed ~ in the middle of the path just to avoid any $HOME
3495
 
        # interpretation
3496
 
        self.make_master_and_checkout('mas~ter', 'checkout')
3497
 
        self.assertUpdateSucceeds(self.bound_location.replace('%2E', '~', 1))