/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-02-18 01:57:45 UTC
  • mto: This revision was merged to the branch mainline in revision 7493.
  • Revision ID: jelmer@jelmer.uk-20200218015745-q2ss9tsk74h4nh61
drop use of future.

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