/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/tests/test_remote.py

  • Committer: Jelmer Vernooij
  • Date: 2020-01-19 15:14:16 UTC
  • mto: This revision was merged to the branch mainline in revision 7455.
  • Revision ID: jelmer@jelmer.uk-20200119151416-f2x9y9rtvwxndr2l
Don't show submodules that are not checked out as deltas.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2013, 2016 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
23
23
These tests correspond to tests.test_smart, which exercises the server side.
24
24
"""
25
25
 
 
26
import base64
26
27
import bz2
27
 
from cStringIO import StringIO
 
28
import tarfile
 
29
import zlib
28
30
 
29
 
from bzrlib import (
 
31
from .. import (
 
32
    bencode,
30
33
    branch,
31
 
    bzrdir,
32
34
    config,
 
35
    controldir,
33
36
    errors,
34
 
    graph,
 
37
    repository,
 
38
    tests,
 
39
    transport,
 
40
    treebuilder,
 
41
    )
 
42
from ..branch import Branch
 
43
from ..bzr import (
 
44
    bzrdir,
35
45
    inventory,
36
46
    inventory_delta,
37
 
    pack,
38
47
    remote,
39
 
    repository,
40
 
    tests,
41
 
    treebuilder,
42
 
    urlutils,
43
48
    versionedfile,
44
 
    )
45
 
from bzrlib.branch import Branch
46
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
47
 
from bzrlib.remote import (
 
49
    vf_search,
 
50
    )
 
51
from ..bzr.bzrdir import (
 
52
    BzrDir,
 
53
    BzrDirFormat,
 
54
    )
 
55
from ..bzr import (
 
56
    RemoteBzrProber,
 
57
    )
 
58
from ..bzr.chk_serializer import chk_bencode_serializer
 
59
from ..bzr.remote import (
48
60
    RemoteBranch,
49
61
    RemoteBranchFormat,
50
62
    RemoteBzrDir,
52
64
    RemoteRepository,
53
65
    RemoteRepositoryFormat,
54
66
    )
55
 
from bzrlib.repofmt import groupcompress_repo, pack_repo
56
 
from bzrlib.revision import NULL_REVISION
57
 
from bzrlib.smart import medium
58
 
from bzrlib.smart.client import _SmartClient
59
 
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
60
 
from bzrlib.tests import (
61
 
    condition_isinstance,
62
 
    split_suite_by_condition,
63
 
    multiply_tests,
 
67
from ..bzr import groupcompress_repo, knitpack_repo
 
68
from ..revision import (
 
69
    NULL_REVISION,
 
70
    Revision,
 
71
    )
 
72
from ..sixish import (
 
73
    BytesIO,
 
74
    PY3,
 
75
    text_type,
 
76
    )
 
77
from ..bzr.smart import medium, request
 
78
from ..bzr.smart.client import _SmartClient
 
79
from ..bzr.smart.repository import (
 
80
    SmartServerRepositoryGetParentMap,
 
81
    SmartServerRepositoryGetStream_1_19,
 
82
    _stream_to_byte_stream,
 
83
    )
 
84
from . import (
64
85
    test_server,
65
86
    )
66
 
from bzrlib.transport import get_transport
67
 
from bzrlib.transport.memory import MemoryTransport
68
 
from bzrlib.transport.remote import (
 
87
from .scenarios import load_tests_apply_scenarios
 
88
from ..transport.memory import MemoryTransport
 
89
from ..transport.remote import (
69
90
    RemoteTransport,
70
91
    RemoteSSHTransport,
71
92
    RemoteTCPTransport,
72
 
)
73
 
 
74
 
def load_tests(standard_tests, module, loader):
75
 
    to_adapt, result = split_suite_by_condition(
76
 
        standard_tests, condition_isinstance(BasicRemoteObjectTests))
77
 
    smart_server_version_scenarios = [
 
93
    )
 
94
 
 
95
 
 
96
load_tests = load_tests_apply_scenarios
 
97
 
 
98
 
 
99
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
100
 
 
101
    scenarios = [
78
102
        ('HPSS-v2',
79
 
         {'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
 
103
            {'transport_server':
 
104
                test_server.SmartTCPServer_for_testing_v2_only}),
80
105
        ('HPSS-v3',
81
 
         {'transport_server': test_server.SmartTCPServer_for_testing})]
82
 
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
83
 
 
84
 
 
85
 
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
106
            {'transport_server': test_server.SmartTCPServer_for_testing})]
86
107
 
87
108
    def setUp(self):
88
109
        super(BasicRemoteObjectTests, self).setUp()
89
110
        self.transport = self.get_transport()
90
111
        # make a branch that can be opened over the smart transport
91
112
        self.local_wt = BzrDir.create_standalone_workingtree('.')
92
 
 
93
 
    def tearDown(self):
94
 
        self.transport.disconnect()
95
 
        tests.TestCaseWithTransport.tearDown(self)
 
113
        self.addCleanup(self.transport.disconnect)
96
114
 
97
115
    def test_create_remote_bzrdir(self):
98
 
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
 
116
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
99
117
        self.assertIsInstance(b, BzrDir)
100
118
 
101
119
    def test_open_remote_branch(self):
102
120
        # open a standalone branch in the working directory
103
 
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
 
121
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
104
122
        branch = b.open_branch()
105
123
        self.assertIsInstance(branch, Branch)
106
124
 
112
130
        self.local_wt.commit(message='test commit', rev_id=revid)
113
131
        self.assertTrue(repo.has_revision(revid))
114
132
 
115
 
    def test_remote_branch_revision_history(self):
116
 
        b = BzrDir.open_from_transport(self.transport).open_branch()
117
 
        self.assertEqual([], b.revision_history())
118
 
        r1 = self.local_wt.commit('1st commit')
119
 
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
120
 
        self.assertEqual([r1, r2], b.revision_history())
121
 
 
122
133
    def test_find_correct_format(self):
123
134
        """Should open a RemoteBzrDir over a RemoteTransport"""
124
135
        fmt = BzrDirFormat.find_format(self.transport)
125
 
        self.assertTrue(RemoteBzrDirFormat
126
 
                        in BzrDirFormat._control_server_formats)
127
 
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
 
136
        self.assertIn(RemoteBzrProber, controldir.ControlDirFormat._probers)
 
137
        self.assertIsInstance(fmt, RemoteBzrDirFormat)
128
138
 
129
139
    def test_open_detected_smart_format(self):
130
140
        fmt = BzrDirFormat.find_format(self.transport)
150
160
 
151
161
    def test_remote_repo_format_supports_external_references(self):
152
162
        t = self.transport
153
 
        bd = self.make_bzrdir('unstackable', format='pack-0.92')
 
163
        bd = self.make_controldir('unstackable', format='pack-0.92')
154
164
        r = bd.create_repository()
155
165
        self.assertFalse(r._format.supports_external_lookups)
156
 
        r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
 
166
        r = BzrDir.open_from_transport(
 
167
            t.clone('unstackable')).open_repository()
157
168
        self.assertFalse(r._format.supports_external_lookups)
158
 
        bd = self.make_bzrdir('stackable', format='1.9')
 
169
        bd = self.make_controldir('stackable', format='1.9')
159
170
        r = bd.create_repository()
160
171
        self.assertTrue(r._format.supports_external_lookups)
161
172
        r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
164
175
    def test_remote_branch_set_append_revisions_only(self):
165
176
        # Make a format 1.9 branch, which supports append_revisions_only
166
177
        branch = self.make_branch('branch', format='1.9')
167
 
        config = branch.get_config()
168
178
        branch.set_append_revisions_only(True)
 
179
        config = branch.get_config_stack()
169
180
        self.assertEqual(
170
 
            'True', config.get_user_option('append_revisions_only'))
 
181
            True, config.get('append_revisions_only'))
171
182
        branch.set_append_revisions_only(False)
 
183
        config = branch.get_config_stack()
172
184
        self.assertEqual(
173
 
            'False', config.get_user_option('append_revisions_only'))
 
185
            False, config.get('append_revisions_only'))
174
186
 
175
187
    def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
176
188
        branch = self.make_branch('branch', format='knit')
177
 
        config = branch.get_config()
178
189
        self.assertRaises(
179
190
            errors.UpgradeRequired, branch.set_append_revisions_only, True)
180
191
 
189
200
 
190
201
    def read_body_bytes(self, count=-1):
191
202
        if self._body_buffer is None:
192
 
            self._body_buffer = StringIO(self.body)
 
203
            self._body_buffer = BytesIO(self.body)
193
204
        bytes = self._body_buffer.read(count)
194
205
        if self._body_buffer.tell() == len(self._body_buffer.getvalue()):
195
206
            self._fake_client.expecting_body = False
218
229
        _SmartClient.__init__(self, FakeMedium(self._calls, fake_medium_base))
219
230
 
220
231
    def add_expected_call(self, call_name, call_args, response_type,
221
 
        response_args, response_body=None):
 
232
                          response_args, response_body=None):
222
233
        if self._expected_calls is None:
223
234
            self._expected_calls = []
224
235
        self._expected_calls.append((call_name, call_args))
225
236
        self.responses.append((response_type, response_args, response_body))
226
237
 
227
238
    def add_success_response(self, *args):
228
 
        self.responses.append(('success', args, None))
 
239
        self.responses.append((b'success', args, None))
229
240
 
230
241
    def add_success_response_with_body(self, body, *args):
231
 
        self.responses.append(('success', args, body))
 
242
        self.responses.append((b'success', args, body))
232
243
        if self._expected_calls is not None:
233
244
            self._expected_calls.append(None)
234
245
 
235
246
    def add_error_response(self, *args):
236
 
        self.responses.append(('error', args))
 
247
        self.responses.append((b'error', args))
237
248
 
238
249
    def add_unknown_method_response(self, verb):
239
 
        self.responses.append(('unknown', verb))
 
250
        self.responses.append((b'unknown', verb))
240
251
 
241
252
    def finished_test(self):
242
253
        if self._expected_calls:
243
254
            raise AssertionError("%r finished but was still expecting %r"
244
 
                % (self, self._expected_calls[0]))
 
255
                                 % (self, self._expected_calls[0]))
245
256
 
246
257
    def _get_next_response(self):
247
258
        try:
248
259
            response_tuple = self.responses.pop(0)
249
 
        except IndexError, e:
250
 
            raise AssertionError("%r didn't expect any more calls"
251
 
                % (self,))
252
 
        if response_tuple[0] == 'unknown':
 
260
        except IndexError:
 
261
            raise AssertionError("%r didn't expect any more calls" % (self,))
 
262
        if response_tuple[0] == b'unknown':
253
263
            raise errors.UnknownSmartMethod(response_tuple[1])
254
 
        elif response_tuple[0] == 'error':
 
264
        elif response_tuple[0] == b'error':
255
265
            raise errors.ErrorFromSmartServer(response_tuple[1])
256
266
        return response_tuple
257
267
 
263
273
            next_call = self._expected_calls.pop(0)
264
274
        except IndexError:
265
275
            raise AssertionError("%r didn't expect any more calls "
266
 
                "but got %r%r"
267
 
                % (self, method, args,))
 
276
                                 "but got %r%r"
 
277
                                 % (self, method, args,))
268
278
        if next_call is None:
269
279
            return
270
280
        if method != next_call[0] or args != next_call[1]:
271
 
            raise AssertionError("%r expected %r%r "
272
 
                "but got %r%r"
273
 
                % (self, next_call[0], next_call[1], method, args,))
 
281
            raise AssertionError(
 
282
                "%r expected %r%r but got %r%r" %
 
283
                (self, next_call[0], next_call[1], method, args,))
274
284
 
275
285
    def call(self, method, *args):
276
286
        self._check_call(method, args)
293
303
    def call_with_body_bytes_expecting_body(self, method, args, body):
294
304
        self._check_call(method, args)
295
305
        self._calls.append(('call_with_body_bytes_expecting_body', method,
296
 
            args, body))
 
306
                            args, body))
297
307
        result = self._get_next_response()
298
308
        self.expecting_body = True
299
309
        return result[1], FakeProtocol(result[2], self)
303
313
        # that's what happens a real medium.
304
314
        stream = list(stream)
305
315
        self._check_call(args[0], args[1:])
306
 
        self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
 
316
        self._calls.append(
 
317
            ('call_with_body_stream', args[0], args[1:], stream))
307
318
        result = self._get_next_response()
308
319
        # The second value returned from call_with_body_stream is supposed to
309
320
        # be a response_handler object, but so far no tests depend on that.
310
 
        response_handler = None 
 
321
        response_handler = None
311
322
        return result[1], response_handler
312
323
 
313
324
 
325
336
 
326
337
    def test_unicode_path(self):
327
338
        client = FakeClient('/')
328
 
        client.add_success_response('yes',)
 
339
        client.add_success_response(b'yes',)
329
340
        transport = RemoteTransport('bzr://localhost/', _client=client)
330
 
        filename = u'/hell\u00d8'.encode('utf8')
331
 
        result = transport.has(filename)
 
341
        filename = u'/hell\u00d8'
 
342
        if PY3:
 
343
            result = transport.has(filename)
 
344
        else:
 
345
            result = transport.has(filename.encode('utf-8'))
332
346
        self.assertEqual(
333
 
            [('call', 'has', (filename,))],
 
347
            [('call', b'has', (filename.encode('utf-8'),))],
334
348
            client._calls)
335
349
        self.assertTrue(result)
336
350
 
338
352
class TestRemote(tests.TestCaseWithMemoryTransport):
339
353
 
340
354
    def get_branch_format(self):
341
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
355
        reference_bzrdir_format = controldir.format_registry.get('default')()
342
356
        return reference_bzrdir_format.get_branch_format()
343
357
 
344
358
    def get_repo_format(self):
345
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
359
        reference_bzrdir_format = controldir.format_registry.get('default')()
346
360
        return reference_bzrdir_format.repository_format
347
361
 
348
362
    def assertFinished(self, fake_client):
359
373
        a given client_base and transport_base.
360
374
        """
361
375
        client_medium = medium.SmartClientMedium(client_base)
362
 
        transport = get_transport(transport_base)
363
 
        result = client_medium.remote_path_from_transport(transport)
 
376
        t = transport.get_transport(transport_base)
 
377
        result = client_medium.remote_path_from_transport(t)
364
378
        self.assertEqual(expected, result)
365
379
 
366
380
    def test_remote_path_from_transport(self):
377
391
        a given transport_base and relpath of that transport.  (Note that
378
392
        HttpTransportBase is a subclass of SmartClientMedium)
379
393
        """
380
 
        base_transport = get_transport(transport_base)
 
394
        base_transport = transport.get_transport(transport_base)
381
395
        client_medium = base_transport.get_smart_medium()
382
396
        cloned_transport = base_transport.clone(relpath)
383
397
        result = client_medium.remote_path_from_transport(cloned_transport)
430
444
 
431
445
    def test_backwards_compat(self):
432
446
        self.setup_smart_server_with_call_log()
433
 
        a_dir = self.make_bzrdir('.')
 
447
        a_dir = self.make_controldir('.')
434
448
        self.reset_smart_call_log()
435
 
        verb = 'BzrDir.cloning_metadir'
 
449
        verb = b'BzrDir.cloning_metadir'
436
450
        self.disable_verb(verb)
437
 
        format = a_dir.cloning_metadir()
 
451
        a_dir.cloning_metadir()
438
452
        call_count = len([call for call in self.hpss_calls if
439
 
            call.call.method == verb])
 
453
                          call.call.method == verb])
440
454
        self.assertEqual(1, call_count)
441
455
 
442
456
    def test_branch_reference(self):
443
457
        transport = self.get_transport('quack')
444
458
        referenced = self.make_branch('referenced')
445
 
        expected = referenced.bzrdir.cloning_metadir()
 
459
        expected = referenced.controldir.cloning_metadir()
446
460
        client = FakeClient(transport.base)
447
461
        client.add_expected_call(
448
 
            'BzrDir.cloning_metadir', ('quack/', 'False'),
449
 
            'error', ('BranchReference',)),
 
462
            b'BzrDir.cloning_metadir', (b'quack/', b'False'),
 
463
            b'error', (b'BranchReference',)),
450
464
        client.add_expected_call(
451
 
            'BzrDir.open_branchV3', ('quack/',),
452
 
            'success', ('ref', self.get_url('referenced'))),
453
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
454
 
            _client=client)
455
 
        result = a_bzrdir.cloning_metadir()
 
465
            b'BzrDir.open_branchV3', (b'quack/',),
 
466
            b'success', (b'ref', self.get_url('referenced').encode('utf-8'))),
 
467
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
468
                                    _client=client)
 
469
        result = a_controldir.cloning_metadir()
456
470
        # We should have got a control dir matching the referenced branch.
457
471
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
458
 
        self.assertEqual(expected._repository_format, result._repository_format)
 
472
        self.assertEqual(expected._repository_format,
 
473
                         result._repository_format)
459
474
        self.assertEqual(expected._branch_format, result._branch_format)
460
475
        self.assertFinished(client)
461
476
 
462
477
    def test_current_server(self):
463
478
        transport = self.get_transport('.')
464
479
        transport = transport.clone('quack')
465
 
        self.make_bzrdir('quack')
 
480
        self.make_controldir('quack')
466
481
        client = FakeClient(transport.base)
467
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
482
        reference_bzrdir_format = controldir.format_registry.get('default')()
468
483
        control_name = reference_bzrdir_format.network_name()
469
484
        client.add_expected_call(
470
 
            'BzrDir.cloning_metadir', ('quack/', 'False'),
471
 
            'success', (control_name, '', ('branch', ''))),
472
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
473
 
            _client=client)
474
 
        result = a_bzrdir.cloning_metadir()
 
485
            b'BzrDir.cloning_metadir', (b'quack/', b'False'),
 
486
            b'success', (control_name, b'', (b'branch', b''))),
 
487
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
488
                                    _client=client)
 
489
        result = a_controldir.cloning_metadir()
475
490
        # We should have got a reference control dir with default branch and
476
491
        # repository formats.
477
492
        # This pokes a little, just to be sure.
480
495
        self.assertEqual(None, result._branch_format)
481
496
        self.assertFinished(client)
482
497
 
 
498
    def test_unknown(self):
 
499
        transport = self.get_transport('quack')
 
500
        referenced = self.make_branch('referenced')
 
501
        referenced.controldir.cloning_metadir()
 
502
        client = FakeClient(transport.base)
 
503
        client.add_expected_call(
 
504
            b'BzrDir.cloning_metadir', (b'quack/', b'False'),
 
505
            b'success', (b'unknown', b'unknown', (b'branch', b''))),
 
506
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
507
                                    _client=client)
 
508
        self.assertRaises(errors.UnknownFormatError,
 
509
                          a_controldir.cloning_metadir)
 
510
 
 
511
 
 
512
class TestBzrDirCheckoutMetaDir(TestRemote):
 
513
 
 
514
    def test__get_checkout_format(self):
 
515
        transport = MemoryTransport()
 
516
        client = FakeClient(transport.base)
 
517
        reference_bzrdir_format = controldir.format_registry.get('default')()
 
518
        control_name = reference_bzrdir_format.network_name()
 
519
        client.add_expected_call(
 
520
            b'BzrDir.checkout_metadir', (b'quack/', ),
 
521
            b'success', (control_name, b'', b''))
 
522
        transport.mkdir('quack')
 
523
        transport = transport.clone('quack')
 
524
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
525
                                    _client=client)
 
526
        result = a_controldir.checkout_metadir()
 
527
        # We should have got a reference control dir with default branch and
 
528
        # repository formats.
 
529
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
 
530
        self.assertEqual(None, result._repository_format)
 
531
        self.assertEqual(None, result._branch_format)
 
532
        self.assertFinished(client)
 
533
 
 
534
    def test_unknown_format(self):
 
535
        transport = MemoryTransport()
 
536
        client = FakeClient(transport.base)
 
537
        client.add_expected_call(
 
538
            b'BzrDir.checkout_metadir', (b'quack/',),
 
539
            b'success', (b'dontknow', b'', b''))
 
540
        transport.mkdir('quack')
 
541
        transport = transport.clone('quack')
 
542
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
543
                                    _client=client)
 
544
        self.assertRaises(errors.UnknownFormatError,
 
545
                          a_controldir.checkout_metadir)
 
546
        self.assertFinished(client)
 
547
 
 
548
 
 
549
class TestBzrDirGetBranches(TestRemote):
 
550
 
 
551
    def test_get_branches(self):
 
552
        transport = MemoryTransport()
 
553
        client = FakeClient(transport.base)
 
554
        reference_bzrdir_format = controldir.format_registry.get('default')()
 
555
        branch_name = reference_bzrdir_format.get_branch_format().network_name()
 
556
        client.add_success_response_with_body(
 
557
            bencode.bencode({
 
558
                b"foo": (b"branch", branch_name),
 
559
                b"": (b"branch", branch_name)}), b"success")
 
560
        client.add_success_response(
 
561
            b'ok', b'', b'no', b'no', b'no',
 
562
            reference_bzrdir_format.repository_format.network_name())
 
563
        client.add_error_response(b'NotStacked')
 
564
        client.add_success_response(
 
565
            b'ok', b'', b'no', b'no', b'no',
 
566
            reference_bzrdir_format.repository_format.network_name())
 
567
        client.add_error_response(b'NotStacked')
 
568
        transport.mkdir('quack')
 
569
        transport = transport.clone('quack')
 
570
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
571
                                    _client=client)
 
572
        result = a_controldir.get_branches()
 
573
        self.assertEqual({"", "foo"}, set(result.keys()))
 
574
        self.assertEqual(
 
575
            [('call_expecting_body', b'BzrDir.get_branches', (b'quack/',)),
 
576
             ('call', b'BzrDir.find_repositoryV3', (b'quack/', )),
 
577
             ('call', b'Branch.get_stacked_on_url', (b'quack/', )),
 
578
             ('call', b'BzrDir.find_repositoryV3', (b'quack/', )),
 
579
             ('call', b'Branch.get_stacked_on_url', (b'quack/', ))],
 
580
            client._calls)
 
581
 
 
582
 
 
583
class TestBzrDirDestroyBranch(TestRemote):
 
584
 
 
585
    def test_destroy_default(self):
 
586
        transport = self.get_transport('quack')
 
587
        self.make_branch('referenced')
 
588
        client = FakeClient(transport.base)
 
589
        client.add_expected_call(
 
590
            b'BzrDir.destroy_branch', (b'quack/', ),
 
591
            b'success', (b'ok',)),
 
592
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
593
                                    _client=client)
 
594
        a_controldir.destroy_branch()
 
595
        self.assertFinished(client)
 
596
 
 
597
 
 
598
class TestBzrDirHasWorkingTree(TestRemote):
 
599
 
 
600
    def test_has_workingtree(self):
 
601
        transport = self.get_transport('quack')
 
602
        client = FakeClient(transport.base)
 
603
        client.add_expected_call(
 
604
            b'BzrDir.has_workingtree', (b'quack/',),
 
605
            b'success', (b'yes',)),
 
606
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
607
                                    _client=client)
 
608
        self.assertTrue(a_controldir.has_workingtree())
 
609
        self.assertFinished(client)
 
610
 
 
611
    def test_no_workingtree(self):
 
612
        transport = self.get_transport('quack')
 
613
        client = FakeClient(transport.base)
 
614
        client.add_expected_call(
 
615
            b'BzrDir.has_workingtree', (b'quack/',),
 
616
            b'success', (b'no',)),
 
617
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
618
                                    _client=client)
 
619
        self.assertFalse(a_controldir.has_workingtree())
 
620
        self.assertFinished(client)
 
621
 
 
622
 
 
623
class TestBzrDirDestroyRepository(TestRemote):
 
624
 
 
625
    def test_destroy_repository(self):
 
626
        transport = self.get_transport('quack')
 
627
        client = FakeClient(transport.base)
 
628
        client.add_expected_call(
 
629
            b'BzrDir.destroy_repository', (b'quack/',),
 
630
            b'success', (b'ok',)),
 
631
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
632
                                    _client=client)
 
633
        a_controldir.destroy_repository()
 
634
        self.assertFinished(client)
 
635
 
483
636
 
484
637
class TestBzrDirOpen(TestRemote):
485
638
 
493
646
    def test_absent(self):
494
647
        client, transport = self.make_fake_client_and_transport()
495
648
        client.add_expected_call(
496
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
 
649
            b'BzrDir.open_2.1', (b'quack/',), b'success', (b'no',))
497
650
        self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
498
 
                remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
 
651
                          RemoteBzrDirFormat(), _client=client,
 
652
                          _force_probe=True)
499
653
        self.assertFinished(client)
500
654
 
501
655
    def test_present_without_workingtree(self):
502
656
        client, transport = self.make_fake_client_and_transport()
503
657
        client.add_expected_call(
504
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
505
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
506
 
            _client=client, _force_probe=True)
 
658
            b'BzrDir.open_2.1', (b'quack/',), b'success', (b'yes', b'no'))
 
659
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
660
                          _client=client, _force_probe=True)
507
661
        self.assertIsInstance(bd, RemoteBzrDir)
508
662
        self.assertFalse(bd.has_workingtree())
509
663
        self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
512
666
    def test_present_with_workingtree(self):
513
667
        client, transport = self.make_fake_client_and_transport()
514
668
        client.add_expected_call(
515
 
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
516
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
517
 
            _client=client, _force_probe=True)
 
669
            b'BzrDir.open_2.1', (b'quack/',), b'success', (b'yes', b'yes'))
 
670
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
671
                          _client=client, _force_probe=True)
518
672
        self.assertIsInstance(bd, RemoteBzrDir)
519
673
        self.assertTrue(bd.has_workingtree())
520
674
        self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
523
677
    def test_backwards_compat(self):
524
678
        client, transport = self.make_fake_client_and_transport()
525
679
        client.add_expected_call(
526
 
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
 
680
            b'BzrDir.open_2.1', (b'quack/',), b'unknown',
 
681
            (b'BzrDir.open_2.1',))
527
682
        client.add_expected_call(
528
 
            'BzrDir.open', ('quack/',), 'success', ('yes',))
529
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
530
 
            _client=client, _force_probe=True)
 
683
            b'BzrDir.open', (b'quack/',), b'success', (b'yes',))
 
684
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
685
                          _client=client, _force_probe=True)
531
686
        self.assertIsInstance(bd, RemoteBzrDir)
532
687
        self.assertFinished(client)
533
688
 
538
693
        # the version is 2 also do _remember_remote_is_before((1, 6)) before
539
694
        # continuing with the RPC.
540
695
        orig_check_call = client._check_call
 
696
 
541
697
        def check_call(method, args):
542
698
            client._medium._protocol_version = 2
543
699
            client._medium._remember_remote_is_before((1, 6))
545
701
            client._check_call(method, args)
546
702
        client._check_call = check_call
547
703
        client.add_expected_call(
548
 
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
 
704
            b'BzrDir.open_2.1', (b'quack/',), b'unknown',
 
705
            (b'BzrDir.open_2.1',))
549
706
        client.add_expected_call(
550
 
            'BzrDir.open', ('quack/',), 'success', ('yes',))
551
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
552
 
            _client=client, _force_probe=True)
 
707
            b'BzrDir.open', (b'quack/',), b'success', (b'yes',))
 
708
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
709
                          _client=client, _force_probe=True)
553
710
        self.assertIsInstance(bd, RemoteBzrDir)
554
711
        self.assertFinished(client)
555
712
 
561
718
        self.make_branch('.')
562
719
        a_dir = BzrDir.open(self.get_url('.'))
563
720
        self.reset_smart_call_log()
564
 
        verb = 'BzrDir.open_branchV3'
 
721
        verb = b'BzrDir.open_branchV3'
565
722
        self.disable_verb(verb)
566
 
        format = a_dir.open_branch()
 
723
        a_dir.open_branch()
567
724
        call_count = len([call for call in self.hpss_calls if
568
 
            call.call.method == verb])
 
725
                          call.call.method == verb])
569
726
        self.assertEqual(1, call_count)
570
727
 
571
728
    def test_branch_present(self):
577
734
        transport = transport.clone('quack')
578
735
        client = FakeClient(transport.base)
579
736
        client.add_expected_call(
580
 
            'BzrDir.open_branchV3', ('quack/',),
581
 
            'success', ('branch', branch_network_name))
582
 
        client.add_expected_call(
583
 
            'BzrDir.find_repositoryV3', ('quack/',),
584
 
            'success', ('ok', '', 'no', 'no', 'no', network_name))
585
 
        client.add_expected_call(
586
 
            'Branch.get_stacked_on_url', ('quack/',),
587
 
            'error', ('NotStacked',))
588
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
589
 
            _client=client)
 
737
            b'BzrDir.open_branchV3', (b'quack/',),
 
738
            b'success', (b'branch', branch_network_name))
 
739
        client.add_expected_call(
 
740
            b'BzrDir.find_repositoryV3', (b'quack/',),
 
741
            b'success', (b'ok', b'', b'no', b'no', b'no', network_name))
 
742
        client.add_expected_call(
 
743
            b'Branch.get_stacked_on_url', (b'quack/',),
 
744
            b'error', (b'NotStacked',))
 
745
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
746
                              _client=client)
590
747
        result = bzrdir.open_branch()
591
748
        self.assertIsInstance(result, RemoteBranch)
592
 
        self.assertEqual(bzrdir, result.bzrdir)
 
749
        self.assertEqual(bzrdir, result.controldir)
593
750
        self.assertFinished(client)
594
751
 
595
752
    def test_branch_missing(self):
597
754
        transport.mkdir('quack')
598
755
        transport = transport.clone('quack')
599
756
        client = FakeClient(transport.base)
600
 
        client.add_error_response('nobranch')
601
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
602
 
            _client=client)
 
757
        client.add_error_response(b'nobranch')
 
758
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
759
                              _client=client)
603
760
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
604
761
        self.assertEqual(
605
 
            [('call', 'BzrDir.open_branchV3', ('quack/',))],
 
762
            [('call', b'BzrDir.open_branchV3', (b'quack/',))],
606
763
            client._calls)
607
764
 
608
765
    def test__get_tree_branch(self):
609
766
        # _get_tree_branch is a form of open_branch, but it should only ask for
610
767
        # branch opening, not any other network requests.
611
768
        calls = []
612
 
        def open_branch():
 
769
 
 
770
        def open_branch(name=None, possible_transports=None):
613
771
            calls.append("Called")
614
772
            return "a-branch"
615
773
        transport = MemoryTransport()
616
774
        # no requests on the network - catches other api calls being made.
617
775
        client = FakeClient(transport.base)
618
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
619
 
            _client=client)
 
776
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
777
                              _client=client)
620
778
        # patch the open_branch call to record that it was called.
621
779
        bzrdir.open_branch = open_branch
622
780
        self.assertEqual((None, "a-branch"), bzrdir._get_tree_branch())
632
790
        network_name = reference_format.network_name()
633
791
        branch_network_name = self.get_branch_format().network_name()
634
792
        client.add_expected_call(
635
 
            'BzrDir.open_branchV3', ('~hello/',),
636
 
            'success', ('branch', branch_network_name))
637
 
        client.add_expected_call(
638
 
            'BzrDir.find_repositoryV3', ('~hello/',),
639
 
            'success', ('ok', '', 'no', 'no', 'no', network_name))
640
 
        client.add_expected_call(
641
 
            'Branch.get_stacked_on_url', ('~hello/',),
642
 
            'error', ('NotStacked',))
643
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
644
 
            _client=client)
645
 
        result = bzrdir.open_branch()
 
793
            b'BzrDir.open_branchV3', (b'~hello/',),
 
794
            b'success', (b'branch', branch_network_name))
 
795
        client.add_expected_call(
 
796
            b'BzrDir.find_repositoryV3', (b'~hello/',),
 
797
            b'success', (b'ok', b'', b'no', b'no', b'no', network_name))
 
798
        client.add_expected_call(
 
799
            b'Branch.get_stacked_on_url', (b'~hello/',),
 
800
            b'error', (b'NotStacked',))
 
801
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
802
                              _client=client)
 
803
        bzrdir.open_branch()
646
804
        self.assertFinished(client)
647
805
 
648
 
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
 
806
    def check_open_repository(self, rich_root, subtrees,
 
807
                              external_lookup=b'no'):
649
808
        reference_format = self.get_repo_format()
650
809
        network_name = reference_format.network_name()
651
810
        transport = MemoryTransport()
652
811
        transport.mkdir('quack')
653
812
        transport = transport.clone('quack')
654
813
        if rich_root:
655
 
            rich_response = 'yes'
 
814
            rich_response = b'yes'
656
815
        else:
657
 
            rich_response = 'no'
 
816
            rich_response = b'no'
658
817
        if subtrees:
659
 
            subtree_response = 'yes'
 
818
            subtree_response = b'yes'
660
819
        else:
661
 
            subtree_response = 'no'
 
820
            subtree_response = b'no'
662
821
        client = FakeClient(transport.base)
663
822
        client.add_success_response(
664
 
            'ok', '', rich_response, subtree_response, external_lookup,
 
823
            b'ok', b'', rich_response, subtree_response, external_lookup,
665
824
            network_name)
666
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
667
 
            _client=client)
 
825
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
826
                              _client=client)
668
827
        result = bzrdir.open_repository()
669
828
        self.assertEqual(
670
 
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
 
829
            [('call', b'BzrDir.find_repositoryV3', (b'quack/',))],
671
830
            client._calls)
672
831
        self.assertIsInstance(result, RemoteRepository)
673
 
        self.assertEqual(bzrdir, result.bzrdir)
 
832
        self.assertEqual(bzrdir, result.controldir)
674
833
        self.assertEqual(rich_root, result._format.rich_root_data)
675
834
        self.assertEqual(subtrees, result._format.supports_tree_reference)
676
835
 
679
838
        self.check_open_repository(False, True)
680
839
        self.check_open_repository(True, False)
681
840
        self.check_open_repository(False, False)
682
 
        self.check_open_repository(False, False, 'yes')
 
841
        self.check_open_repository(False, False, b'yes')
683
842
 
684
843
    def test_old_server(self):
685
844
        """RemoteBzrDirFormat should fail to probe if the server version is too
