~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Vincent Ladeuil
  • Date: 2009-07-02 13:07:14 UTC
  • mto: (4524.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4525.
  • Revision ID: v.ladeuil+lp@free.fr-20090702130714-hsyqfusi8vn3a11m
Use tree.has_changes() where appropriate (the test suite caught a
bug in has_changes() (not filtering out the root) in an impressive
number of tests)

* bzrlib/send.py:
(send): Use tree.has_changes() instead of tree.changes_from().

* bzrlib/reconfigure.py:
(Reconfigure._check): Use tree.has_changes() instead of
tree.changes_from().

* bzrlib/merge.py:
(Merger.ensure_revision_trees, Merger.compare_basis): Use
tree.has_changes() instead of tree.changes_from().

* bzrlib/builtins.py:
(cmd_remove_tree.run, cmd_push.run, cmd_merge.run): Use
tree.has_changes() instead of tree.changes_from().

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
"""
34
34
    pack,
35
35
    remote,
36
36
    repository,
 
37
    smart,
37
38
    tests,
 
39
    treebuilder,
38
40
    urlutils,
39
41
    )
40
42
from bzrlib.branch import Branch
41
43
from bzrlib.bzrdir import BzrDir, BzrDirFormat
42
44
from bzrlib.remote import (
43
45
    RemoteBranch,
 
46
    RemoteBranchFormat,
44
47
    RemoteBzrDir,
45
48
    RemoteBzrDirFormat,
46
49
    RemoteRepository,
 
50
    RemoteRepositoryFormat,
47
51
    )
 
52
from bzrlib.repofmt import groupcompress_repo, pack_repo
48
53
from bzrlib.revision import NULL_REVISION
49
54
from bzrlib.smart import server, medium
50
55
from bzrlib.smart.client import _SmartClient
51
 
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
    KnownFailure,
 
62
    )
52
63
from bzrlib.transport import get_transport, http
53
64
from bzrlib.transport.memory import MemoryTransport
54
65
from bzrlib.transport.remote import (
57
68
    RemoteTCPTransport,
58
69
)
59
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 = [
 
75
        ('HPSS-v2',
 
76
            {'transport_server': server.SmartTCPServer_for_testing_v2_only}),
 
77
        ('HPSS-v3',
 
78
            {'transport_server': server.SmartTCPServer_for_testing})]
 
79
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
 
80
 
60
81
 
61
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
62
83
 
63
84
    def setUp(self):
64
 
        self.transport_server = server.SmartTCPServer_for_testing
65
85
        super(BasicRemoteObjectTests, self).setUp()
66
86
        self.transport = self.get_transport()
67
87
        # make a branch that can be opened over the smart transport
72
92
        tests.TestCaseWithTransport.tearDown(self)
73
93
 
74
94
    def test_create_remote_bzrdir(self):
75
 
        b = remote.RemoteBzrDir(self.transport)
 
95
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
76
96
        self.assertIsInstance(b, BzrDir)
77
97
 
78
98
    def test_open_remote_branch(self):
79
99
        # open a standalone branch in the working directory
80
 
        b = remote.RemoteBzrDir(self.transport)
 
100
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
81
101
        branch = b.open_branch()
82
102
        self.assertIsInstance(branch, Branch)
83
103
 
112
132
        b = BzrDir.open_from_transport(self.transport).open_branch()
113
133
        self.assertStartsWith(str(b), 'RemoteBranch(')
114
134
 
115
 
 
116
 
class FakeRemoteTransport(object):
117
 
    """This class provides the minimum support for use in place of a RemoteTransport.
118
 
    
119
 
    It doesn't actually transmit requests, but rather expects them to be
120
 
    handled by a FakeClient which holds canned responses.  It does not allow
121
 
    any vfs access, therefore is not suitable for testing any operation that
122
 
    will fallback to vfs access.  Backing the test by an instance of this
123
 
    class guarantees that it's - done using non-vfs operations.
124
 
    """
125
 
 
126
 
    _default_url = 'fakeremotetransport://host/path/'
127
 
 
128
 
    def __init__(self, url=None):
129
 
        if url is None:
130
 
            url = self._default_url
131
 
        self.base = url
132
 
 
133
 
    def __repr__(self):
134
 
        return "%r(%r)" % (self.__class__.__name__,
135
 
            self.base)
136
 
 
137
 
    def clone(self, relpath):
138
 
        return FakeRemoteTransport(urlutils.join(self.base, relpath))
139
 
 
140
 
    def get(self, relpath):
141
 
        # only get is specifically stubbed out, because it's usually the first
142
 
        # thing we do.  anything else will fail with an AttributeError.
143
 
        raise AssertionError("%r doesn't support file access to %r"
144
 
            % (self, relpath))
145
 
 
 
135
    def test_remote_branch_format_supports_stacking(self):
 
136
        t = self.transport
 
137
        self.make_branch('unstackable', format='pack-0.92')
 
138
        b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
 
139
        self.assertFalse(b._format.supports_stacking())
 
140
        self.make_branch('stackable', format='1.9')
 
141
        b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
 
142
        self.assertTrue(b._format.supports_stacking())
 
143
 
 
144
    def test_remote_repo_format_supports_external_references(self):
 
145
        t = self.transport
 
146
        bd = self.make_bzrdir('unstackable', format='pack-0.92')
 
147
        r = bd.create_repository()
 
148
        self.assertFalse(r._format.supports_external_lookups)
 
149
        r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
 
150
        self.assertFalse(r._format.supports_external_lookups)
 
151
        bd = self.make_bzrdir('stackable', format='1.9')
 
152
        r = bd.create_repository()
 
153
        self.assertTrue(r._format.supports_external_lookups)
 
154
        r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
 
155
        self.assertTrue(r._format.supports_external_lookups)
 
156
 
 
157
    def test_remote_branch_set_append_revisions_only(self):
 
158
        # Make a format 1.9 branch, which supports append_revisions_only
 
159
        branch = self.make_branch('branch', format='1.9')
 
160
        config = branch.get_config()
 
161
        branch.set_append_revisions_only(True)
 
162
        self.assertEqual(
 
163
            'True', config.get_user_option('append_revisions_only'))
 
164
        branch.set_append_revisions_only(False)
 
165
        self.assertEqual(
 
166
            'False', config.get_user_option('append_revisions_only'))
 
167
 
 
168
    def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
 
169
        branch = self.make_branch('branch', format='knit')
 
170
        config = branch.get_config()
 
171
        self.assertRaises(
 
172
            errors.UpgradeRequired, branch.set_append_revisions_only, True)
146
173
 
147
174
 
148
175
class FakeProtocol(object):
170
197
 
171
198
class FakeClient(_SmartClient):
172
199
    """Lookalike for _SmartClient allowing testing."""
173
 
    
 
200
 
174
201
    def __init__(self, fake_medium_base='fake base'):
175
202
        """Create a FakeClient."""
176
203
        self.responses = []
178
205
        self.expecting_body = False
179
206
        # if non-None, this is the list of expected calls, with only the
180
207
        # method name and arguments included.  the body might be hard to
181
 
        # compute so is not included
 
208
        # compute so is not included. If a call is None, that call can
 
209
        # be anything.
182
210
        self._expected_calls = None
183
211
        _SmartClient.__init__(self, FakeMedium(self._calls, fake_medium_base))
184
212
 
194
222
 
195
223
    def add_success_response_with_body(self, body, *args):
196
224
        self.responses.append(('success', args, body))
 
225
        if self._expected_calls is not None:
 
226
            self._expected_calls.append(None)
197
227
 
198
228
    def add_error_response(self, *args):
199
229
        self.responses.append(('error', args))
228
258
            raise AssertionError("%r didn't expect any more calls "
229
259
                "but got %r%r"
230
260
                % (self, method, args,))
 
261
        if next_call is None:
 
262
            return
231
263
        if method != next_call[0] or args != next_call[1]:
232
264
            raise AssertionError("%r expected %r%r "
233
265
                "but got %r%r"
259
291
        stream = list(stream)
260
292
        self._check_call(args[0], args[1:])
261
293
        self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
262
 
        return self._get_next_response()[1]
 
294
        result = self._get_next_response()
 
295
        # The second value returned from call_with_body_stream is supposed to
 
296
        # be a response_handler object, but so far no tests depend on that.
 
297
        response_handler = None 
 
298
        return result[1], response_handler
263
299
 
264
300
 
265
301
class FakeMedium(medium.SmartClientMedium):
286
322
        self.assertTrue(result)
287
323
 
288
324
 
 
325
class TestRemote(tests.TestCaseWithMemoryTransport):
 
326
 
 
327
    def get_branch_format(self):
 
328
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
329
        return reference_bzrdir_format.get_branch_format()
 
330
 
 
331
    def get_repo_format(self):
 
332
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
333
        return reference_bzrdir_format.repository_format
 
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
 
 
344
 
289
345
class Test_ClientMedium_remote_path_from_transport(tests.TestCase):
290
346
    """Tests for the behaviour of client_medium.remote_path_from_transport."""
291
347
 
318
374
        cloned_transport = base_transport.clone(relpath)
319
375
        result = client_medium.remote_path_from_transport(cloned_transport)
320
376
        self.assertEqual(expected, result)
321
 
        
 
377
 
322
378
    def test_remote_path_from_transport_http(self):
323
379
        """Remote paths for HTTP transports are calculated differently to other
324
380
        transports.  They are just relative to the client base, not the root
340
396
        """
341
397
        client_medium = medium.SmartClientMedium('dummy base')
342
398
        self.assertFalse(client_medium._is_remote_before((99, 99)))
343
 
    
 
399
 
344
400
    def test__remember_remote_is_before(self):
345
401
        """Calling _remember_remote_is_before ratchets down the known remote
346
402
        version.
359
415
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
360
416
 
361
417
 
362
 
class TestBzrDirOpenBranch(tests.TestCase):
 
418
class TestBzrDirCloningMetaDir(TestRemote):
 
419
 
 
420
    def test_backwards_compat(self):
 
421
        self.setup_smart_server_with_call_log()
 
422
        a_dir = self.make_bzrdir('.')
 
423
        self.reset_smart_call_log()
 
424
        verb = 'BzrDir.cloning_metadir'
 
425
        self.disable_verb(verb)
 
426
        format = a_dir.cloning_metadir()
 
427
        call_count = len([call for call in self.hpss_calls if
 
428
            call.call.method == verb])
 
429
        self.assertEqual(1, call_count)
 
430
 
 
431
    def test_branch_reference(self):
 
432
        transport = self.get_transport('quack')
 
433
        referenced = self.make_branch('referenced')
 
434
        expected = referenced.bzrdir.cloning_metadir()
 
435
        client = FakeClient(transport.base)
 
436
        client.add_expected_call(
 
437
            'BzrDir.cloning_metadir', ('quack/', 'False'),
 
438
            'error', ('BranchReference',)),
 
439
        client.add_expected_call(
 
440
            'BzrDir.open_branchV2', ('quack/',),
 
441
            'success', ('ref', self.get_url('referenced'))),
 
442
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
443
            _client=client)
 
