/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: 2019-09-01 15:33:59 UTC
  • mto: This revision was merged to the branch mainline in revision 7404.
  • Revision ID: jelmer@jelmer.uk-20190901153359-9gl0ai0x5wuiv444
Rename init-repo to init-shared-repo.

Show diffs side-by-side

added added

removed removed

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