686
845
        old.
687
846
        """
688
 
        self.assertRaises(errors.NotBranchError,
689
 
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
 
847
        self.assertRaises(
 
848
            errors.NotBranchError,
 
849
            RemoteBzrProber.probe_transport, OldServerTransport())
690
850
 
691
851
 
692
852
class TestBzrDirCreateBranch(TestRemote):
695
855
        self.setup_smart_server_with_call_log()
696
856
        repo = self.make_repository('.')
697
857
        self.reset_smart_call_log()
698
 
        self.disable_verb('BzrDir.create_branch')
699
 
        branch = repo.bzrdir.create_branch()
700
 
        create_branch_call_count = len([call for call in self.hpss_calls if
701
 
            call.call.method == 'BzrDir.create_branch'])
 
858
        self.disable_verb(b'BzrDir.create_branch')
 
859
        repo.controldir.create_branch()
 
860
        create_branch_call_count = len(
 
861
            [call for call in self.hpss_calls
 
862
             if call.call.method == b'BzrDir.create_branch'])
702
863
        self.assertEqual(1, create_branch_call_count)
703
864
 
704
865
    def test_current_server(self):
706
867
        transport = transport.clone('quack')
707
868
        self.make_repository('quack')
708
869
        client = FakeClient(transport.base)
709
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
710
 
        reference_format = reference_bzrdir_format.get_branch_format()
711
 
        network_name = reference_format.network_name()
712
 
        reference_repo_fmt = reference_bzrdir_format.repository_format
713
 
        reference_repo_name = reference_repo_fmt.network_name()
714
 
        client.add_expected_call(
715
 
            'BzrDir.create_branch', ('quack/', network_name),
716
 
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
717
 
            reference_repo_name))
718
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
719
 
            _client=client)
720
 
        branch = a_bzrdir.create_branch()
 
870
        reference_bzrdir_format = controldir.format_registry.get('default')()
 
871
        reference_format = reference_bzrdir_format.get_branch_format()
 
872
        network_name = reference_format.network_name()
 
873
        reference_repo_fmt = reference_bzrdir_format.repository_format
 
874
        reference_repo_name = reference_repo_fmt.network_name()
 
875
        client.add_expected_call(
 
876
            b'BzrDir.create_branch', (b'quack/', network_name),
 
877
            b'success', (b'ok', network_name, b'', b'no', b'no', b'yes',
 
878
                         reference_repo_name))
 
879
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
880
                                    _client=client)
 
881
        branch = a_controldir.create_branch()
 
882
        # We should have got a remote branch
 
883
        self.assertIsInstance(branch, remote.RemoteBranch)
 
884
        # its format should have the settings from the response
 
885
        format = branch._format
 
886
        self.assertEqual(network_name, format.network_name())
 
887
 
 
888
    def test_already_open_repo_and_reused_medium(self):
 
889
        """Bug 726584: create_branch(..., repository=repo) should work
 
890
        regardless of what the smart medium's base URL is.
 
891
        """
 
892
        self.transport_server = test_server.SmartTCPServer_for_testing
 
893
        transport = self.get_transport('.')
 
894
        repo = self.make_repository('quack')
 
895
        # Client's medium rooted a transport root (not at the bzrdir)
 
896
        client = FakeClient(transport.base)
 
897
        transport = transport.clone('quack')
 
898
        reference_bzrdir_format = controldir.format_registry.get('default')()
 
899
        reference_format = reference_bzrdir_format.get_branch_format()
 
900
        network_name = reference_format.network_name()
 
901
        reference_repo_fmt = reference_bzrdir_format.repository_format
 
902
        reference_repo_name = reference_repo_fmt.network_name()
 
903
        client.add_expected_call(
 
904
            b'BzrDir.create_branch', (b'extra/quack/', network_name),
 
905
            b'success', (b'ok', network_name, b'', b'no', b'no', b'yes',
 
906
                         reference_repo_name))
 
907
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
908
                                    _client=client)
 
909
        branch = a_controldir.create_branch(repository=repo)
721
910
        # We should have got a remote branch
722
911
        self.assertIsInstance(branch, remote.RemoteBranch)
723
912
        # its format should have the settings from the response
729
918
 
730
919
    def test_backwards_compat(self):
731
920
        self.setup_smart_server_with_call_log()
732
 
        bzrdir = self.make_bzrdir('.')
 
921
        bzrdir = self.make_controldir('.')
733
922
        self.reset_smart_call_log()
734
 
        self.disable_verb('BzrDir.create_repository')
735
 
        repo = bzrdir.create_repository()
 
923
        self.disable_verb(b'BzrDir.create_repository')
 
924
        bzrdir.create_repository()
736
925
        create_repo_call_count = len([call for call in self.hpss_calls if
737
 
            call.call.method == 'BzrDir.create_repository'])
 
926
                                      call.call.method == b'BzrDir.create_repository'])
738
927
        self.assertEqual(1, create_repo_call_count)
739
928
 
740
929
    def test_current_server(self):
741
930
        transport = self.get_transport('.')
742
931
        transport = transport.clone('quack')
743
 
        self.make_bzrdir('quack')
 
932
        self.make_controldir('quack')
744
933
        client = FakeClient(transport.base)
745
 
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
934
        reference_bzrdir_format = controldir.format_registry.get('default')()
746
935
        reference_format = reference_bzrdir_format.repository_format
747
936
        network_name = reference_format.network_name()
748
937
        client.add_expected_call(
749
 
            'BzrDir.create_repository', ('quack/',
750
 
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
751
 
                'False'),
752
 
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
753
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
754
 
            _client=client)
755
 
        repo = a_bzrdir.create_repository()
 
938
            b'BzrDir.create_repository', (b'quack/',
 
939
                                          b'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
940
                                          b'False'),
 
941
            b'success', (b'ok', b'yes', b'yes', b'yes', network_name))
 
942
        a_controldir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
943
                                    _client=client)
 
944
        repo = a_controldir.create_repository()
756
945
        # We should have got a remote repository
757
946
        self.assertIsInstance(repo, remote.RemoteRepository)
758
947
        # its format should have the settings from the response
772
961
        server_url = 'bzr://example.com/'
773
962
        self.permit_url(server_url)
774
963
        client = FakeClient(server_url)
775
 
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
776
 
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
777
 
        client.add_success_response('ok', '', 'no', 'no')
 
964
        client.add_unknown_method_response(b'BzrDir.find_repositoryV3')
 
965
        client.add_unknown_method_response(b'BzrDir.find_repositoryV2')
 
966
        client.add_success_response(b'ok', b'', b'no', b'no')
778
967
        # A real repository instance will be created to determine the network
779
968
        # name.
780
969
        client.add_success_response_with_body(
781
 
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
970
            b"Bazaar-NG meta directory, format 1\n", b'ok')
 
971
        client.add_success_response(b'stat', b'0', b'65535')
782
972
        client.add_success_response_with_body(
783
 
            reference_format.get_format_string(), 'ok')
 
973
            reference_format.get_format_string(), b'ok')
784
974
        # PackRepository wants to do a stat
785
 
        client.add_success_response('stat', '0', '65535')
 
975
        client.add_success_response(b'stat', b'0', b'65535')
786
976
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
787
 
            _client=client)
788
 
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
789
 
            _client=client)
 
977
                                           _client=client)
 
978
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
 
979
                              _client=client)
790
980
        repo = bzrdir.open_repository()
791
981
        self.assertEqual(
792
 
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
793
 
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
794
 
             ('call', 'BzrDir.find_repository', ('quack/',)),
795
 
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
796
 
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
797
 
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
982
            [('call', b'BzrDir.find_repositoryV3', (b'quack/',)),
 
983
             ('call', b'BzrDir.find_repositoryV2', (b'quack/',)),
 
984
             ('call', b'BzrDir.find_repository', (b'quack/',)),
 
985
             ('call_expecting_body', b'get', (b'/quack/.bzr/branch-format',)),
 
986
             ('call', b'stat', (b'/quack/.bzr',)),
 
987
             ('call_expecting_body', b'get', (b'/quack/.bzr/repository/format',)),
 
988
             ('call', b'stat', (b'/quack/.bzr/repository',)),
798
989
             ],
799
990
            client._calls)
800
991
        self.assertEqual(network_name, repo._format.network_name())
806
997
        server_url = 'bzr://example.com/'
807
998
        self.permit_url(server_url)
808
999
        client = FakeClient(server_url)
809
 
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
810
 
        client.add_success_response('ok', '', 'no', 'no', 'no')
 
1000
        client.add_unknown_method_response(b'BzrDir.find_repositoryV3')
 
1001
        client.add_success_response(b'ok', b'', b'no', b'no', b'no')
811
1002
        # A real repository instance will be created to determine the network
812
1003
        # name.
813
1004
        client.add_success_response_with_body(
814
 
            "Bazaar-NG meta directory, format 1\n", 'ok')
 
1005
            b"Bazaar-NG meta directory, format 1\n", b'ok')
 
1006
        client.add_success_response(b'stat', b'0', b'65535')
815
1007
        client.add_success_response_with_body(
816
 
            reference_format.get_format_string(), 'ok')
 
1008
            reference_format.get_format_string(), b'ok')
817
1009
        # PackRepository wants to do a stat
818
 
        client.add_success_response('stat', '0', '65535')
 
1010
        client.add_success_response(b'stat', b'0', b'65535')
819
1011
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
820
 
            _client=client)
821
 
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
822
 
            _client=client)
 
1012
                                           _client=client)
 
1013
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
 
1014
                              _client=client)
823
1015
        repo = bzrdir.open_repository()
824
1016
        self.assertEqual(
825
 
            [('call', 'BzrDir.find_repositoryV3', ('quack/',)),
826
 
             ('call', 'BzrDir.find_repositoryV2', ('quack/',)),
827
 
             ('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
828
 
             ('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
829
 
             ('call', 'stat', ('/quack/.bzr/repository',)),
 
1017
            [('call', b'BzrDir.find_repositoryV3', (b'quack/',)),
 
1018
             ('call', b'BzrDir.find_repositoryV2', (b'quack/',)),
 
1019
             ('call_expecting_body', b'get', (b'/quack/.bzr/branch-format',)),
 
1020
             ('call', b'stat', (b'/quack/.bzr',)),
 
1021
             ('call_expecting_body', b'get',
 
1022
                 (b'/quack/.bzr/repository/format',)),
 
1023
             ('call', b'stat', (b'/quack/.bzr/repository',)),
830
1024
             ],
831
1025
            client._calls)
832
1026
        self.assertEqual(network_name, repo._format.network_name())
838
1032
        transport.mkdir('quack')
839
1033
        transport = transport.clone('quack')
840
1034
        client = FakeClient(transport.base)
841
 
        client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
842
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
843
 
            _client=client)
 
1035
        client.add_success_response(
 
1036
            b'ok', b'', b'no', b'no', b'no', network_name)
 
1037
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
1038
                              _client=client)
844
1039
        repo = bzrdir.open_repository()
845
1040
        self.assertEqual(
846
 
            [('call', 'BzrDir.find_repositoryV3', ('quack/',))],
 
1041
            [('call', b'BzrDir.find_repositoryV3', (b'quack/',))],
847
1042
            client._calls)
848
1043
        self.assertEqual(network_name, repo._format.network_name())
849
1044
 
852
1047
 
853
1048
    def test_success(self):
854
1049
        """Simple test for typical successful call."""
855
 
        fmt = bzrdir.RemoteBzrDirFormat()
 
1050
        fmt = RemoteBzrDirFormat()
856
1051
        default_format_name = BzrDirFormat.get_default_format().network_name()
857
1052
        transport = self.get_transport()
858
1053
        client = FakeClient(transport.base)
859
1054
        client.add_expected_call(
860
 
            'BzrDirFormat.initialize_ex_1.16',
861
 
                (default_format_name, 'path', 'False', 'False', 'False', '',
862
 
                 '', '', '', 'False'),
863
 
            'success',
864
 
                ('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
865
 
                 'bzrdir fmt', 'False', '', '', 'repo lock token'))
 
1055
            b'BzrDirFormat.initialize_ex_1.16',
 
1056
            (default_format_name, b'path', b'False', b'False', b'False', b'',
 
1057
             b'', b'', b'', b'False'),
 
1058
            b'success',
 
1059
            (b'.', b'no', b'no', b'yes', b'repo fmt', b'repo bzrdir fmt',
 
1060
             b'bzrdir fmt', b'False', b'', b'', b'repo lock token'))
866
1061
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
867
1062
        # it's currently hard to test that without supplying a real remote
868
1063
        # transport connected to a real server.
869
 
        result = fmt._initialize_on_transport_ex_rpc(client, 'path',
870
 
            transport, False, False, False, None, None, None, None, False)
 
1064
        fmt._initialize_on_transport_ex_rpc(
 
1065
            client, b'path', transport, False, False, False, None, None, None,
 
1066
            None, False)
871
1067
        self.assertFinished(client)
872
1068
 
873
1069
    def test_error(self):
874
1070
        """Error responses are translated, e.g. 'PermissionDenied' raises the
875
1071
        corresponding error from the client.
876
1072
        """
877
 
        fmt = bzrdir.RemoteBzrDirFormat()
 
1073
        fmt = RemoteBzrDirFormat()
878
1074
        default_format_name = BzrDirFormat.get_default_format().network_name()
879
1075
        transport = self.get_transport()
880
1076
        client = FakeClient(transport.base)
881
1077
        client.add_expected_call(
882
 
            'BzrDirFormat.initialize_ex_1.16',
883
 
                (default_format_name, 'path', 'False', 'False', 'False', '',
884
 
                 '', '', '', 'False'),
885
 
            'error',
886
 
                ('PermissionDenied', 'path', 'extra info'))
 
1078
            b'BzrDirFormat.initialize_ex_1.16',
 
1079
            (default_format_name, b'path', b'False', b'False', b'False', b'',
 
1080
             b'', b'', b'', b'False'),
 
1081
            b'error',
 
1082
            (b'PermissionDenied', b'path', b'extra info'))
887
1083
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
888
1084
        # it's currently hard to test that without supplying a real remote
889
1085
        # transport connected to a real server.
890
 
        err = self.assertRaises(errors.PermissionDenied,
891
 
            fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
 
1086
        err = self.assertRaises(
 
1087
            errors.PermissionDenied,
 
1088
            fmt._initialize_on_transport_ex_rpc, client, b'path', transport,
892
1089
            False, False, False, None, None, None, None, False)
893
1090
        self.assertEqual('path', err.path)
894
1091
        self.assertEqual(': extra info', err.extra)
898
1095
        """Integration test for error translation."""
899
1096
        transport = self.make_smart_server('foo')
900
1097
        transport = transport.clone('no-such-path')
901
 
        fmt = bzrdir.RemoteBzrDirFormat()
902
 
        err = self.assertRaises(errors.NoSuchFile,
903
 
            fmt.initialize_on_transport_ex, transport, create_prefix=False)
 
1098
        fmt = RemoteBzrDirFormat()
 
1099
        self.assertRaises(
 
1100
            errors.NoSuchFile, fmt.initialize_on_transport_ex, transport,
 
1101
            create_prefix=False)
904
1102
 
905
1103
 
906
1104
class OldSmartClient(object):
909
1107
    """
910
1108
 
911
1109
    def get_request(self):
912
 
        input_file = StringIO('ok\x011\n')
913
 
        output_file = StringIO()
 
1110
        input_file = BytesIO(b'ok\x011\n')
 
1111
        output_file = BytesIO()
914
1112
        client_medium = medium.SmartSimplePipesClientMedium(
915
1113
            input_file, output_file)
916
1114
        return medium.SmartClientStreamMediumRequest(client_medium)
935
1133
 
936
1134
    def make_remote_bzrdir(self, transport, client):
937
1135
        """Make a RemotebzrDir using 'client' as the _client."""
938
 
        return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
939
 
            _client=client)
 
1136
        return RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
1137
                            _client=client)
940
1138
 
941
1139
 
942
1140
class RemoteBranchTestCase(RemoteBzrDirTestCase):
945
1143
        """Trick a RemoteBranch into thinking it is locked."""
946
1144
        branch._lock_mode = 'w'
947
1145
        branch._lock_count = 2
948
 
        branch._lock_token = 'branch token'
949
 
        branch._repo_lock_token = 'repo token'
 
1146
        branch._lock_token = b'branch token'
 
1147
        branch._repo_lock_token = b'repo token'
950
1148
        branch.repository._lock_mode = 'w'
951
1149
        branch.repository._lock_count = 2
952
 
        branch.repository._lock_token = 'repo token'
 
1150
        branch.repository._lock_token = b'repo token'
953
1151
 
954
1152
    def make_remote_branch(self, transport, client):