444
        result = a_bzrdir.cloning_metadir()
 
445
        # We should have got a control dir matching the referenced branch.
 
446
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
 
447
        self.assertEqual(expected._repository_format, result._repository_format)
 
448
        self.assertEqual(expected._branch_format, result._branch_format)
 
449
        client.finished_test()
 
450
 
 
451
    def test_current_server(self):
 
452
        transport = self.get_transport('.')
 
453
        transport = transport.clone('quack')
 
454
        self.make_bzrdir('quack')
 
455
        client = FakeClient(transport.base)
 
456
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
457
        control_name = reference_bzrdir_format.network_name()
 
458
        client.add_expected_call(
 
459
            'BzrDir.cloning_metadir', ('quack/', 'False'),
 
460
            'success', (control_name, '', ('branch', ''))),
 
461
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
462
            _client=client)
 
463
        result = a_bzrdir.cloning_metadir()
 
464
        # We should have got a reference control dir with default branch and
 
465
        # repository formats.
 
466
        # This pokes a little, just to be sure.
 
467
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
 
468
        self.assertEqual(None, result._repository_format)
 
469
        self.assertEqual(None, result._branch_format)
 
470
        client.finished_test()
 
471
 
 
472
 
 
473
class TestBzrDirOpenBranch(TestRemote):
 
474
 
 
475
    def test_backwards_compat(self):
 
476
        self.setup_smart_server_with_call_log()
 
477
        self.make_branch('.')
 
478
        a_dir = BzrDir.open(self.get_url('.'))
 
479
        self.reset_smart_call_log()
 
480
        verb = 'BzrDir.open_branchV2'
 
481
        self.disable_verb(verb)
 
482
        format = a_dir.open_branch()
 
483
        call_count = len([call for call in self.hpss_calls if
 
484
            call.call.method == verb])
 
485
        self.assertEqual(1, call_count)
363
486
 
364
487
    def test_branch_present(self):
 
488
        reference_format = self.get_repo_format()
 
489
        network_name = reference_format.network_name()
 
490
        branch_network_name = self.get_branch_format().network_name()
365
491
        transport = MemoryTransport()
366
492
        transport.mkdir('quack')
367
493
        transport = transport.clone('quack')
368
494
        client = FakeClient(transport.base)
369
495
        client.add_expected_call(
370
 
            'BzrDir.open_branch', ('quack/',),
371
 
            'success', ('ok', ''))
 
496
            'BzrDir.open_branchV2', ('quack/',),
 
497
            'success', ('branch', branch_network_name))
372
498
        client.add_expected_call(
373
 
            'BzrDir.find_repositoryV2', ('quack/',),
374
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
499
            'BzrDir.find_repositoryV3', ('quack/',),
 
500
            'success', ('ok', '', 'no', 'no', 'no', network_name))
375
501
        client.add_expected_call(
376
502
            'Branch.get_stacked_on_url', ('quack/',),
377
503
            'error', ('NotStacked',))
378
 
        bzrdir = RemoteBzrDir(transport, _client=client)
 
504
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
505
            _client=client)
379
506
        result = bzrdir.open_branch()
380
507
        self.assertIsInstance(result, RemoteBranch)
381
508
        self.assertEqual(bzrdir, result.bzrdir)
387
514
        transport = transport.clone('quack')
388
515
        client = FakeClient(transport.base)
389
516
        client.add_error_response('nobranch')
390
 
        bzrdir = RemoteBzrDir(transport, _client=client)
 
517
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
518
            _client=client)
391
519
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
392
520
        self.assertEqual(
393
 
            [('call', 'BzrDir.open_branch', ('quack/',))],
 
521
            [('call', 'BzrDir.open_branchV2', ('quack/',))],
394
522
            client._calls)
395
523
 
396
524
    def test__get_tree_branch(self):
403
531
        transport = MemoryTransport()
404
532
        # no requests on the network - catches other api calls being made.
405
533
        client = FakeClient(transport.base)
406
 
        bzrdir = RemoteBzrDir(transport, _client=client)
 
534
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
535
            _client=client)
407
536
        # patch the open_branch call to record that it was called.
408
537
        bzrdir.open_branch = open_branch
409
538
        self.assertEqual((None, "a-branch"), bzrdir._get_tree_branch())
415
544
        # transmitted as "~", not "%7E".
416
545
        transport = RemoteTCPTransport('bzr://localhost/~hello/')
417
546
        client = FakeClient(transport.base)
418
 
        client.add_expected_call(
419
 
            'BzrDir.open_branch', ('~hello/',),
420
 
            'success', ('ok', ''))
421
 
        client.add_expected_call(
422
 
            'BzrDir.find_repositoryV2', ('~hello/',),
423
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
547
        reference_format = self.get_repo_format()
 
548
        network_name = reference_format.network_name()
 
549
        branch_network_name = self.get_branch_format().network_name()
 
550
        client.add_expected_call(
 
551
            'BzrDir.open_branchV2', ('~hello/',),
 
552
            'success', ('branch', branch_network_name))
 
553
        client.add_expected_call(
 
554
            'BzrDir.find_repositoryV3', ('~hello/',),
 
555
            'success', ('ok', '', 'no', 'no', 'no', network_name))
424
556
        client.add_expected_call(
425
557
            'Branch.get_stacked_on_url', ('~hello/',),
426
558
            'error', ('NotStacked',))
427
 
        bzrdir = RemoteBzrDir(transport, _client=client)
 
559
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
560
            _client=client)
428
561
        result = bzrdir.open_branch()
429
562
        client.finished_test()
430
563
 
431
564
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
 
565
        reference_format = self.get_repo_format()
 
566
        network_name = reference_format.network_name()
432
567
        transport = MemoryTransport()
433
568
        transport.mkdir('quack')
434
569
        transport = transport.clone('quack')
442
577
            subtree_response = 'no'
443
578
        client = FakeClient(transport.base)
444
579
        client.add_success_response(
445
 
            'ok', '', rich_response, subtree_response, external_lookup)
446
 
        bzrdir = RemoteBzrDir(transport, _client=client)
 
580
            'ok', '', rich_response, subtree_response, external_lookup,
 
581
            network_name)
 
582
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
583
            _client=client)
447
584
        result = bzrdir.open_repository()
448
585
        self.assertEqual(
449
 
            [('call', 'BzrDir.find_repositoryV2', ('quack/',))],
 
586
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
450
587
            client._calls)
451
588
        self.assertIsInstance(result, RemoteRepository)
452
589
        self.assertEqual(bzrdir, result.bzrdir)
468
605
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
469
606
 
470
607
 
471
 
class TestBzrDirOpenRepository(tests.TestCase):
472
 
 
473
 
    def test_backwards_compat_1_2(self):
474
 
        transport = MemoryTransport()
475
 
        transport.mkdir('quack')
476
 
        transport = transport.clone('quack')
477
 
        client = FakeClient(transport.base)
478
 
        client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
 
608
class TestBzrDirCreateBranch(TestRemote):
 
609
 
 
610
    def test_backwards_compat(self):
 
611
        self.setup_smart_server_with_call_log()
 
612
        repo = self.make_repository('.')
 
613
        self.reset_smart_call_log()
 
614
        self.disable_verb('BzrDir.create_branch')
 
615
        branch = repo.bzrdir.create_branch()
 
616
        create_branch_call_count = len([call for call in self.hpss_calls if
 
617
            call.call.method == 'BzrDir.create_branch'])
 
618
        self.assertEqual(1, create_branch_call_count)
 
619
 
 
620
    def test_current_server(self):
 
621
        transport = self.get_transport('.')
 
622
        transport = transport.clone('quack')
 
623
        self.make_repository('quack')
 
624
        client = FakeClient(transport.base)
 
625
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
626
        reference_format = reference_bzrdir_format.get_branch_format()
 
627
        network_name = reference_format.network_name()
 
628
        reference_repo_fmt = reference_bzrdir_format.repository_format
 
629
        reference_repo_name = reference_repo_fmt.network_name()
 
630
        client.add_expected_call(
 
631
            'BzrDir.create_branch', ('quack/', network_name),
 
632
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
 
633
            reference_repo_name))
 
634
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
635
            _client=client)
 
636
        branch = a_bzrdir.create_branch()
 
637
        # We should have got a remote branch
 
638
        self.assertIsInstance(branch, remote.RemoteBranch)
 
639
        # its format should have the settings from the response
 
640
        format = branch._format
 
641
        self.assertEqual(network_name, format.network_name())
 
642
 
 
643
 
 
644
class TestBzrDirCreateRepository(TestRemote):
 
645
 
 
646
    def test_backwards_compat(self):
 
647
        self.setup_smart_server_with_call_log()
 
648
        bzrdir = self.make_bzrdir('.')
 
649
        self.reset_smart_call_log()
 
650
        self.disable_verb('BzrDir.create_repository')
 
651
        repo = bzrdir.create_repository()
 
652
        create_repo_call_count = len([call for call in self.hpss_calls if
 
653
            call.call.method == 'BzrDir.create_repository'])
 
654
        self.assertEqual(1, create_repo_call_count)
 
655
 
 
656
    def test_current_server(self):
 
657
        transport = self.get_transport('.')
 
658
        transport = transport.clone('quack')
 
659
        self.make_bzrdir('quack')
 
660
        client = FakeClient(transport.base)
 
661
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
662
        reference_format = reference_bzrdir_format.repository_format
 