955
1153
        """Make a RemoteBranch using 'client' as its _SmartClient.
967
1165
        return RemoteBranch(bzrdir, repo, _client=client, format=format)
968
1166
 
969
1167
 
 
1168
class TestBranchBreakLock(RemoteBranchTestCase):
 
1169
 
 
1170
    def test_break_lock(self):
 
1171
        transport = MemoryTransport()
 
1172
        client = FakeClient(transport.base)
 
1173
        client.add_expected_call(
 
1174
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1175
            b'error', (b'NotStacked',))
 
1176
        client.add_expected_call(
 
1177
            b'Branch.break_lock', (b'quack/',),
 
1178
            b'success', (b'ok',))
 
1179
        transport.mkdir('quack')
 
1180
        transport = transport.clone('quack')
 
1181
        branch = self.make_remote_branch(transport, client)
 
1182
        branch.break_lock()
 
1183
        self.assertFinished(client)
 
1184
 
 
1185
 
 
1186
class TestBranchGetPhysicalLockStatus(RemoteBranchTestCase):
 
1187
 
 
1188
    def test_get_physical_lock_status_yes(self):
 
1189
        transport = MemoryTransport()
 
1190
        client = FakeClient(transport.base)
 
1191
        client.add_expected_call(
 
1192
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1193
            b'error', (b'NotStacked',))
 
1194
        client.add_expected_call(
 
1195
            b'Branch.get_physical_lock_status', (b'quack/',),
 
1196
            b'success', (b'yes',))
 
1197
        transport.mkdir('quack')
 
1198
        transport = transport.clone('quack')
 
1199
        branch = self.make_remote_branch(transport, client)
 
1200
        result = branch.get_physical_lock_status()
 
1201
        self.assertFinished(client)
 
1202
        self.assertEqual(True, result)
 
1203
 
 
1204
    def test_get_physical_lock_status_no(self):
 
1205
        transport = MemoryTransport()
 
1206
        client = FakeClient(transport.base)
 
1207
        client.add_expected_call(
 
1208
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1209
            b'error', (b'NotStacked',))
 
1210
        client.add_expected_call(
 
1211
            b'Branch.get_physical_lock_status', (b'quack/',),
 
1212
            b'success', (b'no',))
 
1213
        transport.mkdir('quack')
 
1214
        transport = transport.clone('quack')
 
1215
        branch = self.make_remote_branch(transport, client)
 
1216
        result = branch.get_physical_lock_status()
 
1217
        self.assertFinished(client)
 
1218
        self.assertEqual(False, result)
 
1219
 
 
1220
 
970
1221
class TestBranchGetParent(RemoteBranchTestCase):
971
1222
 
972
1223
    def test_no_parent(self):
974
1225
        transport = MemoryTransport()
975
1226
        client = FakeClient(transport.base)
976
1227
        client.add_expected_call(
977
 
            'Branch.get_stacked_on_url', ('quack/',),
978
 
            'error', ('NotStacked',))
 
1228
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1229
            b'error', (b'NotStacked',))
979
1230
        client.add_expected_call(
980
 
            'Branch.get_parent', ('quack/',),
981
 
            'success', ('',))
 
1231
            b'Branch.get_parent', (b'quack/',),
 
1232
            b'success', (b'',))
982
1233
        transport.mkdir('quack')
983
1234
        transport = transport.clone('quack')
984
1235
        branch = self.make_remote_branch(transport, client)
990
1241
        transport = MemoryTransport()
991
1242
        client = FakeClient(transport.base)
992
1243
        client.add_expected_call(
993
 
            'Branch.get_stacked_on_url', ('kwaak/',),
994
 
            'error', ('NotStacked',))
 
1244
            b'Branch.get_stacked_on_url', (b'kwaak/',),
 
1245
            b'error', (b'NotStacked',))
995
1246
        client.add_expected_call(
996
 
            'Branch.get_parent', ('kwaak/',),
997
 
            'success', ('../foo/',))
 
1247
            b'Branch.get_parent', (b'kwaak/',),
 
1248
            b'success', (b'../foo/',))
998
1249
        transport.mkdir('kwaak')
999
1250
        transport = transport.clone('kwaak')
1000
1251
        branch = self.make_remote_branch(transport, client)
1005
1256
        transport = MemoryTransport()
1006
1257
        client = FakeClient(transport.base)
1007
1258
        client.add_expected_call(
1008
 
            'Branch.get_stacked_on_url', ('kwaak/',),
1009
 
            'error', ('NotStacked',))
 
1259
            b'Branch.get_stacked_on_url', (b'kwaak/',),
 
1260
            b'error', (b'NotStacked',))
1010
1261
        client.add_expected_call(
1011
 
            'Branch.get_parent', ('kwaak/',),
1012
 
            'success', ('http://foo/',))
 
1262
            b'Branch.get_parent', (b'kwaak/',),
 
1263
            b'success', (b'http://foo/',))
1013
1264
        transport.mkdir('kwaak')
1014
1265
        transport = transport.clone('kwaak')
1015
1266
        branch = self.make_remote_branch(transport, client)
1025
1276
        transport = MemoryTransport()
1026
1277
        client = FakeClient(transport.base)
1027
1278
        client.add_expected_call(
1028
 
            'Branch.get_stacked_on_url', ('quack/',),
1029
 
            'error', ('NotStacked',))
 
1279
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1280
            b'error', (b'NotStacked',))
1030
1281
        client.add_expected_call(
1031
 
            'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1032
 
            'success', ())
 
1282
            b'Branch.set_parent_location', (b'quack/', b'b', b'r', b''),
 
1283
            b'success', ())
1033
1284
        transport.mkdir('quack')
1034
1285
        transport = transport.clone('quack')
1035
1286
        branch = self.make_remote_branch(transport, client)
1036
 
        branch._lock_token = 'b'
1037
 
        branch._repo_lock_token = 'r'
 
1287
        branch._lock_token = b'b'
 
1288
        branch._repo_lock_token = b'r'
1038
1289
        branch._set_parent_location(None)
1039
1290
        self.assertFinished(client)
1040
1291
 
1042
1293
        transport = MemoryTransport()
1043
1294
        client = FakeClient(transport.base)
1044
1295
        client.add_expected_call(
1045
 
            'Branch.get_stacked_on_url', ('kwaak/',),
1046
 
            'error', ('NotStacked',))
 
1296
            b'Branch.get_stacked_on_url', (b'kwaak/',),
 
1297
            b'error', (b'NotStacked',))
1047
1298
        client.add_expected_call(
1048
 
            'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1049
 
            'success', ())
 
1299
            b'Branch.set_parent_location', (b'kwaak/', b'b', b'r', b'foo'),
 
1300
            b'success', ())
1050
1301
        transport.mkdir('kwaak')
1051
1302
        transport = transport.clone('kwaak')
1052
1303
        branch = self.make_remote_branch(transport, client)
1053
 
        branch._lock_token = 'b'
1054
 
        branch._repo_lock_token = 'r'
 
1304
        branch._lock_token = b'b'
 
1305
        branch._repo_lock_token = b'r'
1055
1306
        branch._set_parent_location('foo')
1056
1307
        self.assertFinished(client)
1057
1308
 
1059
1310
        self.setup_smart_server_with_call_log()
1060
1311
        branch = self.make_branch('.')
1061
1312
        self.reset_smart_call_log()
1062
 
        verb = 'Branch.set_parent_location'
 
1313
        verb = b'Branch.set_parent_location'
1063
1314
        self.disable_verb(verb)
1064
1315
        branch.set_parent('http://foo/')
1065
 
        self.assertLength(12, self.hpss_calls)
 
1316
        self.assertLength(14, self.hpss_calls)
1066
1317
 
1067
1318
 
1068
1319
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1071
1322
        self.setup_smart_server_with_call_log()
1072
1323
        branch = self.make_branch('.')
1073
1324
        self.reset_smart_call_log()
1074
 
        verb = 'Branch.get_tags_bytes'
 
1325
        verb = b'Branch.get_tags_bytes'
1075
1326
        self.disable_verb(verb)
1076
1327
        branch.tags.get_tag_dict()
1077
1328
        call_count = len([call for call in self.hpss_calls if
1078
 
            call.call.method == verb])
 
1329
                          call.call.method == verb])
1079
1330
        self.assertEqual(1, call_count)
1080
1331
 
1081
1332
    def test_trivial(self):
1082
1333
        transport = MemoryTransport()
1083
1334
        client = FakeClient(transport.base)
1084
1335
        client.add_expected_call(
1085
 
            'Branch.get_stacked_on_url', ('quack/',),
1086
 
            'error', ('NotStacked',))
 
1336
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1337
            b'error', (b'NotStacked',))
1087
1338
        client.add_expected_call(
1088
 
            'Branch.get_tags_bytes', ('quack/',),
1089
 
            'success', ('',))
 
1339
            b'Branch.get_tags_bytes', (b'quack/',),
 
1340
            b'success', (b'',))
1090
1341
        transport.mkdir('quack')
1091
1342
        transport = transport.clone('quack')
1092
1343
        branch = self.make_remote_branch(transport, client)
1101
1352
        transport = MemoryTransport()
1102
1353
        client = FakeClient(transport.base)
1103
1354
        client.add_expected_call(
1104
 
            'Branch.get_stacked_on_url', ('quack/',),
1105
 
            'error', ('NotStacked',))
 
1355
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1356
            b'error', (b'NotStacked',))
1106
1357
        client.add_expected_call(
1107
 
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1108
 
            'success', ('',))
 
1358
            b'Branch.set_tags_bytes', (b'quack/',
 
1359
                                       b'branch token', b'repo token'),
 
1360
            b'success', ('',))
1109
1361
        transport.mkdir('quack')
1110
1362
        transport = transport.clone('quack')
1111
1363
        branch = self.make_remote_branch(transport, client)
1112
1364
        self.lock_remote_branch(branch)
1113
 
        branch._set_tags_bytes('tags bytes')
 
1365
        branch._set_tags_bytes(b'tags bytes')
1114
1366
        self.assertFinished(client)
1115
 
        self.assertEqual('tags bytes', client._calls[-1][-1])
 
1367
        self.assertEqual(b'tags bytes', client._calls[-1][-1])
1116
1368
 
1117
1369
    def test_backwards_compatible(self):
1118
1370
        transport = MemoryTransport()
1119
1371
        client = FakeClient(transport.base)
1120
1372
        client.add_expected_call(
1121
 
            'Branch.get_stacked_on_url', ('quack/',),
1122
 
            'error', ('NotStacked',))
 
1373
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1374
            b'error', (b'NotStacked',))
1123
1375
        client.add_expected_call(
1124
 
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1125
 
            'unknown', ('Branch.set_tags_bytes',))
 
1376
            b'Branch.set_tags_bytes', (b'quack/',
 
1377
                                       b'branch token', b'repo token'),
 
1378
            b'unknown', (b'Branch.set_tags_bytes',))
1126
1379
        transport.mkdir('quack')
1127
1380
        transport = transport.clone('quack')
1128
1381
        branch = self.make_remote_branch(transport, client)
1129
1382
        self.lock_remote_branch(branch)
 
1383
 
1130
1384
        class StubRealBranch(object):
1131
1385
            def __init__(self):
1132
1386
                self.calls = []
 
1387
 
1133
1388
            def _set_tags_bytes(self, bytes):
1134
1389
                self.calls.append(('set_tags_bytes', bytes))
1135
1390
        real_branch = StubRealBranch()
1136
1391
        branch._real_branch = real_branch
1137
 
        branch._set_tags_bytes('tags bytes')
 
1392
        branch._set_tags_bytes(b'tags bytes')
1138
1393
        # Call a second time, to exercise the 'remote version already inferred'
1139
1394
        # code path.
1140
 
        branch._set_tags_bytes('tags bytes')
1141
 
        self.assertFinished(client)
1142
 
        self.assertEqual(
1143
 
            [('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
 
1395
        branch._set_tags_bytes(b'tags bytes')
 
1396
        self.assertFinished(client)
 
1397
        self.assertEqual(
 
1398
            [('set_tags_bytes', b'tags bytes')] * 2, real_branch.calls)
 
1399
 
 
1400
 
 
1401
class TestBranchHeadsToFetch(RemoteBranchTestCase):
 
1402
 
 
1403
    def test_uses_last_revision_info_and_tags_by_default(self):
 
1404
        transport = MemoryTransport()
 
1405
        client = FakeClient(transport.base)
 
1406
        client.add_expected_call(
 
1407
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1408
            b'error', (b'NotStacked',))
 
1409
        client.add_expected_call(
 
1410
            b'Branch.last_revision_info', (b'quack/',),
 
1411
            b'success', (b'ok', b'1', b'rev-tip'))
 
1412
        client.add_expected_call(
 
1413
            b'Branch.get_config_file', (b'quack/',),
 
1414
            b'success', (b'ok',), b'')
 
1415
        transport.mkdir('quack')
 
1416
        transport = transport.clone('quack')
 
1417
        branch = self.make_remote_branch(transport, client)
 
1418
        result = branch.heads_to_fetch()
 
1419
        self.assertFinished(client)
 
1420
        self.assertEqual(({b'rev-tip'}, set()), result)
 
1421
 
 
1422
    def test_uses_last_revision_info_and_tags_when_set(self):
 
1423
        transport = MemoryTransport()
 
1424
        client = FakeClient(transport.base)
 
1425
        client.add_expected_call(
 
1426
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1427
            b'error', (b'NotStacked',))
 
1428
        client.add_expected_call(
 
1429
            b'Branch.last_revision_info', (b'quack/',),
 
1430
            b'success', (b'ok', b'1', b'rev-tip'))
 
1431
        client.add_expected_call(
 
1432
            b'Branch.get_config_file', (b'quack/',),
 
1433
            b'success', (b'ok',), b'branch.fetch_tags = True')
 
1434
        # XXX: this will break if the default format's serialization of tags
 
1435
        # changes, or if the RPC for fetching tags changes from get_tags_bytes.
 
1436
        client.add_expected_call(
 
1437
            b'Branch.get_tags_bytes', (b'quack/',),
 
1438
            b'success', (b'd5:tag-17:rev-foo5:tag-27:rev-bare',))
 
1439
        transport.mkdir('quack')
 
1440
        transport = transport.clone('quack')
 
1441
        branch = self.make_remote_branch(transport, client)
 
1442
        result = branch.heads_to_fetch()
 
1443
        self.assertFinished(client)
 
1444
        self.assertEqual(
 
1445
            ({b'rev-tip'}, {b'rev-foo', b'rev-bar'}), result)
 
1446
 
 
1447
    def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
 
1448
        transport = MemoryTransport()
 
1449
        client = FakeClient(transport.base)
 
1450
        client.add_expected_call(
 
1451
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1452
            b'error', (b'NotStacked',))
 
1453
        client.add_expected_call(
 
1454
            b'Branch.heads_to_fetch', (b'quack/',),
 
1455
            b'success', ([b'tip'], [b'tagged-1', b'tagged-2']))
 
1456
        transport.mkdir('quack')
 
1457
        transport = transport.clone('quack')
 
1458
        branch = self.make_remote_branch(transport, client)
 
1459
        branch._format._use_default_local_heads_to_fetch = lambda: False
 
1460
        result = branch.heads_to_fetch()
 
1461
        self.assertFinished(client)
 
1462
        self.assertEqual(({b'tip'}, {b'tagged-1', b'tagged-2'}), result)
 
1463
 
 
1464
    def make_branch_with_tags(self):
 
1465
        self.setup_smart_server_with_call_log()
 
1466
        # Make a branch with a single revision.
 
1467
        builder = self.make_branch_builder('foo')
 
1468
        builder.start_series()
 
1469
        builder.build_snapshot(None, [
 
1470
            ('add', ('', b'root-id', 'directory', ''))],
 
1471
            revision_id=b'tip')
 
1472
        builder.finish_series()
 
1473
        branch = builder.get_branch()
 
1474
        # Add two tags to that branch
 
1475
        branch.tags.set_tag('tag-1', b'rev-1')
 
1476
        branch.tags.set_tag('tag-2', b'rev-2')
 
1477
        return branch
 
1478
 
 
1479
    def test_backwards_compatible(self):
 
1480
        br = self.make_branch_with_tags()
 
1481
        br.get_config_stack().set('branch.fetch_tags', True)
 
1482
        self.addCleanup(br.lock_read().unlock)
 
1483
        # Disable the heads_to_fetch verb
 
1484
        verb = b'Branch.heads_to_fetch'
 
1485
        self.disable_verb(verb)
 
1486
        self.reset_smart_call_log()
 
1487
        result = br.heads_to_fetch()
 
1488
        self.assertEqual(({b'tip'}, {b'rev-1', b'rev-2'}), result)
 
1489
        self.assertEqual(
 
1490
            [b'Branch.last_revision_info', b'Branch.get_tags_bytes'],
 
1491
            [call.call.method for call in self.hpss_calls])
 
1492
 
 
1493
    def test_backwards_compatible_no_tags(self):
 
1494
        br = self.make_branch_with_tags()
 
1495
        br.get_config_stack().set('branch.fetch_tags', False)
 
1496
        self.addCleanup(br.lock_read().unlock)
 
1497
        # Disable the heads_to_fetch verb
 
1498
        verb = b'Branch.heads_to_fetch'
 
1499
        self.disable_verb(verb)
 
1500
        self.reset_smart_call_log()
 
1501
        result = br.heads_to_fetch()
 
1502
        self.assertEqual(({b'tip'}, set()), result)
 
1503
        self.assertEqual(
 
1504
            [b'Branch.last_revision_info'],
 
1505
            [call.call.method for call in self.hpss_calls])
1144
1506
 
1145
1507
 
1146
1508
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1150
1512
        transport = MemoryTransport()
1151
1513
        client = FakeClient(transport.base)
1152
1514
        client.add_expected_call(
1153
 
            'Branch.get_stacked_on_url', ('quack/',),
1154
 
            'error', ('NotStacked',))
 
1515
            b'Branch.get_stacked_on_url', (b'quack/',),
 
1516
            b'error', (b'NotStacked',))
1155
1517
        client.add_expected_call(
1156
 
            'Branch.last_revision_info', ('quack/',),
1157
 
            'success', ('ok', '0', 'null:'))
 
1518
            b'Branch.last_revision_info', (b'quack/',),
 
1519
            b'success', (b'ok', b'0', b'null:'))
1158
1520
        transport.mkdir('quack')
1159
1521
        transport = transport.clone('quack')
1160
1522
        branch = self.make_remote_branch(transport, client)
1168
1530
        transport = MemoryTransport()
1169
1531
        client = FakeClient(transport.base)
1170
1532
        client.add_expected_call(
1171
 
            'Branch.get_stacked_on_url', ('kwaak/',),
1172
 
            'error', ('NotStacked',))
 
1533
            b'Branch.get_stacked_on_url', (b'kwaak/',),
 
1534
            b'error', (b'NotStacked',))
1173
1535
        client.add_expected_call(
1174
 
            'Branch.last_revision_info', ('kwaak/',),
1175
 
            'success', ('ok', '2', revid))
 
1536
            b'Branch.last_revision_info', (b'kwaak/',),
 
1537
            b'success', (b'ok', b'2', revid))
1176
1538
        transport.mkdir('kwaak')
1177
1539
        transport = transport.clone('kwaak')
1178
1540
        branch = self.make_remote_branch(transport, client)
1190
1552
        # doesn't just open in - this test probably needs to be rewritten using
1191
1553
        # a spawn()ed server.
1192
1554
        stacked_branch = self.make_branch('stacked', format='1.9')
1193
 
        memory_branch = self.make_branch('base', format='1.9')
 
1555
        self.make_branch('base', format='1.9')
1194
1556
        vfs_url = self.get_vfs_only_url('base')
1195
1557
        stacked_branch.set_stacked_on_url(vfs_url)
1196
 
        transport = stacked_branch.bzrdir.root_transport
 
1558
        transport = stacked_branch.controldir.root_transport
1197
1559
        client = FakeClient(transport.base)
1198
1560
        client.add_expected_call(
1199
 
            'Branch.get_stacked_on_url', ('stacked/',),
1200
 
            'success', ('ok', vfs_url))
 
1561
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1562
            b'success', (b'ok', vfs_url.encode('utf-8')))
1201
1563
        # XXX: Multiple calls are bad, this second call documents what is
1202
1564
        # today.
1203
1565
        client.add_expected_call(
1204
 
            'Branch.get_stacked_on_url', ('stacked/',),
1205
 
            'success', ('ok', vfs_url))
1206
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1207
 
            _client=client)
 
1566
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1567
            b'success', (b'ok', vfs_url.encode('utf-8')))
 
1568
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
1569
                              _client=client)
1208
1570
        repo_fmt = remote.RemoteRepositoryFormat()
1209
1571
        repo_fmt._custom_format = stacked_branch.repository._format
1210
1572
        branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
1211
 
            _client=client)
 
1573
                              _client=client)
1212
1574
        result = branch.get_stacked_on_url()
1213
1575
        self.assertEqual(vfs_url, result)
1214
1576
 
1215
1577
    def test_backwards_compatible(self):
1216
1578
        # like with bzr1.6 with no Branch.get_stacked_on_url rpc
1217
 
        base_branch = self.make_branch('base', format='1.6')
 
1579
        self.make_branch('base', format='1.6')
1218
1580
        stacked_branch = self.make_branch('stacked', format='1.6')
1219
1581
        stacked_branch.set_stacked_on_url('../base')
1220
1582
        client = FakeClient(self.get_url())
1221
1583
        branch_network_name = self.get_branch_format().network_name()
1222
1584
        client.add_expected_call(
1223
 
            'BzrDir.open_branchV3', ('stacked/',),
1224
 
            'success', ('branch', branch_network_name))
 
1585
            b'BzrDir.open_branchV3', (b'stacked/',),
 
1586
            b'success', (b'branch', branch_network_name))
1225
1587
        client.add_expected_call(
1226
 
            'BzrDir.find_repositoryV3', ('stacked/',),
1227
 
            'success', ('ok', '', 'no', 'no', 'yes',
1228
 
                stacked_branch.repository._format.network_name()))
 
1588
            b'BzrDir.find_repositoryV3', (b'stacked/',),
 
1589
            b'success', (b'ok', b'', b'no', b'no', b'yes',
 
1590
                         stacked_branch.repository._format.network_name()))
1229
1591
        # called twice, once from constructor and then again by us
1230
1592
        client.add_expected_call(
1231
 
            'Branch.get_stacked_on_url', ('stacked/',),
1232
 
            'unknown', ('Branch.get_stacked_on_url',))
 
1593
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1594
            b'unknown', (b'Branch.get_stacked_on_url',))
1233
1595
        client.add_expected_call(
1234
 
            'Branch.get_stacked_on_url', ('stacked/',),
1235
 
            'unknown', ('Branch.get_stacked_on_url',))
 
1596
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1597
            b'unknown', (b'Branch.get_stacked_on_url',))
1236
1598
        # this will also do vfs access, but that goes direct to the transport
1237
1599
        # and isn't seen by the FakeClient.
1238
1600
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1239
 
            remote.RemoteBzrDirFormat(), _client=client)
 
1601
                              RemoteBzrDirFormat(), _client=client)
1240
1602
        branch = bzrdir.open_branch()
1241
1603
        result = branch.get_stacked_on_url()
1242
1604
        self.assertEqual('../base', result)
1245
1607
        # repository
1246
1608
        self.assertEqual(1, len(branch.repository._fallback_repositories))
1247
1609
        self.assertEqual(1,
1248
 
            len(branch.repository._real_repository._fallback_repositories))
 
1610
                         len(branch.repository._real_repository._fallback_repositories))
1249
1611
 
1250
1612
    def test_get_stacked_on_real_branch(self):
1251
 
        base_branch = self.make_branch('base')
 
1613
        self.make_branch('base')
1252
1614
        stacked_branch = self.make_branch('stacked')
1253
1615
        stacked_branch.set_stacked_on_url('../base')
1254
1616
        reference_format = self.get_repo_format()
1256
1618
        client = FakeClient(self.get_url())
1257
1619
        branch_network_name = self.get_branch_format().network_name()
1258
1620
        client.add_expected_call(
1259
 
            'BzrDir.open_branchV3', ('stacked/',),
1260
 
            'success', ('branch', branch_network_name))
 
1621
            b'BzrDir.open_branchV3', (b'stacked/',),
 
1622
            b'success', (b'branch', branch_network_name))
1261
1623
        client.add_expected_call(
1262
 
            'BzrDir.find_repositoryV3', ('stacked/',),
1263
 
            'success', ('ok', '', 'yes', 'no', 'yes', network_name))
 
1624
            b'BzrDir.find_repositoryV3', (b'stacked/',),
 
1625
            b'success', (b'ok', b'', b'yes', b'no', b'yes', network_name))
1264
1626
        # called twice, once from constructor and then again by us
1265
1627
        client.add_expected_call(
1266
 
            'Branch.get_stacked_on_url', ('stacked/',),
1267
 
            'success', ('ok', '../base'))
 
1628
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1629
            b'success', (b'ok', b'../base'))
1268
1630
        client.add_expected_call(
1269
 
            'Branch.get_stacked_on_url', ('stacked/',),
1270
 
            'success', ('ok', '../base'))
 
1631
            b'Branch.get_stacked_on_url', (b'stacked/',),
 
1632
            b'success', (b'ok', b'../base'))
1271
1633
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1272
 
            remote.RemoteBzrDirFormat(), _client=client)
 
1634
                              RemoteBzrDirFormat(), _client=client)
1273
1635
        branch = bzrdir.open_branch()
1274
1636
        result = branch.get_stacked_on_url()
1275
1637
        self.assertEqual('../base', result)
1283
1645
class TestBranchSetLastRevision(RemoteBranchTestCase):
1284
1646
 
1285
1647
    def test_set_empty(self):
1286
 
        # set_revision_history([]) is translated to calling
 
1648
        # _set_last_revision_info('null:') is translated to calling
1287
1649
        # Branch.set_last_revision(path, '') on the wire.
1288
1650
        transport = MemoryTransport()
1289
1651
        transport.mkdir('branch')
1291
1653
 
1292
1654
        client = FakeClient(transport.base)
1293
1655
        client.add_expected_call(
1294
 
            'Branch.get_stacked_on_url', ('branch/',),
1295
 
            'error', ('NotStacked',))
1296
 
        client.add_expected_call(
1297
 
            'Branch.lock_write', ('branch/', '', ''),
1298
 
            'success', ('ok', 'branch token', 'repo token'))
1299
 
        client.add_expected_call(
1300
 
            'Branch.last_revision_info',
1301
 
            ('branch/',),
1302
 
            'success', ('ok', '0', 'null:'))
1303
 
        client.add_expected_call(
1304
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
1305
 
            'success', ('ok',))
1306
 
        client.add_expected_call(
1307
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1308
 
            'success', ('ok',))
 
1656
            b'Branch.get_stacked_on_url', (b'branch/',),
 
1657
            b'error', (b'NotStacked',))
 
1658
        client.add_expected_call(
 
1659
            b'Branch.lock_write', (b'branch/', b'', b''),
 
1660
            b'success', (b'ok', b'branch token', b'repo token'))
 
1661
        client.add_expected_call(
 
1662
            b'Branch.last_revision_info',
 
1663
            (b'branch/',),
 
1664
            b'success', (b'ok', b'0', b'null:'))
 
1665
        client.add_expected_call(
 
1666
            b'Branch.set_last_revision', (b'branch/',
 
1667
                                          b'branch token', b'repo token', b'null:',),
 
1668
            b'success', (b'ok',))
 
1669
        client.add_expected_call(
 
1670
            b'Branch.unlock', (b'branch/', b'branch token', b'repo token'),
 
1671
            b'success', (b'ok',))
1309
1672
        branch = self.make_remote_branch(transport, client)
1310
 
        # This is a hack to work around the problem that RemoteBranch currently
1311
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
1312
 
        branch._ensure_real = lambda: None
1313
1673
        branch.lock_write()
1314
 
        result = branch.set_revision_history([])
 
1674
        result = branch._set_last_revision(NULL_REVISION)
1315
1675
        branch.unlock()
1316
1676
        self.assertEqual(None, result)
1317
1677
        self.assertFinished(client)
1318
1678
 
1319
1679
    def test_set_nonempty(self):
1320
 
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
 
1680
        # set_last_revision_info(N, rev-idN) is translated to calling
1321
1681
        # Branch.set_last_revision(path, rev-idN) on the wire.
1322
1682
        transport = MemoryTransport()
1323
1683
        transport.mkdir('branch')
1325
1685
 
1326
1686
        client = FakeClient(transport.base)
1327
1687
        client.add_expected_call(
1328
 
            'Branch.get_stacked_on_url', ('branch/',),
1329
 
            'error', ('NotStacked',))
1330
 
        client.add_expected_call(
1331
 
            'Branch.lock_write', ('branch/', '', ''),
1332
 
            'success', ('ok', 'branch token', 'repo token'))
1333
 
        client.add_expected_call(
1334
 
            'Branch.last_revision_info',
1335
 
            ('branch/',),
1336
 
            'success', ('ok', '0', 'null:'))
1337
 
        lines = ['rev-id2']
1338
 
        encoded_body = bz2.compress('\n'.join(lines))
1339
 
        client.add_success_response_with_body(encoded_body, 'ok')
1340
 
        client.add_expected_call(
1341
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
1342
 
            'success', ('ok',))
1343
 
        client.add_expected_call(
1344
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1345
 
            'success', ('ok',))
 
1688
            b'Branch.get_stacked_on_url', (b'branch/',),
 
1689
            b'error', (b'NotStacked',))
 
1690
        client.add_expected_call(
 
1691
            b'Branch.lock_write', (b'branch/', b'', b''),
 
1692
            b'success', (b'ok', b'branch token', b'repo token'))
 
1693
        client.add_expected_call(
 
1694
            b'Branch.last_revision_info',
 
1695
            (b'branch/',),
 
1696
            b'success', (b'ok', b'0', b'null:'))
 
1697
        lines = [b'rev-id2']
 
1698
        encoded_body = bz2.compress(b'\n'.join(lines))
 
1699
        client.add_success_response_with_body(encoded_body, b'ok')
 
1700
        client.add_expected_call(
 
1701
            b'Branch.set_last_revision', (b'branch/',
 
1702
                                          b'branch token', b'repo token', b'rev-id2',),
 
1703
            b'success', (b'ok',))
 
1704
        client.add_expected_call(
 
1705
            b'Branch.unlock', (b'branch/', b'branch token', b'repo token'),
 
1706
            b'success', (b'ok',))
1346
1707
        branch = self.make_remote_branch(transport, client)
1347
 
        # This is a hack to work around the problem that RemoteBranch currently
1348
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
1349
 
        branch._ensure_real = lambda: None
1350
1708
        # Lock the branch, reset the record of remote calls.
1351
1709
        branch.lock_write()
1352
 
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
 
1710
        result = branch._set_last_revision(b'rev-id2')
1353
1711
        branch.unlock()
1354
1712
        self.assertEqual(None, result)
1355
1713
        self.assertFinished(client)
1361
1719
        # A response of 'NoSuchRevision' is translated into an exception.
1362
1720
        client = FakeClient(transport.base)
1363
1721
        client.add_expected_call(
1364
 
            'Branch.get_stacked_on_url', ('branch/',),
1365
 
            'error', ('NotStacked',))
1366
 
        client.add_expected_call(
1367
 
            'Branch.lock_write', ('branch/', '', ''),
1368
 
            'success', ('ok', 'branch token', 'repo token'))
1369
 
        client.add_expected_call(
1370
 
            'Branch.last_revision_info',
1371
 
            ('branch/',),
1372
 
            'success', ('ok', '0', 'null:'))
 
1722
            b'Branch.get_stacked_on_url', (b'branch/',),
 
1723
            b'error', (b'NotStacked',))
 
1724
        client.add_expected_call(
 
1725
            b'Branch.lock_write', (b'branch/', b'', b''),
 
1726
            b'success', (b'ok', b'branch token', b'repo token'))
 
1727
        client.add_expected_call(
 
1728
            b'Branch.last_revision_info',
 
1729
            (b'branch/',),
 
1730
            b'success', (b'ok', b'0', b'null:'))
1373
1731
        # get_graph calls to construct the revision history, for the set_rh
1374
1732
        # hook
1375
 
        lines = ['rev-id']
1376
 
        encoded_body = bz2.compress('\n'.join(lines))
1377
 
        client.add_success_response_with_body(encoded_body, 'ok')
1378
 
        client.add_expected_call(
1379
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
1380
 
            'error', ('NoSuchRevision', 'rev-id'))
1381
 
        client.add_expected_call(
1382
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1383
 
            'success', ('ok',))
 
1733
        lines = [b'rev-id']
 
1734
        encoded_body = bz2.compress(b'\n'.join(lines))
 
1735
        client.add_success_response_with_body(encoded_body, b'ok')
 
1736
        client.add_expected_call(
 
1737
            b'Branch.set_last_revision', (b'branch/',
 
1738
                                          b'branch token', b'repo token', b'rev-id',),
 
1739
            b'error', (b'NoSuchRevision', b'rev-id'))
 
1740
        client.add_expected_call(
 
1741
            b'Branch.unlock', (b'branch/', b'branch token', b'repo token'),
 
1742
            b'success', (b'ok',))
1384
1743
 
1385
1744
        branch = self.make_remote_branch(transport, client)
1386
1745
        branch.lock_write()
1387
1746
        self.assertRaises(
1388
 
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
 
1747
            errors.NoSuchRevision, branch._set_last_revision, b'rev-id')
1389
1748
        branch.unlock()
1390
1749
        self.assertFinished(client)
1391
1750
 
1400
1759
        rejection_msg_unicode = u'rejection message\N{INTERROBANG}'
1401
1760
        rejection_msg_utf8 = rejection_msg_unicode.encode('utf8')
1402
1761
        client.add_expected_call(
1403
 
            'Branch.get_stacked_on_url', ('branch/',),
1404
 
            'error', ('NotStacked',))
1405
 
        client.add_expected_call(
1406
 
            'Branch.lock_write', ('branch/', '', ''),
1407
 
            'success', ('ok', 'branch token', 'repo token'))
1408
 
        client.add_expected_call(
1409
 
            'Branch.last_revision_info',
1410
 
            ('branch/',),
1411
 
            'success', ('ok', '0', 'null:'))
1412
 
        lines = ['rev-id']
1413
 
        encoded_body = bz2.compress('\n'.join(lines))
1414
 
        client.add_success_response_with_body(encoded_body, 'ok')
1415
 
        client.add_expected_call(
1416
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
1417
 
            'error', ('TipChangeRejected', rejection_msg_utf8))
1418
 
        client.add_expected_call(
1419
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1420
 
            'success', ('ok',))
 
1762
            b'Branch.get_stacked_on_url', (b'branch/',),
 
1763
            b'error', (b'NotStacked',))
 
1764
        client.add_expected_call(
 
1765
            b'Branch.lock_write', (b'branch/', b'', b''),
 
1766
            b'success', (b'ok', b'branch token', b'repo token'))
 
1767
        client.add_expected_call(
 
1768
            b'Branch.last_revision_info',
 
1769
            (b'branch/',),
 
1770
            b'success', (b'ok', b'0', b'null:'))
 
1771
        lines = [b'rev-id']
 
1772
        encoded_body = bz2.compress(b'\n'.join(lines))
 
1773
        client.add_success_response_with_body(encoded_body, b'ok')
 
1774
        client.add_expected_call(
 
1775
            b'Branch.set_last_revision', (b'branch/',
 
1776
                                          b'branch token', b'repo token', b'rev-id',),
 
1777
            b'error', (b'TipChangeRejected', rejection_msg_utf8))
 
1778
        client.add_expected_call(
 
1779
            b'Branch.unlock', (b'branch/', b'branch token', b'repo token'),
 
1780
            b'success', (b'ok',))
1421
1781
        branch = self.make_remote_branch(transport, client)
1422
 
        branch._ensure_real = lambda: None
1423
1782
        branch.lock_write()
1424
1783
        # The 'TipChangeRejected' error response triggered by calling
1425
 
        # set_revision_history causes a TipChangeRejected exception.
 
1784
        # set_last_revision_info causes a TipChangeRejected exception.
1426
1785
        err = self.assertRaises(
1427
 
            errors.TipChangeRejected, branch.set_revision_history, ['rev-id'])
 
1786
            errors.TipChangeRejected,
 
1787
            branch._set_last_revision, b'rev-id')
1428
1788
        # The UTF-8 message from the response has been decoded into a unicode
1429
1789
        # object.
1430
 
        self.assertIsInstance(err.msg, unicode)
 
1790
        self.assertIsInstance(err.msg, text_type)
1431
1791
        self.assertEqual(rejection_msg_unicode, err.msg)
1432
1792
        branch.unlock()
1433
1793
        self.assertFinished(client)
1436
1796
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
1437
1797
 
1438
1798
    def test_set_last_revision_info(self):
1439
 
        # set_last_revision_info(num, 'rev-id') is translated to calling
 
1799
        # set_last_revision_info(num, b'rev-id') is translated to calling
1440
1800
        # Branch.set_last_revision_info(num, 'rev-id') on the wire.
1441
1801
        transport = MemoryTransport()
1442
1802
        transport.mkdir('branch')
1443
1803
        transport = transport.clone('branch')
1444
1804
        client = FakeClient(transport.base)
1445
1805
        # get_stacked_on_url
1446
 
        client.add_error_response('NotStacked')
 
1806
        client.add_error_response(b'NotStacked')
1447
1807
        # lock_write
1448
 
        client.add_success_response('ok', 'branch token', 'repo token')
 
1808
        client.add_success_response(b'ok', b'branch token', b'repo token')
1449
1809
        # query the current revision
1450
 
        client.add_success_response('ok', '0', 'null:')
 
1810
        client.add_success_response(b'ok', b'0', b'null:')
1451
1811
        # set_last_revision
1452
 
        client.add_success_response('ok')
 
1812
        client.add_success_response(b'ok')
1453
1813
        # unlock
1454
 
        client.add_success_response('ok')
 
1814
        client.add_success_response(b'ok')
1455
1815
 
1456
1816
        branch = self.make_remote_branch(transport, client)
1457
1817
        # Lock the branch, reset the record of remote calls.
1458
1818
        branch.lock_write()
1459
1819
        client._calls = []
1460
 
        result = branch.set_last_revision_info(1234, 'a-revision-id')
 
1820
        result = branch.set_last_revision_info(1234, b'a-revision-id')
1461
1821
        self.assertEqual(
1462
 
            [('call', 'Branch.last_revision_info', ('branch/',)),
1463
 
             ('call', 'Branch.set_last_revision_info',
1464
 
                ('branch/', 'branch token', 'repo token',
1465
 
                 '1234', 'a-revision-id'))],
 
1822
            [('call', b'Branch.last_revision_info', (b'branch/',)),
 
1823
             ('call', b'Branch.set_last_revision_info',
 
1824
                (b'branch/', b'branch token', b'repo token',
 
1825
                 b'1234', b'a-revision-id'))],
1466
1826
            client._calls)
1467
1827
        self.assertEqual(None, result)
1468
1828
 
1473
1833
        transport = transport.clone('branch')
1474
1834
        client = FakeClient(transport.base)
1475
1835
        # get_stacked_on_url
1476
 
        client.add_error_response('NotStacked')
 
1836
        client.add_error_response(b'NotStacked')
1477
1837
        # lock_write
1478
 
        client.add_success_response('ok', 'branch token', 'repo token')
 
1838
        client.add_success_response(b'ok', b'branch token', b'repo token')
1479
1839
        # set_last_revision
1480
 
        client.add_error_response('NoSuchRevision', 'revid')
 
1840
        client.add_error_response(b'NoSuchRevision', b'revid')
1481
1841
        # unlock
1482
 
        client.add_success_response('ok')
 
1842
        client.add_success_response(b'ok')
1483
1843
 
1484
1844
        branch = self.make_remote_branch(transport, client)
1485
1845
        # Lock the branch, reset the record of remote calls.
1487
1847
        client._calls = []
1488
1848
 
1489
1849
        self.assertRaises(
1490
 
            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
 
1850
            errors.NoSuchRevision, branch.set_last_revision_info, 123, b'revid')
1491
1851
        branch.unlock()
1492
1852
 
1493
1853
    def test_backwards_compatibility(self):
1507
1867
        transport = transport.clone('branch')
1508
1868
        client = FakeClient(transport.base)
1509
1869
        client.add_expected_call(
1510
 
            'Branch.get_stacked_on_url', ('branch/',),
1511
 
            'error', ('NotStacked',))
1512
 
        client.add_expected_call(
1513
 
            'Branch.last_revision_info',
1514
 
            ('branch/',),
1515
 
            'success', ('ok', '0', 'null:'))
1516
 
        client.add_expected_call(
1517
 
            'Branch.set_last_revision_info',
1518
 
            ('branch/', 'branch token', 'repo token', '1234', 'a-revision-id',),
1519
 
            'unknown', 'Branch.set_last_revision_info')
 
1870
            b'Branch.get_stacked_on_url', (b'branch/',),
 
1871
            b'error', (b'NotStacked',))
 
1872
        client.add_expected_call(
 
1873
            b'Branch.last_revision_info',
 
1874
            (b'branch/',),
 
1875
            b'success', (b'ok', b'0', b'null:'))
 
1876
        client.add_expected_call(
 
1877
            b'Branch.set_last_revision_info',
 
1878
            (b'branch/', b'branch token', b'repo token', b'1234', b'a-revision-id',),
 
1879
            b'unknown', b'Branch.set_last_revision_info')
1520
1880
 
1521
1881
        branch = self.make_remote_branch(transport, client)
 
1882
 
1522
1883
        class StubRealBranch(object):
1523
1884
            def __init__(self):
1524
1885
                self.calls = []
 
1886
 
1525
1887
            def set_last_revision_info(self, revno, revision_id):
1526
1888
                self.calls.append(
1527
1889
                    ('set_last_revision_info', revno, revision_id))
 
1890
 
1528
1891
            def _clear_cached_state(self):
1529
1892
                pass
1530
1893
        real_branch = StubRealBranch()
1532
1895
        self.lock_remote_branch(branch)
1533
1896
 
1534
1897
        # Call set_last_revision_info, and verify it behaved as expected.
1535
 
        result = branch.set_last_revision_info(1234, 'a-revision-id')
 
1898
        branch.set_last_revision_info(1234, b'a-revision-id')
1536
1899
        self.assertEqual(
1537
 
            [('set_last_revision_info', 1234, 'a-revision-id')],
 
1900
            [('set_last_revision_info', 1234, b'a-revision-id')],
1538
1901
            real_branch.calls)
1539
1902
        self.assertFinished(client)
1540
1903
 
1547
1910
        transport = transport.clone('branch')
1548
1911
        client = FakeClient(transport.base)
1549
1912
        # get_stacked_on_url
1550
 
        client.add_error_response('NotStacked')
 
1913
        client.add_error_response(b'NotStacked')
1551
1914
        # lock_write
1552
 
        client.add_success_response('ok', 'branch token', 'repo token')
 
1915
        client.add_success_response(b'ok', b'branch token', b'repo token')
1553
1916
        # set_last_revision
1554
 
        client.add_error_response('UnexpectedError')
 
1917
        client.add_error_response(b'UnexpectedError')
1555
1918
        # unlock
1556
 
        client.add_success_response('ok')
 
1919
        client.add_success_response(b'ok')
1557
1920
 
1558
1921
        branch = self.make_remote_branch(transport, client)
1559
1922
        # Lock the branch, reset the record of remote calls.
1562
1925
 
1563
1926
        err = self.assertRaises(
1564
1927
            errors.UnknownErrorFromSmartServer,
1565
 
            branch.set_last_revision_info, 123, 'revid')
1566
 
        self.assertEqual(('UnexpectedError',), err.error_tuple)
 
1928
            branch.set_last_revision_info, 123, b'revid')
 
1929
        self.assertEqual((b'UnexpectedError',), err.error_tuple)
1567
1930
        branch.unlock()
1568
1931
 
1569
1932
    def test_tip_change_rejected(self):
1575
1938
        transport = transport.clone('branch')
1576
1939
        client = FakeClient(transport.base)
1577
1940
        # get_stacked_on_url
1578
 
        client.add_error_response('NotStacked')
 
1941
        client.add_error_response(b'NotStacked')
1579
1942
        # lock_write
1580
 
        client.add_success_response('ok', 'branch token', 'repo token')
 
1943
        client.add_success_response(b'ok', b'branch token', b'repo token')
1581
1944
        # set_last_revision
1582
 
        client.add_error_response('TipChangeRejected', 'rejection message')
 
1945
        client.add_error_response(b'TipChangeRejected', b'rejection message')
1583
1946
        # unlock
1584
 
        client.add_success_response('ok')
 
1947
        client.add_success_response(b'ok')
1585
1948
 
1586
1949
        branch = self.make_remote_branch(transport, client)
1587
1950
        # Lock the branch, reset the record of remote calls.
1593
1956
        # set_last_revision_info causes a TipChangeRejected exception.
1594
1957
        err = self.assertRaises(
1595
1958
            errors.TipChangeRejected,
1596
 
            branch.set_last_revision_info, 123, 'revid')
 
1959
            branch.set_last_revision_info, 123, b'revid')
1597
1960
        self.assertEqual('rejection message', err.msg)
1598
1961
 
1599
1962
 
1603
1966
        # in an empty branch we decode the response properly
1604
1967
        client = FakeClient()
1605
1968
        client.add_expected_call(
1606
 
            'Branch.get_stacked_on_url', ('memory:///',),
1607
 
            'error', ('NotStacked',),)
1608
 
        client.add_success_response_with_body('# config file body', 'ok')
 
1969
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
1970
            b'error', (b'NotStacked',),)
 
1971
        client.add_success_response_with_body(b'# config file body', b'ok')
1609
1972
        transport = MemoryTransport()
1610
1973
        branch = self.make_remote_branch(transport, client)
1611
1974
        config = branch.get_config()
1612
1975
        config.has_explicit_nickname()
1613
1976
        self.assertEqual(
1614
 
            [('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1615
 
             ('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
 
1977
            [('call', b'Branch.get_stacked_on_url', (b'memory:///',)),
 
1978
             ('call_expecting_body', b'Branch.get_config_file', (b'memory:///',))],
1616
1979
            client._calls)
1617
1980
 
1618
1981
    def test_get_multi_line_branch_conf(self):
1619
1982
        # Make sure that multiple-line branch.conf files are supported
1620
1983
        #
1621
 
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
 
1984
        # https://bugs.launchpad.net/bzr/+bug/354075
1622
1985
        client = FakeClient()
1623
1986
        client.add_expected_call(
1624
 
            'Branch.get_stacked_on_url', ('memory:///',),
1625
 
            'error', ('NotStacked',),)
1626
 
        client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
 
1987
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
1988
            b'error', (b'NotStacked',),)
 
1989
        client.add_success_response_with_body(b'a = 1\nb = 2\nc = 3\n', b'ok')
1627
1990
        transport = MemoryTransport()
1628
1991
        branch = self.make_remote_branch(transport, client)
1629
1992
        config = branch.get_config()
1632
1995
    def test_set_option(self):
1633
1996
        client = FakeClient()
1634
1997
        client.add_expected_call(
1635
 
            'Branch.get_stacked_on_url', ('memory:///',),
1636
 
            'error', ('NotStacked',),)
1637
 
        client.add_expected_call(
1638
 
            'Branch.lock_write', ('memory:///', '', ''),
1639
 
            'success', ('ok', 'branch token', 'repo token'))
1640
 
        client.add_expected_call(
1641
 
            'Branch.set_config_option', ('memory:///', 'branch token',
1642
 
            'repo token', 'foo', 'bar', ''),
1643
 
            'success', ())
1644
 
        client.add_expected_call(
1645
 
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1646
 
            'success', ('ok',))
 
1998
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
1999
            b'error', (b'NotStacked',),)
 
2000
        client.add_expected_call(
 
2001
            b'Branch.lock_write', (b'memory:///', b'', b''),
 
2002
            b'success', (b'ok', b'branch token', b'repo token'))
 
2003
        client.add_expected_call(
 
2004
            b'Branch.set_config_option', (b'memory:///', b'branch token',
 
2005
                                          b'repo token', b'foo', b'bar', b''),
 
2006
            b'success', ())
 
2007
        client.add_expected_call(
 
2008
            b'Branch.unlock', (b'memory:///', b'branch token', b'repo token'),
 
2009
            b'success', (b'ok',))
1647
2010
        transport = MemoryTransport()
1648
2011
        branch = self.make_remote_branch(transport, client)
1649
2012
        branch.lock_write()
1652
2015
        branch.unlock()
1653
2016
        self.assertFinished(client)
1654
2017
 
 
2018
    def test_set_option_with_dict(self):
 
2019
        client = FakeClient()
 
2020
        client.add_expected_call(
 
2021
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2022
            b'error', (b'NotStacked',),)
 
2023
        client.add_expected_call(
 
2024
            b'Branch.lock_write', (b'memory:///', b'', b''),
 
2025
            b'success', (b'ok', b'branch token', b'repo token'))
 
2026
        encoded_dict_value = b'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
 
2027
        client.add_expected_call(
 
2028
            b'Branch.set_config_option_dict', (b'memory:///', b'branch token',
 
2029
                                               b'repo token', encoded_dict_value, b'foo', b''),
 
2030
            b'success', ())
 
2031
        client.add_expected_call(
 
2032
            b'Branch.unlock', (b'memory:///', b'branch token', b'repo token'),
 
2033
            b'success', (b'ok',))
 
2034
        transport = MemoryTransport()
 
2035
        branch = self.make_remote_branch(transport, client)
 
2036
        branch.lock_write()
 
2037
        config = branch._get_config()
 
2038
        config.set_option(
 
2039
            {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
 
2040
            'foo')
 
2041
        branch.unlock()
 
2042
        self.assertFinished(client)
 
2043
 
 
2044
    def test_set_option_with_bool(self):
 
2045
        client = FakeClient()
 
2046
        client.add_expected_call(
 
2047
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2048
            b'error', (b'NotStacked',),)
 
2049
        client.add_expected_call(
 
2050
            b'Branch.lock_write', (b'memory:///', b'', b''),
 
2051
            b'success', (b'ok', b'branch token', b'repo token'))
 
2052
        client.add_expected_call(
 
2053
            b'Branch.set_config_option', (b'memory:///', b'branch token',
 
2054
                                          b'repo token', b'True', b'foo', b''),
 
2055
            b'success', ())
 
2056
        client.add_expected_call(
 
2057
            b'Branch.unlock', (b'memory:///', b'branch token', b'repo token'),
 
2058
            b'success', (b'ok',))
 
2059
        transport = MemoryTransport()
 
2060
        branch = self.make_remote_branch(transport, client)
 
2061
        branch.lock_write()
 
2062
        config = branch._get_config()
 
2063
        config.set_option(True, 'foo')
 
2064
        branch.unlock()
 
2065
        self.assertFinished(client)
 
2066
 
1655
2067
    def test_backwards_compat_set_option(self):
1656
2068
        self.setup_smart_server_with_call_log()
1657
2069
        branch = self.make_branch('.')
1658
 
        verb = 'Branch.set_config_option'
 
2070
        verb = b'Branch.set_config_option'
1659
2071
        self.disable_verb(verb)
1660
2072
        branch.lock_write()
1661
2073
        self.addCleanup(branch.unlock)
1662
2074
        self.reset_smart_call_log()
1663
2075
        branch._get_config().set_option('value', 'name')
1664
 
        self.assertLength(10, self.hpss_calls)
 
2076
        self.assertLength(11, self.hpss_calls)
1665
2077
        self.assertEqual('value', branch._get_config().get_option('name'))
1666
2078
 
 
2079
    def test_backwards_compat_set_option_with_dict(self):
 
2080
        self.setup_smart_server_with_call_log()
 
2081
        branch = self.make_branch('.')
 
2082
        verb = b'Branch.set_config_option_dict'
 
2083
        self.disable_verb(verb)
 
2084
        branch.lock_write()
 
2085
        self.addCleanup(branch.unlock)
 
2086
        self.reset_smart_call_log()
 
2087
        config = branch._get_config()
 
2088
        value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
 
2089
        config.set_option(value_dict, 'name')
 
2090
        self.assertLength(11, self.hpss_calls)
 
2091
        self.assertEqual(value_dict, branch._get_config().get_option('name'))
 
2092
 
 
2093
 
 
2094
class TestBranchGetPutConfigStore(RemoteBranchTestCase):
 
2095
 
 
2096
    def test_get_branch_conf(self):
 
2097
        # in an empty branch we decode the response properly
 
2098
        client = FakeClient()
 
2099
        client.add_expected_call(
 
2100
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2101
            b'error', (b'NotStacked',),)
 
2102
        client.add_success_response_with_body(b'# config file body', b'ok')
 
2103
        transport = MemoryTransport()
 
2104
        branch = self.make_remote_branch(transport, client)
 
2105
        config = branch.get_config_stack()
 
2106
        config.get("email")
 
2107
        config.get("log_format")
 
2108
        self.assertEqual(
 
2109
            [('call', b'Branch.get_stacked_on_url', (b'memory:///',)),
 
2110
             ('call_expecting_body', b'Branch.get_config_file', (b'memory:///',))],
 
2111
            client._calls)
 
2112
 
 
2113
    def test_set_branch_conf(self):
 
2114
        client = FakeClient()
 
2115
        client.add_expected_call(
 
2116
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2117
            b'error', (b'NotStacked',),)
 
2118
        client.add_expected_call(
 
2119
            b'Branch.lock_write', (b'memory:///', b'', b''),
 
2120
            b'success', (b'ok', b'branch token', b'repo token'))
 
2121
        client.add_expected_call(
 
2122
            b'Branch.get_config_file', (b'memory:///', ),
 
2123
            b'success', (b'ok', ), b"# line 1\n")
 
2124
        client.add_expected_call(
 
2125
            b'Branch.get_config_file', (b'memory:///', ),
 
2126
            b'success', (b'ok', ), b"# line 1\n")
 
2127
        client.add_expected_call(
 
2128
            b'Branch.put_config_file', (b'memory:///', b'branch token',
 
2129
                                        b'repo token'),
 
2130
            b'success', (b'ok',))
 
2131
        client.add_expected_call(
 
2132
            b'Branch.unlock', (b'memory:///', b'branch token', b'repo token'),
 
2133
            b'success', (b'ok',))
 
2134
        transport = MemoryTransport()
 
2135
        branch = self.make_remote_branch(transport, client)
 
2136
        branch.lock_write()
 
2137
        config = branch.get_config_stack()
 
2138
        config.set('email', 'The Dude <lebowski@example.com>')
 
2139
        branch.unlock()
 
2140
        self.assertFinished(client)
 
2141
        self.assertEqual(
 
2142
            [('call', b'Branch.get_stacked_on_url', (b'memory:///',)),
 
2143
             ('call', b'Branch.lock_write', (b'memory:///', b'', b'')),
 
2144
             ('call_expecting_body', b'Branch.get_config_file',
 
2145
                 (b'memory:///',)),
 
2146
             ('call_expecting_body', b'Branch.get_config_file',
 
2147
                 (b'memory:///',)),
 
2148
             ('call_with_body_bytes_expecting_body', b'Branch.put_config_file',
 
2149
                 (b'memory:///', b'branch token', b'repo token'),
 
2150
                 b'# line 1\nemail = The Dude <lebowski@example.com>\n'),
 
2151
             ('call', b'Branch.unlock',
 
2152
                 (b'memory:///', b'branch token', b'repo token'))],
 
2153
            client._calls)
 
2154
 
1667
2155
 
1668
2156
class TestBranchLockWrite(RemoteBranchTestCase):
1669
2157
 
1671
2159
        transport = MemoryTransport()
1672
2160
        client = FakeClient(transport.base)
1673
2161
        client.add_expected_call(
1674
 
            'Branch.get_stacked_on_url', ('quack/',),
1675
 
            'error', ('NotStacked',),)
 
2162
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2163
            b'error', (b'NotStacked',),)
1676
2164
        client.add_expected_call(
1677
 
            'Branch.lock_write', ('quack/', '', ''),
1678
 
            'error', ('UnlockableTransport',))
 
2165
            b'Branch.lock_write', (b'quack/', b'', b''),
 
2166
            b'error', (b'UnlockableTransport',))
1679
2167
        transport.mkdir('quack')
1680
2168
        transport = transport.clone('quack')
1681
2169
        branch = self.make_remote_branch(transport, client)
1683
2171
        self.assertFinished(client)
1684
2172
 
1685
2173
 
 
2174
class TestBranchRevisionIdToRevno(RemoteBranchTestCase):
 
2175
 
 
2176
    def test_simple(self):
 
2177
        transport = MemoryTransport()
 
2178
        client = FakeClient(transport.base)
 
2179
        client.add_expected_call(
 
2180
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2181
            b'error', (b'NotStacked',),)
 
2182
        client.add_expected_call(
 
2183
            b'Branch.revision_id_to_revno', (b'quack/', b'null:'),
 
2184
            b'success', (b'ok', b'0',),)
 
2185
        client.add_expected_call(
 
2186
            b'Branch.revision_id_to_revno', (b'quack/', b'unknown'),
 
2187
            b'error', (b'NoSuchRevision', b'unknown',),)
 
2188
        transport.mkdir('quack')
 
2189
        transport = transport.clone('quack')
 
2190
        branch = self.make_remote_branch(transport, client)
 
2191
        self.assertEqual(0, branch.revision_id_to_revno(b'null:'))
 
2192
        self.assertRaises(errors.NoSuchRevision,
 
2193
                          branch.revision_id_to_revno, b'unknown')
 
2194
        self.assertFinished(client)
 
2195
 
 
2196
    def test_dotted(self):
 
2197
        transport = MemoryTransport()
 
2198
        client = FakeClient(transport.base)
 
2199
        client.add_expected_call(
 
2200
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2201
            b'error', (b'NotStacked',),)
 
2202
        client.add_expected_call(
 
2203
            b'Branch.revision_id_to_revno', (b'quack/', b'null:'),
 
2204
            b'success', (b'ok', b'0',),)
 
2205
        client.add_expected_call(
 
2206
            b'Branch.revision_id_to_revno', (b'quack/', b'unknown'),
 
2207
            b'error', (b'NoSuchRevision', b'unknown',),)
 
2208
        transport.mkdir('quack')
 
2209
        transport = transport.clone('quack')
 
2210
        branch = self.make_remote_branch(transport, client)
 
2211
        self.assertEqual((0, ), branch.revision_id_to_dotted_revno(b'null:'))
 
2212
        self.assertRaises(errors.NoSuchRevision,
 
2213
                          branch.revision_id_to_dotted_revno, b'unknown')
 
2214
        self.assertFinished(client)
 
2215
 
 
2216
    def test_ghost_revid(self):
 
2217
        transport = MemoryTransport()
 
2218
        client = FakeClient(transport.base)
 
2219
        client.add_expected_call(
 
2220
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2221
            b'error', (b'NotStacked',),)
 
2222
        # Some older versions of bzr/brz didn't explicitly return
 
2223
        # GhostRevisionsHaveNoRevno
 
2224
        client.add_expected_call(
 
2225
            b'Branch.revision_id_to_revno', (b'quack/', b'revid'),
 
2226
            b'error', (b'error', b'GhostRevisionsHaveNoRevno',
 
2227
                       b'The reivison {revid} was not found because there was '
 
2228
                       b'a ghost at {ghost-revid}'))
 
2229
        client.add_expected_call(
 
2230
            b'Branch.revision_id_to_revno', (b'quack/', b'revid'),
 
2231
            b'error', (b'GhostRevisionsHaveNoRevno', b'revid', b'ghost-revid',))
 
2232
        transport.mkdir('quack')
 
2233
        transport = transport.clone('quack')
 
2234
        branch = self.make_remote_branch(transport, client)
 
2235
        self.assertRaises(errors.GhostRevisionsHaveNoRevno,
 
2236
                          branch.revision_id_to_dotted_revno, b'revid')
 
2237
        self.assertRaises(errors.GhostRevisionsHaveNoRevno,
 
2238
                          branch.revision_id_to_dotted_revno, b'revid')
 
2239
        self.assertFinished(client)
 
2240
 
 
2241
    def test_dotted_no_smart_verb(self):
 
2242
        self.setup_smart_server_with_call_log()
 
2243
        branch = self.make_branch('.')
 
2244
        self.disable_verb(b'Branch.revision_id_to_revno')
 
2245
        self.reset_smart_call_log()
 
2246
        self.assertEqual((0, ),
 
2247
                         branch.revision_id_to_dotted_revno(b'null:'))
 
2248
        self.assertLength(8, self.hpss_calls)
 
2249
 
 
2250
 
1686
2251
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1687
2252
 
1688
2253
    def test__get_config(self):
1689
2254
        client = FakeClient()
1690
 
        client.add_success_response_with_body('default_stack_on = /\n', 'ok')
 
2255
        client.add_success_response_with_body(b'default_stack_on = /\n', b'ok')
1691
2256
        transport = MemoryTransport()
1692
2257
        bzrdir = self.make_remote_bzrdir(transport, client)
1693
2258
        config = bzrdir.get_config()
1694
2259
        self.assertEqual('/', config.get_default_stack_on())
1695
2260
        self.assertEqual(
1696
 
            [('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
 
2261
            [('call_expecting_body', b'BzrDir.get_config_file',
 
2262
                (b'memory:///',))],
1697
2263
            client._calls)
1698
2264
 
1699
2265
    def test_set_option_uses_vfs(self):
1700
2266
        self.setup_smart_server_with_call_log()
1701
 
        bzrdir = self.make_bzrdir('.')
 
2267
        bzrdir = self.make_controldir('.')
1702
2268
        self.reset_smart_call_log()
1703
2269
        config = bzrdir.get_config()
1704
2270
        config.set_default_stack_on('/')
1705
 
        self.assertLength(3, self.hpss_calls)
 
2271
        self.assertLength(4, self.hpss_calls)
1706
2272
 
1707
2273
    def test_backwards_compat_get_option(self):
1708
2274
        self.setup_smart_server_with_call_log()
1709
 
        bzrdir = self.make_bzrdir('.')
1710
 
        verb = 'BzrDir.get_config_file'
 
2275
        bzrdir = self.make_controldir('.')
 
2276
        verb = b'BzrDir.get_config_file'
1711
2277
        self.disable_verb(verb)
1712
2278
        self.reset_smart_call_log()
1713
2279
        self.assertEqual(None,
1714
 
            bzrdir._get_config().get_option('default_stack_on'))
1715
 
        self.assertLength(3, self.hpss_calls)
 
2280
                         bzrdir._get_config().get_option('default_stack_on'))
 
2281
        self.assertLength(4, self.hpss_calls)
1716
2282
 
1717
2283
 
1718
2284
class TestTransportIsReadonly(tests.TestCase):
1719
2285
 
1720
2286
    def test_true(self):
1721
2287
        client = FakeClient()
1722
 
        client.add_success_response('yes')
 
2288
        client.add_success_response(b'yes')
1723
2289
        transport = RemoteTransport('bzr://example.com/', medium=False,
1724
2290
                                    _client=client)
1725
2291
        self.assertEqual(True, transport.is_readonly())
1726
2292
        self.assertEqual(
1727
 
            [('call', 'Transport.is_readonly', ())],
 
2293
            [('call', b'Transport.is_readonly', ())],
1728
2294
            client._calls)
1729
2295
 
1730
2296
    def test_false(self):
1731
2297
        client = FakeClient()
1732
 
        client.add_success_response('no')
 
2298
        client.add_success_response(b'no')
1733
2299
        transport = RemoteTransport('bzr://example.com/', medium=False,
1734
2300
                                    _client=client)
1735
2301
        self.assertEqual(False, transport.is_readonly())
1736
2302
        self.assertEqual(
1737
 
            [('call', 'Transport.is_readonly', ())],
 
2303
            [('call', b'Transport.is_readonly', ())],
1738
2304
            client._calls)
1739
2305
 
1740
2306
    def test_error_from_old_server(self):
1745
2311
        underlying filesystem could be readonly anyway).
1746
2312
        """