663
        network_name = reference_format.network_name()
 
664
        client.add_expected_call(
 
665
            'BzrDir.create_repository', ('quack/',
 
666
                'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
 
667
            'success', ('ok', 'no', 'no', 'no', network_name))
 
668
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
669
            _client=client)
 
670
        repo = a_bzrdir.create_repository()
 
671
        # We should have got a remote repository
 
672
        self.assertIsInstance(repo, remote.RemoteRepository)
 
673
        # its format should have the settings from the response
 
674
        format = repo._format
 
675
        self.assertFalse(format.rich_root_data)
 
676
        self.assertFalse(format.supports_tree_reference)
 
677
        self.assertFalse(format.supports_external_lookups)
 
678
        self.assertEqual(network_name, format.network_name())
 
679
 
 
680
 
 
681
class TestBzrDirOpenRepository(TestRemote):
 
682
 
 
683
    def test_backwards_compat_1_2_3(self):
 
684
        # fallback all the way to the first version.
 
685
        reference_format = self.get_repo_format()
 
686
        network_name = reference_format.network_name()
 
687
        client = FakeClient('bzr://example.com/')
 
688
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
 
689
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
479
690
        client.add_success_response('ok', '', 'no', 'no')
480
 
        bzrdir = RemoteBzrDir(transport, _client=client)
481
 
        repo = bzrdir.open_repository()
482
 
        self.assertEqual(
483
 
            [('call', 'BzrDir.find_repositoryV2', ('quack/',)),
484
 
             ('call', 'BzrDir.find_repository', ('quack/',))],
485
 
            client._calls)
 
691
        # A real repository instance will be created to determine the network
 
692
        # name.
 
693
        client.add_success_response_with_body(
 
694
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
695
        client.add_success_response_with_body(
 
696
            reference_format.get_format_string(), 'ok')
 
697
        # PackRepository wants to do a stat
 
698
        client.add_success_response('stat', '0', '65535')
 
699
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
700
            _client=client)
 
701
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
702
            _client=client)
 
703
        repo = bzrdir.open_repository()
 
704
        self.assertEqual(
 
705
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
 
706
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
 
707
             ('call', 'BzrDir.find_repository', ('quack/',)),
 
708
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
 
709
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
 
710
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
711
             ],
 
712
            client._calls)
 
713
        self.assertEqual(network_name, repo._format.network_name())
 
714
 
 
715
    def test_backwards_compat_2(self):
 
716
        # fallback to find_repositoryV2
 
717
        reference_format = self.get_repo_format()
 
718
        network_name = reference_format.network_name()
 
719
        client = FakeClient('bzr://example.com/')
 
720
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
 
721
        client.add_success_response('ok', '', 'no', 'no', 'no')
 
722
        # A real repository instance will be created to determine the network
 
723
        # name.
 
724
        client.add_success_response_with_body(
 
725
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
726
        client.add_success_response_with_body(
 
727
            reference_format.get_format_string(), 'ok')
 
728
        # PackRepository wants to do a stat
 
729
        client.add_success_response('stat', '0', '65535')
 
730
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
731
            _client=client)
 
732
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
733
            _client=client)
 
734
        repo = bzrdir.open_repository()
 
735
        self.assertEqual(
 
736
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
 
737
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
 
738
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
 
739
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
 
740
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
741
             ],
 
742
            client._calls)
 
743
        self.assertEqual(network_name, repo._format.network_name())
 
744
 
 
745
    def test_current_server(self):
 
746
        reference_format = self.get_repo_format()
 
747
        network_name = reference_format.network_name()
 
748
        transport = MemoryTransport()
 
749
        transport.mkdir('quack')
 
750
        transport = transport.clone('quack')
 
751
        client = FakeClient(transport.base)
 
752
        client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
 
753
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
754
            _client=client)
 
755
        repo = bzrdir.open_repository()
 
756
        self.assertEqual(
 
757
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
 
758
            client._calls)
 
759
        self.assertEqual(network_name, repo._format.network_name())
 
760
 
 
761
 
 
762
class TestBzrDirFormatInitializeEx(TestRemote):
 
763
 
 
764
    def test_success(self):
 
765
        """Simple test for typical successful call."""
 
766
        fmt = bzrdir.RemoteBzrDirFormat()
 
767
        default_format_name = BzrDirFormat.get_default_format().network_name()
 
768
        transport = self.get_transport()
 
769
        client = FakeClient(transport.base)
 
770
        client.add_expected_call(
 
771
            'BzrDirFormat.initialize_ex_1.16',
 
772
                (default_format_name, 'path', 'False', 'False', 'False', '',
 
773
                 '', '', '', 'False'),
 
774
            'success',
 
775
                ('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
 
776
                 'bzrdir fmt', 'False', '', '', 'repo lock token'))
 
777
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
 
778
        # it's currently hard to test that without supplying a real remote
 
779
        # transport connected to a real server.
 
780
        result = fmt._initialize_on_transport_ex_rpc(client, 'path',
 
781
            transport, False, False, False, None, None, None, None, False)
 
782
        client.finished_test()
 
783
 
 
784
    def test_error(self):
 
785
        """Error responses are translated, e.g. 'PermissionDenied' raises the
 
786
        corresponding error from the client.
 
787
        """
 
788
        fmt = bzrdir.RemoteBzrDirFormat()
 
789
        default_format_name = BzrDirFormat.get_default_format().network_name()
 
790
        transport = self.get_transport()
 
791
        client = FakeClient(transport.base)
 
792
        client.add_expected_call(
 
793
            'BzrDirFormat.initialize_ex_1.16',
 
794
                (default_format_name, 'path', 'False', 'False', 'False', '',
 
795
                 '', '', '', 'False'),
 
796
            'error',
 
797
                ('PermissionDenied', 'path', 'extra info'))
 
798
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
 
799
        # it's currently hard to test that without supplying a real remote
 
800
        # transport connected to a real server.
 
801
        err = self.assertRaises(errors.PermissionDenied,
 
802
            fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
 
803
            False, False, False, None, None, None, None, False)
 
804
        self.assertEqual('path', err.path)
 
805
        self.assertEqual(': extra info', err.extra)
 
806
        client.finished_test()
 
807
 
 
808
    def test_error_from_real_server(self):
 
809
        """Integration test for error translation."""
 
810
        transport = self.make_smart_server('foo')
 
811
        transport = transport.clone('no-such-path')
 
812
        fmt = bzrdir.RemoteBzrDirFormat()
 
813
        err = self.assertRaises(errors.NoSuchFile,
 
814
            fmt.initialize_on_transport_ex, transport, create_prefix=False)
486
815
 
487
816
 
488
817
class OldSmartClient(object):
513
842
        return OldSmartClient()
514
843
 
515
844
 
516
 
class RemoteBranchTestCase(tests.TestCase):
 
845
class RemoteBzrDirTestCase(TestRemote):
 
846
 
 
847
    def make_remote_bzrdir(self, transport, client):
 
848
        """Make a RemotebzrDir using 'client' as the _client."""
 
849
        return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
850
            _client=client)
 
851
 
 
852
 
 
853
class RemoteBranchTestCase(RemoteBzrDirTestCase):
517
854
 
518
855
    def make_remote_branch(self, transport, client):
519
856
        """Make a RemoteBranch using 'client' as its _SmartClient.
520
 
        
 
857
 
521
858
        A RemoteBzrDir and RemoteRepository will also be created to fill out
522
859
        the RemoteBranch, albeit with stub values for some of their attributes.
523
860
        """
524
861
        # we do not want bzrdir to make any remote calls, so use False as its
525
862
        # _client.  If it tries to make a remote call, this will fail
526
863
        # immediately.
527
 
        bzrdir = RemoteBzrDir(transport, _client=False)
 
864
        bzrdir = self.make_remote_bzrdir(transport, False)
528
865
        repo = RemoteRepository(bzrdir, None, _client=client)
529
 
        return RemoteBranch(bzrdir, repo, _client=client)
 
866
        branch_format = self.get_branch_format()
 
867
        format = RemoteBranchFormat(network_name=branch_format.network_name())
 
868
        return RemoteBranch(bzrdir, repo, _client=client, format=format)
 
869
 
 
870
 
 
871
class TestBranchGetParent(RemoteBranchTestCase):
 
872
 
 
873
    def test_no_parent(self):
 
874
        # in an empty branch we decode the response properly
 
875
        transport = MemoryTransport()
 
876
        client = FakeClient(transport.base)
 
877
        client.add_expected_call(
 
878
            'Branch.get_stacked_on_url', ('quack/',),
 
879
            'error', ('NotStacked',))
 
880
        client.add_expected_call(
 
881
            'Branch.get_parent', ('quack/',),
 
882
            'success', ('',))
 
883
        transport.mkdir('quack')
 
884
        transport = transport.clone('quack')
 
885
        branch = self.make_remote_branch(transport, client)
 
886
        result = branch.get_parent()
 
887
        client.finished_test()
 
888
        self.assertEqual(None, result)
 
889
 
 
890
    def test_parent_relative(self):
 
891
        transport = MemoryTransport()
 
892
        client = FakeClient(transport.base)
 
893
        client.add_expected_call(
 
894
            'Branch.get_stacked_on_url', ('kwaak/',),
 
895
            'error', ('NotStacked',))
 
896
        client.add_expected_call(
 
897
            'Branch.get_parent', ('kwaak/',),
 
898
            'success', ('../foo/',))
 
899
        transport.mkdir('kwaak')
 
900
        transport = transport.clone('kwaak')
 
901
        branch = self.make_remote_branch(transport, client)
 
902
        result = branch.get_parent()
 
903
        self.assertEqual(transport.clone('../foo').base, result)
 
904
 
 
905
    def test_parent_absolute(self):
 
906
        transport = MemoryTransport()
 
907
        client = FakeClient(transport.base)
 
908
        client.add_expected_call(
 
909
            'Branch.get_stacked_on_url', ('kwaak/',),
 
910
            'error', ('NotStacked',))
 
911
        client.add_expected_call(
 
912
            'Branch.get_parent', ('kwaak/',),
 
913
            'success', ('http://foo/',))
 
914
        transport.mkdir('kwaak')
 
915
        transport = transport.clone('kwaak')
 