1747
2313
        client = FakeClient()
1748
 
        client.add_unknown_method_response('Transport.is_readonly')
 
2314
        client.add_unknown_method_response(b'Transport.is_readonly')
1749
2315
        transport = RemoteTransport('bzr://example.com/', medium=False,
1750
2316
                                    _client=client)
1751
2317
        self.assertEqual(False, transport.is_readonly())
1752
2318
        self.assertEqual(
1753
 
            [('call', 'Transport.is_readonly', ())],
 
2319
            [('call', b'Transport.is_readonly', ())],
1754
2320
            client._calls)
1755
2321
 
1756
2322
 
1758
2324
 
1759
2325
    def test_permissiondenied(self):
1760
2326
        client = FakeClient()
1761
 
        client.add_error_response('PermissionDenied', 'remote path', 'extra')
 
2327
        client.add_error_response(
 
2328
            b'PermissionDenied', b'remote path', b'extra')
1762
2329
        transport = RemoteTransport('bzr://example.com/', medium=False,
1763
2330
                                    _client=client)
1764
2331
        exc = self.assertRaises(
1777
2344
        conf = config.AuthenticationConfig()
1778
2345
        conf._get_config().update(
1779
2346
            {'bzr+sshtest': {'scheme': 'ssh', 'user': 'bar', 'host':
1780
 
            'example.com'}})
 
2347
                             'example.com'}})
1781
2348
        conf._save()
1782
2349
        t = RemoteSSHTransport('bzr+ssh://example.com')
1783
2350
        self.assertEqual('bar', t._get_credentials()[0])
1805
2372
        client = FakeClient(transport.base)
1806
2373
        transport = transport.clone(transport_path)
1807
2374
        # we do not want bzrdir to make any remote calls
1808
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1809
 
            _client=False)
 
2375
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
2376
                              _client=False)
1810
2377
        repo = RemoteRepository(bzrdir, None, _client=client)
1811
2378
        return repo, client
1812
2379
 
1819
2386
 
1820
2387
    def test_get_format_description(self):
1821
2388
        remote_format = RemoteBranchFormat()
1822
 
        real_format = branch.BranchFormat.get_default_format()
 
2389
        real_format = branch.format_registry.get_default()
1823
2390
        remote_format._network_name = real_format.network_name()
1824
2391
        self.assertEqual(remoted_description(real_format),
1825
 
            remote_format.get_format_description())
 
2392
                         remote_format.get_format_description())
1826
2393
 
1827
2394
 
1828
2395
class TestRepositoryFormat(TestRemoteRepository):
1829
2396
 
1830
2397
    def test_fast_delta(self):
1831
 
        true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
 
2398
        true_name = groupcompress_repo.RepositoryFormat2a().network_name()
1832
2399
        true_format = RemoteRepositoryFormat()
1833
2400
        true_format._network_name = true_name
1834
2401
        self.assertEqual(True, true_format.fast_deltas)
1835
 
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
 
2402
        false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
1836
2403
        false_format = RemoteRepositoryFormat()
1837
2404
        false_format._network_name = false_name
1838
2405
        self.assertEqual(False, false_format.fast_deltas)
1839
2406
 
1840
2407
    def test_get_format_description(self):
1841
2408
        remote_repo_format = RemoteRepositoryFormat()
1842
 
        real_format = repository.RepositoryFormat.get_default_format()
 
2409
        real_format = repository.format_registry.get_default()
1843
2410
        remote_repo_format._network_name = real_format.network_name()
1844
2411
        self.assertEqual(remoted_description(real_format),
1845
 
            remote_repo_format.get_format_description())
 
2412
                         remote_repo_format.get_format_description())
 
2413
 
 
2414
 
 
2415
class TestRepositoryAllRevisionIds(TestRemoteRepository):
 
2416
 
 
2417
    def test_empty(self):
 
2418
        transport_path = 'quack'
 
2419
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2420
        client.add_success_response_with_body(b'', b'ok')
 
2421
        self.assertEqual([], repo.all_revision_ids())
 
2422
        self.assertEqual(
 
2423
            [('call_expecting_body', b'Repository.all_revision_ids',
 
2424
              (b'quack/',))],
 
2425
            client._calls)
 
2426
 
 
2427
    def test_with_some_content(self):
 
2428
        transport_path = 'quack'
 
2429
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2430
        client.add_success_response_with_body(
 
2431
            b'rev1\nrev2\nanotherrev\n', b'ok')
 
2432
        self.assertEqual(
 
2433
            set([b"rev1", b"rev2", b"anotherrev"]),
 
2434
            set(repo.all_revision_ids()))
 
2435
        self.assertEqual(
 
2436
            [('call_expecting_body', b'Repository.all_revision_ids',
 
2437
              (b'quack/',))],
 
2438
            client._calls)
1846
2439
 
1847
2440
 
1848
2441
class TestRepositoryGatherStats(TestRemoteRepository):
1852
2445
        transport_path = 'quack'
1853
2446
        repo, client = self.setup_fake_client_and_repository(transport_path)
1854
2447
        client.add_success_response_with_body(
1855
 
            'revisions: 2\nsize: 18\n', 'ok')
 
2448
            b'revisions: 2\nsize: 18\n', b'ok')
1856
2449
        result = repo.gather_stats(None)
1857
2450
        self.assertEqual(
1858
 
            [('call_expecting_body', 'Repository.gather_stats',
1859
 
             ('quack/','','no'))],
 
2451
            [('call_expecting_body', b'Repository.gather_stats',
 
2452
              (b'quack/', b'', b'no'))],
1860
2453
            client._calls)
1861
2454
        self.assertEqual({'revisions': 2, 'size': 18}, result)
1862
2455
 
1863
2456
    def test_revid_no_committers(self):
1864
2457
        # ('ok',), body without committers
1865
 
        body = ('firstrev: 123456.300 3600\n'
1866
 
                'latestrev: 654231.400 0\n'
1867
 
                'revisions: 2\n'
1868
 
                'size: 18\n')
 
2458
        body = (b'firstrev: 123456.300 3600\n'
 
2459
                b'latestrev: 654231.400 0\n'
 
2460
                b'revisions: 2\n'
 
2461
                b'size: 18\n')
1869
2462
        transport_path = 'quick'
1870
2463
        revid = u'\xc8'.encode('utf8')
1871
2464
        repo, client = self.setup_fake_client_and_repository(transport_path)
1872
 
        client.add_success_response_with_body(body, 'ok')
 
2465
        client.add_success_response_with_body(body, b'ok')
1873
2466
        result = repo.gather_stats(revid)
1874
2467
        self.assertEqual(
1875
 
            [('call_expecting_body', 'Repository.gather_stats',
1876
 
              ('quick/', revid, 'no'))],
 
2468
            [('call_expecting_body', b'Repository.gather_stats',
 
2469
              (b'quick/', revid, b'no'))],
1877
2470
            client._calls)
1878
2471
        self.assertEqual({'revisions': 2, 'size': 18,
1879
2472
                          'firstrev': (123456.300, 3600),
1880
 
                          'latestrev': (654231.400, 0),},
 
2473
                          'latestrev': (654231.400, 0), },
1881
2474
                         result)
1882
2475
 
1883
2476
    def test_revid_with_committers(self):
1884
2477
        # ('ok',), body with committers
1885
 
        body = ('committers: 128\n'
1886
 
                'firstrev: 123456.300 3600\n'
1887
 
                'latestrev: 654231.400 0\n'
1888
 
                'revisions: 2\n'
1889
 
                'size: 18\n')
 
2478
        body = (b'committers: 128\n'
 
2479
                b'firstrev: 123456.300 3600\n'
 
2480
                b'latestrev: 654231.400 0\n'
 
2481
                b'revisions: 2\n'
 
2482
                b'size: 18\n')
1890
2483
        transport_path = 'buick'
1891
2484
        revid = u'\xc8'.encode('utf8')
1892
2485
        repo, client = self.setup_fake_client_and_repository(transport_path)
1893
 
        client.add_success_response_with_body(body, 'ok')
 
2486
        client.add_success_response_with_body(body, b'ok')
1894
2487
        result = repo.gather_stats(revid, True)
1895
2488
        self.assertEqual(
1896
 
            [('call_expecting_body', 'Repository.gather_stats',
1897
 
              ('buick/', revid, 'yes'))],
 
2489
            [('call_expecting_body', b'Repository.gather_stats',
 
2490
              (b'buick/', revid, b'yes'))],
1898
2491
            client._calls)
1899
2492
        self.assertEqual({'revisions': 2, 'size': 18,
1900
2493
                          'committers': 128,
1901
2494
                          'firstrev': (123456.300, 3600),
1902
 
                          'latestrev': (654231.400, 0),},
 
2495
                          'latestrev': (654231.400, 0), },
1903
2496
                         result)
1904
2497
 
1905
2498
 
 
2499
class TestRepositoryBreakLock(TestRemoteRepository):
 
2500
 
 
2501
    def test_break_lock(self):
 
2502
        transport_path = 'quack'
 
2503
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2504
        client.add_success_response(b'ok')
 
2505
        repo.break_lock()
 
2506
        self.assertEqual(
 
2507
            [('call', b'Repository.break_lock', (b'quack/',))],
 
2508
            client._calls)
 
2509
 
 
2510
 
 
2511
class TestRepositoryGetSerializerFormat(TestRemoteRepository):
 
2512
 
 
2513
    def test_get_serializer_format(self):
 
2514
        transport_path = 'hill'
 
2515
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2516
        client.add_success_response(b'ok', b'7')
 
2517
        self.assertEqual(b'7', repo.get_serializer_format())
 
2518
        self.assertEqual(
 
2519
            [('call', b'VersionedFileRepository.get_serializer_format',
 
2520
              (b'hill/', ))],
 
2521
            client._calls)
 
2522
 
 
2523
 
 
2524
class TestRepositoryReconcile(TestRemoteRepository):
 
2525
 
 
2526
    def test_reconcile(self):
 
2527
        transport_path = 'hill'
 
2528
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2529
        body = (b"garbage_inventories: 2\n"
 
2530
                b"inconsistent_parents: 3\n")
 
2531
        client.add_expected_call(
 
2532
            b'Repository.lock_write', (b'hill/', b''),
 
2533
            b'success', (b'ok', b'a token'))
 
2534
        client.add_success_response_with_body(body, b'ok')
 
2535
        reconciler = repo.reconcile()
 
2536
        self.assertEqual(
 
2537
            [('call', b'Repository.lock_write', (b'hill/', b'')),
 
2538
             ('call_expecting_body', b'Repository.reconcile',
 
2539
                (b'hill/', b'a token'))],
 
2540
            client._calls)
 
2541
        self.assertEqual(2, reconciler.garbage_inventories)
 
2542
        self.assertEqual(3, reconciler.inconsistent_parents)
 
2543
 
 
2544
 
 
2545
class TestRepositoryGetRevisionSignatureText(TestRemoteRepository):
 
2546
 
 
2547
    def test_text(self):
 
2548
        # ('ok',), body with signature text
 
2549
        transport_path = 'quack'
 
2550
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2551
        client.add_success_response_with_body(
 
2552
            b'THETEXT', b'ok')
 
2553
        self.assertEqual(b"THETEXT", repo.get_signature_text(b"revid"))
 
2554
        self.assertEqual(
 
2555
            [('call_expecting_body', b'Repository.get_revision_signature_text',
 
2556
              (b'quack/', b'revid'))],
 
2557
            client._calls)
 
2558
 
 
2559
    def test_no_signature(self):
 
2560
        transport_path = 'quick'
 
2561
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2562
        client.add_error_response(b'nosuchrevision', b'unknown')
 
2563
        self.assertRaises(errors.NoSuchRevision, repo.get_signature_text,
 
2564
                          b"unknown")
 
2565
        self.assertEqual(
 
2566
            [('call_expecting_body', b'Repository.get_revision_signature_text',
 
2567
              (b'quick/', b'unknown'))],
 
2568
            client._calls)
 
2569
 
 
2570
 
1906
2571
class TestRepositoryGetGraph(TestRemoteRepository):
1907
2572
 
1908
2573
    def test_get_graph(self):
1913
2578
        self.assertNotEqual(graph._parents_provider, repo)
1914
2579
 
1915
2580
 
 
2581
class TestRepositoryAddSignatureText(TestRemoteRepository):
 
2582
 
 
2583
    def test_add_signature_text(self):
 
2584
        transport_path = 'quack'
 
2585
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2586
        client.add_expected_call(
 
2587
            b'Repository.lock_write', (b'quack/', b''),
 
2588
            b'success', (b'ok', b'a token'))
 
2589
        client.add_expected_call(
 
2590
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
2591
            b'success', (b'ok', (b'token1', )))
 
2592
        client.add_expected_call(
 
2593
            b'Repository.add_signature_text', (b'quack/', b'a token', b'rev1',
 
2594
                                               b'token1'),
 
2595
            b'success', (b'ok', ), None)
 
2596
        repo.lock_write()
 
2597
        repo.start_write_group()
 
2598
        self.assertIs(
 
2599
            None, repo.add_signature_text(b"rev1", b"every bloody emperor"))
 
2600
        self.assertEqual(
 
2601
            ('call_with_body_bytes_expecting_body',
 
2602
             b'Repository.add_signature_text',
 
2603
                (b'quack/', b'a token', b'rev1', b'token1'),
 
2604
             b'every bloody emperor'),
 
2605
            client._calls[-1])
 
2606
 
 
2607
 
1916
2608
class TestRepositoryGetParentMap(TestRemoteRepository):
1917
2609
 
1918
2610
    def test_get_parent_map_caching(self):
1920
2612
        # setup a reponse with two revisions
1921
2613
        r1 = u'\u0e33'.encode('utf8')
1922
2614
        r2 = u'\u0dab'.encode('utf8')
1923
 
        lines = [' '.join([r2, r1]), r1]
1924
 
        encoded_body = bz2.compress('\n'.join(lines))
 
2615
        lines = [b' '.join([r2, r1]), r1]
 
2616
        encoded_body = bz2.compress(b'\n'.join(lines))
1925
2617
 
1926
2618
        transport_path = 'quack'
1927
2619
        repo, client = self.setup_fake_client_and_repository(transport_path)
1928
 
        client.add_success_response_with_body(encoded_body, 'ok')
1929
 
        client.add_success_response_with_body(encoded_body, 'ok')
 
2620
        client.add_success_response_with_body(encoded_body, b'ok')
 
2621
        client.add_success_response_with_body(encoded_body, b'ok')
1930
2622
        repo.lock_read()
1931
2623
        graph = repo.get_graph()
1932
2624
        parents = graph.get_parent_map([r2])
1938
2630
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1939
2631
        self.assertEqual(
1940
2632
            [('call_with_body_bytes_expecting_body',
1941
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1942
 
              '\n\n0')],
 
2633
              b'Repository.get_parent_map', (b'quack/',
 
2634
                                             b'include-missing:', r2),
 
2635
              b'\n\n0')],
1943
2636
            client._calls)
1944
2637
        repo.unlock()
1945
2638
        # now we call again, and it should use the second response.
1949
2642
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1950
2643
        self.assertEqual(
1951
2644
            [('call_with_body_bytes_expecting_body',
1952
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1953
 
              '\n\n0'),
 
2645
              b'Repository.get_parent_map', (b'quack/',
 
2646
                                             b'include-missing:', r2),
 
2647
              b'\n\n0'),
1954
2648
             ('call_with_body_bytes_expecting_body',
1955
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
1956
 
              '\n\n0'),
1957
 
            ],
 
2649
              b'Repository.get_parent_map', (b'quack/',
 
2650
                                             b'include-missing:', r1),
 
2651
              b'\n\n0'),
 
2652
             ],
1958
2653
            client._calls)
1959
2654
        repo.unlock()
1960
2655
 
1961
2656
    def test_get_parent_map_reconnects_if_unknown_method(self):
1962
2657
        transport_path = 'quack'
1963
 
        rev_id = 'revision-id'
 
2658
        rev_id = b'revision-id'
1964
2659
        repo, client = self.setup_fake_client_and_repository(transport_path)
1965
 
        client.add_unknown_method_response('Repository.get_parent_map')
1966
 
        client.add_success_response_with_body(rev_id, 'ok')
 
2660
        client.add_unknown_method_response(b'Repository.get_parent_map')
 
2661
        client.add_success_response_with_body(rev_id, b'ok')
1967
2662
        self.assertFalse(client._medium._is_remote_before((1, 2)))
1968
2663
        parents = repo.get_parent_map([rev_id])
1969
2664
        self.assertEqual(
1970
2665
            [('call_with_body_bytes_expecting_body',
1971
 
              'Repository.get_parent_map', ('quack/', 'include-missing:',
1972
 
              rev_id), '\n\n0'),
 
2666
              b'Repository.get_parent_map',
 
2667
              (b'quack/', b'include-missing:', rev_id), b'\n\n0'),
1973
2668
             ('disconnect medium',),
1974
 
             ('call_expecting_body', 'Repository.get_revision_graph',
1975
 
              ('quack/', ''))],
 
2669
             ('call_expecting_body', b'Repository.get_revision_graph',
 
2670
              (b'quack/', b''))],
1976
2671
            client._calls)
1977
2672
        # The medium is now marked as being connected to an older server
1978
2673
        self.assertTrue(client._medium._is_remote_before((1, 2)))
1979
 
        self.assertEqual({rev_id: ('null:',)}, parents)
 
2674
        self.assertEqual({rev_id: (b'null:',)}, parents)
1980
2675
 
1981
2676
    def test_get_parent_map_fallback_parentless_node(self):
1982
2677
        """get_parent_map falls back to get_revision_graph on old servers.  The
1989
2684
 
1990
2685
        This is the test for https://bugs.launchpad.net/bzr/+bug/214894
1991
2686
        """
1992
 
        rev_id = 'revision-id'
 
2687
        rev_id = b'revision-id'
1993
2688
        transport_path = 'quack'
1994
2689
        repo, client = self.setup_fake_client_and_repository(transport_path)
1995
 
        client.add_success_response_with_body(rev_id, 'ok')
 
2690
        client.add_success_response_with_body(rev_id, b'ok')
1996
2691
        client._medium._remember_remote_is_before((1, 2))
1997
2692
        parents = repo.get_parent_map([rev_id])
1998
2693
        self.assertEqual(
1999
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2000
 
             ('quack/', ''))],
 
2694
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2695
              (b'quack/', b''))],
2001
2696
            client._calls)
2002
 
        self.assertEqual({rev_id: ('null:',)}, parents)
 
2697
        self.assertEqual({rev_id: (b'null:',)}, parents)
2003
2698
 
2004
2699
    def test_get_parent_map_unexpected_response(self):
2005
2700
        repo, client = self.setup_fake_client_and_repository('path')
2006
 
        client.add_success_response('something unexpected!')
 
2701
        client.add_success_response(b'something unexpected!')
2007
2702
        self.assertRaises(
2008
2703
            errors.UnexpectedSmartServerResponse,
2009
 
            repo.get_parent_map, ['a-revision-id'])
 
2704
            repo.get_parent_map, [b'a-revision-id'])
2010
2705
 
2011
2706
    def test_get_parent_map_negative_caches_missing_keys(self):
2012
2707
        self.setup_smart_server_with_call_log()
2016
2711
        self.addCleanup(repo.unlock)
2017
2712
        self.reset_smart_call_log()
2018
2713
        graph = repo.get_graph()
2019
 
        self.assertEqual({},
2020
 
            graph.get_parent_map(['some-missing', 'other-missing']))
 
2714
        self.assertEqual(
 
2715
            {}, graph.get_parent_map([b'some-missing', b'other-missing']))
2021
2716
        self.assertLength(1, self.hpss_calls)
2022
2717
        # No call if we repeat this
2023
2718
        self.reset_smart_call_log()
2024
2719
        graph = repo.get_graph()
2025
 
        self.assertEqual({},
2026
 
            graph.get_parent_map(['some-missing', 'other-missing']))
 
2720
        self.assertEqual(
 
2721
            {}, graph.get_parent_map([b'some-missing', b'other-missing']))
2027
2722
        self.assertLength(0, self.hpss_calls)
2028
2723
        # Asking for more unknown keys makes a request.
2029
2724
        self.reset_smart_call_log()
2030
2725
        graph = repo.get_graph()
2031
 
        self.assertEqual({},
2032
 
            graph.get_parent_map(['some-missing', 'other-missing',
2033
 
                'more-missing']))
 
2726
        self.assertEqual(
 
2727
            {}, graph.get_parent_map([b'some-missing', b'other-missing',
 
2728
                                     b'more-missing']))
2034
2729
        self.assertLength(1, self.hpss_calls)
2035
2730
 
2036
2731
    def disableExtraResults(self):
2042
2737
        # Make a branch with a single revision.
2043
2738
        builder = self.make_branch_builder('foo')
2044
2739
        builder.start_series()
2045
 
        builder.build_snapshot('first', None, [
2046
 
            ('add', ('', 'root-id', 'directory', ''))])
 
2740
        builder.build_snapshot(None, [
 
2741
            ('add', ('', b'root-id', 'directory', ''))],
 
2742
            revision_id=b'first')
2047
2743
        builder.finish_series()
2048
2744
        branch = builder.get_branch()
2049
2745
        repo = branch.repository
2054
2750
        self.addCleanup(repo.unlock)
2055
2751
        self.reset_smart_call_log()
2056
2752
        graph = repo.get_graph()
2057
 
        # Query for 'first' and 'null:'.  Because 'null:' is a parent of
 
2753
        # Query for b'first' and b'null:'.  Because b'null:' is a parent of
2058
2754
        # 'first' it will be a candidate for the stop_keys of subsequent
2059
 
        # requests, and because 'null:' was queried but not returned it will be
2060
 
        # cached as missing.
2061
 
        self.assertEqual({'first': ('null:',)},
2062
 
            graph.get_parent_map(['first', 'null:']))
 
2755
        # requests, and because b'null:' was queried but not returned it will
 
2756
        # be cached as missing.
 
2757
        self.assertEqual({b'first': (b'null:',)},
 
2758
                         graph.get_parent_map([b'first', b'null:']))
2063
2759
        # Now query for another key.  This request will pass along a recipe of
2064
2760
        # start and stop keys describing the already cached results, and this
2065
2761
        # recipe's revision count must be correct (or else it will trigger an
2066
2762
        # error from the server).
2067
 
        self.assertEqual({}, graph.get_parent_map(['another-key']))
 
2763
        self.assertEqual({}, graph.get_parent_map([b'another-key']))
2068
2764
        # This assertion guards against disableExtraResults silently failing to
2069
2765
        # work, thus invalidating the test.
2070
2766
        self.assertLength(2, self.hpss_calls)
2074
2770
        # ancestry.
2075
2771
        self.setup_smart_server_with_call_log()
2076
2772
        tree = self.make_branch_and_memory_tree('foo')
2077
 
        tree.lock_write()
2078
 
        try:
 
2773
        with tree.lock_write():
2079
2774
            builder = treebuilder.TreeBuilder()
2080
2775
            builder.start_tree(tree)
2081
2776
            builder.build([])
2082
2777
            builder.finish_tree()
2083
 
            tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
 
2778
            tree.set_parent_ids([b'non-existant'],
 
2779
                                allow_leftmost_as_ghost=True)
2084
2780
            rev_id = tree.commit('')
2085
 
        finally:
2086
 
            tree.unlock()
2087
2781
        tree.lock_read()
2088
2782
        self.addCleanup(tree.unlock)
2089
2783
        repo = tree.branch.repository
2092
2786
        repo.get_parent_map([rev_id])
2093
2787
        self.reset_smart_call_log()
2094
2788
        # Now asking for rev_id's ghost parent should not make calls
2095
 
        self.assertEqual({}, repo.get_parent_map(['non-existant']))
 