916
        branch = self.make_remote_branch(transport, client)
 
917
        result = branch.get_parent()
 
918
        self.assertEqual('http://foo/', result)
 
919
        client.finished_test()
 
920
 
 
921
 
 
922
class TestBranchSetParentLocation(RemoteBranchTestCase):
 
923
 
 
924
    def test_no_parent(self):
 
925
        # We call the verb when setting parent to None
 
926
        transport = MemoryTransport()
 
927
        client = FakeClient(transport.base)
 
928
        client.add_expected_call(
 
929
            'Branch.get_stacked_on_url', ('quack/',),
 
930
            'error', ('NotStacked',))
 
931
        client.add_expected_call(
 
932
            'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
 
933
            'success', ())
 
934
        transport.mkdir('quack')
 
935
        transport = transport.clone('quack')
 
936
        branch = self.make_remote_branch(transport, client)
 
937
        branch._lock_token = 'b'
 
938
        branch._repo_lock_token = 'r'
 
939
        branch._set_parent_location(None)
 
940
        client.finished_test()
 
941
 
 
942
    def test_parent(self):
 
943
        transport = MemoryTransport()
 
944
        client = FakeClient(transport.base)
 
945
        client.add_expected_call(
 
946
            'Branch.get_stacked_on_url', ('kwaak/',),
 
947
            'error', ('NotStacked',))
 
948
        client.add_expected_call(
 
949
            'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
 
950
            'success', ())
 
951
        transport.mkdir('kwaak')
 
952
        transport = transport.clone('kwaak')
 
953
        branch = self.make_remote_branch(transport, client)
 
954
        branch._lock_token = 'b'
 
955
        branch._repo_lock_token = 'r'
 
956
        branch._set_parent_location('foo')
 
957
        client.finished_test()
 
958
 
 
959
    def test_backwards_compat(self):
 
960
        self.setup_smart_server_with_call_log()
 
961
        branch = self.make_branch('.')
 
962
        self.reset_smart_call_log()
 
963
        verb = 'Branch.set_parent_location'
 
964
        self.disable_verb(verb)
 
965
        branch.set_parent('http://foo/')
 
966
        self.assertLength(12, self.hpss_calls)
 
967
 
 
968
 
 
969
class TestBranchGetTagsBytes(RemoteBranchTestCase):
 
970
 
 
971
    def test_backwards_compat(self):
 
972
        self.setup_smart_server_with_call_log()
 
973
        branch = self.make_branch('.')
 
974
        self.reset_smart_call_log()
 
975
        verb = 'Branch.get_tags_bytes'
 
976
        self.disable_verb(verb)
 
977
        branch.tags.get_tag_dict()
 
978
        call_count = len([call for call in self.hpss_calls if
 
979
            call.call.method == verb])
 
980
        self.assertEqual(1, call_count)
 
981
 
 
982
    def test_trivial(self):
 
983
        transport = MemoryTransport()
 
984
        client = FakeClient(transport.base)
 
985
        client.add_expected_call(
 
986
            'Branch.get_stacked_on_url', ('quack/',),
 
987
            'error', ('NotStacked',))
 
988
        client.add_expected_call(
 
989
            'Branch.get_tags_bytes', ('quack/',),
 
990
            'success', ('',))
 
991
        transport.mkdir('quack')
 
992
        transport = transport.clone('quack')
 
993
        branch = self.make_remote_branch(transport, client)
 
994
        result = branch.tags.get_tag_dict()
 
995
        client.finished_test()
 
996
        self.assertEqual({}, result)
530
997
 
531
998
 
532
999
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
566
1033
        self.assertEqual((2, revid), result)
567
1034
 
568
1035
 
569
 
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
 
1036
class TestBranch_get_stacked_on_url(TestRemote):
570
1037
    """Test Branch._get_stacked_on_url rpc"""
571
1038
 
572
1039
    def test_get_stacked_on_invalid_url(self):
573
 
        raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
574
 
        transport = FakeRemoteTransport('fakeremotetransport:///')
 
1040
        # test that asking for a stacked on url the server can't access works.
 
1041
        # This isn't perfect, but then as we're in the same process there
 
1042
        # really isn't anything we can do to be 100% sure that the server
 
1043
        # doesn't just open in - this test probably needs to be rewritten using
 
1044
        # a spawn()ed server.
 
1045
        stacked_branch = self.make_branch('stacked', format='1.9')
 
1046
        memory_branch = self.make_branch('base', format='1.9')
 
1047
        vfs_url = self.get_vfs_only_url('base')
 
1048
        stacked_branch.set_stacked_on_url(vfs_url)
 
1049
        transport = stacked_branch.bzrdir.root_transport
575
1050
        client = FakeClient(transport.base)
576
1051
        client.add_expected_call(
577
 
            'Branch.get_stacked_on_url', ('.',),
578
 
            'success', ('ok', 'file:///stacked/on'))
579
 
        bzrdir = RemoteBzrDir(transport, _client=client)
580
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
1052
            'Branch.get_stacked_on_url', ('stacked/',),
 
1053
            'success', ('ok', vfs_url))
 
1054
        # XXX: Multiple calls are bad, this second call documents what is
 
1055
        # today.
 
1056
        client.add_expected_call(
 
1057
            'Branch.get_stacked_on_url', ('stacked/',),
 
1058
            'success', ('ok', vfs_url))
 
1059
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
1060
            _client=client)
 
1061
        repo_fmt = remote.RemoteRepositoryFormat()
 
1062
        repo_fmt._custom_format = stacked_branch.repository._format
 
1063
        branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
 
1064
            _client=client)
581
1065
        result = branch.get_stacked_on_url()
582
 
        self.assertEqual(
583
 
            'file:///stacked/on', result)
 
1066
        self.assertEqual(vfs_url, result)
584
1067
 
585
1068
    def test_backwards_compatible(self):
586
1069
        # like with bzr1.6 with no Branch.get_stacked_on_url rpc
588
1071
        stacked_branch = self.make_branch('stacked', format='1.6')
589
1072
        stacked_branch.set_stacked_on_url('../base')
590
1073
        client = FakeClient(self.get_url())
591
 
        client.add_expected_call(
592
 
            'BzrDir.open_branch', ('stacked/',),
593
 
            'success', ('ok', ''))
594
 
        client.add_expected_call(
595
 
            'BzrDir.find_repositoryV2', ('stacked/',),
596
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
1074
        branch_network_name = self.get_branch_format().network_name()
 
1075
        client.add_expected_call(
 
1076
            'BzrDir.open_branchV2', ('stacked/',),
 
1077
            'success', ('branch', branch_network_name))
 
1078
        client.add_expected_call(
 
1079
            'BzrDir.find_repositoryV3', ('stacked/',),
 
1080
            'success', ('ok', '', 'no', 'no', 'yes',
 
1081
                stacked_branch.repository._format.network_name()))
597
1082
        # called twice, once from constructor and then again by us
598
1083
        client.add_expected_call(
599
1084
            'Branch.get_stacked_on_url', ('stacked/',),
603
1088
            'unknown', ('Branch.get_stacked_on_url',))
604
1089
        # this will also do vfs access, but that goes direct to the transport
605
1090
        # and isn't seen by the FakeClient.
606
 
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
 
1091
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
 
1092
            remote.RemoteBzrDirFormat(), _client=client)
607
1093
        branch = bzrdir.open_branch()
608
1094
        result = branch.get_stacked_on_url()
609
1095
        self.assertEqual('../base', result)
618
1104
        base_branch = self.make_branch('base', format='1.6')
619
1105
        stacked_branch = self.make_branch('stacked', format='1.6')
620
1106
        stacked_branch.set_stacked_on_url('../base')
 
1107
        reference_format = self.get_repo_format()
 
1108
        network_name = reference_format.network_name()
621
1109
        client = FakeClient(self.get_url())
622
 
        client.add_expected_call(
623
 
            'BzrDir.open_branch', ('stacked/',),
624
 
            'success', ('ok', ''))
625
 
        client.add_expected_call(
626
 
            'BzrDir.find_repositoryV2', ('stacked/',),
627
 
            'success', ('ok', '', 'no', 'no', 'no'))
 
1110
        branch_network_name = self.get_branch_format().network_name()
 
1111
        client.add_expected_call(
 
1112
            'BzrDir.open_branchV2', ('stacked/',),
 
1113
            'success', ('branch', branch_network_name))
 
1114
        client.add_expected_call(
 
1115
            'BzrDir.find_repositoryV3', ('stacked/',),
 
1116
            'success', ('ok', '', 'no', 'no', 'yes', network_name))
628
1117
        # called twice, once from constructor and then again by us
629
1118
        client.add_expected_call(
630
1119
            'Branch.get_stacked_on_url', ('stacked/',),
632
1121
        client.add_expected_call(
633
1122
            'Branch.get_stacked_on_url', ('stacked/',),
634
1123
            'success', ('ok', '../base'))
635
 
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
 
1124
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
 
1125
            remote.RemoteBzrDirFormat(), _client=client)
636
1126
        branch = bzrdir.open_branch()
637
1127
        result = branch.get_stacked_on_url()
638
1128
        self.assertEqual('../base', result)
639
1129
        client.finished_test()
640
 
        # it's in the fallback list both for the RemoteRepository and its vfs
641
 
        # repository
 
1130
        # it's in the fallback list both for the RemoteRepository.
642
1131
        self.assertEqual(1, len(branch.repository._fallback_repositories))
643
 
        self.assertEqual(1,
644
 
            len(branch.repository._real_repository._fallback_repositories))
 
1132
        # And we haven't had to construct a real repository.
 
1133
        self.assertEqual(None, branch.repository._real_repository)
645
1134
 
646
1135
 
647
1136
class TestBranchSetLastRevision(RemoteBranchTestCase):
661
1150
            'Branch.lock_write', ('branch/', '', ''),
662
1151
            'success', ('ok', 'branch token', 'repo token'))
663
1152
        client.add_expected_call(
 
1153
            'Branch.last_revision_info',
 
1154
            ('branch/',),
 
1155
            'success', ('ok', '0', 'null:'))
 
1156
        client.add_expected_call(
664
1157
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
665
1158
            'success', ('ok',))