2789
        self.assertEqual({}, repo.get_parent_map([b'non-existant']))
2096
2790
        self.assertLength(0, self.hpss_calls)
2097
2791
 
 
2792
    def test_exposes_get_cached_parent_map(self):
 
2793
        """RemoteRepository exposes get_cached_parent_map from
 
2794
        _unstacked_provider
 
2795
        """
 
2796
        r1 = u'\u0e33'.encode('utf8')
 
2797
        r2 = u'\u0dab'.encode('utf8')
 
2798
        lines = [b' '.join([r2, r1]), r1]
 
2799
        encoded_body = bz2.compress(b'\n'.join(lines))
 
2800
 
 
2801
        transport_path = 'quack'
 
2802
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2803
        client.add_success_response_with_body(encoded_body, b'ok')
 
2804
        repo.lock_read()
 
2805
        # get_cached_parent_map should *not* trigger an RPC
 
2806
        self.assertEqual({}, repo.get_cached_parent_map([r1]))
 
2807
        self.assertEqual([], client._calls)
 
2808
        self.assertEqual({r2: (r1,)}, repo.get_parent_map([r2]))
 
2809
        self.assertEqual({r1: (NULL_REVISION,)},
 
2810
                         repo.get_cached_parent_map([r1]))
 
2811
        self.assertEqual(
 
2812
            [('call_with_body_bytes_expecting_body',
 
2813
              b'Repository.get_parent_map', (b'quack/',
 
2814
                                             b'include-missing:', r2),
 
2815
              b'\n\n0')],
 
2816
            client._calls)
 
2817
        repo.unlock()
 
2818
 
2098
2819
 
2099
2820
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2100
2821
 
2109
2830
        self.addCleanup(tree.unlock)
2110
2831
        graph = tree.branch.repository.get_graph()
2111
2832
        # This provides an opportunity for the missing rev-id to be cached.
2112
 
        self.assertEqual({}, graph.get_parent_map(['rev1']))
2113
 
        tree.commit('message', rev_id='rev1')
 
2833
        self.assertEqual({}, graph.get_parent_map([b'rev1']))
 
2834
        tree.commit('message', rev_id=b'rev1')
2114
2835
        graph = tree.branch.repository.get_graph()
2115
 
        self.assertEqual({'rev1': ('null:',)}, graph.get_parent_map(['rev1']))
 
2836
        self.assertEqual({b'rev1': (b'null:',)},
 
2837
                         graph.get_parent_map([b'rev1']))
 
2838
 
 
2839
 
 
2840
class TestRepositoryGetRevisions(TestRemoteRepository):
 
2841
 
 
2842
    def test_hpss_missing_revision(self):
 
2843
        transport_path = 'quack'
 
2844
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2845
        client.add_success_response_with_body(
 
2846
            b'', b'ok', b'10')
 
2847
        self.assertRaises(errors.NoSuchRevision, repo.get_revisions,
 
2848
                          [b'somerev1', b'anotherrev2'])
 
2849
        self.assertEqual(
 
2850
            [('call_with_body_bytes_expecting_body',
 
2851
              b'Repository.iter_revisions', (b'quack/', ),
 
2852
              b"somerev1\nanotherrev2")],
 
2853
            client._calls)
 
2854
 
 
2855
    def test_hpss_get_single_revision(self):
 
2856
        transport_path = 'quack'
 
2857
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2858
        somerev1 = Revision(b"somerev1")
 
2859
        somerev1.committer = "Joe Committer <joe@example.com>"
 
2860
        somerev1.timestamp = 1321828927
 
2861
        somerev1.timezone = -60
 
2862
        somerev1.inventory_sha1 = b"691b39be74c67b1212a75fcb19c433aaed903c2b"
 
2863
        somerev1.message = "Message"
 
2864
        body = zlib.compress(chk_bencode_serializer.write_revision_to_string(
 
2865
            somerev1))
 
2866
        # Split up body into two bits to make sure the zlib compression object
 
2867
        # gets data fed twice.
 
2868
        client.add_success_response_with_body(
 
2869
            [body[:10], body[10:]], b'ok', b'10')
 
2870
        revs = repo.get_revisions([b'somerev1'])
 
2871
        self.assertEqual(revs, [somerev1])
 
2872
        self.assertEqual(
 
2873
            [('call_with_body_bytes_expecting_body',
 
2874
              b'Repository.iter_revisions',
 
2875
              (b'quack/', ), b"somerev1")],
 
2876
            client._calls)
2116
2877
 
2117
2878
 
2118
2879
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
2122
2883
        # traffic when calling it with this argument
2123
2884
        transport_path = 'empty'
2124
2885
        repo, client = self.setup_fake_client_and_repository(transport_path)
2125
 
        client.add_success_response('notused')
 
2886
        client.add_success_response(b'notused')
2126
2887
        # actual RemoteRepository.get_revision_graph is gone, but there's an
2127
2888
        # equivalent private method for testing
2128
2889
        result = repo._get_revision_graph(NULL_REVISION)
2133
2894
        # with none we want the entire graph
2134
2895
        r1 = u'\u0e33'.encode('utf8')
2135
2896
        r2 = u'\u0dab'.encode('utf8')
2136
 
        lines = [' '.join([r2, r1]), r1]
2137
 
        encoded_body = '\n'.join(lines)
 
2897
        lines = [b' '.join([r2, r1]), r1]
 
2898
        encoded_body = b'\n'.join(lines)
2138
2899
 
2139
2900
        transport_path = 'sinhala'
2140
2901
        repo, client = self.setup_fake_client_and_repository(transport_path)
2141
 
        client.add_success_response_with_body(encoded_body, 'ok')
 
2902
        client.add_success_response_with_body(encoded_body, b'ok')
2142
2903
        # actual RemoteRepository.get_revision_graph is gone, but there's an
2143
2904
        # equivalent private method for testing
2144
2905
        result = repo._get_revision_graph(None)
2145
2906
        self.assertEqual(
2146
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2147
 
             ('sinhala/', ''))],
 
2907
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2908
              (b'sinhala/', b''))],
2148
2909
            client._calls)
2149
2910
        self.assertEqual({r1: (), r2: (r1, )}, result)
2150
2911
 
2154
2915
        r11 = u'\u0e33'.encode('utf8')
2155
2916
        r12 = u'\xc9'.encode('utf8')
2156
2917
        r2 = u'\u0dab'.encode('utf8')
2157
 
        lines = [' '.join([r2, r11, r12]), r11, r12]
2158
 
        encoded_body = '\n'.join(lines)
 
2918
        lines = [b' '.join([r2, r11, r12]), r11, r12]
 
2919
        encoded_body = b'\n'.join(lines)
2159
2920
 
2160
2921
        transport_path = 'sinhala'
2161
2922
        repo, client = self.setup_fake_client_and_repository(transport_path)
2162
 
        client.add_success_response_with_body(encoded_body, 'ok')
 
2923
        client.add_success_response_with_body(encoded_body, b'ok')
2163
2924
        result = repo._get_revision_graph(r2)
2164
2925
        self.assertEqual(
2165
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2166
 
             ('sinhala/', r2))],
 
2926
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2927
              (b'sinhala/', r2))],
2167
2928
            client._calls)
2168
2929
        self.assertEqual({r11: (), r12: (), r2: (r11, r12), }, result)
2169
2930
 
2170
2931
    def test_no_such_revision(self):
2171
 
        revid = '123'
 
2932
        revid = b'123'
2172
2933
        transport_path = 'sinhala'
2173
2934
        repo, client = self.setup_fake_client_and_repository(transport_path)
2174
 
        client.add_error_response('nosuchrevision', revid)
 
2935
        client.add_error_response(b'nosuchrevision', revid)
2175
2936
        # also check that the right revision is reported in the error
2176
2937
        self.assertRaises(errors.NoSuchRevision,
2177
 
            repo._get_revision_graph, revid)
 
2938
                          repo._get_revision_graph, revid)
2178
2939
        self.assertEqual(
2179
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2180
 
             ('sinhala/', revid))],
 
2940
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2941
              (b'sinhala/', revid))],
2181
2942
            client._calls)
2182
2943
 
2183
2944
    def test_unexpected_error(self):
2184
2945
        revid = '123'
2185
2946
        transport_path = 'sinhala'
2186
2947
        repo, client = self.setup_fake_client_and_repository(transport_path)
2187
 
        client.add_error_response('AnUnexpectedError')
 
2948
        client.add_error_response(b'AnUnexpectedError')
2188
2949
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2189
 
            repo._get_revision_graph, revid)
2190
 
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
 
2950
                              repo._get_revision_graph, revid)
 
2951
        self.assertEqual((b'AnUnexpectedError',), e.error_tuple)
2191
2952
 
2192
2953
 
2193
2954
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2195
2956
    def test_ok(self):
2196
2957
        repo, client = self.setup_fake_client_and_repository('quack')
2197
2958
        client.add_expected_call(
2198
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2199
 
            'success', ('ok', 'rev-five'))
2200
 
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2201
 
        self.assertEqual((True, 'rev-five'), result)
 
2959
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2960
                                                 5, (42, b'rev-foo')),
 
2961
            b'success', (b'ok', b'rev-five'))
 
2962
        result = repo.get_rev_id_for_revno(5, (42, b'rev-foo'))
 
2963
        self.assertEqual((True, b'rev-five'), result)
2202
2964
        self.assertFinished(client)
2203
2965
 
2204
2966
    def test_history_incomplete(self):
2205
2967
        repo, client = self.setup_fake_client_and_repository('quack')
2206
2968
        client.add_expected_call(
2207
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2208
 
            'success', ('history-incomplete', 10, 'rev-ten'))
2209
 
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2210
 
        self.assertEqual((False, (10, 'rev-ten')), result)
 
2969
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2970
                                                 5, (42, b'rev-foo')),
 
2971
            b'success', (b'history-incomplete', 10, b'rev-ten'))
 
2972
        result = repo.get_rev_id_for_revno(5, (42, b'rev-foo'))
 
2973
        self.assertEqual((False, (10, b'rev-ten')), result)
2211
2974
        self.assertFinished(client)
2212
2975
 
2213
2976
    def test_history_incomplete_with_fallback(self):
2216
2979
        """
2217
2980
        # Make a repo with a fallback repo, both using a FakeClient.
2218
2981
        format = remote.response_tuple_to_repo_format(
2219
 
            ('yes', 'no', 'yes', self.get_repo_format().network_name()))
 
2982
            (b'yes', b'no', b'yes', self.get_repo_format().network_name()))
2220
2983
        repo, client = self.setup_fake_client_and_repository('quack')
2221
2984
        repo._format = format
2222
2985
        fallback_repo, ignored = self.setup_fake_client_and_repository(
2226
2989
        repo.add_fallback_repository(fallback_repo)
2227
2990
        # First the client should ask the primary repo
2228
2991
        client.add_expected_call(
2229
 
            'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2230
 
            'success', ('history-incomplete', 2, 'rev-two'))
 
2992
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2993
                                                 1, (42, b'rev-foo')),
 
2994
            b'success', (b'history-incomplete', 2, b'rev-two'))
2231
2995
        # Then it should ask the fallback, using revno/revid from the
2232
2996
        # history-incomplete response as the known revno/revid.
2233
2997
        client.add_expected_call(
2234
 
            'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2235
 
            'success', ('ok', 'rev-one'))
2236
 
        result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2237
 
        self.assertEqual((True, 'rev-one'), result)
 
2998
            b'Repository.get_rev_id_for_revno', (
 
2999
                b'fallback/', 1, (2, b'rev-two')),
 
3000
            b'success', (b'ok', b'rev-one'))
 
3001
        result = repo.get_rev_id_for_revno(1, (42, b'rev-foo'))
 
3002
        self.assertEqual((True, b'rev-one'), result)
2238
3003
        self.assertFinished(client)
2239
3004
 
2240
3005
    def test_nosuchrevision(self):
2242
3007
        # remote repo.  The client translates that response to NoSuchRevision.
2243
3008
        repo, client = self.setup_fake_client_and_repository('quack')
2244
3009
        client.add_expected_call(
2245
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2246
 
            'error', ('nosuchrevision', 'rev-foo'))
 
3010
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
3011
                                                 5, (42, b'rev-foo')),
 
3012
            b'error', (b'nosuchrevision', b'rev-foo'))
2247
3013
        self.assertRaises(
2248
3014
            errors.NoSuchRevision,
2249
 
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
 
3015
            repo.get_rev_id_for_revno, 5, (42, b'rev-foo'))
 
3016
        self.assertFinished(client)
 
3017
 
 
3018
    def test_outofbounds(self):
 
3019
        repo, client = self.setup_fake_client_and_repository('quack')
 
3020
        client.add_expected_call(
 
3021
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
3022
                                                 43, (42, b'rev-foo')),
 
3023
            b'error', (b'revno-outofbounds', 43, 0, 42))
 
3024
        self.assertRaises(
 
3025
            errors.RevnoOutOfBounds,
 
3026
            repo.get_rev_id_for_revno, 43, (42, b'rev-foo'))
 
3027
        self.assertFinished(client)
 
3028
 
 
3029
    def test_outofbounds_old(self):
 
3030
        # Older versions of bzr didn't support RevnoOutOfBounds
 
3031
        repo, client = self.setup_fake_client_and_repository('quack')
 
3032
        client.add_expected_call(
 
3033
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
3034
                                                 43, (42, b'rev-foo')),
 
3035
            b'error', (
 
3036
                b'error', b'ValueError',
 
3037
                b'requested revno (43) is later than given known revno (42)'))
 
3038
        self.assertRaises(
 
3039
            errors.RevnoOutOfBounds,
 
3040
            repo.get_rev_id_for_revno, 43, (42, b'rev-foo'))
2250
3041
        self.assertFinished(client)
2251
3042
 
2252
3043
    def test_branch_fallback_locking(self):
2257
3048
        self.setup_smart_server_with_call_log()
2258
3049
        tree = self.make_branch_and_memory_tree('.')
2259
3050
        tree.lock_write()
 
3051
        tree.add('')
2260
3052
        rev1 = tree.commit('First')
2261
 
        rev2 = tree.commit('Second')
 
3053
        tree.commit('Second')
2262
3054
        tree.unlock()
2263
3055
        branch = tree.branch
2264
3056
        self.assertFalse(branch.is_locked())
2265
3057
        self.reset_smart_call_log()
2266
 
        verb = 'Repository.get_rev_id_for_revno'
 
3058
        verb = b'Repository.get_rev_id_for_revno'
2267
3059
        self.disable_verb(verb)
2268
3060
        self.assertEqual(rev1, branch.get_rev_id(1))
2269
3061
        self.assertLength(1, [call for call in self.hpss_calls if
2270
3062
                              call.call.method == verb])
2271
3063
 
2272
3064
 
 
3065
class TestRepositoryHasSignatureForRevisionId(TestRemoteRepository):
 
3066
 
 
3067
    def test_has_signature_for_revision_id(self):
 
3068
        # ('yes', ) for Repository.has_signature_for_revision_id -> 'True'.
 
3069
        transport_path = 'quack'
 
3070
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3071
        client.add_success_response(b'yes')
 
3072
        result = repo.has_signature_for_revision_id(b'A')
 
3073
        self.assertEqual(
 
3074
            [('call', b'Repository.has_signature_for_revision_id',
 
3075
              (b'quack/', b'A'))],
 
3076
            client._calls)
 
3077
        self.assertEqual(True, result)
 
3078
 
 
3079
    def test_is_not_shared(self):
 
3080
        # ('no', ) for Repository.has_signature_for_revision_id -> 'False'.
 
3081
        transport_path = 'qwack'
 
3082
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3083
        client.add_success_response(b'no')
 
3084
        result = repo.has_signature_for_revision_id(b'A')
 
3085
        self.assertEqual(
 
3086
            [('call', b'Repository.has_signature_for_revision_id',
 
3087
              (b'qwack/', b'A'))],
 
3088
            client._calls)
 
3089
        self.assertEqual(False, result)
 
3090
 
 
3091
 
 
3092
class TestRepositoryPhysicalLockStatus(TestRemoteRepository):
 
3093
 
 
3094
    def test_get_physical_lock_status_yes(self):
 
3095
        transport_path = 'qwack'
 
3096
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3097
        client.add_success_response(b'yes')
 
3098
        result = repo.get_physical_lock_status()
 
3099
        self.assertEqual(
 
3100
            [('call', b'Repository.get_physical_lock_status',
 
3101
              (b'qwack/', ))],
 
3102
            client._calls)
 
3103
        self.assertEqual(True, result)
 
3104
 
 
3105
    def test_get_physical_lock_status_no(self):
 
3106
        transport_path = 'qwack'
 
3107
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3108
        client.add_success_response(b'no')
 
3109
        result = repo.get_physical_lock_status()
 
3110
        self.assertEqual(
 
3111
            [('call', b'Repository.get_physical_lock_status',
 
3112
              (b'qwack/', ))],
 
3113
            client._calls)
 
3114
        self.assertEqual(False, result)
 
3115
 
 
3116
 
2273
3117
class TestRepositoryIsShared(TestRemoteRepository):
2274
3118
 
2275
3119
    def test_is_shared(self):
2276
3120
        # ('yes', ) for Repository.is_shared -> 'True'.
2277
3121
        transport_path = 'quack'
2278
3122
        repo, client = self.setup_fake_client_and_repository(transport_path)
2279
 
        client.add_success_response('yes')
 
3123
        client.add_success_response(b'yes')
2280
3124
        result = repo.is_shared()
2281
3125
        self.assertEqual(
2282
 
            [('call', 'Repository.is_shared', ('quack/',))],
 
3126
            [('call', b'Repository.is_shared', (b'quack/',))],
2283
3127
            client._calls)
2284
3128
        self.assertEqual(True, result)
2285
3129
 
2287
3131
        # ('no', ) for Repository.is_shared -> 'False'.
2288
3132
        transport_path = 'qwack'
2289
3133
        repo, client = self.setup_fake_client_and_repository(transport_path)
2290
 
        client.add_success_response('no')
 
3134
        client.add_success_response(b'no')
2291
3135
        result = repo.is_shared()
2292
3136
        self.assertEqual(
2293
 
            [('call', 'Repository.is_shared', ('qwack/',))],
 
3137
            [('call', b'Repository.is_shared', (b'qwack/',))],
 
3138
            client._calls)
 
3139
        self.assertEqual(False, result)
 
3140
 
 
3141
 
 
3142
class TestRepositoryMakeWorkingTrees(TestRemoteRepository):
 
3143
 
 
3144
    def test_make_working_trees(self):
 
3145
        # ('yes', ) for Repository.make_working_trees -> 'True'.
 
3146
        transport_path = 'quack'
 
3147
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3148
        client.add_success_response(b'yes')
 
3149
        result = repo.make_working_trees()
 
3150
        self.assertEqual(
 
3151
            [('call', b'Repository.make_working_trees', (b'quack/',))],
 
3152
            client._calls)
 
3153
        self.assertEqual(True, result)
 
3154
 
 
3155
    def test_no_working_trees(self):
 
3156
        # ('no', ) for Repository.make_working_trees -> 'False'.
 
3157
        transport_path = 'qwack'
 
3158
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3159
        client.add_success_response(b'no')
 
3160
        result = repo.make_working_trees()
 
3161
        self.assertEqual(
 
3162
            [('call', b'Repository.make_working_trees', (b'qwack/',))],
2294
3163
            client._calls)
2295
3164
        self.assertEqual(False, result)
2296
3165
 
2300
3169
    def test_lock_write(self):
2301
3170
        transport_path = 'quack'
2302
3171
        repo, client = self.setup_fake_client_and_repository(transport_path)
2303
 
        client.add_success_response('ok', 'a token')
2304
 
        result = repo.lock_write()
 
3172
        client.add_success_response(b'ok', b'a token')
 
3173
        token = repo.lock_write().repository_token
2305
3174
        self.assertEqual(
2306
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3175
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2307
3176
            client._calls)
2308
 
        self.assertEqual('a token', result)
 
3177
        self.assertEqual(b'a token', token)
2309
3178
 
2310
3179
    def test_lock_write_already_locked(self):
2311
3180
        transport_path = 'quack'
2312
3181
        repo, client = self.setup_fake_client_and_repository(transport_path)
2313
 
        client.add_error_response('LockContention')
 
3182
        client.add_error_response(b'LockContention')
2314
3183
        self.assertRaises(errors.LockContention, repo.lock_write)
2315
3184
        self.assertEqual(
2316
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3185
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2317
3186
            client._calls)
2318
3187
 
2319
3188
    def test_lock_write_unlockable(self):
2320
3189
        transport_path = 'quack'
2321
3190
        repo, client = self.setup_fake_client_and_repository(transport_path)
2322
 
        client.add_error_response('UnlockableTransport')
 
3191
        client.add_error_response(b'UnlockableTransport')
2323
3192
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
2324
3193
        self.assertEqual(
2325
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3194
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2326
3195
            client._calls)
2327
3196
 
2328
3197
 
 
3198
class TestRepositoryWriteGroups(TestRemoteRepository):
 
3199
 
 
3200
    def test_start_write_group(self):
 
3201
        transport_path = 'quack'
 
3202
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3203
        client.add_expected_call(
 
3204
            b'Repository.lock_write', (b'quack/', b''),
 
3205
            b'success', (b'ok', b'a token'))
 
3206
        client.add_expected_call(
 
3207
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3208
            b'success', (b'ok', (b'token1', )))
 
3209
        repo.lock_write()
 
3210
        repo.start_write_group()
 
3211
 
 
3212
    def test_start_write_group_unsuspendable(self):
 
3213
        # Some repositories do not support suspending write
 
3214
        # groups. For those, fall back to the "real" repository.
 
3215
        transport_path = 'quack'
 
3216
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3217
 
 
3218
        def stub_ensure_real():
 
3219
            client._calls.append(('_ensure_real',))
 
3220
            repo._real_repository = _StubRealPackRepository(client._calls)
 
3221
        repo._ensure_real = stub_ensure_real
 
3222
        client.add_expected_call(
 
3223
            b'Repository.lock_write', (b'quack/', b''),
 
3224
            b'success', (b'ok', b'a token'))
 
3225
        client.add_expected_call(
 
3226
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3227
            b'error', (b'UnsuspendableWriteGroup',))
 
3228
        repo.lock_write()
 
3229
        repo.start_write_group()
 
3230
        self.assertEqual(client._calls[-2:], [
 
3231
            ('_ensure_real',),
 
3232
            ('start_write_group',)])
 
3233
 
 
3234
    def test_commit_write_group(self):
 
3235
        transport_path = 'quack'
 
3236
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3237
        client.add_expected_call(
 
3238
            b'Repository.lock_write', (b'quack/', b''),
 
3239
            b'success', (b'ok', b'a token'))
 
3240
        client.add_expected_call(
 
3241
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3242
            b'success', (b'ok', [b'token1']))
 
3243
        client.add_expected_call(
 
3244
            b'Repository.commit_write_group', (b'quack/',
 
3245
                                               b'a token', [b'token1']),
 
3246
            b'success', (b'ok',))
 
3247
        repo.lock_write()
 
3248
        repo.start_write_group()
 
3249
        repo.commit_write_group()
 
3250
 
 
3251
    def test_abort_write_group(self):
 
3252
        transport_path = 'quack'
 
3253
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3254
        client.add_expected_call(
 
3255
            b'Repository.lock_write', (b'quack/', b''),
 
3256
            b'success', (b'ok', b'a token'))
 
3257
        client.add_expected_call(
 
3258
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3259
            b'success', (b'ok', [b'token1']))
 
3260
        client.add_expected_call(
 
3261
            b'Repository.abort_write_group', (b'quack/',
 
3262
                                              b'a token', [b'token1']),
 
3263
            b'success', (b'ok',))
 
3264
        repo.lock_write()
 
3265
        repo.start_write_group()
 
3266
        repo.abort_write_group(False)
 
3267
 
 
3268
    def test_suspend_write_group(self):
 
3269
        transport_path = 'quack'
 
3270
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3271
        self.assertEqual([], repo.suspend_write_group())
 
3272
 
 
3273
    def test_resume_write_group(self):
 
3274
        transport_path = 'quack'
 
3275
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3276
        client.add_expected_call(
 
3277
            b'Repository.lock_write', (b'quack/', b''),
 
3278
            b'success', (b'ok', b'a token'))
 
3279
        client.add_expected_call(
 
3280
            b'Repository.check_write_group', (b'quack/',
 
3281
                                              b'a token', [b'token1']),
 
3282
            b'success', (b'ok',))
 
3283
        repo.lock_write()
 
3284
        repo.resume_write_group(['token1'])
 
3285
 
 
3286
 
2329
3287
class TestRepositorySetMakeWorkingTrees(TestRemoteRepository):
2330
3288
 
2331
3289
    def test_backwards_compat(self):
2332
3290
        self.setup_smart_server_with_call_log()
2333
3291
        repo = self.make_repository('.')
2334
3292
        self.reset_smart_call_log()
2335
 
        verb = 'Repository.set_make_working_trees'
 
3293
        verb = b'Repository.set_make_working_trees'
2336
3294
        self.disable_verb(verb)
2337
3295
        repo.set_make_working_trees(True)
2338
3296
        call_count = len([call for call in self.hpss_calls if
2339
 
            call.call.method == verb])
 
3297
                          call.call.method == verb])
2340
3298
        self.assertEqual(1, call_count)
2341
3299
 
2342
3300
    def test_current(self):
2343
3301
        transport_path = 'quack'
2344
3302
        repo, client = self.setup_fake_client_and_repository(transport_path)
2345
3303
        client.add_expected_call(
2346
 
            'Repository.set_make_working_trees', ('quack/', 'True'),
2347
 
            'success', ('ok',))
 
3304
            b'Repository.set_make_working_trees', (b'quack/', b'True'),
 
3305
            b'success', (b'ok',))
2348
3306
        client.add_expected_call(
2349
 
            'Repository.set_make_working_trees', ('quack/', 'False'),
2350
 
            'success', ('ok',))
 
3307
            b'Repository.set_make_working_trees', (b'quack/', b'False'),
 
3308
            b'success', (b'ok',))
2351
3309
        repo.set_make_working_trees(True)
2352
3310
        repo.set_make_working_trees(False)
2353
3311
 
2357
3315
    def test_unlock(self):
2358
3316
        transport_path = 'quack'
2359
3317
        repo, client = self.setup_fake_client_and_repository(transport_path)
2360
 
        client.add_success_response('ok', 'a token')
2361
 
        client.add_success_response('ok')
 
3318
        client.add_success_response(b'ok', b'a token')
 
3319
        client.add_success_response(b'ok')
2362
3320
        repo.lock_write()
2363
3321
        repo.unlock()
2364
3322
        self.assertEqual(
2365
 
            [('call', 'Repository.lock_write', ('quack/', '')),
2366
 
             ('call', 'Repository.unlock', ('quack/', 'a token'))],
 
3323
            [('call', b'Repository.lock_write', (b'quack/', b'')),
 
3324
             ('call', b'Repository.unlock', (b'quack/', b'a token'))],
2367
3325
            client._calls)
2368
3326
 
2369
3327
    def test_unlock_wrong_token(self):
2370
3328
        # If somehow the token is wrong, unlock will raise TokenMismatch.
2371
3329
        transport_path = 'quack'
2372
3330
        repo, client = self.setup_fake_client_and_repository(transport_path)
2373
 
        client.add_success_response('ok', 'a token')
2374
 
        client.add_error_response('TokenMismatch')
 
3331
        client.add_success_response(b'ok', b'a token')
 
3332
        client.add_error_response(b'TokenMismatch')
2375
3333
        repo.lock_write()
2376
3334
        self.assertRaises(errors.TokenMismatch, repo.unlock)
2377
3335
 
2390
3348
        self.assertEqual([], client._calls)
2391
3349
 
2392
3350
 
 
3351
class TestRepositoryIterFilesBytes(TestRemoteRepository):
 
3352
    """Test Repository.iter_file_bytes."""
 
3353
 
 
3354
    def test_single(self):
 
3355
        transport_path = 'quack'
 
3356
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3357
        client.add_expected_call(
 
3358
            b'Repository.iter_files_bytes', (b'quack/', ),
 
3359
            b'success', (b'ok',), iter([b"ok\x000", b"\n", zlib.compress(b"mydata" * 10)]))
 
3360
        for (identifier, byte_stream) in repo.iter_files_bytes([(b"somefile",
 
3361
                                                                 b"somerev", b"myid")]):
 
3362
            self.assertEqual(b"myid", identifier)
 
3363
            self.assertEqual(b"".join(byte_stream), b"mydata" * 10)
 
3364
 
 
3365
    def test_missing(self):
 
3366
        transport_path = 'quack'
 
3367
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3368
        client.add_expected_call(
 
3369
            b'Repository.iter_files_bytes',
 
3370
            (b'quack/', ),
 
3371
            b'error', (b'RevisionNotPresent', b'somefile', b'somerev'),
 
3372
            iter([b"absent\0somefile\0somerev\n"]))
 
3373
        self.assertRaises(errors.RevisionNotPresent, list,
 
3374
                          repo.iter_files_bytes(
 
3375
                              [(b"somefile", b"somerev", b"myid")]))
 
3376
 
 
3377
 
2393
3378
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2394
3379
    """Base class for Repository.insert_stream and .insert_stream_1.19
2395
3380
    tests.
2396
3381
    """
2397
 
    
 
3382
 
2398
3383
    def checkInsertEmptyStream(self, repo, client):
2399
3384
        """Insert an empty stream, checking the result.
2400
3385
 
2402
3387
        the client is finished.
2403
3388
        """
2404
3389
        sink = repo._get_sink()
2405
 
        fmt = repository.RepositoryFormat.get_default_format()
 
3390
        fmt = repository.format_registry.get_default()
2406
3391
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
3392
        self.assertEqual([], resume_tokens)
2408
3393
        self.assertEqual(set(), missing_keys)
2417
3402
    """
2418
3403
 
2419
3404
    def setUp(self):
2420
 
        TestRemoteRepository.setUp(self)
2421
 
        self.disable_verb('Repository.insert_stream_1.19')
 
3405
        super(TestRepositoryInsertStream, self).setUp()
 
3406
        self.disable_verb(b'Repository.insert_stream_1.19')
2422
3407
 
2423
3408
    def test_unlocked_repo(self):
2424
3409
        transport_path = 'quack'
2425
3410
        repo, client = self.setup_fake_client_and_repository(transport_path)
2426
3411
        client.add_expected_call(
2427
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2428
 
            'unknown', ('Repository.insert_stream_1.19',))
2429
 
        client.add_expected_call(
2430
 
            'Repository.insert_stream', ('quack/', ''),
2431
 
            'success', ('ok',))
2432
 
        client.add_expected_call(
2433
 
            'Repository.insert_stream', ('quack/', ''),
2434
 
            'success', ('ok',))
 
3412
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3413
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3414
        client.add_expected_call(
 
3415
            b'Repository.insert_stream', (b'quack/', b''),
 
3416
            b'success', (b'ok',))
 
3417
        client.add_expected_call(
 
3418
            b'Repository.insert_stream', (b'quack/', b''),
 
3419
            b'success', (b'ok',))
2435
3420
        self.checkInsertEmptyStream(repo, client)
2436
3421
 
2437
3422
    def test_locked_repo_with_no_lock_token(self):
2438
3423
        transport_path = 'quack'
2439
3424
        repo, client = self.setup_fake_client_and_repository(transport_path)
2440
3425
        client.add_expected_call(
2441
 
            'Repository.lock_write', ('quack/', ''),
2442
 
            'success', ('ok', ''))
2443
 
        client.add_expected_call(
2444
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2445
 
            'unknown', ('Repository.insert_stream_1.19',))
2446
 
        client.add_expected_call(
2447
 
            'Repository.insert_stream', ('quack/', ''),
2448
 
            'success', ('ok',))
2449
 
        client.add_expected_call(
2450
 
            'Repository.insert_stream', ('quack/', ''),
2451
 
            'success', ('ok',))
 
3426
            b'Repository.lock_write', (b'quack/', b''),
 
3427
            b'success', (b'ok', b''))
 
3428
        client.add_expected_call(
 
3429
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3430
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3431
        client.add_expected_call(
 
3432
            b'Repository.insert_stream', (b'quack/', b''),
 
3433
            b'success', (b'ok',))
 
3434
        client.add_expected_call(
 
3435
            b'Repository.insert_stream', (b'quack/', b''),
 
3436
            b'success', (b'ok',))
2452
3437
        repo.lock_write()
2453
3438
        self.checkInsertEmptyStream(repo, client)
2454
3439
 
2456
3441
        transport_path = 'quack'
2457
3442
        repo, client = self.setup_fake_client_and_repository(transport_path)
2458
3443
        client.add_expected_call(
2459
 
            'Repository.lock_write', ('quack/', ''),
2460
 
            'success', ('ok', 'a token'))
2461
 
        client.add_expected_call(
2462
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2463
 
            'unknown', ('Repository.insert_stream_1.19',))
2464
 
        client.add_expected_call(
2465
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2466
 
            'success', ('ok',))
2467
 
        client.add_expected_call(
2468
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2469
 
            'success', ('ok',))
 
3444
            b'Repository.lock_write', (b'quack/', b''),
 
3445
            b'success', (b'ok', b'a token'))
 
3446
        client.add_expected_call(
 
3447
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3448
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3449
        client.add_expected_call(
 
3450
            b'Repository.insert_stream_locked', (b'quack/', b'', b'a token'),
 
3451
            b'success', (b'ok',))
 
3452
        client.add_expected_call(
 
3453
            b'Repository.insert_stream_locked', (b'quack/', b'', b'a token'),
 
3454
            b'success', (b'ok',))
2470
3455
        repo.lock_write()
2471
3456
        self.checkInsertEmptyStream(repo, client)
2472
3457
 
2479
3464
        transport_path = 'quack'
2480
3465
        repo, client = self.setup_fake_client_and_repository(transport_path)
2481
3466
        client.add_expected_call(
2482
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2483
 
            'unknown', ('Repository.insert_stream_1.19',))
2484
 
        client.add_expected_call(
2485
 
            'Repository.insert_stream', ('quack/', ''),
2486
 
            'success', ('ok',))
2487
 
        client.add_expected_call(
2488
 
            'Repository.insert_stream', ('quack/', ''),
2489
 
            'success', ('ok',))
 
3467
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3468
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3469
        client.add_expected_call(
 
3470
            b'Repository.insert_stream', (b'quack/', b''),
 
3471
            b'success', (b'ok',))
 
3472
        client.add_expected_call(
 
3473
            b'Repository.insert_stream', (b'quack/', b''),
 
3474
            b'success', (b'ok',))
2490
3475
        # Create a fake real repository for insert_stream to fall back on, so
2491
3476
        # that we can directly see the records the RemoteSink passes to the
2492
3477
        # real sink.
 
3478
 
2493
3479
        class FakeRealSink:
2494
3480
            def __init__(self):
2495
3481
                self.records = []
 
3482
 
2496
3483
            def insert_stream(self, stream, src_format, resume_tokens):
2497
3484
                for substream_kind, substream in stream:
2498
3485
                    self.records.append(
2499
3486
                        (substream_kind, [record.key for record in substream]))
2500
 
                return ['fake tokens'], ['fake missing keys']
 
3487
                return [b'fake tokens'], [b'fake missing keys']
2501
3488
        fake_real_sink = FakeRealSink()
 
3489
 
2502
3490
        class FakeRealRepository:
2503
3491
            def _get_sink(self):
2504
3492
                return fake_real_sink
 
3493
 
2505
3494
            def is_in_write_group(self):
2506
3495
                return False
 
3496
 
2507
3497
            def refresh_data(self):
2508
3498
                return True
2509
3499
        repo._real_repository = FakeRealRepository()
2510
3500
        sink = repo._get_sink()
2511
 
        fmt = repository.RepositoryFormat.get_default_format()
 
3501
        fmt = repository.format_registry.get_default()
2512
3502
        stream = self.make_stream_with_inv_deltas(fmt)
2513
3503
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
3504
        # Every record from the first inventory delta should have been sent to
2515
3505
        # the VFS sink.
2516
3506
        expected_records = [
2517
 
            ('inventory-deltas', [('rev2',), ('rev3',)]),
2518
 
            ('texts', [('some-rev', 'some-file')])]
 
3507
            ('inventory-deltas', [(b'rev2',), (b'rev3',)]),
 
3508
            ('texts', [(b'some-rev', b'some-file')])]
2519
3509
        self.assertEqual(expected_records, fake_real_sink.records)
2520
3510
        # The return values from the real sink's insert_stream are propagated
2521
3511
        # back to the original caller.
2522
 
        self.assertEqual(['fake tokens'], resume_tokens)
2523
 
        self.assertEqual(['fake missing keys'], missing_keys)
 
3512
        self.assertEqual([b'fake tokens'], resume_tokens)
 
3513
        self.assertEqual([b'fake missing keys'], missing_keys)
2524
3514
        self.assertFinished(client)
2525
3515
 
2526
3516
    def make_stream_with_inv_deltas(self, fmt):
2534
3524
           * texts substream: (some-rev, some-file)
2535
3525
        """
2536
3526
        # Define a stream using generators so that it isn't rewindable.
2537
 
        inv = inventory.Inventory(revision_id='rev1')
2538
 
        inv.root.revision = 'rev1'
 
3527
        inv = inventory.Inventory(revision_id=b'rev1')
 
3528
        inv.root.revision = b'rev1'
 
3529
 
2539
3530
        def stream_with_inv_delta():
2540
3531
            yield ('inventories', inventories_substream())
2541
3532
            yield ('inventory-deltas', inventory_delta_substream())
2542
3533
            yield ('texts', [
2543
3534
                versionedfile.FulltextContentFactory(
2544
 
                    ('some-rev', 'some-file'), (), None, 'content')])
 
3535
                    (b'some-rev', b'some-file'), (), None, b'content')])
 
3536
 
2545
3537
        def inventories_substream():
2546
3538
            # An empty inventory fulltext.  This will be streamed normally.
2547
3539
            text = fmt._serializer.write_inventory_to_string(inv)
2548
3540
            yield versionedfile.FulltextContentFactory(
2549
 
                ('rev1',), (), None, text)
 
3541
                (b'rev1',), (), None, text)
 
3542
 
2550
3543
        def inventory_delta_substream():
2551
3544
            # An inventory delta.  This can't be streamed via this verb, so it
2552
3545
            # will trigger a fallback to VFS insert_stream.
2553
3546
            entry = inv.make_entry(
2554
 
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
2555
 
            entry.revision = 'ghost'
2556
 
            delta = [(None, 'newdir', 'newdir-id', entry)]
 
3547
                'directory', 'newdir', inv.root.file_id, b'newdir-id')
 
3548
            entry.revision = b'ghost'
 
3549
            delta = [(None, 'newdir', b'newdir-id', entry)]
2557
3550
            serializer = inventory_delta.InventoryDeltaSerializer(
2558
3551
                versioned_root=True, tree_references=False)
2559
 
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
 
3552
            lines = serializer.delta_to_lines(b'rev1', b'rev2', delta)
2560
3553
            yield versionedfile.ChunkedContentFactory(
2561
 
                ('rev2',), (('rev1',)), None, lines)
 
3554
                (b'rev2',), ((b'rev1',)), None, lines)
2562
3555
            # Another delta.
2563
 
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
 
3556
            lines = serializer.delta_to_lines(b'rev1', b'rev3', delta)
2564
3557
            yield versionedfile.ChunkedContentFactory(
2565
 
                ('rev3',), (('rev1',)), None, lines)
 
3558
                (b'rev3',), ((b'rev1',)), None, lines)
2566
3559
        return stream_with_inv_delta()
2567
3560
 
2568
3561
 
2572
3565
        transport_path = 'quack'
2573
3566
        repo, client = self.setup_fake_client_and_repository(transport_path)
2574
3567
        client.add_expected_call(
2575
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2576
 
            'success', ('ok',))
 
3568
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3569
            b'success', (b'ok',))
2577
3570
        client.add_expected_call(
2578
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2579
 
            'success', ('ok',))
 
3571
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3572
            b'success', (b'ok',))
2580
3573
        self.checkInsertEmptyStream(repo, client)
2581
3574
 
2582
3575
    def test_locked_repo_with_no_lock_token(self):
2583
3576
        transport_path = 'quack'
2584
3577
        repo, client = self.setup_fake_client_and_repository(transport_path)
2585
3578
        client.add_expected_call(
2586
 
            'Repository.lock_write', ('quack/', ''),
2587
 
            'success', ('ok', ''))
2588
 
        client.add_expected_call(
2589
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2590
 
            'success', ('ok',))
2591
 
        client.add_expected_call(
2592
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2593
 
            'success', ('ok',))
 
3579
            b'Repository.lock_write', (b'quack/', b''),
 
3580
            b'success', (b'ok', b''))
 
3581
        client.add_expected_call(
 
3582
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3583
            b'success', (b'ok',))
 
3584
        client.add_expected_call(
 
3585
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3586
            b'success', (b'ok',))
2594
3587
        repo.lock_write()
2595
3588
        self.checkInsertEmptyStream(repo, client)
2596
3589
 
2598
3591
        transport_path = 'quack'
2599
3592
        repo, client = self.setup_fake_client_and_repository(transport_path)
2600
3593
        client.add_expected_call(
2601
 
            'Repository.lock_write', ('quack/', ''),
2602
 
            'success', ('ok', 'a token'))
2603
 
        client.add_expected_call(
2604
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2605
 
            'success', ('ok',))
2606
 
        client.add_expected_call(
2607
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2608
 
            'success', ('ok',))
 
3594
            b'Repository.lock_write', (b'quack/', b''),
 
3595
            b'success', (b'ok', b'a token'))
 
3596
        client.add_expected_call(
 
3597
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3598
            b'success', (b'ok',))
 
3599
        client.add_expected_call(
 
3600
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3601
            b'success', (b'ok',))
2609
3602
        repo.lock_write()
2610
3603
        self.checkInsertEmptyStream(repo, client)
2611
3604
 
2613
3606
class TestRepositoryTarball(TestRemoteRepository):
2614
3607
 
2615
3608
    # This is a canned tarball reponse we can validate against
2616
 
    tarball_content = (
 
3609
    tarball_content = base64.b64decode(
2617
3610
        'QlpoOTFBWSZTWdGkj3wAAWF/k8aQACBIB//A9+8cIX/v33AACEAYABAECEACNz'
2618
3611
        'JqsgJJFPTSnk1A3qh6mTQAAAANPUHkagkSTEkaA09QaNAAAGgAAAcwCYCZGAEY'
2619
3612
        'mJhMJghpiaYBUkKammSHqNMZQ0NABkNAeo0AGneAevnlwQoGzEzNVzaYxp/1Uk'
2626
3619
        'QplPKp2nqBWAfwBGaOwVrz3y1T+UZZNismXHsb2Jq18T+VaD9k4P8DqE3g70qV'
2627
3620
        'JLurpnDI6VS5oqDDPVbtVjMxMxMg4rzQVipn2Bv1fVNK0iq3Gl0hhnnHKm/egy'
2628
3621
        'nWQ7QH/F3JFOFCQ0aSPfA='
2629
 
        ).decode('base64')
 
3622
        )
2630
3623
 
2631
3624
    def test_repository_tarball(self):
2632
3625
        # Test that Repository.tarball generates the right operations
2633
3626
        transport_path = 'repo'
2634
 
        expected_calls = [('call_expecting_body', 'Repository.tarball',
2635
 
                           ('repo/', 'bz2',),),
2636
 
            ]
 
3627
        expected_calls = [('call_expecting_body', b'Repository.tarball',
 
3628
                           (b'repo/', b'bz2',),),
 
3629
                          ]
2637
3630
        repo, client = self.setup_fake_client_and_repository(transport_path)
2638
 
        client.add_success_response_with_body(self.tarball_content, 'ok')
 
3631
        client.add_success_response_with_body(self.tarball_content, b'ok')
2639
3632
        # Now actually ask for the tarball
2640
3633
        tarball_file = repo._get_tarball('bz2')
2641
3634
        try:
2670
3663
        self.calls = calls
2671
3664
        self._pack_collection = _StubPackCollection(calls)
2672
3665
 
 
3666
    def start_write_group(self):
 
3667
        self.calls.append(('start_write_group',))
 
3668
 
2673
3669
    def is_in_write_group(self):
2674
3670
        return False
2675
3671
 
2696
3692
        transport_path = 'quack'
2697
3693
        repo, client = self.setup_fake_client_and_repository(transport_path)
2698
3694
        client.add_expected_call(
2699
 
            'PackRepository.autopack', ('quack/',), 'success', ('ok',))
 
3695
            b'PackRepository.autopack', (b'quack/',), b'success', (b'ok',))
2700
3696
        repo.autopack()
2701
3697
        self.assertFinished(client)
2702
3698
 
2707
3703
        transport_path = 'quack'
2708
3704
        repo, client = self.setup_fake_client_and_repository(transport_path)
2709
3705
        client.add_expected_call(
2710
 
            'PackRepository.autopack', ('quack/',),
2711
 
            'success', ('ok',))
 
3706
            b'PackRepository.autopack', (b'quack/',),
 
3707
            b'success', (b'ok',))
2712
3708
        repo._real_repository = _StubRealPackRepository(client._calls)
2713
3709
        repo.autopack()
2714
3710
        self.assertEqual(
2715
 
            [('call', 'PackRepository.autopack', ('quack/',)),
 
3711
            [('call', b'PackRepository.autopack', (b'quack/',)),
2716
3712
             ('pack collection reload_pack_names',)],
2717
3713
            client._calls)
2718
3714
 
2722
3718
        """
2723
3719
        transport_path = 'quack'
2724
3720
        repo, client = self.setup_fake_client_and_repository(transport_path)
2725
 
        client.add_unknown_method_response('PackRepository.autopack')
 
3721
        client.add_unknown_method_response(b'PackRepository.autopack')
 
3722
 
2726
3723
        def stub_ensure_real():
2727
3724
            client._calls.append(('_ensure_real',))
2728
3725
            repo._real_repository = _StubRealPackRepository(client._calls)
2729
3726
        repo._ensure_real = stub_ensure_real
2730
3727
        repo.autopack()
2731
3728
        self.assertEqual(
2732
 
            [('call', 'PackRepository.autopack', ('quack/',)),
 
3729
            [('call', b'PackRepository.autopack', (b'quack/',)),
2733
3730
             ('_ensure_real',),
2734
3731
             ('pack collection autopack',)],
2735
3732
            client._calls)
2736
3733
 
 
3734
    def test_oom_error_reporting(self):
 
3735
        """An out-of-memory condition on the server is reported clearly"""
 
3736
        transport_path = 'quack'
 
3737
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3738
        client.add_expected_call(
 
3739
            b'PackRepository.autopack', (b'quack/',),
 
3740
            b'error', (b'MemoryError',))
 
3741
        err = self.assertRaises(errors.BzrError, repo.autopack)
 
3742
        self.assertContainsRe(str(err), "^remote server out of mem")
 
3743
 
2737
3744
 
2738
3745
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2739
 
    """Base class for unit tests for bzrlib.remote._translate_error."""
 
3746
    """Base class for unit tests for breezy.bzr.remote._translate_error."""
2740
3747
 
2741
3748
    def translateTuple(self, error_tuple, **context):
2742
3749
        """Call _translate_error with an ErrorFromSmartServer built from the
2762
3769
        """
2763
3770
        try:
2764
3771
            raise error_object
2765
 
        except errors.ErrorFromSmartServer, server_error:
 
3772
        except errors.ErrorFromSmartServer as server_error:
2766
3773
            translated_error = self.assertRaises(
2767
3774
                errors.BzrError, remote._translate_error, server_error,
2768
3775
                **context)
2770
3777
 
2771
3778
 
2772
3779
class TestErrorTranslationSuccess(TestErrorTranslationBase):
2773
 
    """Unit tests for bzrlib.remote._translate_error.
 
3780
    """Unit tests for breezy.bzr.remote._translate_error.
2774
3781
 
2775
3782
    Given an ErrorFromSmartServer (which has an error tuple from a smart
2776
3783
    server) and some context, _translate_error raises more specific errors from
2777
 
    bzrlib.errors.
 
3784
    breezy.errors.
2778
3785
 
2779
3786
    This test case covers the cases where _translate_error succeeds in
2780
3787
    translating an ErrorFromSmartServer to something better.  See
2783
3790
 
2784
3791
    def test_NoSuchRevision(self):
2785
3792
        branch = self.make_branch('')
2786
 
        revid = 'revid'
 
3793
        revid = b'revid'
2787
3794
        translated_error = self.translateTuple(
2788
 
            ('NoSuchRevision', revid), branch=branch)
 
3795
            (b'NoSuchRevision', revid), branch=branch)
2789
3796
        expected_error = errors.NoSuchRevision(branch, revid)
2790
3797
        self.assertEqual(expected_error, translated_error)
2791
3798
 
2792
3799
    def test_nosuchrevision(self):
2793
3800
        repository = self.make_repository('')
2794
 
        revid = 'revid'
 
3801
        revid = b'revid'
2795
3802
        translated_error = self.translateTuple(
2796
 
            ('nosuchrevision', revid), repository=repository)
 
3803
            (b'nosuchrevision', revid), repository=repository)
2797
3804
        expected_error = errors.NoSuchRevision(repository, revid)
2798
3805
        self.assertEqual(expected_error, translated_error)
2799
3806
 
2800
3807
    def test_nobranch(self):
2801
 
        bzrdir = self.make_bzrdir('')
2802
 
        translated_error = self.translateTuple(('nobranch',), bzrdir=bzrdir)
 
3808
        bzrdir = self.make_controldir('')
 
3809
        translated_error = self.translateTuple((b'nobranch',), bzrdir=bzrdir)
2803
3810
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
2804
3811
        self.assertEqual(expected_error, translated_error)
2805
3812
 
2806
3813
    def test_nobranch_one_arg(self):
2807
 
        bzrdir = self.make_bzrdir('')
 
3814
        bzrdir = self.make_controldir('')
2808
3815
        translated_error = self.translateTuple(
2809
 
            ('nobranch', 'extra detail'), bzrdir=bzrdir)
 
3816
            (b'nobranch', b'extra detail'), bzrdir=bzrdir)
2810
3817
        expected_error = errors.NotBranchError(
2811
3818
            path=bzrdir.root_transport.base,
2812
3819
            detail='extra detail')
2813
3820
        self.assertEqual(expected_error, translated_error)
2814
3821
 
 
3822
    def test_norepository(self):
 
3823
        bzrdir = self.make_controldir('')
 
3824
        translated_error = self.translateTuple((b'norepository',),
 
3825
                                               bzrdir=bzrdir)
 
3826
        expected_error = errors.NoRepositoryPresent(bzrdir)
 
3827
        self.assertEqual(expected_error, translated_error)
 
3828
 
2815
3829
    def test_LockContention(self):
2816
 
        translated_error = self.translateTuple(('LockContention',))
 
3830
        translated_error = self.translateTuple((b'LockContention',))
2817
3831
        expected_error = errors.LockContention('(remote lock)')
2818
3832
        self.assertEqual(expected_error, translated_error)
2819
3833
 
2820
3834
    def test_UnlockableTransport(self):
2821
 
        bzrdir = self.make_bzrdir('')
 
3835
        bzrdir = self.make_controldir('')
2822
3836
        translated_error = self.translateTuple(
2823
 
            ('UnlockableTransport',), bzrdir=bzrdir)
 
3837
            (b'UnlockableTransport',), bzrdir=bzrdir)
2824
3838
        expected_error = errors.UnlockableTransport(bzrdir.root_transport)
2825
3839
        self.assertEqual(expected_error, translated_error)
2826
3840
 
2827
3841
    def test_LockFailed(self):
2828
3842
        lock = 'str() of a server lock'
2829
3843
        why = 'str() of why'
2830
 
        translated_error = self.translateTuple(('LockFailed', lock, why))
 
3844
        translated_error = self.translateTuple(
 
3845
            (b'LockFailed', lock.encode('ascii'), why.encode('ascii')))
2831
3846
        expected_error = errors.LockFailed(lock, why)
2832
3847
        self.assertEqual(expected_error, translated_error)
2833
3848
 
2834
3849
    def test_TokenMismatch(self):
2835
3850
        token = 'a lock token'
2836
 
        translated_error = self.translateTuple(('TokenMismatch',), token=token)
 
3851
        translated_error = self.translateTuple(
 
3852
            (b'TokenMismatch',), token=token)
2837
3853
        expected_error = errors.TokenMismatch(token, '(remote token)')
2838
3854
        self.assertEqual(expected_error, translated_error)
2839
3855
 
2841
3857
        branch = self.make_branch('a')
2842
3858
        other_branch = self.make_branch('b')
2843
3859
        translated_error = self.translateTuple(
2844
 
            ('Diverged',), branch=branch, other_branch=other_branch)
 
3860
            (b'Diverged',), branch=branch, other_branch=other_branch)
2845
3861
        expected_error = errors.DivergedBranches(branch, other_branch)
2846
3862
        self.assertEqual(expected_error, translated_error)
2847
3863
 
 
3864
    def test_NotStacked(self):
 
3865
        branch = self.make_branch('')
 
3866
        translated_error = self.translateTuple((b'NotStacked',), branch=branch)
 
3867
        expected_error = errors.NotStacked(branch)
 
3868
        self.assertEqual(expected_error, translated_error)
 
3869
 
2848
3870
    def test_ReadError_no_args(self):
2849
3871
        path = 'a path'
2850
 
        translated_error = self.translateTuple(('ReadError',), path=path)
 
3872
        translated_error = self.translateTuple((b'ReadError',), path=path)
2851
3873
        expected_error = errors.ReadError(path)
2852
3874
        self.assertEqual(expected_error, translated_error)
2853
3875
 
2854
3876
    def test_ReadError(self):
2855
3877
        path = 'a path'
2856
 
        translated_error = self.translateTuple(('ReadError', path))
 
3878
        translated_error = self.translateTuple(
 
3879
            (b'ReadError', path.encode('utf-8')))
2857
3880
        expected_error = errors.ReadError(path)
2858
3881
        self.assertEqual(expected_error, translated_error)
2859
3882
 
2860
3883
    def test_IncompatibleRepositories(self):
2861
 
        translated_error = self.translateTuple(('IncompatibleRepositories',
2862
 
            "repo1", "repo2", "details here"))
 
3884
        translated_error = self.translateTuple((b'IncompatibleRepositories',
 
3885
                                                b"repo1", b"repo2", b"details here"))
2863
3886
        expected_error = errors.IncompatibleRepositories("repo1", "repo2",
2864
 
            "details here")
 
3887
                                                         "details here")
 
3888
        self.assertEqual(expected_error, translated_error)
 
3889
 
 
3890
    def test_GhostRevisionsHaveNoRevno(self):
 
3891
        translated_error = self.translateTuple((b'GhostRevisionsHaveNoRevno',
 
3892
                                                b"revid1", b"revid2"))
 
3893
        expected_error = errors.GhostRevisionsHaveNoRevno(b"revid1", b"revid2")
2865
3894
        self.assertEqual(expected_error, translated_error)
2866
3895
 
2867
3896
    def test_PermissionDenied_no_args(self):
2868
3897
        path = 'a path'
2869
 
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
 