666
1159
        client.add_expected_call(
691
1184
            'Branch.lock_write', ('branch/', '', ''),
692
1185
            'success', ('ok', 'branch token', 'repo token'))
693
1186
        client.add_expected_call(
 
1187
            'Branch.last_revision_info',
 
1188
            ('branch/',),
 
1189
            'success', ('ok', '0', 'null:'))
 
1190
        lines = ['rev-id2']
 
1191
        encoded_body = bz2.compress('\n'.join(lines))
 
1192
        client.add_success_response_with_body(encoded_body, 'ok')
 
1193
        client.add_expected_call(
694
1194
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
695
1195
            'success', ('ok',))
696
1196
        client.add_expected_call(
720
1220
            'Branch.lock_write', ('branch/', '', ''),
721
1221
            'success', ('ok', 'branch token', 'repo token'))
722
1222
        client.add_expected_call(
 
1223
            'Branch.last_revision_info',
 
1224
            ('branch/',),
 
1225
            'success', ('ok', '0', 'null:'))
 
1226
        # get_graph calls to construct the revision history, for the set_rh
 
1227
        # hook
 
1228
        lines = ['rev-id']
 
1229
        encoded_body = bz2.compress('\n'.join(lines))
 
1230
        client.add_success_response_with_body(encoded_body, 'ok')
 
1231
        client.add_expected_call(
723
1232
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
724
1233
            'error', ('NoSuchRevision', 'rev-id'))
725
1234
        client.add_expected_call(
750
1259
            'Branch.lock_write', ('branch/', '', ''),
751
1260
            'success', ('ok', 'branch token', 'repo token'))
752
1261
        client.add_expected_call(
 
1262
            'Branch.last_revision_info',
 
1263
            ('branch/',),
 
1264
            'success', ('ok', '0', 'null:'))
 
1265
        lines = ['rev-id']
 
1266
        encoded_body = bz2.compress('\n'.join(lines))
 
1267
        client.add_success_response_with_body(encoded_body, 'ok')
 
1268
        client.add_expected_call(
753
1269
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
754
1270
            'error', ('TipChangeRejected', rejection_msg_utf8))