3898
        translated_error = self.translateTuple((b'PermissionDenied',),
 
3899
                                               path=path)
2870
3900
        expected_error = errors.PermissionDenied(path)
2871
3901
        self.assertEqual(expected_error, translated_error)
2872
3902
 
2873
3903
    def test_PermissionDenied_one_arg(self):
2874
3904
        path = 'a path'
2875
 
        translated_error = self.translateTuple(('PermissionDenied', path))
 
3905
        translated_error = self.translateTuple(
 
3906
            (b'PermissionDenied', path.encode('utf-8')))
2876
3907
        expected_error = errors.PermissionDenied(path)
2877
3908
        self.assertEqual(expected_error, translated_error)
2878
3909
 
2883
3914
        local_path = 'local path'
2884
3915
        remote_path = 'remote path'
2885
3916
        translated_error = self.translateTuple(
2886
 
            ('PermissionDenied', remote_path), path=local_path)
 
3917
            (b'PermissionDenied', remote_path.encode('utf-8')), path=local_path)
2887
3918
        expected_error = errors.PermissionDenied(local_path)
2888
3919
        self.assertEqual(expected_error, translated_error)
2889
3920
 
2891
3922
        path = 'a path'
2892
3923
        extra = 'a string with extra info'
2893
3924
        translated_error = self.translateTuple(
2894
 
            ('PermissionDenied', path, extra))
 
3925
            (b'PermissionDenied', path.encode('utf-8'), extra.encode('utf-8')))
2895
3926
        expected_error = errors.PermissionDenied(path, extra)
2896
3927
        self.assertEqual(expected_error, translated_error)
2897
3928
 
 
3929
    # GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
 
3930
 
 
3931
    def test_NoSuchFile_context_path(self):
 
3932
        local_path = "local path"
 
3933
        translated_error = self.translateTuple((b'ReadError', b"remote path"),
 
3934
                                               path=local_path)
 
3935
        expected_error = errors.ReadError(local_path)
 
3936
        self.assertEqual(expected_error, translated_error)
 
3937
 
 
3938
    def test_NoSuchFile_without_context(self):
 
3939
        remote_path = "remote path"
 
3940
        translated_error = self.translateTuple(
 
3941
            (b'ReadError', remote_path.encode('utf-8')))
 
3942
        expected_error = errors.ReadError(remote_path)
 
3943
        self.assertEqual(expected_error, translated_error)
 
3944
 
 
3945
    def test_ReadOnlyError(self):
 
3946
        translated_error = self.translateTuple((b'ReadOnlyError',))
 
3947
        expected_error = errors.TransportNotPossible("readonly transport")
 
3948
        self.assertEqual(expected_error, translated_error)
 
3949
 
 
3950
    def test_MemoryError(self):
 
3951
        translated_error = self.translateTuple((b'MemoryError',))
 
3952
        self.assertStartsWith(str(translated_error),
 
3953
                              "remote server out of memory")
 
3954
 
 
3955
    def test_generic_IndexError_no_classname(self):
 
3956
        err = errors.ErrorFromSmartServer(
 
3957
            (b'error', b"list index out of range"))
 
3958
        translated_error = self.translateErrorFromSmartServer(err)
 
3959
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3960
        self.assertEqual(expected_error, translated_error)
 
3961
 
 
3962
    # GZ 2011-03-02: TODO test generic non-ascii error string
 
3963
 
 
3964
    def test_generic_KeyError(self):
 
3965
        err = errors.ErrorFromSmartServer((b'error', b'KeyError', b"1"))
 
3966
        translated_error = self.translateErrorFromSmartServer(err)
 
3967
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3968
        self.assertEqual(expected_error, translated_error)
 
3969
 
 
3970
    def test_RevnoOutOfBounds(self):
 
3971
        translated_error = self.translateTuple(
 
3972
            ((b'revno-outofbounds', 5, 0, 3)), path=b'path')
 
3973
        expected_error = errors.RevnoOutOfBounds(5, (0, 3))
 
3974
        self.assertEqual(expected_error, translated_error)
 
3975
 
2898
3976
 
2899
3977
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2900
 
    """Unit tests for bzrlib.remote._translate_error's robustness.
 
3978
    """Unit tests for breezy.bzr.remote._translate_error's robustness.
2901
3979
 
2902
3980
    TestErrorTranslationSuccess is for cases where _translate_error can
2903
3981
    translate successfully.  This class about how _translate_err behaves when
2908
3986
        """If the error code from the server is not recognised, the original
2909
3987
        ErrorFromSmartServer is propagated unmodified.
2910
3988
        """
2911
 
        error_tuple = ('An unknown error tuple',)
 
3989
        error_tuple = (b'An unknown error tuple',)
2912
3990
        server_error = errors.ErrorFromSmartServer(error_tuple)
2913
3991
        translated_error = self.translateErrorFromSmartServer(server_error)
2914
3992
        expected_error = errors.UnknownErrorFromSmartServer(server_error)
2922
4000
        # To translate a NoSuchRevision error _translate_error needs a 'branch'
2923
4001
        # in the context dict.  So let's give it an empty context dict instead
2924
4002
        # to exercise its error recovery.
2925
 
        empty_context = {}
2926
 
        error_tuple = ('NoSuchRevision', 'revid')
 
4003
        error_tuple = (b'NoSuchRevision', b'revid')
2927
4004
        server_error = errors.ErrorFromSmartServer(error_tuple)
2928
4005
        translated_error = self.translateErrorFromSmartServer(server_error)
2929
4006
        self.assertEqual(server_error, translated_error)
2938
4015
        'path' variable from either the wire or the local context.  If neither
2939
4016
        has it, then an error is raised.
2940
4017
        """
2941
 
        error_tuple = ('ReadError',)
 
4018
        error_tuple = (b'ReadError',)
2942
4019
        server_error = errors.ErrorFromSmartServer(error_tuple)
2943
4020
        translated_error = self.translateErrorFromSmartServer(server_error)
2944
4021
        self.assertEqual(server_error, translated_error)
2958
4035
        # make a branch stacked on another repository containing an empty
2959
4036
        # revision, then open it over hpss - we should be able to see that
2960
4037
        # revision.
2961
 
        base_transport = self.get_transport()
2962
4038
        base_builder = self.make_branch_builder('base', format='1.9')
2963
4039
        base_builder.start_series()
2964
 
        base_revid = base_builder.build_snapshot('rev-id', None,
2965
 
            [('add', ('', None, 'directory', None))],
2966
 
            'message')
 
4040
        base_revid = base_builder.build_snapshot(None,
 
4041
                                                 [('add', ('', None, 'directory', None))],
 
4042
                                                 'message', revision_id=b'rev-id')
2967
4043
        base_builder.finish_series()
2968
4044
        stacked_branch = self.make_branch('stacked', format='1.9')
2969
4045
        stacked_branch.set_stacked_on_url('../base')
2980
4056
            # be a RemoteRepository
2981
4057
            self.assertLength(1, remote_repo._fallback_repositories)
2982
4058
            self.assertIsInstance(remote_repo._fallback_repositories[0],
2983
 
                RemoteRepository)
 
4059
                                  RemoteRepository)
2984
4060
            # and it has the revision committed to the underlying repository;
2985
4061
            # these have varying implementations so we try several of them
2986
4062
            self.assertTrue(remote_repo.has_revisions([base_revid]))
2987
4063
            self.assertTrue(remote_repo.has_revision(base_revid))
2988
4064
            self.assertEqual(remote_repo.get_revision(base_revid).message,
2989
 
                'message')
 
4065
                             'message')
2990
4066
        finally:
2991
4067
            remote_repo.unlock()
2992
4068
 
2994
4070
        """Get stacked_upon and stacked branches with content in each."""
2995
4071
        self.setup_smart_server_with_call_log()
2996
4072
        tree1 = self.make_branch_and_tree('tree1', format='1.9')
2997
 
        tree1.commit('rev1', rev_id='rev1')
2998
 
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2999
 
            ).open_workingtree()
 
4073
        tree1.commit('rev1', rev_id=b'rev1')
 
4074
        tree2 = tree1.branch.controldir.sprout('tree2', stacked=True
 
4075
                                               ).open_workingtree()
3000
4076
        local_tree = tree2.branch.create_checkout('local')
3001
4077
        local_tree.commit('local changes make me feel good.')
3002
4078
        branch2 = Branch.open(self.get_url('tree2'))
3008
4084
        # the public implementation of get_parent_map obeys stacking
3009
4085
        _, branch = self.prepare_stacked_remote_branch()
3010
4086
        repo = branch.repository
3011
 
        self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
 
4087
        self.assertEqual({b'rev1'}, set(repo.get_parent_map([b'rev1'])))
3012
4088
 
3013
4089
    def test_unstacked_get_parent_map(self):
3014
4090
        # _unstacked_provider.get_parent_map ignores stacking
3015
4091
        _, branch = self.prepare_stacked_remote_branch()
3016
4092
        provider = branch.repository._unstacked_provider
3017
 
        self.assertEqual([], provider.get_parent_map(['rev1']).keys())
 
4093
        self.assertEqual(set(), set(provider.get_parent_map([b'rev1'])))
3018
4094
 
3019
4095
    def fetch_stream_to_rev_order(self, stream):
3020
4096
        result = []
3036
4112
        :result: The revision ids in the stream, in the order seen,
3037
4113
            the topological order of revisions in the source.
3038
4114
        """
3039
 
        unordered_format = bzrdir.format_registry.get(format)()
 
4115
        unordered_format = controldir.format_registry.get(format)()
3040
4116
        target_repository_format = unordered_format.repository_format
3041
4117
        # Cross check
3042
4118
        self.assertEqual(order, target_repository_format._fetch_order)
3045
4121
        _, stacked = branch_factory()
3046
4122
        source = stacked.repository._get_source(target_repository_format)
3047
4123
        tip = stacked.last_revision()
3048
 
        revs = stacked.repository.get_ancestry(tip)
3049
 
        search = graph.PendingAncestryResult([tip], stacked.repository)
 
4124
        stacked.repository._ensure_real()
 
4125
        graph = stacked.repository.get_graph()
 
4126
        revs = [r for (r, ps) in graph.iter_ancestry([tip])
 
4127
                if r != NULL_REVISION]
 
4128
        revs.reverse()
 
4129
        search = vf_search.PendingAncestryResult([tip], stacked.repository)
3050
4130
        self.reset_smart_call_log()
3051
4131
        stream = source.get_stream(search)
3052
 
        if None in revs:
3053
 
            revs.remove(None)
3054
4132
        # We trust that if a revision is in the stream the rest of the new
3055
4133
        # content for it is too, as per our main fetch tests; here we are
3056
4134
        # checking that the revisions are actually included at all, and their
3072
4150
        # is itself stacked yields the full data from all three sources.
3073
4151
        def make_stacked_stacked():
3074
4152
            _, stacked = self.prepare_stacked_remote_branch()
3075
 
            tree = stacked.bzrdir.sprout('tree3', stacked=True
3076
 
                ).open_workingtree()
 
4153
            tree = stacked.controldir.sprout('tree3', stacked=True
 
4154
                                             ).open_workingtree()
3077
4155
            local_tree = tree.branch.create_checkout('local-tree3')
3078
4156
            local_tree.commit('more local changes are better')
3079
4157
            branch = Branch.open(self.get_url('tree3'))
3080
4158
            branch.lock_read()
3081
4159
            self.addCleanup(branch.unlock)
3082
4160
            return None, branch
3083
 
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3084
 
            branch_factory=make_stacked_stacked)
 
4161
        rev_ord, expected_revs = self.get_ordered_revs(
 
4162
            '1.9', 'unordered', branch_factory=make_stacked_stacked)
3085
4163
        self.assertEqual(set(expected_revs), set(rev_ord))
3086
4164
        # Getting unordered results should have made a streaming data request
3087
4165
        # from the server, and one from each backing repo
3095
4173
        self.assertEqual(expected_revs, rev_ord)
3096
4174
        # Getting topological sort requires VFS calls still - one of which is
3097
4175
        # pushing up from the bound branch.
3098
 
        self.assertLength(13, self.hpss_calls)
 
4176
        self.assertLength(14, self.hpss_calls)
3099
4177
 
3100
4178
    def test_stacked_get_stream_groupcompress(self):
3101
4179
        # Repository._get_source.get_stream() from a stacked repository with
3116
4194
        # branch pulling content from stacked and trunk.
3117
4195
        self.setup_smart_server_with_call_log()
3118
4196
        trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3119
 
        r1 = trunk.commit('start')
 
4197
        trunk.commit('start')
3120
4198
        stacked_branch = trunk.branch.create_clone_on_transport(
3121
4199
            self.get_transport('stacked'), stacked_on=trunk.branch.base)
3122
4200
        local = self.make_branch('local', format='1.9-rich-root')
3123
4201
        local.repository.fetch(stacked_branch.repository,
3124
 
            stacked_branch.last_revision())
 
4202
                               stacked_branch.last_revision())
3125
4203
 
3126
4204
 
3127
4205
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
3142
4220
 
3143
4221
    def test_copy_content_into_avoids_revision_history(self):
3144
4222
        local = self.make_branch('local')
3145
 
        remote_backing_tree = self.make_branch_and_tree('remote')
3146
 
        remote_backing_tree.commit("Commit.")
 
4223
        builder = self.make_branch_builder('remote')
 
4224
        builder.build_commit(message="Commit.")
3147
4225
        remote_branch_url = self.smart_server.get_url() + 'remote'
3148
4226
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3149
4227
        local.repository.fetch(remote_branch.repository)
3150
4228
        self.hpss_calls = []
3151
4229
        remote_branch.copy_content_into(local)
3152
 
        self.assertFalse('Branch.revision_history' in self.hpss_calls)
 
4230
        self.assertFalse(b'Branch.revision_history' in self.hpss_calls)
 
4231
 
 
4232
    def test_fetch_everything_needs_just_one_call(self):
 
4233
        local = self.make_branch('local')
 
4234
        builder = self.make_branch_builder('remote')
 
4235
        builder.build_commit(message="Commit.")
 
4236
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
4237
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
4238
        self.hpss_calls = []
 
4239
        local.repository.fetch(
 
4240
            remote_branch.repository,
 
4241
            fetch_spec=vf_search.EverythingResult(remote_branch.repository))
 
4242
        self.assertEqual([b'Repository.get_stream_1.19'], self.hpss_calls)
 
4243
 
 
4244
    def override_verb(self, verb_name, verb):
 
4245
        request_handlers = request.request_handlers
 
4246
        orig_verb = request_handlers.get(verb_name)
 
4247
        orig_info = request_handlers.get_info(verb_name)
 
4248
        request_handlers.register(verb_name, verb, override_existing=True)
 
4249
        self.addCleanup(request_handlers.register, verb_name, orig_verb,
 
4250
                        override_existing=True, info=orig_info)
 
4251
 
 
4252
    def test_fetch_everything_backwards_compat(self):
 
4253
        """Can fetch with EverythingResult even with pre 2.4 servers.
 
4254
 
 
4255
        Pre-2.4 do not support 'everything' searches with the
 
4256
        Repository.get_stream_1.19 verb.
 
4257
        """
 
4258
        verb_log = []
 
4259
 
 
4260
        class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
 
4261
            """A version of the Repository.get_stream_1.19 verb patched to
 
4262
            reject 'everything' searches the way 2.3 and earlier do.
 
4263
            """
 
4264
 
 
4265
            def recreate_search(self, repository, search_bytes,
 
4266
                                discard_excess=False):
 
4267
                verb_log.append(search_bytes.split(b'\n', 1)[0])
 
4268
                if search_bytes == b'everything':
 
4269
                    return (None,
 
4270
                            request.FailedSmartServerResponse((b'BadSearch',)))
 
4271
                return super(OldGetStreamVerb,
 
4272
                             self).recreate_search(repository, search_bytes,
 
4273
                                                   discard_excess=discard_excess)
 
4274
        self.override_verb(b'Repository.get_stream_1.19', OldGetStreamVerb)
 
4275
        local = self.make_branch('local')
 
4276
        builder = self.make_branch_builder('remote')
 
4277
        builder.build_commit(message="Commit.")
 
4278
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
4279
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
4280
        self.hpss_calls = []
 
4281
        local.repository.fetch(
 
4282
            remote_branch.repository,
 
4283
            fetch_spec=vf_search.EverythingResult(remote_branch.repository))
 
4284
        # make sure the overridden verb was used
 
4285
        self.assertLength(1, verb_log)
 
4286
        # more than one HPSS call is needed, but because it's a VFS callback
 
4287
        # its hard to predict exactly how many.
 
4288
        self.assertTrue(len(self.hpss_calls) > 1)
 
4289
 
 
4290
 
 
4291
class TestUpdateBoundBranchWithModifiedBoundLocation(
 
4292
        tests.TestCaseWithTransport):
 
4293
    """Ensure correct handling of bound_location modifications.
 
4294
 
 
4295
    This is tested against a smart server as http://pad.lv/786980 was about a
 
4296
    ReadOnlyError (write attempt during a read-only transaction) which can only
 
4297
    happen in this context.
 
4298
    """
 
4299
 
 
4300
    def setUp(self):
 
4301
        super(TestUpdateBoundBranchWithModifiedBoundLocation, self).setUp()
 
4302
        self.transport_server = test_server.SmartTCPServer_for_testing
 
4303
 
 
4304
    def make_master_and_checkout(self, master_name, checkout_name):
 
4305
        # Create the master branch and its associated checkout
 
4306
        self.master = self.make_branch_and_tree(master_name)
 
4307
        self.checkout = self.master.branch.create_checkout(checkout_name)
 
4308
        # Modify the master branch so there is something to update
 
4309
        self.master.commit('add stuff')
 
4310
        self.last_revid = self.master.commit('even more stuff')
 
4311
        self.bound_location = self.checkout.branch.get_bound_location()
 
4312
 
 
4313
    def assertUpdateSucceeds(self, new_location):
 
4314
        self.checkout.branch.set_bound_location(new_location)
 
4315
        self.checkout.update()
 
4316
        self.assertEqual(self.last_revid, self.checkout.last_revision())
 
4317
 
 
4318
    def test_without_final_slash(self):
 
4319
        self.make_master_and_checkout('master', 'checkout')
 
4320
        # For unclear reasons some users have a bound_location without a final
 
4321
        # '/', simulate that by forcing such a value
 
4322
        self.assertEndsWith(self.bound_location, '/')
 
4323
        self.assertUpdateSucceeds(self.bound_location.rstrip('/'))
 
4324
 
 
4325
    def test_plus_sign(self):
 
4326
        self.make_master_and_checkout('+master', 'checkout')
 
4327
        self.assertUpdateSucceeds(self.bound_location.replace('%2B', '+', 1))
 
4328
 
 
4329
    def test_tilda(self):
 
4330
        # Embed ~ in the middle of the path just to avoid any $HOME
 
4331
        # interpretation
 
4332
        self.make_master_and_checkout('mas~ter', 'checkout')
 
4333
        self.assertUpdateSucceeds(self.bound_location.replace('%2E', '~', 1))
 
4334
 
 
4335
 
 
4336
class TestWithCustomErrorHandler(RemoteBranchTestCase):
 
4337
 
 
4338
    def test_no_context(self):
 
4339
        class OutOfCoffee(errors.BzrError):
 
4340
            """A dummy exception for testing."""
 
4341
 
 
4342
            def __init__(self, urgency):
 
4343
                self.urgency = urgency
 
4344
        remote.no_context_error_translators.register(b"OutOfCoffee",
 
4345
                                                     lambda err: OutOfCoffee(err.error_args[0]))
 
4346
        transport = MemoryTransport()
 
4347
        client = FakeClient(transport.base)
 
4348
        client.add_expected_call(
 
4349
            b'Branch.get_stacked_on_url', (b'quack/',),
 
4350
            b'error', (b'NotStacked',))
 
4351
        client.add_expected_call(
 
4352
            b'Branch.last_revision_info',
 
4353
            (b'quack/',),
 
4354
            b'error', (b'OutOfCoffee', b'low'))
 
4355
        transport.mkdir('quack')
 
4356
        transport = transport.clone('quack')
 
4357
        branch = self.make_remote_branch(transport, client)
 
4358
        self.assertRaises(OutOfCoffee, branch.last_revision_info)
 
4359
        self.assertFinished(client)
 
4360
 
 
4361
    def test_with_context(self):
 
4362
        class OutOfTea(errors.BzrError):
 
4363
            def __init__(self, branch, urgency):
 
4364
                self.branch = branch
 
4365
                self.urgency = urgency
 
4366
        remote.error_translators.register(b"OutOfTea",
 
4367
                                          lambda err, find, path: OutOfTea(
 
4368
                                              err.error_args[0].decode(
 
4369
                                                  'utf-8'),
 
4370
                                              find("branch")))
 
4371
        transport = MemoryTransport()
 
4372
        client = FakeClient(transport.base)
 
4373
        client.add_expected_call(
 
4374
            b'Branch.get_stacked_on_url', (b'quack/',),
 
4375
            b'error', (b'NotStacked',))
 
4376
        client.add_expected_call(
 
4377
            b'Branch.last_revision_info',
 
4378
            (b'quack/',),
 
4379
            b'error', (b'OutOfTea', b'low'))
 
4380
        transport.mkdir('quack')
 
4381
        transport = transport.clone('quack')
 
4382
        branch = self.make_remote_branch(transport, client)
 
4383
        self.assertRaises(OutOfTea, branch.last_revision_info)
 
4384
        self.assertFinished(client)
 
4385
 
 
4386
 
 
4387
class TestRepositoryPack(TestRemoteRepository):
 
4388
 
 
4389
    def test_pack(self):
 
4390
        transport_path = 'quack'
 
4391
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4392
        client.add_expected_call(
 
4393
            b'Repository.lock_write', (b'quack/', b''),
 
4394
            b'success', (b'ok', b'token'))
 
4395
        client.add_expected_call(
 
4396
            b'Repository.pack', (b'quack/', b'token', b'False'),
 
4397
            b'success', (b'ok',), )
 
4398
        client.add_expected_call(
 
4399
            b'Repository.unlock', (b'quack/', b'token'),
 
4400
            b'success', (b'ok', ))
 
4401
        repo.pack()
 
4402
 
 
4403
    def test_pack_with_hint(self):
 
4404
        transport_path = 'quack'
 
4405
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4406
        client.add_expected_call(
 
4407
            b'Repository.lock_write', (b'quack/', b''),
 
4408
            b'success', (b'ok', b'token'))
 
4409
        client.add_expected_call(
 
4410
            b'Repository.pack', (b'quack/', b'token', b'False'),
 
4411
            b'success', (b'ok',), )
 
4412
        client.add_expected_call(
 
4413
            b'Repository.unlock', (b'quack/', b'token', b'False'),
 
4414
            b'success', (b'ok', ))
 
4415
        repo.pack(['hinta', 'hintb'])
 
4416
 
 
4417
 
 
4418
class TestRepositoryIterInventories(TestRemoteRepository):
 
4419
    """Test Repository.iter_inventories."""
 
4420
 
 
4421
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4422
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4423
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4424
 
 
4425
    def test_single_empty(self):
 
4426
        transport_path = 'quack'
 
4427
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4428
        fmt = controldir.format_registry.get('2a')().repository_format
 
4429
        repo._format = fmt
 
4430
        stream = [('inventory-deltas', [
 
4431
            versionedfile.FulltextContentFactory(b'somerevid', None, None,
 
4432
                                                 self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4433
        client.add_expected_call(
 
4434
            b'VersionedFileRepository.get_inventories', (
 
4435
                b'quack/', b'unordered'),
 
4436
            b'success', (b'ok', ),
 
4437
            _stream_to_byte_stream(stream, fmt))
 
4438
        ret = list(repo.iter_inventories([b"somerevid"]))
 
4439
        self.assertLength(1, ret)
 
4440
        inv = ret[0]
 
4441
        self.assertEqual(b"somerevid", inv.revision_id)
 
4442
 
 
4443
    def test_empty(self):
 
4444
        transport_path = 'quack'
 
4445
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4446
        ret = list(repo.iter_inventories([]))
 
4447
        self.assertEqual(ret, [])
 
4448
 
 
4449
    def test_missing(self):
 
4450
        transport_path = 'quack'
 
4451
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4452
        client.add_expected_call(
 
4453
            b'VersionedFileRepository.get_inventories', (
 
4454
                b'quack/', b'unordered'),
 
4455
            b'success', (b'ok', ), iter([]))
 
4456
        self.assertRaises(errors.NoSuchRevision, list, repo.iter_inventories(
 
4457
            [b"somerevid"]))
 
4458
 
 
4459
 
 
4460
class TestRepositoryRevisionTreeArchive(TestRemoteRepository):
 
4461
    """Test Repository.iter_inventories."""
 
4462
 
 
4463
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4464
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4465
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4466
 
 
4467
    def test_simple(self):
 
4468
        transport_path = 'quack'
 
4469
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4470
        fmt = controldir.format_registry.get('2a')().repository_format
 
4471
        repo._format = fmt
 
4472
        stream = [('inventory-deltas', [
 
4473
            versionedfile.FulltextContentFactory(b'somerevid', None, None,
 
4474
                                                 self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4475
        client.add_expected_call(
 
4476
            b'VersionedFileRepository.get_inventories', (
 
4477
                b'quack/', b'unordered'),
 
4478
            b'success', (b'ok', ),
 
4479
            _stream_to_byte_stream(stream, fmt))
 
4480
        f = BytesIO()
 
4481
        with tarfile.open(mode='w', fileobj=f) as tf:
 
4482
            info = tarfile.TarInfo('somefile')
 
4483
            info.mtime = 432432
 
4484
            contents = b'some data'
 
4485
            info.type = tarfile.REGTYPE
 
4486
            info.mode = 0o644
 
4487
            info.size = len(contents)
 
4488
            tf.addfile(info, BytesIO(contents))
 
4489
        client.add_expected_call(
 
4490
            b'Repository.revision_archive', (b'quack/',
 
4491
                                             b'somerevid', b'tar', b'foo.tar', b'', b'', None),
 
4492
            b'success', (b'ok', ),
 
4493
            f.getvalue())
 
4494
        tree = repo.revision_tree(b'somerevid')
 
4495
        self.assertEqual(f.getvalue(), b''.join(
 
4496
            tree.archive('tar', 'foo.tar')))
 
4497
 
 
4498
 
 
4499
class TestRepositoryAnnotate(TestRemoteRepository):
 
4500
    """Test RemoteRevisionTree.annotate.."""
 
4501
 
 
4502
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4503
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4504
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4505
 
 
4506
    def test_simple(self):
 
4507
        transport_path = 'quack'
 
4508
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4509
        fmt = controldir.format_registry.get('2a')().repository_format
 
4510
        repo._format = fmt
 
4511
        stream = [
 
4512
            ('inventory-deltas', [
 
4513
                versionedfile.FulltextContentFactory(
 
4514
                    b'somerevid', None, None,
 
4515
                    self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4516
        client.add_expected_call(
 
4517
            b'VersionedFileRepository.get_inventories', (
 
4518
                b'quack/', b'unordered'),
 
4519
            b'success', (b'ok', ),
 
4520
            _stream_to_byte_stream(stream, fmt))
 
4521
        client.add_expected_call(
 
4522
            b'Repository.annotate_file_revision',
 
4523
            (b'quack/', b'somerevid', b'filename', b'', b'current:'),
 
4524
            b'success', (b'ok', ),
 
4525
            bencode.bencode([[b'baserevid', b'line 1\n'],
 
4526
                             [b'somerevid', b'line2\n']]))
 
4527
        tree = repo.revision_tree(b'somerevid')
 
4528
        self.assertEqual([
 
4529
            (b'baserevid', b'line 1\n'),
 
4530
            (b'somerevid', b'line2\n')],
 
4531
            list(tree.annotate_iter('filename')))