755
1271
        client.add_expected_call(
758
1274
        branch = self.make_remote_branch(transport, client)
759
1275
        branch._ensure_real = lambda: None
760
1276
        branch.lock_write()
761
 
        self.addCleanup(branch.unlock)
762
1277
        # The 'TipChangeRejected' error response triggered by calling
763
1278
        # set_revision_history causes a TipChangeRejected exception.
764
1279
        err = self.assertRaises(
784
1299
        client.add_error_response('NotStacked')
785
1300
        # lock_write
786
1301
        client.add_success_response('ok', 'branch token', 'repo token')
 
1302
        # query the current revision
 
1303
        client.add_success_response('ok', '0', 'null:')
787
1304
        # set_last_revision
788
1305
        client.add_success_response('ok')
789
1306
        # unlock
795
1312
        client._calls = []
796
1313
        result = branch.set_last_revision_info(1234, 'a-revision-id')
797
1314
        self.assertEqual(
798
 
            [('call', 'Branch.set_last_revision_info',
 
1315
            [('call', 'Branch.last_revision_info', ('branch/',)),
 
1316
             ('call', 'Branch.set_last_revision_info',
799
1317
                ('branch/', 'branch token', 'repo token',
800
1318
                 '1234', 'a-revision-id'))],
801
1319
            client._calls)
855
1373
            'Branch.get_stacked_on_url', ('branch/',),
856
1374
            'error', ('NotStacked',))
857
1375
        client.add_expected_call(
 
1376
            'Branch.last_revision_info',
 
1377
            ('branch/',),
 
1378
            'success', ('ok', '0', 'null:'))
 
1379
        client.add_expected_call(
858
1380
            'Branch.set_last_revision_info',
859
1381
            ('branch/', 'branch token', 'repo token', '1234', 'a-revision-id',),
860
1382
            'unknown', 'Branch.set_last_revision_info')
938
1460
        self.assertEqual('rejection message', err.msg)
939
1461
 
940
1462
 
941
 
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
942
 
    """Getting the branch configuration should use an abstract method not vfs.
943
 
    """
 
1463
class TestBranchGetSetConfig(RemoteBranchTestCase):
944
1464
 
945
1465
    def test_get_branch_conf(self):
946
 
        raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
947
 
        ## # We should see that branch.get_config() does a single rpc to get the
948
 
        ## # remote configuration file, abstracting away where that is stored on
949
 
        ## # the server.  However at the moment it always falls back to using the
950
 
        ## # vfs, and this would need some changes in config.py.
951
 
 
952
 
        ## # in an empty branch we decode the response properly
953
 
        ## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
954
 
        ## # we need to make a real branch because the remote_branch.control_files
955
 
        ## # will trigger _ensure_real.
956
 
        ## branch = self.make_branch('quack')
957
 
        ## transport = branch.bzrdir.root_transport
958
 
        ## # we do not want bzrdir to make any remote calls
959
 
        ## bzrdir = RemoteBzrDir(transport, _client=False)
960
 
        ## branch = RemoteBranch(bzrdir, None, _client=client)
961
 
        ## config = branch.get_config()
962
 
        ## self.assertEqual(
963
 
        ##     [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
964
 
        ##     client._calls)
 
1466
        # in an empty branch we decode the response properly
 
1467
        client = FakeClient()
 
1468
        client.add_expected_call(
 
1469
            'Branch.get_stacked_on_url', ('memory:///',),
 
1470
            'error', ('NotStacked',),)
 
1471
        client.add_success_response_with_body('# config file body', 'ok')
 
1472
        transport = MemoryTransport()
 
1473
        branch = self.make_remote_branch(transport, client)
 
1474
        config = branch.get_config()
 
1475
        config.has_explicit_nickname()
 
1476
        self.assertEqual(
 
1477
            [('call', 'Branch.get_stacked_on_url', ('memory:///',)),
 
1478
             ('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
 
1479
            client._calls)
 
1480
 
 
1481
    def test_get_multi_line_branch_conf(self):
 
1482
        # Make sure that multiple-line branch.conf files are supported
 
1483
        #
 
1484
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
 
1485
        client = FakeClient()
 
1486
        client.add_expected_call(
 
1487
            'Branch.get_stacked_on_url', ('memory:///',),
 
1488
            'error', ('NotStacked',),)
 
1489
        client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
 
1490
        transport = MemoryTransport()
 
1491
        branch = self.make_remote_branch(transport, client)
 
1492
        config = branch.get_config()
 
1493
        self.assertEqual(u'2', config.get_user_option('b'))
 
1494
 
 
1495
    def test_set_option(self):
 
1496
        client = FakeClient()
 
1497
        client.add_expected_call(
 
1498
            'Branch.get_stacked_on_url', ('memory:///',),
 
1499
            'error', ('NotStacked',),)
 
1500
        client.add_expected_call(
 
1501
            'Branch.lock_write', ('memory:///', '', ''),
 
1502
            'success', ('ok', 'branch token', 'repo token'))
 
1503
        client.add_expected_call(
 
1504
            'Branch.set_config_option', ('memory:///', 'branch token',
 
1505
            'repo token', 'foo', 'bar', ''),
 
1506
            'success', ())
 
1507
        client.add_expected_call(
 
1508
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
 
1509
            'success', ('ok',))
 
1510
        transport = MemoryTransport()
 
1511
        branch = self.make_remote_branch(transport, client)
 
1512
        branch.lock_write()
 
1513
        config = branch._get_config()
 
1514
        config.set_option('foo', 'bar')
 
1515
        branch.unlock()
 
1516
        client.finished_test()
 
1517
 
 
1518
    def test_backwards_compat_set_option(self):
 
1519
        self.setup_smart_server_with_call_log()
 
1520
        branch = self.make_branch('.')
 
1521
        verb = 'Branch.set_config_option'
 
1522
        self.disable_verb(verb)
 
1523
        branch.lock_write()
 
1524
        self.addCleanup(branch.unlock)
 
1525
        self.reset_smart_call_log()
 
1526
        branch._get_config().set_option('value', 'name')
 
1527
        self.assertLength(10, self.hpss_calls)
 
1528
        self.assertEqual('value', branch._get_config().get_option('name'))
965
1529
 
966
1530
 
967
1531
class TestBranchLockWrite(RemoteBranchTestCase):
982
1546
        client.finished_test()
983
1547
 
984
1548
 
 
1549
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
 
1550
 
 
1551
    def test__get_config(self):
 
1552
        client = FakeClient()
 
1553
        client.add_success_response_with_body('default_stack_on = /\n', 'ok')
 
1554
        transport = MemoryTransport()
 
1555
        bzrdir = self.make_remote_bzrdir(transport, client)
 
1556
        config = bzrdir.get_config()
 
1557
        self.assertEqual('/', config.get_default_stack_on())
 
1558
        self.assertEqual(
 
1559
            [('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
 
1560
            client._calls)
 
1561
 
 
1562
    def test_set_option_uses_vfs(self):
 
1563
        self.setup_smart_server_with_call_log()
 
1564
        bzrdir = self.make_bzrdir('.')
 
1565
        self.reset_smart_call_log()
 
1566
        config = bzrdir.get_config()
 
1567
        config.set_default_stack_on('/')
 
1568
        self.assertLength(3, self.hpss_calls)
 
1569
 
 
1570
    def test_backwards_compat_get_option(self):
 
1571
        self.setup_smart_server_with_call_log()
 
1572
        bzrdir = self.make_bzrdir('.')
 
1573
        verb = 'BzrDir.get_config_file'
 
1574
        self.disable_verb(verb)
 
1575
        self.reset_smart_call_log()
 
1576
        self.assertEqual(None,
 
1577
            bzrdir._get_config().get_option('default_stack_on'))
 
1578
        self.assertLength(3, self.hpss_calls)
 
1579
 
 
1580
 
985
1581
class TestTransportIsReadonly(tests.TestCase):
986
1582
 
987
1583
    def test_true(self):
1006
1602
 
1007
1603
    def test_error_from_old_server(self):
1008
1604
        """bzr 0.15 and earlier servers don't recognise the is_readonly verb.
1009
 
        
 
1605
 
1010
1606
        Clients should treat it as a "no" response, because is_readonly is only
1011
1607
        advisory anyway (a transport could be read-write, but then the
1012
1608
        underlying filesystem could be readonly anyway).
1050
1646
        self.assertEqual('bar', t._get_credentials()[0])
1051
1647
 
1052
1648
 
1053
 
class TestRemoteRepository(tests.TestCase):
 
1649
class TestRemoteRepository(TestRemote):
1054
1650
    """Base for testing RemoteRepository protocol usage.
1055
 
    
1056
 
    These tests contain frozen requests and responses.  We want any changes to 
 
1651
 
 
1652
    These tests contain frozen requests and responses.  We want any changes to
1057
1653
    what is sent or expected to be require a thoughtful update to these tests
1058
1654
    because they might break compatibility with different-versioned servers.
1059
1655
    """
1060
1656
 
1061
1657
    def setup_fake_client_and_repository(self, transport_path):
1062
1658
        """Create the fake client and repository for testing with.
1063
 
        
 
1659
 
1064
1660
        There's no real server here; we just have canned responses sent
1065
1661
        back one by one.
1066
 
        
 
1662
 
1067
1663
        :param transport_path: Path below the root of the MemoryTransport
1068
1664
            where the repository will be created.
1069
1665
        """
1072
1668
        client = FakeClient(transport.base)
1073
1669
        transport = transport.clone(transport_path)
1074
1670
        # we do not want bzrdir to make any remote calls
1075
 
        bzrdir = RemoteBzrDir(transport, _client=False)
 
1671
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
1672
            _client=False)
1076
1673
        repo = RemoteRepository(bzrdir, None, _client=client)
1077
1674
        return repo, client
1078
1675
 
1079
1676
 
 
1677
class TestRepositoryFormat(TestRemoteRepository):
 
1678
 
 
1679
    def test_fast_delta(self):
 
1680
        true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
 
1681
        true_format = RemoteRepositoryFormat()
 
1682
        true_format._network_name = true_name
 
1683
        self.assertEqual(True, true_format.fast_deltas)
 
1684
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
 
1685
        false_format = RemoteRepositoryFormat()
 
1686
        false_format._network_name = false_name
 
1687
        self.assertEqual(False, false_format.fast_deltas)
 
1688
 
 
1689
 
1080
1690
class TestRepositoryGatherStats(TestRemoteRepository):
1081
1691
 
1082
1692
    def test_revid_none(self):
1170
1780
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1171
1781
        self.assertEqual(
1172
1782
            [('call_with_body_bytes_expecting_body',
1173
 
              'Repository.get_parent_map', ('quack/', r2), '\n\n0')],
 
1783
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
 
1784
              '\n\n0')],
1174
1785
            client._calls)
1175
1786
        repo.unlock()
1176
1787
        # now we call again, and it should use the second response.
1180
1791
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1181
1792
        self.assertEqual(
1182
1793
            [('call_with_body_bytes_expecting_body',
1183
 
              'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
 
1794
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
 
1795
              '\n\n0'),
1184
1796
             ('call_with_body_bytes_expecting_body',
1185
 
              'Repository.get_parent_map', ('quack/', r1), '\n\n0'),
 
1797
              'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
 
1798
              '\n\n0'),
1186
1799
            ],
1187
1800
            client._calls)
1188
1801
        repo.unlock()
1189
1802
 
1190
1803
    def test_get_parent_map_reconnects_if_unknown_method(self):
1191
1804
        transport_path = 'quack'
 
1805
        rev_id = 'revision-id'
1192
1806
        repo, client = self.setup_fake_client_and_repository(transport_path)
1193
 
        client.add_unknown_method_response('Repository,get_parent_map')
1194
 
        client.add_success_response_with_body('', 'ok')
 
1807
        client.add_unknown_method_response('Repository.get_parent_map')
 
1808
        client.add_success_response_with_body(rev_id, 'ok')
1195
1809
        self.assertFalse(client._medium._is_remote_before((1, 2)))
1196
 
        rev_id = 'revision-id'
1197
 
        expected_deprecations = [
1198
 
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1199
 
            'in version 1.4.']
1200
 
        parents = self.callDeprecated(
1201
 
            expected_deprecations, repo.get_parent_map, [rev_id])
 
1810
        parents = repo.get_parent_map([rev_id])
1202
1811
        self.assertEqual(
1203
1812
            [('call_with_body_bytes_expecting_body',
1204
 
              'Repository.get_parent_map', ('quack/', rev_id), '\n\n0'),
 
1813
              'Repository.get_parent_map', ('quack/', 'include-missing:',
 
1814
              rev_id), '\n\n0'),
1205
1815
             ('disconnect medium',),
1206
1816
             ('call_expecting_body', 'Repository.get_revision_graph',
1207
1817
              ('quack/', ''))],
1208
1818
            client._calls)
1209
1819
        # The medium is now marked as being connected to an older server
1210
1820
        self.assertTrue(client._medium._is_remote_before((1, 2)))
 
1821
        self.assertEqual({rev_id: ('null:',)}, parents)
1211
1822
 
1212
1823
    def test_get_parent_map_fallback_parentless_node(self):
1213
1824
        """get_parent_map falls back to get_revision_graph on old servers.  The
1225
1836
        repo, client = self.setup_fake_client_and_repository(transport_path)
1226
1837
        client.add_success_response_with_body(rev_id, 'ok')
1227
1838
        client._medium._remember_remote_is_before((1, 2))
1228
 
        expected_deprecations = [
1229
 
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1230
 
            'in version 1.4.']
1231
 
        parents = self.callDeprecated(
1232
 
            expected_deprecations, repo.get_parent_map, [rev_id])
 
1839
        parents = repo.get_parent_map([rev_id])
1233
1840
        self.assertEqual(
1234
1841
            [('call_expecting_body', 'Repository.get_revision_graph',
1235
1842
             ('quack/', ''))],
1243
1850
            errors.UnexpectedSmartServerResponse,
1244
1851
            repo.get_parent_map, ['a-revision-id'])
1245
1852
 
 
1853
    def test_get_parent_map_negative_caches_missing_keys(self):
 
1854
        self.setup_smart_server_with_call_log()
 
1855
        repo = self.make_repository('foo')
 
1856
        self.assertIsInstance(repo, RemoteRepository)
 
1857
        repo.lock_read()
 
1858
        self.addCleanup(repo.unlock)
 
1859
        self.reset_smart_call_log()
 
1860
        graph = repo.get_graph()
 
1861
        self.assertEqual({},
 
1862
            graph.get_parent_map(['some-missing', 'other-missing']))
 
1863
        self.assertLength(1, self.hpss_calls)
 
1864
        # No call if we repeat this
 
1865
        self.reset_smart_call_log()
 
1866
        graph = repo.get_graph()
 
1867
        self.assertEqual({},
 
1868
            graph.get_parent_map(['some-missing', 'other-missing']))
 
1869
        self.assertLength(0, self.hpss_calls)
 
1870
        # Asking for more unknown keys makes a request.
 
1871
        self.reset_smart_call_log()
 
1872
        graph = repo.get_graph()
 
1873
        self.assertEqual({},
 
1874
            graph.get_parent_map(['some-missing', 'other-missing',
 
1875
                'more-missing']))
 
1876
        self.assertLength(1, self.hpss_calls)
 
1877
 
 
1878
    def disableExtraResults(self):
 
1879
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
 
1880
        SmartServerRepositoryGetParentMap.no_extra_results = True
 
1881
        def reset_values():
 
1882
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
 
1883
        self.addCleanup(reset_values)
 
1884
 
 
1885
    def test_null_cached_missing_and_stop_key(self):
 
1886
        self.setup_smart_server_with_call_log()
 
1887
        # Make a branch with a single revision.
 
1888
        builder = self.make_branch_builder('foo')
 
1889
        builder.start_series()
 
1890
        builder.build_snapshot('first', None, [
 
1891
            ('add', ('', 'root-id', 'directory', ''))])
 
1892
        builder.finish_series()
 
1893
        branch = builder.get_branch()
 
1894
        repo = branch.repository
 
1895
        self.assertIsInstance(repo, RemoteRepository)
 
1896
        # Stop the server from sending extra results.
 
1897
        self.disableExtraResults()
 
1898
        repo.lock_read()
 
1899
        self.addCleanup(repo.unlock)
 
1900
        self.reset_smart_call_log()
 
1901
        graph = repo.get_graph()
 
1902
        # Query for 'first' and 'null:'.  Because 'null:' is a parent of
 
1903
        # 'first' it will be a candidate for the stop_keys of subsequent
 
1904
        # requests, and because 'null:' was queried but not returned it will be
 
1905
        # cached as missing.
 
1906
        self.assertEqual({'first': ('null:',)},
 
1907
            graph.get_parent_map(['first', 'null:']))
 
1908
        # Now query for another key.  This request will pass along a recipe of
 
1909
        # start and stop keys describing the already cached results, and this
 
1910
        # recipe's revision count must be correct (or else it will trigger an
 
1911
        # error from the server).
 
1912
        self.assertEqual({}, graph.get_parent_map(['another-key']))
 
1913
        # This assertion guards against disableExtraResults silently failing to
 
1914
        # work, thus invalidating the test.
 
1915
        self.assertLength(2, self.hpss_calls)
 
1916
 
 
1917
    def test_get_parent_map_gets_ghosts_from_result(self):
 
1918
        # asking for a revision should negatively cache close ghosts in its
 
1919
        # ancestry.
 
1920
        self.setup_smart_server_with_call_log()
 
1921
        tree = self.make_branch_and_memory_tree('foo')
 
1922
        tree.lock_write()
 
1923
        try:
 
1924
            builder = treebuilder.TreeBuilder()
 
1925
            builder.start_tree(tree)
 
1926
            builder.build([])
 
1927
            builder.finish_tree()
 
1928
            tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
 
1929
            rev_id = tree.commit('')
 
1930
        finally:
 
1931
            tree.unlock()
 
1932
        tree.lock_read()
 
1933
        self.addCleanup(tree.unlock)
 
1934
        repo = tree.branch.repository
 
1935
        self.assertIsInstance(repo, RemoteRepository)
 
1936
        # ask for rev_id
 
1937
        repo.get_parent_map([rev_id])
 
1938
        self.reset_smart_call_log()
 
1939
        # Now asking for rev_id's ghost parent should not make calls
 
1940
        self.assertEqual({}, repo.get_parent_map(['non-existant']))
 
1941
        self.assertLength(0, self.hpss_calls)
 
1942
 
1246
1943
 
1247
1944
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1248
1945
 
1265
1962
 
1266
1963
 
1267
1964
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
1268
 
    
 
1965
 
1269
1966
    def test_null_revision(self):
1270
1967
        # a null revision has the predictable result {}, we should have no wire
1271
1968
        # traffic when calling it with this argument
1272
1969
        transport_path = 'empty'
1273
1970
        repo, client = self.setup_fake_client_and_repository(transport_path)
1274
1971
        client.add_success_response('notused')
1275
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph,
1276
 
            NULL_REVISION)
 
1972
        # actual RemoteRepository.get_revision_graph is gone, but there's an
 
1973
        # equivalent private method for testing
 
1974
        result = repo._get_revision_graph(NULL_REVISION)
1277
1975
        self.assertEqual([], client._calls)
1278
1976
        self.assertEqual({}, result)
1279
1977
 
1287
1985
        transport_path = 'sinhala'
1288
1986
        repo, client = self.setup_fake_client_and_repository(transport_path)
1289
1987
        client.add_success_response_with_body(encoded_body, 'ok')
1290
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph)
 
1988
        # actual RemoteRepository.get_revision_graph is gone, but there's an
 
1989
        # equivalent private method for testing
 
1990
        result = repo._get_revision_graph(None)
1291
1991
        self.assertEqual(
1292
1992
            [('call_expecting_body', 'Repository.get_revision_graph',
1293
1993
             ('sinhala/', ''))],
1306
2006
        transport_path = 'sinhala'
1307
2007
        repo, client = self.setup_fake_client_and_repository(transport_path)
1308
2008
        client.add_success_response_with_body(encoded_body, 'ok')
1309
 
        result = self.applyDeprecated(one_four, repo.get_revision_graph, r2)
 
2009
        result = repo._get_revision_graph(r2)
1310
2010
        self.assertEqual(
1311
2011
            [('call_expecting_body', 'Repository.get_revision_graph',
1312
2012
             ('sinhala/', r2))],
1320
2020
        client.add_error_response('nosuchrevision', revid)
1321
2021
        # also check that the right revision is reported in the error
1322
2022
        self.assertRaises(errors.NoSuchRevision,
1323
 
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
 
2023
            repo._get_revision_graph, revid)
1324
2024
        self.assertEqual(
1325
2025
            [('call_expecting_body', 'Repository.get_revision_graph',
1326
2026
             ('sinhala/', revid))],
1332
2032
        repo, client = self.setup_fake_client_and_repository(transport_path)
1333
2033
        client.add_error_response('AnUnexpectedError')
1334
2034
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1335
 
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
 
2035
            repo._get_revision_graph, revid)
1336
2036
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
1337
2037
 
1338
 
        
 
2038
 
 
2039
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
 
2040
 
 
2041
    def test_ok(self):
 
2042
        repo, client = self.setup_fake_client_and_repository('quack')
 
2043
        client.add_expected_call(
 
2044
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
 
2045
            'success', ('ok', 'rev-five'))
 
2046
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
 
2047
        self.assertEqual((True, 'rev-five'), result)
 
2048
        client.finished_test()
 
2049
 
 
2050
    def test_history_incomplete(self):
 
2051
        repo, client = self.setup_fake_client_and_repository('quack')
 
2052
        client.add_expected_call(
 
2053
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
 
2054
            'success', ('history-incomplete', 10, 'rev-ten'))
 
2055
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
 
2056
        self.assertEqual((False, (10, 'rev-ten')), result)
 
2057
        client.finished_test()
 
2058
 
 
2059
    def test_history_incomplete_with_fallback(self):
 
2060
        """A 'history-incomplete' response causes the fallback repository to be
 
2061
        queried too, if one is set.
 
2062
        """
 
2063
        # Make a repo with a fallback repo, both using a FakeClient.
 
2064
        format = remote.response_tuple_to_repo_format(
 
2065
            ('yes', 'no', 'yes', 'fake-network-name'))
 
2066
        repo, client = self.setup_fake_client_and_repository('quack')
 
2067
        repo._format = format
 
2068
        fallback_repo, ignored = self.setup_fake_client_and_repository(
 
2069
            'fallback')
 
2070
        fallback_repo._client = client
 
2071
        repo.add_fallback_repository(fallback_repo)
 
2072
        # First the client should ask the primary repo
 
2073
        client.add_expected_call(
 
2074
            'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
 
2075
            'success', ('history-incomplete', 2, 'rev-two'))
 
2076
        # Then it should ask the fallback, using revno/revid from the
 
2077
        # history-incomplete response as the known revno/revid.
 
2078
        client.add_expected_call(
 
2079
            'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
 
2080
            'success', ('ok', 'rev-one'))
 
2081
        result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
 
2082
        self.assertEqual((True, 'rev-one'), result)
 
2083
        client.finished_test()
 
2084
 
 
2085
    def test_nosuchrevision(self):
 
2086
        # 'nosuchrevision' is returned when the known-revid is not found in the
 
2087
        # remote repo.  The client translates that response to NoSuchRevision.
 
2088
        repo, client = self.setup_fake_client_and_repository('quack')
 
2089
        client.add_expected_call(
 
2090
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
 
2091
            'error', ('nosuchrevision', 'rev-foo'))
 
2092
        self.assertRaises(
 
2093
            errors.NoSuchRevision,
 
2094
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
 
2095
        client.finished_test()
 
2096
 
 
2097
 
1339
2098
class TestRepositoryIsShared(TestRemoteRepository):
1340
2099
 
1341
2100
    def test_is_shared(self):
1392
2151
            client._calls)
1393
2152
 
1394
2153
 
 
2154
class TestRepositorySetMakeWorkingTrees(TestRemoteRepository):
 
2155
 
 
2156
    def test_backwards_compat(self):
 
2157
        self.setup_smart_server_with_call_log()
 
2158
        repo = self.make_repository('.')
 
2159
        self.reset_smart_call_log()
 
2160
        verb = 'Repository.set_make_working_trees'
 
2161
        self.disable_verb(verb)
 
2162
        repo.set_make_working_trees(True)
 
2163
        call_count = len([call for call in self.hpss_calls if
 
2164
            call.call.method == verb])
 
2165
        self.assertEqual(1, call_count)
 
2166
 
 
2167
    def test_current(self):
 
2168
        transport_path = 'quack'
 
2169
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2170
        client.add_expected_call(
 
2171
            'Repository.set_make_working_trees', ('quack/', 'True'),
 
2172
            'success', ('ok',))
 
2173
        client.add_expected_call(
 
2174
            'Repository.set_make_working_trees', ('quack/', 'False'),
 
2175
            'success', ('ok',))
 
2176
        repo.set_make_working_trees(True)
 
2177
        repo.set_make_working_trees(False)
 
2178
 
 
2179
 
1395
2180
class TestRepositoryUnlock(TestRemoteRepository):
1396
2181
 
1397
2182
    def test_unlock(self):
1430
2215
        self.assertEqual([], client._calls)
1431
2216
 
1432
2217
 
 
2218
class TestRepositoryInsertStream(TestRemoteRepository):
 
2219
 
 
2220
    def test_unlocked_repo(self):
 
2221
        transport_path = 'quack'
 
2222
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2223
        client.add_expected_call(
 
2224
            'Repository.insert_stream', ('quack/', ''),
 
2225
            'success', ('ok',))
 
2226
        client.add_expected_call(
 
2227
            'Repository.insert_stream', ('quack/', ''),
 
2228
            'success', ('ok',))
 
2229
        sink = repo._get_sink()
 
2230
        fmt = repository.RepositoryFormat.get_default_format()
 
2231
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2232
        self.assertEqual([], resume_tokens)
 
2233
        self.assertEqual(set(), missing_keys)
 
2234
        client.finished_test()
 
2235
 
 
2236
    def test_locked_repo_with_no_lock_token(self):
 
2237
        transport_path = 'quack'
 
2238
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2239
        client.add_expected_call(
 
2240
            'Repository.lock_write', ('quack/', ''),
 
2241
            'success', ('ok', ''))
 
2242
        client.add_expected_call(
 
2243
            'Repository.insert_stream', ('quack/', ''),
 
2244
            'success', ('ok',))
 
2245
        client.add_expected_call(
 
2246
            'Repository.insert_stream', ('quack/', ''),
 
2247
            'success', ('ok',))
 
2248
        repo.lock_write()
 
2249
        sink = repo._get_sink()
 
2250
        fmt = repository.RepositoryFormat.get_default_format()
 
2251
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2252
        self.assertEqual([], resume_tokens)
 
2253
        self.assertEqual(set(), missing_keys)
 
2254
        client.finished_test()
 
2255
 
 
2256
    def test_locked_repo_with_lock_token(self):
 
2257
        transport_path = 'quack'
 
2258
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2259
        client.add_expected_call(
 
2260
            'Repository.lock_write', ('quack/', ''),
 
2261
            'success', ('ok', 'a token'))
 
2262
        client.add_expected_call(
 
2263
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2264
            'success', ('ok',))
 
2265
        client.add_expected_call(
 
2266
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2267
            'success', ('ok',))
 
2268
        repo.lock_write()
 
2269
        sink = repo._get_sink()
 
2270
        fmt = repository.RepositoryFormat.get_default_format()
 
2271
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2272
        self.assertEqual([], resume_tokens)
 
2273
        self.assertEqual(set(), missing_keys)
 
2274
        client.finished_test()
 
2275
 
 
2276
 
1433
2277
class TestRepositoryTarball(TestRemoteRepository):
1434
2278
 
1435
2279
    # This is a canned tarball reponse we can validate against
1487
2331
class _StubRealPackRepository(object):
1488
2332
 
1489
2333
    def __init__(self, calls):
 
2334
        self.calls = calls
1490
2335
        self._pack_collection = _StubPackCollection(calls)
1491
2336
 
 
2337
    def is_in_write_group(self):
 
2338
        return False
 
2339
 
 
2340
    def refresh_data(self):
 
2341
        self.calls.append(('pack collection reload_pack_names',))
 
2342
 
1492
2343
 
1493
2344
class _StubPackCollection(object):
1494
2345
 
1498
2349
    def autopack(self):
1499
2350
        self.calls.append(('pack collection autopack',))
1500
2351
 
1501
 
    def reload_pack_names(self):
1502
 
        self.calls.append(('pack collection reload_pack_names',))
1503
2352
 
1504
 
    
1505
2353
class TestRemotePackRepositoryAutoPack(TestRemoteRepository):
1506
2354
    """Tests for RemoteRepository.autopack implementation."""
1507
2355
 
1531
2379
            [('call', 'PackRepository.autopack', ('quack/',)),
1532
2380
             ('pack collection reload_pack_names',)],
1533
2381
            client._calls)
1534
 
        
 
2382
 
1535
2383
    def test_backwards_compatibility(self):
1536
2384
        """If the server does not recognise the PackRepository.autopack verb,
1537
2385
        fallback to the real_repository's implementation.
1587
2435
 
1588
2436
class TestErrorTranslationSuccess(TestErrorTranslationBase):
1589
2437
    """Unit tests for bzrlib.remote._translate_error.
1590
 
    
 
2438
 
1591
2439
    Given an ErrorFromSmartServer (which has an error tuple from a smart
1592
2440
    server) and some context, _translate_error raises more specific errors from
1593
2441
    bzrlib.errors.
1698
2546
 
1699
2547
class TestErrorTranslationRobustness(TestErrorTranslationBase):
1700
2548
    """Unit tests for bzrlib.remote._translate_error's robustness.
1701
 
    
 
2549
 
1702
2550
    TestErrorTranslationSuccess is for cases where _translate_error can
1703
2551
    translate successfully.  This class about how _translate_err behaves when
1704
2552
    it fails to translate: it re-raises the original error.
1732
2580
        self.assertContainsRe(
1733
2581
            self._get_log(keep_log_file=True),
1734
2582
            "Missing key 'branch' in context")
1735
 
        
 
2583
 
1736
2584
    def test_path_missing(self):
1737
2585
        """Some translations (PermissionDenied, ReadError) can determine the
1738
2586
        'path' variable from either the wire or the local context.  If neither
1750
2598
 
1751
2599
class TestStacking(tests.TestCaseWithTransport):
1752
2600
    """Tests for operations on stacked remote repositories.
1753
 
    
 
2601
 
1754
2602
    The underlying format type must support stacking.
1755
2603
    """
1756
2604
 
1760
2608
        # revision, then open it over hpss - we should be able to see that
1761
2609
        # revision.
1762
2610
        base_transport = self.get_transport()
1763
 
        base_builder = self.make_branch_builder('base', format='1.6')
 
2611
        base_builder = self.make_branch_builder('base', format='1.9')
1764
2612
        base_builder.start_series()
1765
2613
        base_revid = base_builder.build_snapshot('rev-id', None,
1766
2614
            [('add', ('', None, 'directory', None))],
1767
2615
            'message')
1768
2616
        base_builder.finish_series()
1769
 
        stacked_branch = self.make_branch('stacked', format='1.6')
 
2617
        stacked_branch = self.make_branch('stacked', format='1.9')
1770
2618
        stacked_branch.set_stacked_on_url('../base')
1771
2619
        # start a server looking at this
1772
2620
        smart_server = server.SmartTCPServer_for_testing()
1780
2628
        try:
1781
2629
            # it should have an appropriate fallback repository, which should also
1782
2630
            # be a RemoteRepository
1783
 
            self.assertEquals(len(remote_repo._fallback_repositories), 1)
 
2631
            self.assertLength(1, remote_repo._fallback_repositories)
1784
2632
            self.assertIsInstance(remote_repo._fallback_repositories[0],
1785
2633
                RemoteRepository)
1786
2634
            # and it has the revision committed to the underlying repository;
1793
2641
            remote_repo.unlock()
1794
2642
 
1795
2643
    def prepare_stacked_remote_branch(self):
1796
 
        smart_server = server.SmartTCPServer_for_testing()
1797
 
        smart_server.setUp()
1798
 
        self.addCleanup(smart_server.tearDown)
1799
 
        tree1 = self.make_branch_and_tree('tree1')
 
2644
        """Get stacked_upon and stacked branches with content in each."""
 
2645
        self.setup_smart_server_with_call_log()
 
2646
        tree1 = self.make_branch_and_tree('tree1', format='1.9')
1800
2647
        tree1.commit('rev1', rev_id='rev1')
1801
 
        tree2 = self.make_branch_and_tree('tree2', format='1.6')
1802
 
        tree2.branch.set_stacked_on_url(tree1.branch.base)
1803
 
        branch2 = Branch.open(smart_server.get_url() + '/tree2')
 
2648
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
 
2649
            ).open_workingtree()
 
2650
        tree2.commit('local changes make me feel good.')
 
2651
        branch2 = Branch.open(self.get_url('tree2'))
1804
2652
        branch2.lock_read()
1805
2653
        self.addCleanup(branch2.unlock)
1806
 
        return branch2
 
2654
        return tree1.branch, branch2
1807
2655
 
1808
2656
    def test_stacked_get_parent_map(self):
1809
2657
        # the public implementation of get_parent_map obeys stacking
1810
 
        branch = self.prepare_stacked_remote_branch()
 
2658
        _, branch = self.prepare_stacked_remote_branch()
1811
2659
        repo = branch.repository
1812
2660
        self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1813
2661
 
1814
2662
    def test_unstacked_get_parent_map(self):
1815
2663
        # _unstacked_provider.get_parent_map ignores stacking
1816
 
        branch = self.prepare_stacked_remote_branch()
 
2664
        _, branch = self.prepare_stacked_remote_branch()
1817
2665
        provider = branch.repository._unstacked_provider
1818
2666
        self.assertEqual([], provider.get_parent_map(['rev1']).keys())
1819
2667
 
 
2668
    def fetch_stream_to_rev_order(self, stream):
 
2669
        result = []
 
2670
        for kind, substream in stream:
 
2671
            if not kind == 'revisions':
 
2672
                list(substream)
 
2673
            else:
 
2674
                for content in substream:
 
2675
                    result.append(content.key[-1])
 
2676
        return result
 
2677
 
 
2678
    def get_ordered_revs(self, format, order):
 
2679
        """Get a list of the revisions in a stream to format format.
 
2680
 
 
2681
        :param format: The format of the target.
 
2682
        :param order: the order that target should have requested.
 
2683
        :result: The revision ids in the stream, in the order seen,
 
2684
            the topological order of revisions in the source.
 
2685
        """
 
2686
        unordered_format = bzrdir.format_registry.get(format)()
 
2687
        target_repository_format = unordered_format.repository_format
 
2688
        # Cross check
 
2689
        self.assertEqual(order, target_repository_format._fetch_order)
 
2690
        trunk, stacked = self.prepare_stacked_remote_branch()
 
2691
        source = stacked.repository._get_source(target_repository_format)
 
2692
        tip = stacked.last_revision()
 
2693
        revs = stacked.repository.get_ancestry(tip)
 
2694
        search = graph.PendingAncestryResult([tip], stacked.repository)
 
2695
        self.reset_smart_call_log()
 
2696
        stream = source.get_stream(search)
 
2697
        if None in revs:
 
2698
            revs.remove(None)
 
2699
        # We trust that if a revision is in the stream the rest of the new
 
2700
        # content for it is too, as per our main fetch tests; here we are
 
2701
        # checking that the revisions are actually included at all, and their
 
2702
        # order.
 
2703
        return self.fetch_stream_to_rev_order(stream), revs
 
2704
 
 
2705
    def test_stacked_get_stream_unordered(self):
 
2706
        # Repository._get_source.get_stream() from a stacked repository with
 
2707
        # unordered yields the full data from both stacked and stacked upon
 
2708
        # sources.
 
2709
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
 
2710
        self.assertEqual(set(expected_revs), set(rev_ord))
 
2711
        # Getting unordered results should have made a streaming data request
 
2712
        # from the server, then one from the backing branch.
 
2713
        self.assertLength(2, self.hpss_calls)
 
2714
 
 
2715
    def test_stacked_get_stream_topological(self):
 
2716
        # Repository._get_source.get_stream() from a stacked repository with
 
2717
        # topological sorting yields the full data from both stacked and
 
2718
        # stacked upon sources in topological order.
 
2719
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
 
2720
        self.assertEqual(expected_revs, rev_ord)
 
2721
        # Getting topological sort requires VFS calls still
 
2722
        self.assertLength(12, self.hpss_calls)
 
2723
 
 
2724
    def test_stacked_get_stream_groupcompress(self):
 
2725
        # Repository._get_source.get_stream() from a stacked repository with
 
2726
        # groupcompress sorting yields the full data from both stacked and
 
2727
        # stacked upon sources in groupcompress order.
 
2728
        raise tests.TestSkipped('No groupcompress ordered format available')
 
2729
        rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
 
2730
        self.assertEqual(expected_revs, reversed(rev_ord))
 
2731
        # Getting unordered results should have made a streaming data request
 
2732
        # from the backing branch, and one from the stacked on branch.
 
2733
        self.assertLength(2, self.hpss_calls)
 
2734
 
 
2735
    def test_stacked_pull_more_than_stacking_has_bug_360791(self):
 
2736
        # When pulling some fixed amount of content that is more than the
 
2737
        # source has (because some is coming from a fallback branch, no error
 
2738
        # should be received. This was reported as bug 360791.
 
2739
        # Need three branches: a trunk, a stacked branch, and a preexisting
 
2740
        # branch pulling content from stacked and trunk.
 
2741
        self.setup_smart_server_with_call_log()
 
2742
        trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
 
2743
        r1 = trunk.commit('start')
 
2744
        stacked_branch = trunk.branch.create_clone_on_transport(
 
2745
            self.get_transport('stacked'), stacked_on=trunk.branch.base)
 
2746
        local = self.make_branch('local', format='1.9-rich-root')
 
2747
        local.repository.fetch(stacked_branch.repository,
 
2748
            stacked_branch.last_revision())
 
2749
 
1820
2750
 
1821
2751
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
1822
2752