/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: 2018-11-17 17:17:02 UTC
  • mto: This revision was merged to the branch mainline in revision 7187.
  • Revision ID: jelmer@jelmer.uk-20181117171702-4tm0yznbaw3rq3es
More beees.

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
 
1655
2045
    def test_backwards_compat_set_option(self):
1656
2046
        self.setup_smart_server_with_call_log()
1657
2047
        branch = self.make_branch('.')
1658
 
        verb = 'Branch.set_config_option'
 
2048
        verb = b'Branch.set_config_option'
1659
2049
        self.disable_verb(verb)
1660
2050
        branch.lock_write()
1661
2051
        self.addCleanup(branch.unlock)
1662
2052
        self.reset_smart_call_log()
1663
2053
        branch._get_config().set_option('value', 'name')
1664
 
        self.assertLength(10, self.hpss_calls)
 
2054
        self.assertLength(11, self.hpss_calls)
1665
2055
        self.assertEqual('value', branch._get_config().get_option('name'))
1666
2056
 
 
2057
    def test_backwards_compat_set_option_with_dict(self):
 
2058
        self.setup_smart_server_with_call_log()
 
2059
        branch = self.make_branch('.')
 
2060
        verb = b'Branch.set_config_option_dict'
 
2061
        self.disable_verb(verb)
 
2062
        branch.lock_write()
 
2063
        self.addCleanup(branch.unlock)
 
2064
        self.reset_smart_call_log()
 
2065
        config = branch._get_config()
 
2066
        value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
 
2067
        config.set_option(value_dict, 'name')
 
2068
        self.assertLength(11, self.hpss_calls)
 
2069
        self.assertEqual(value_dict, branch._get_config().get_option('name'))
 
2070
 
 
2071
 
 
2072
class TestBranchGetPutConfigStore(RemoteBranchTestCase):
 
2073
 
 
2074
    def test_get_branch_conf(self):
 
2075
        # in an empty branch we decode the response properly
 
2076
        client = FakeClient()
 
2077
        client.add_expected_call(
 
2078
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2079
            b'error', (b'NotStacked',),)
 
2080
        client.add_success_response_with_body(b'# config file body', b'ok')
 
2081
        transport = MemoryTransport()
 
2082
        branch = self.make_remote_branch(transport, client)
 
2083
        config = branch.get_config_stack()
 
2084
        config.get("email")
 
2085
        config.get("log_format")
 
2086
        self.assertEqual(
 
2087
            [('call', b'Branch.get_stacked_on_url', (b'memory:///',)),
 
2088
             ('call_expecting_body', b'Branch.get_config_file', (b'memory:///',))],
 
2089
            client._calls)
 
2090
 
 
2091
    def test_set_branch_conf(self):
 
2092
        client = FakeClient()
 
2093
        client.add_expected_call(
 
2094
            b'Branch.get_stacked_on_url', (b'memory:///',),
 
2095
            b'error', (b'NotStacked',),)
 
2096
        client.add_expected_call(
 
2097
            b'Branch.lock_write', (b'memory:///', b'', b''),
 
2098
            b'success', (b'ok', b'branch token', b'repo token'))
 
2099
        client.add_expected_call(
 
2100
            b'Branch.get_config_file', (b'memory:///', ),
 
2101
            b'success', (b'ok', ), b"# line 1\n")
 
2102
        client.add_expected_call(
 
2103
            b'Branch.get_config_file', (b'memory:///', ),
 
2104
            b'success', (b'ok', ), b"# line 1\n")
 
2105
        client.add_expected_call(
 
2106
            b'Branch.put_config_file', (b'memory:///', b'branch token',
 
2107
                                        b'repo token'),
 
2108
            b'success', (b'ok',))
 
2109
        client.add_expected_call(
 
2110
            b'Branch.unlock', (b'memory:///', b'branch token', b'repo token'),
 
2111
            b'success', (b'ok',))
 
2112
        transport = MemoryTransport()
 
2113
        branch = self.make_remote_branch(transport, client)
 
2114
        branch.lock_write()
 
2115
        config = branch.get_config_stack()
 
2116
        config.set('email', 'The Dude <lebowski@example.com>')
 
2117
        branch.unlock()
 
2118
        self.assertFinished(client)
 
2119
        self.assertEqual(
 
2120
            [('call', b'Branch.get_stacked_on_url', (b'memory:///',)),
 
2121
             ('call', b'Branch.lock_write', (b'memory:///', b'', b'')),
 
2122
             ('call_expecting_body', b'Branch.get_config_file',
 
2123
                 (b'memory:///',)),
 
2124
             ('call_expecting_body', b'Branch.get_config_file',
 
2125
                 (b'memory:///',)),
 
2126
             ('call_with_body_bytes_expecting_body', b'Branch.put_config_file',
 
2127
                 (b'memory:///', b'branch token', b'repo token'),
 
2128
                 b'# line 1\nemail = The Dude <lebowski@example.com>\n'),
 
2129
             ('call', b'Branch.unlock',
 
2130
                 (b'memory:///', b'branch token', b'repo token'))],
 
2131
            client._calls)
 
2132
 
1667
2133
 
1668
2134
class TestBranchLockWrite(RemoteBranchTestCase):
1669
2135
 
1671
2137
        transport = MemoryTransport()
1672
2138
        client = FakeClient(transport.base)
1673
2139
        client.add_expected_call(
1674
 
            'Branch.get_stacked_on_url', ('quack/',),
1675
 
            'error', ('NotStacked',),)
 
2140
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2141
            b'error', (b'NotStacked',),)
1676
2142
        client.add_expected_call(
1677
 
            'Branch.lock_write', ('quack/', '', ''),
1678
 
            'error', ('UnlockableTransport',))
 
2143
            b'Branch.lock_write', (b'quack/', b'', b''),
 
2144
            b'error', (b'UnlockableTransport',))
1679
2145
        transport.mkdir('quack')
1680
2146
        transport = transport.clone('quack')
1681
2147
        branch = self.make_remote_branch(transport, client)
1683
2149
        self.assertFinished(client)
1684
2150
 
1685
2151
 
 
2152
class TestBranchRevisionIdToRevno(RemoteBranchTestCase):
 
2153
 
 
2154
    def test_simple(self):
 
2155
        transport = MemoryTransport()
 
2156
        client = FakeClient(transport.base)
 
2157
        client.add_expected_call(
 
2158
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2159
            b'error', (b'NotStacked',),)
 
2160
        client.add_expected_call(
 
2161
            b'Branch.revision_id_to_revno', (b'quack/', b'null:'),
 
2162
            b'success', (b'ok', b'0',),)
 
2163
        client.add_expected_call(
 
2164
            b'Branch.revision_id_to_revno', (b'quack/', b'unknown'),
 
2165
            b'error', (b'NoSuchRevision', b'unknown',),)
 
2166
        transport.mkdir('quack')
 
2167
        transport = transport.clone('quack')
 
2168
        branch = self.make_remote_branch(transport, client)
 
2169
        self.assertEqual(0, branch.revision_id_to_revno(b'null:'))
 
2170
        self.assertRaises(errors.NoSuchRevision,
 
2171
                          branch.revision_id_to_revno, b'unknown')
 
2172
        self.assertFinished(client)
 
2173
 
 
2174
    def test_dotted(self):
 
2175
        transport = MemoryTransport()
 
2176
        client = FakeClient(transport.base)
 
2177
        client.add_expected_call(
 
2178
            b'Branch.get_stacked_on_url', (b'quack/',),
 
2179
            b'error', (b'NotStacked',),)
 
2180
        client.add_expected_call(
 
2181
            b'Branch.revision_id_to_revno', (b'quack/', b'null:'),
 
2182
            b'success', (b'ok', b'0',),)
 
2183
        client.add_expected_call(
 
2184
            b'Branch.revision_id_to_revno', (b'quack/', b'unknown'),
 
2185
            b'error', (b'NoSuchRevision', b'unknown',),)
 
2186
        transport.mkdir('quack')
 
2187
        transport = transport.clone('quack')
 
2188
        branch = self.make_remote_branch(transport, client)
 
2189
        self.assertEqual((0, ), branch.revision_id_to_dotted_revno(b'null:'))
 
2190
        self.assertRaises(errors.NoSuchRevision,
 
2191
                          branch.revision_id_to_dotted_revno, b'unknown')
 
2192
        self.assertFinished(client)
 
2193
 
 
2194
    def test_dotted_no_smart_verb(self):
 
2195
        self.setup_smart_server_with_call_log()
 
2196
        branch = self.make_branch('.')
 
2197
        self.disable_verb(b'Branch.revision_id_to_revno')
 
2198
        self.reset_smart_call_log()
 
2199
        self.assertEqual((0, ),
 
2200
                         branch.revision_id_to_dotted_revno(b'null:'))
 
2201
        self.assertLength(8, self.hpss_calls)
 
2202
 
 
2203
 
1686
2204
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1687
2205
 
1688
2206
    def test__get_config(self):
1689
2207
        client = FakeClient()
1690
 
        client.add_success_response_with_body('default_stack_on = /\n', 'ok')
 
2208
        client.add_success_response_with_body(b'default_stack_on = /\n', b'ok')
1691
2209
        transport = MemoryTransport()
1692
2210
        bzrdir = self.make_remote_bzrdir(transport, client)
1693
2211
        config = bzrdir.get_config()
1694
2212
        self.assertEqual('/', config.get_default_stack_on())
1695
2213
        self.assertEqual(
1696
 
            [('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
 
2214
            [('call_expecting_body', b'BzrDir.get_config_file',
 
2215
                (b'memory:///',))],
1697
2216
            client._calls)
1698
2217
 
1699
2218
    def test_set_option_uses_vfs(self):
1700
2219
        self.setup_smart_server_with_call_log()
1701
 
        bzrdir = self.make_bzrdir('.')
 
2220
        bzrdir = self.make_controldir('.')
1702
2221
        self.reset_smart_call_log()
1703
2222
        config = bzrdir.get_config()
1704
2223
        config.set_default_stack_on('/')
1705
 
        self.assertLength(3, self.hpss_calls)
 
2224
        self.assertLength(4, self.hpss_calls)
1706
2225
 
1707
2226
    def test_backwards_compat_get_option(self):
1708
2227
        self.setup_smart_server_with_call_log()
1709
 
        bzrdir = self.make_bzrdir('.')
1710
 
        verb = 'BzrDir.get_config_file'
 
2228
        bzrdir = self.make_controldir('.')
 
2229
        verb = b'BzrDir.get_config_file'
1711
2230
        self.disable_verb(verb)
1712
2231
        self.reset_smart_call_log()
1713
2232
        self.assertEqual(None,
1714
 
            bzrdir._get_config().get_option('default_stack_on'))
1715
 
        self.assertLength(3, self.hpss_calls)
 
2233
                         bzrdir._get_config().get_option('default_stack_on'))
 
2234
        self.assertLength(4, self.hpss_calls)
1716
2235
 
1717
2236
 
1718
2237
class TestTransportIsReadonly(tests.TestCase):
1719
2238
 
1720
2239
    def test_true(self):
1721
2240
        client = FakeClient()
1722
 
        client.add_success_response('yes')
 
2241
        client.add_success_response(b'yes')
1723
2242
        transport = RemoteTransport('bzr://example.com/', medium=False,
1724
2243
                                    _client=client)
1725
2244
        self.assertEqual(True, transport.is_readonly())
1726
2245
        self.assertEqual(
1727
 
            [('call', 'Transport.is_readonly', ())],
 
2246
            [('call', b'Transport.is_readonly', ())],
1728
2247
            client._calls)
1729
2248
 
1730
2249
    def test_false(self):
1731
2250
        client = FakeClient()
1732
 
        client.add_success_response('no')
 
2251
        client.add_success_response(b'no')
1733
2252
        transport = RemoteTransport('bzr://example.com/', medium=False,
1734
2253
                                    _client=client)
1735
2254
        self.assertEqual(False, transport.is_readonly())
1736
2255
        self.assertEqual(
1737
 
            [('call', 'Transport.is_readonly', ())],
 
2256
            [('call', b'Transport.is_readonly', ())],
1738
2257
            client._calls)
1739
2258
 
1740
2259
    def test_error_from_old_server(self):
1745
2264
        underlying filesystem could be readonly anyway).
1746
2265
        """
1747
2266
        client = FakeClient()
1748
 
        client.add_unknown_method_response('Transport.is_readonly')
 
2267
        client.add_unknown_method_response(b'Transport.is_readonly')
1749
2268
        transport = RemoteTransport('bzr://example.com/', medium=False,
1750
2269
                                    _client=client)
1751
2270
        self.assertEqual(False, transport.is_readonly())
1752
2271
        self.assertEqual(
1753
 
            [('call', 'Transport.is_readonly', ())],
 
2272
            [('call', b'Transport.is_readonly', ())],
1754
2273
            client._calls)
1755
2274
 
1756
2275
 
1758
2277
 
1759
2278
    def test_permissiondenied(self):
1760
2279
        client = FakeClient()
1761
 
        client.add_error_response('PermissionDenied', 'remote path', 'extra')
 
2280
        client.add_error_response(
 
2281
            b'PermissionDenied', b'remote path', b'extra')
1762
2282
        transport = RemoteTransport('bzr://example.com/', medium=False,
1763
2283
                                    _client=client)
1764
2284
        exc = self.assertRaises(
1777
2297
        conf = config.AuthenticationConfig()
1778
2298
        conf._get_config().update(
1779
2299
            {'bzr+sshtest': {'scheme': 'ssh', 'user': 'bar', 'host':
1780
 
            'example.com'}})
 
2300
                             'example.com'}})
1781
2301
        conf._save()
1782
2302
        t = RemoteSSHTransport('bzr+ssh://example.com')
1783
2303
        self.assertEqual('bar', t._get_credentials()[0])
1805
2325
        client = FakeClient(transport.base)
1806
2326
        transport = transport.clone(transport_path)
1807
2327
        # we do not want bzrdir to make any remote calls
1808
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1809
 
            _client=False)
 
2328
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
2329
                              _client=False)
1810
2330
        repo = RemoteRepository(bzrdir, None, _client=client)
1811
2331
        return repo, client
1812
2332
 
1819
2339
 
1820
2340
    def test_get_format_description(self):
1821
2341
        remote_format = RemoteBranchFormat()
1822
 
        real_format = branch.BranchFormat.get_default_format()
 
2342
        real_format = branch.format_registry.get_default()
1823
2343
        remote_format._network_name = real_format.network_name()
1824
2344
        self.assertEqual(remoted_description(real_format),
1825
 
            remote_format.get_format_description())
 
2345
                         remote_format.get_format_description())
1826
2346
 
1827
2347
 
1828
2348
class TestRepositoryFormat(TestRemoteRepository):
1829
2349
 
1830
2350
    def test_fast_delta(self):
1831
 
        true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
 
2351
        true_name = groupcompress_repo.RepositoryFormat2a().network_name()
1832
2352
        true_format = RemoteRepositoryFormat()
1833
2353
        true_format._network_name = true_name
1834
2354
        self.assertEqual(True, true_format.fast_deltas)
1835
 
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
 
2355
        false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
1836
2356
        false_format = RemoteRepositoryFormat()
1837
2357
        false_format._network_name = false_name
1838
2358
        self.assertEqual(False, false_format.fast_deltas)
1839
2359
 
1840
2360
    def test_get_format_description(self):
1841
2361
        remote_repo_format = RemoteRepositoryFormat()
1842
 
        real_format = repository.RepositoryFormat.get_default_format()
 
2362
        real_format = repository.format_registry.get_default()
1843
2363
        remote_repo_format._network_name = real_format.network_name()
1844
2364
        self.assertEqual(remoted_description(real_format),
1845
 
            remote_repo_format.get_format_description())
 
2365
                         remote_repo_format.get_format_description())
 
2366
 
 
2367
 
 
2368
class TestRepositoryAllRevisionIds(TestRemoteRepository):
 
2369
 
 
2370
    def test_empty(self):
 
2371
        transport_path = 'quack'
 
2372
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2373
        client.add_success_response_with_body(b'', b'ok')
 
2374
        self.assertEqual([], repo.all_revision_ids())
 
2375
        self.assertEqual(
 
2376
            [('call_expecting_body', b'Repository.all_revision_ids',
 
2377
              (b'quack/',))],
 
2378
            client._calls)
 
2379
 
 
2380
    def test_with_some_content(self):
 
2381
        transport_path = 'quack'
 
2382
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2383
        client.add_success_response_with_body(
 
2384
            b'rev1\nrev2\nanotherrev\n', b'ok')
 
2385
        self.assertEqual(
 
2386
            set([b"rev1", b"rev2", b"anotherrev"]),
 
2387
            set(repo.all_revision_ids()))
 
2388
        self.assertEqual(
 
2389
            [('call_expecting_body', b'Repository.all_revision_ids',
 
2390
              (b'quack/',))],
 
2391
            client._calls)
1846
2392
 
1847
2393
 
1848
2394
class TestRepositoryGatherStats(TestRemoteRepository):
1852
2398
        transport_path = 'quack'
1853
2399
        repo, client = self.setup_fake_client_and_repository(transport_path)
1854
2400
        client.add_success_response_with_body(
1855
 
            'revisions: 2\nsize: 18\n', 'ok')
 
2401
            b'revisions: 2\nsize: 18\n', b'ok')
1856
2402
        result = repo.gather_stats(None)
1857
2403
        self.assertEqual(
1858
 
            [('call_expecting_body', 'Repository.gather_stats',
1859
 
             ('quack/','','no'))],
 
2404
            [('call_expecting_body', b'Repository.gather_stats',
 
2405
              (b'quack/', b'', b'no'))],
1860
2406
            client._calls)
1861
2407
        self.assertEqual({'revisions': 2, 'size': 18}, result)
1862
2408
 
1863
2409
    def test_revid_no_committers(self):
1864
2410
        # ('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')
 
2411
        body = (b'firstrev: 123456.300 3600\n'
 
2412
                b'latestrev: 654231.400 0\n'
 
2413
                b'revisions: 2\n'
 
2414
                b'size: 18\n')
1869
2415
        transport_path = 'quick'
1870
2416
        revid = u'\xc8'.encode('utf8')
1871
2417
        repo, client = self.setup_fake_client_and_repository(transport_path)
1872
 
        client.add_success_response_with_body(body, 'ok')
 
2418
        client.add_success_response_with_body(body, b'ok')
1873
2419
        result = repo.gather_stats(revid)
1874
2420
        self.assertEqual(
1875
 
            [('call_expecting_body', 'Repository.gather_stats',
1876
 
              ('quick/', revid, 'no'))],
 
2421
            [('call_expecting_body', b'Repository.gather_stats',
 
2422
              (b'quick/', revid, b'no'))],
1877
2423
            client._calls)
1878
2424
        self.assertEqual({'revisions': 2, 'size': 18,
1879
2425
                          'firstrev': (123456.300, 3600),
1880
 
                          'latestrev': (654231.400, 0),},
 
2426
                          'latestrev': (654231.400, 0), },
1881
2427
                         result)
1882
2428
 
1883
2429
    def test_revid_with_committers(self):
1884
2430
        # ('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')
 
2431
        body = (b'committers: 128\n'
 
2432
                b'firstrev: 123456.300 3600\n'
 
2433
                b'latestrev: 654231.400 0\n'
 
2434
                b'revisions: 2\n'
 
2435
                b'size: 18\n')
1890
2436
        transport_path = 'buick'
1891
2437
        revid = u'\xc8'.encode('utf8')
1892
2438
        repo, client = self.setup_fake_client_and_repository(transport_path)
1893
 
        client.add_success_response_with_body(body, 'ok')
 
2439
        client.add_success_response_with_body(body, b'ok')
1894
2440
        result = repo.gather_stats(revid, True)
1895
2441
        self.assertEqual(
1896
 
            [('call_expecting_body', 'Repository.gather_stats',
1897
 
              ('buick/', revid, 'yes'))],
 
2442
            [('call_expecting_body', b'Repository.gather_stats',
 
2443
              (b'buick/', revid, b'yes'))],
1898
2444
            client._calls)
1899
2445
        self.assertEqual({'revisions': 2, 'size': 18,
1900
2446
                          'committers': 128,
1901
2447
                          'firstrev': (123456.300, 3600),
1902
 
                          'latestrev': (654231.400, 0),},
 
2448
                          'latestrev': (654231.400, 0), },
1903
2449
                         result)
1904
2450
 
1905
2451
 
 
2452
class TestRepositoryBreakLock(TestRemoteRepository):
 
2453
 
 
2454
    def test_break_lock(self):
 
2455
        transport_path = 'quack'
 
2456
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2457
        client.add_success_response(b'ok')
 
2458
        repo.break_lock()
 
2459
        self.assertEqual(
 
2460
            [('call', b'Repository.break_lock', (b'quack/',))],
 
2461
            client._calls)
 
2462
 
 
2463
 
 
2464
class TestRepositoryGetSerializerFormat(TestRemoteRepository):
 
2465
 
 
2466
    def test_get_serializer_format(self):
 
2467
        transport_path = 'hill'
 
2468
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2469
        client.add_success_response(b'ok', b'7')
 
2470
        self.assertEqual(b'7', repo.get_serializer_format())
 
2471
        self.assertEqual(
 
2472
            [('call', b'VersionedFileRepository.get_serializer_format',
 
2473
              (b'hill/', ))],
 
2474
            client._calls)
 
2475
 
 
2476
 
 
2477
class TestRepositoryReconcile(TestRemoteRepository):
 
2478
 
 
2479
    def test_reconcile(self):
 
2480
        transport_path = 'hill'
 
2481
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2482
        body = (b"garbage_inventories: 2\n"
 
2483
                b"inconsistent_parents: 3\n")
 
2484
        client.add_expected_call(
 
2485
            b'Repository.lock_write', (b'hill/', b''),
 
2486
            b'success', (b'ok', b'a token'))
 
2487
        client.add_success_response_with_body(body, b'ok')
 
2488
        reconciler = repo.reconcile()
 
2489
        self.assertEqual(
 
2490
            [('call', b'Repository.lock_write', (b'hill/', b'')),
 
2491
             ('call_expecting_body', b'Repository.reconcile',
 
2492
                (b'hill/', b'a token'))],
 
2493
            client._calls)
 
2494
        self.assertEqual(2, reconciler.garbage_inventories)
 
2495
        self.assertEqual(3, reconciler.inconsistent_parents)
 
2496
 
 
2497
 
 
2498
class TestRepositoryGetRevisionSignatureText(TestRemoteRepository):
 
2499
 
 
2500
    def test_text(self):
 
2501
        # ('ok',), body with signature text
 
2502
        transport_path = 'quack'
 
2503
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2504
        client.add_success_response_with_body(
 
2505
            b'THETEXT', b'ok')
 
2506
        self.assertEqual(b"THETEXT", repo.get_signature_text(b"revid"))
 
2507
        self.assertEqual(
 
2508
            [('call_expecting_body', b'Repository.get_revision_signature_text',
 
2509
              (b'quack/', b'revid'))],
 
2510
            client._calls)
 
2511
 
 
2512
    def test_no_signature(self):
 
2513
        transport_path = 'quick'
 
2514
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2515
        client.add_error_response(b'nosuchrevision', b'unknown')
 
2516
        self.assertRaises(errors.NoSuchRevision, repo.get_signature_text,
 
2517
                          b"unknown")
 
2518
        self.assertEqual(
 
2519
            [('call_expecting_body', b'Repository.get_revision_signature_text',
 
2520
              (b'quick/', b'unknown'))],
 
2521
            client._calls)
 
2522
 
 
2523
 
1906
2524
class TestRepositoryGetGraph(TestRemoteRepository):
1907
2525
 
1908
2526
    def test_get_graph(self):
1913
2531
        self.assertNotEqual(graph._parents_provider, repo)
1914
2532
 
1915
2533
 
 
2534
class TestRepositoryAddSignatureText(TestRemoteRepository):
 
2535
 
 
2536
    def test_add_signature_text(self):
 
2537
        transport_path = 'quack'
 
2538
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2539
        client.add_expected_call(
 
2540
            b'Repository.lock_write', (b'quack/', b''),
 
2541
            b'success', (b'ok', b'a token'))
 
2542
        client.add_expected_call(
 
2543
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
2544
            b'success', (b'ok', (b'token1', )))
 
2545
        client.add_expected_call(
 
2546
            b'Repository.add_signature_text', (b'quack/', b'a token', b'rev1',
 
2547
                                               b'token1'),
 
2548
            b'success', (b'ok', ), None)
 
2549
        repo.lock_write()
 
2550
        repo.start_write_group()
 
2551
        self.assertIs(
 
2552
            None, repo.add_signature_text(b"rev1", b"every bloody emperor"))
 
2553
        self.assertEqual(
 
2554
            ('call_with_body_bytes_expecting_body',
 
2555
             b'Repository.add_signature_text',
 
2556
                (b'quack/', b'a token', b'rev1', b'token1'),
 
2557
             b'every bloody emperor'),
 
2558
            client._calls[-1])
 
2559
 
 
2560
 
1916
2561
class TestRepositoryGetParentMap(TestRemoteRepository):
1917
2562
 
1918
2563
    def test_get_parent_map_caching(self):
1920
2565
        # setup a reponse with two revisions
1921
2566
        r1 = u'\u0e33'.encode('utf8')
1922
2567
        r2 = u'\u0dab'.encode('utf8')
1923
 
        lines = [' '.join([r2, r1]), r1]
1924
 
        encoded_body = bz2.compress('\n'.join(lines))
 
2568
        lines = [b' '.join([r2, r1]), r1]
 
2569
        encoded_body = bz2.compress(b'\n'.join(lines))
1925
2570
 
1926
2571
        transport_path = 'quack'
1927
2572
        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')
 
2573
        client.add_success_response_with_body(encoded_body, b'ok')
 
2574
        client.add_success_response_with_body(encoded_body, b'ok')
1930
2575
        repo.lock_read()
1931
2576
        graph = repo.get_graph()
1932
2577
        parents = graph.get_parent_map([r2])
1938
2583
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1939
2584
        self.assertEqual(
1940
2585
            [('call_with_body_bytes_expecting_body',
1941
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1942
 
              '\n\n0')],
 
2586
              b'Repository.get_parent_map', (b'quack/',
 
2587
                                             b'include-missing:', r2),
 
2588
              b'\n\n0')],
1943
2589
            client._calls)
1944
2590
        repo.unlock()
1945
2591
        # now we call again, and it should use the second response.
1949
2595
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
1950
2596
        self.assertEqual(
1951
2597
            [('call_with_body_bytes_expecting_body',
1952
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1953
 
              '\n\n0'),
 
2598
              b'Repository.get_parent_map', (b'quack/',
 
2599
                                             b'include-missing:', r2),
 
2600
              b'\n\n0'),
1954
2601
             ('call_with_body_bytes_expecting_body',
1955
 
              'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
1956
 
              '\n\n0'),
1957
 
            ],
 
2602
              b'Repository.get_parent_map', (b'quack/',
 
2603
                                             b'include-missing:', r1),
 
2604
              b'\n\n0'),
 
2605
             ],
1958
2606
            client._calls)
1959
2607
        repo.unlock()
1960
2608
 
1961
2609
    def test_get_parent_map_reconnects_if_unknown_method(self):
1962
2610
        transport_path = 'quack'
1963
 
        rev_id = 'revision-id'
 
2611
        rev_id = b'revision-id'
1964
2612
        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')
 
2613
        client.add_unknown_method_response(b'Repository.get_parent_map')
 
2614
        client.add_success_response_with_body(rev_id, b'ok')
1967
2615
        self.assertFalse(client._medium._is_remote_before((1, 2)))
1968
2616
        parents = repo.get_parent_map([rev_id])
1969
2617
        self.assertEqual(
1970
2618
            [('call_with_body_bytes_expecting_body',
1971
 
              'Repository.get_parent_map', ('quack/', 'include-missing:',
1972
 
              rev_id), '\n\n0'),
 
2619
              b'Repository.get_parent_map',
 
2620
              (b'quack/', b'include-missing:', rev_id), b'\n\n0'),
1973
2621
             ('disconnect medium',),
1974
 
             ('call_expecting_body', 'Repository.get_revision_graph',
1975
 
              ('quack/', ''))],
 
2622
             ('call_expecting_body', b'Repository.get_revision_graph',
 
2623
              (b'quack/', b''))],
1976
2624
            client._calls)
1977
2625
        # The medium is now marked as being connected to an older server
1978
2626
        self.assertTrue(client._medium._is_remote_before((1, 2)))
1979
 
        self.assertEqual({rev_id: ('null:',)}, parents)
 
2627
        self.assertEqual({rev_id: (b'null:',)}, parents)
1980
2628
 
1981
2629
    def test_get_parent_map_fallback_parentless_node(self):
1982
2630
        """get_parent_map falls back to get_revision_graph on old servers.  The
1989
2637
 
1990
2638
        This is the test for https://bugs.launchpad.net/bzr/+bug/214894
1991
2639
        """
1992
 
        rev_id = 'revision-id'
 
2640
        rev_id = b'revision-id'
1993
2641
        transport_path = 'quack'
1994
2642
        repo, client = self.setup_fake_client_and_repository(transport_path)
1995
 
        client.add_success_response_with_body(rev_id, 'ok')
 
2643
        client.add_success_response_with_body(rev_id, b'ok')
1996
2644
        client._medium._remember_remote_is_before((1, 2))
1997
2645
        parents = repo.get_parent_map([rev_id])
1998
2646
        self.assertEqual(
1999
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2000
 
             ('quack/', ''))],
 
2647
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2648
              (b'quack/', b''))],
2001
2649
            client._calls)
2002
 
        self.assertEqual({rev_id: ('null:',)}, parents)
 
2650
        self.assertEqual({rev_id: (b'null:',)}, parents)
2003
2651
 
2004
2652
    def test_get_parent_map_unexpected_response(self):
2005
2653
        repo, client = self.setup_fake_client_and_repository('path')
2006
 
        client.add_success_response('something unexpected!')
 
2654
        client.add_success_response(b'something unexpected!')
2007
2655
        self.assertRaises(
2008
2656
            errors.UnexpectedSmartServerResponse,
2009
 
            repo.get_parent_map, ['a-revision-id'])
 
2657
            repo.get_parent_map, [b'a-revision-id'])
2010
2658
 
2011
2659
    def test_get_parent_map_negative_caches_missing_keys(self):
2012
2660
        self.setup_smart_server_with_call_log()
2016
2664
        self.addCleanup(repo.unlock)
2017
2665
        self.reset_smart_call_log()
2018
2666
        graph = repo.get_graph()
2019
 
        self.assertEqual({},
2020
 
            graph.get_parent_map(['some-missing', 'other-missing']))
 
2667
        self.assertEqual(
 
2668
            {}, graph.get_parent_map([b'some-missing', b'other-missing']))
2021
2669
        self.assertLength(1, self.hpss_calls)
2022
2670
        # No call if we repeat this
2023
2671
        self.reset_smart_call_log()
2024
2672
        graph = repo.get_graph()
2025
 
        self.assertEqual({},
2026
 
            graph.get_parent_map(['some-missing', 'other-missing']))
 
2673
        self.assertEqual(
 
2674
            {}, graph.get_parent_map([b'some-missing', b'other-missing']))
2027
2675
        self.assertLength(0, self.hpss_calls)
2028
2676
        # Asking for more unknown keys makes a request.
2029
2677
        self.reset_smart_call_log()
2030
2678
        graph = repo.get_graph()
2031
 
        self.assertEqual({},
2032
 
            graph.get_parent_map(['some-missing', 'other-missing',
2033
 
                'more-missing']))
 
2679
        self.assertEqual(
 
2680
            {}, graph.get_parent_map([b'some-missing', b'other-missing',
 
2681
                                     b'more-missing']))
2034
2682
        self.assertLength(1, self.hpss_calls)
2035
2683
 
2036
2684
    def disableExtraResults(self):
2042
2690
        # Make a branch with a single revision.
2043
2691
        builder = self.make_branch_builder('foo')
2044
2692
        builder.start_series()
2045
 
        builder.build_snapshot('first', None, [
2046
 
            ('add', ('', 'root-id', 'directory', ''))])
 
2693
        builder.build_snapshot(None, [
 
2694
            ('add', ('', b'root-id', 'directory', ''))],
 
2695
            revision_id=b'first')
2047
2696
        builder.finish_series()
2048
2697
        branch = builder.get_branch()
2049
2698
        repo = branch.repository
2054
2703
        self.addCleanup(repo.unlock)
2055
2704
        self.reset_smart_call_log()
2056
2705
        graph = repo.get_graph()
2057
 
        # Query for 'first' and 'null:'.  Because 'null:' is a parent of
 
2706
        # Query for b'first' and b'null:'.  Because b'null:' is a parent of
2058
2707
        # '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:']))
 
2708
        # requests, and because b'null:' was queried but not returned it will
 
2709
        # be cached as missing.
 
2710
        self.assertEqual({b'first': (b'null:',)},
 
2711
                         graph.get_parent_map([b'first', b'null:']))
2063
2712
        # Now query for another key.  This request will pass along a recipe of
2064
2713
        # start and stop keys describing the already cached results, and this
2065
2714
        # recipe's revision count must be correct (or else it will trigger an
2066
2715
        # error from the server).
2067
 
        self.assertEqual({}, graph.get_parent_map(['another-key']))
 
2716
        self.assertEqual({}, graph.get_parent_map([b'another-key']))
2068
2717
        # This assertion guards against disableExtraResults silently failing to
2069
2718
        # work, thus invalidating the test.
2070
2719
        self.assertLength(2, self.hpss_calls)
2074
2723
        # ancestry.
2075
2724
        self.setup_smart_server_with_call_log()
2076
2725
        tree = self.make_branch_and_memory_tree('foo')
2077
 
        tree.lock_write()
2078
 
        try:
 
2726
        with tree.lock_write():
2079
2727
            builder = treebuilder.TreeBuilder()
2080
2728
            builder.start_tree(tree)
2081
2729
            builder.build([])
2082
2730
            builder.finish_tree()
2083
 
            tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
 
2731
            tree.set_parent_ids([b'non-existant'],
 
2732
                                allow_leftmost_as_ghost=True)
2084
2733
            rev_id = tree.commit('')
2085
 
        finally:
2086
 
            tree.unlock()
2087
2734
        tree.lock_read()
2088
2735
        self.addCleanup(tree.unlock)
2089
2736
        repo = tree.branch.repository
2092
2739
        repo.get_parent_map([rev_id])
2093
2740
        self.reset_smart_call_log()
2094
2741
        # Now asking for rev_id's ghost parent should not make calls
2095
 
        self.assertEqual({}, repo.get_parent_map(['non-existant']))
 
2742
        self.assertEqual({}, repo.get_parent_map([b'non-existant']))
2096
2743
        self.assertLength(0, self.hpss_calls)
2097
2744
 
 
2745
    def test_exposes_get_cached_parent_map(self):
 
2746
        """RemoteRepository exposes get_cached_parent_map from
 
2747
        _unstacked_provider
 
2748
        """
 
2749
        r1 = u'\u0e33'.encode('utf8')
 
2750
        r2 = u'\u0dab'.encode('utf8')
 
2751
        lines = [b' '.join([r2, r1]), r1]
 
2752
        encoded_body = bz2.compress(b'\n'.join(lines))
 
2753
 
 
2754
        transport_path = 'quack'
 
2755
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2756
        client.add_success_response_with_body(encoded_body, b'ok')
 
2757
        repo.lock_read()
 
2758
        # get_cached_parent_map should *not* trigger an RPC
 
2759
        self.assertEqual({}, repo.get_cached_parent_map([r1]))
 
2760
        self.assertEqual([], client._calls)
 
2761
        self.assertEqual({r2: (r1,)}, repo.get_parent_map([r2]))
 
2762
        self.assertEqual({r1: (NULL_REVISION,)},
 
2763
                         repo.get_cached_parent_map([r1]))
 
2764
        self.assertEqual(
 
2765
            [('call_with_body_bytes_expecting_body',
 
2766
              b'Repository.get_parent_map', (b'quack/',
 
2767
                                             b'include-missing:', r2),
 
2768
              b'\n\n0')],
 
2769
            client._calls)
 
2770
        repo.unlock()
 
2771
 
2098
2772
 
2099
2773
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2100
2774
 
2109
2783
        self.addCleanup(tree.unlock)
2110
2784
        graph = tree.branch.repository.get_graph()
2111
2785
        # 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')
 
2786
        self.assertEqual({}, graph.get_parent_map([b'rev1']))
 
2787
        tree.commit('message', rev_id=b'rev1')
2114
2788
        graph = tree.branch.repository.get_graph()
2115
 
        self.assertEqual({'rev1': ('null:',)}, graph.get_parent_map(['rev1']))
 
2789
        self.assertEqual({b'rev1': (b'null:',)},
 
2790
                         graph.get_parent_map([b'rev1']))
 
2791
 
 
2792
 
 
2793
class TestRepositoryGetRevisions(TestRemoteRepository):
 
2794
 
 
2795
    def test_hpss_missing_revision(self):
 
2796
        transport_path = 'quack'
 
2797
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2798
        client.add_success_response_with_body(
 
2799
            b'', b'ok', b'10')
 
2800
        self.assertRaises(errors.NoSuchRevision, repo.get_revisions,
 
2801
                          [b'somerev1', b'anotherrev2'])
 
2802
        self.assertEqual(
 
2803
            [('call_with_body_bytes_expecting_body',
 
2804
              b'Repository.iter_revisions', (b'quack/', ),
 
2805
              b"somerev1\nanotherrev2")],
 
2806
            client._calls)
 
2807
 
 
2808
    def test_hpss_get_single_revision(self):
 
2809
        transport_path = 'quack'
 
2810
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2811
        somerev1 = Revision(b"somerev1")
 
2812
        somerev1.committer = "Joe Committer <joe@example.com>"
 
2813
        somerev1.timestamp = 1321828927
 
2814
        somerev1.timezone = -60
 
2815
        somerev1.inventory_sha1 = b"691b39be74c67b1212a75fcb19c433aaed903c2b"
 
2816
        somerev1.message = "Message"
 
2817
        body = zlib.compress(chk_bencode_serializer.write_revision_to_string(
 
2818
            somerev1))
 
2819
        # Split up body into two bits to make sure the zlib compression object
 
2820
        # gets data fed twice.
 
2821
        client.add_success_response_with_body(
 
2822
            [body[:10], body[10:]], b'ok', b'10')
 
2823
        revs = repo.get_revisions([b'somerev1'])
 
2824
        self.assertEqual(revs, [somerev1])
 
2825
        self.assertEqual(
 
2826
            [('call_with_body_bytes_expecting_body',
 
2827
              b'Repository.iter_revisions',
 
2828
              (b'quack/', ), b"somerev1")],
 
2829
            client._calls)
2116
2830
 
2117
2831
 
2118
2832
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
2122
2836
        # traffic when calling it with this argument
2123
2837
        transport_path = 'empty'
2124
2838
        repo, client = self.setup_fake_client_and_repository(transport_path)
2125
 
        client.add_success_response('notused')
 
2839
        client.add_success_response(b'notused')
2126
2840
        # actual RemoteRepository.get_revision_graph is gone, but there's an
2127
2841
        # equivalent private method for testing
2128
2842
        result = repo._get_revision_graph(NULL_REVISION)
2133
2847
        # with none we want the entire graph
2134
2848
        r1 = u'\u0e33'.encode('utf8')
2135
2849
        r2 = u'\u0dab'.encode('utf8')
2136
 
        lines = [' '.join([r2, r1]), r1]
2137
 
        encoded_body = '\n'.join(lines)
 
2850
        lines = [b' '.join([r2, r1]), r1]
 
2851
        encoded_body = b'\n'.join(lines)
2138
2852
 
2139
2853
        transport_path = 'sinhala'
2140
2854
        repo, client = self.setup_fake_client_and_repository(transport_path)
2141
 
        client.add_success_response_with_body(encoded_body, 'ok')
 
2855
        client.add_success_response_with_body(encoded_body, b'ok')
2142
2856
        # actual RemoteRepository.get_revision_graph is gone, but there's an
2143
2857
        # equivalent private method for testing
2144
2858
        result = repo._get_revision_graph(None)
2145
2859
        self.assertEqual(
2146
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2147
 
             ('sinhala/', ''))],
 
2860
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2861
              (b'sinhala/', b''))],
2148
2862
            client._calls)
2149
2863
        self.assertEqual({r1: (), r2: (r1, )}, result)
2150
2864
 
2154
2868
        r11 = u'\u0e33'.encode('utf8')
2155
2869
        r12 = u'\xc9'.encode('utf8')
2156
2870
        r2 = u'\u0dab'.encode('utf8')
2157
 
        lines = [' '.join([r2, r11, r12]), r11, r12]
2158
 
        encoded_body = '\n'.join(lines)
 
2871
        lines = [b' '.join([r2, r11, r12]), r11, r12]
 
2872
        encoded_body = b'\n'.join(lines)
2159
2873
 
2160
2874
        transport_path = 'sinhala'
2161
2875
        repo, client = self.setup_fake_client_and_repository(transport_path)
2162
 
        client.add_success_response_with_body(encoded_body, 'ok')
 
2876
        client.add_success_response_with_body(encoded_body, b'ok')
2163
2877
        result = repo._get_revision_graph(r2)
2164
2878
        self.assertEqual(
2165
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2166
 
             ('sinhala/', r2))],
 
2879
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2880
              (b'sinhala/', r2))],
2167
2881
            client._calls)
2168
2882
        self.assertEqual({r11: (), r12: (), r2: (r11, r12), }, result)
2169
2883
 
2170
2884
    def test_no_such_revision(self):
2171
 
        revid = '123'
 
2885
        revid = b'123'
2172
2886
        transport_path = 'sinhala'
2173
2887
        repo, client = self.setup_fake_client_and_repository(transport_path)
2174
 
        client.add_error_response('nosuchrevision', revid)
 
2888
        client.add_error_response(b'nosuchrevision', revid)
2175
2889
        # also check that the right revision is reported in the error
2176
2890
        self.assertRaises(errors.NoSuchRevision,
2177
 
            repo._get_revision_graph, revid)
 
2891
                          repo._get_revision_graph, revid)
2178
2892
        self.assertEqual(
2179
 
            [('call_expecting_body', 'Repository.get_revision_graph',
2180
 
             ('sinhala/', revid))],
 
2893
            [('call_expecting_body', b'Repository.get_revision_graph',
 
2894
              (b'sinhala/', revid))],
2181
2895
            client._calls)
2182
2896
 
2183
2897
    def test_unexpected_error(self):
2184
2898
        revid = '123'
2185
2899
        transport_path = 'sinhala'
2186
2900
        repo, client = self.setup_fake_client_and_repository(transport_path)
2187
 
        client.add_error_response('AnUnexpectedError')
 
2901
        client.add_error_response(b'AnUnexpectedError')
2188
2902
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2189
 
            repo._get_revision_graph, revid)
2190
 
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
 
2903
                              repo._get_revision_graph, revid)
 
2904
        self.assertEqual((b'AnUnexpectedError',), e.error_tuple)
2191
2905
 
2192
2906
 
2193
2907
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2195
2909
    def test_ok(self):
2196
2910
        repo, client = self.setup_fake_client_and_repository('quack')
2197
2911
        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)
 
2912
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2913
                                                 5, (42, b'rev-foo')),
 
2914
            b'success', (b'ok', b'rev-five'))
 
2915
        result = repo.get_rev_id_for_revno(5, (42, b'rev-foo'))
 
2916
        self.assertEqual((True, b'rev-five'), result)
2202
2917
        self.assertFinished(client)
2203
2918
 
2204
2919
    def test_history_incomplete(self):
2205
2920
        repo, client = self.setup_fake_client_and_repository('quack')
2206
2921
        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)
 
2922
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2923
                                                 5, (42, b'rev-foo')),
 
2924
            b'success', (b'history-incomplete', 10, b'rev-ten'))
 
2925
        result = repo.get_rev_id_for_revno(5, (42, b'rev-foo'))
 
2926
        self.assertEqual((False, (10, b'rev-ten')), result)
2211
2927
        self.assertFinished(client)
2212
2928
 
2213
2929
    def test_history_incomplete_with_fallback(self):
2216
2932
        """
2217
2933
        # Make a repo with a fallback repo, both using a FakeClient.
2218
2934
        format = remote.response_tuple_to_repo_format(
2219
 
            ('yes', 'no', 'yes', self.get_repo_format().network_name()))
 
2935
            (b'yes', b'no', b'yes', self.get_repo_format().network_name()))
2220
2936
        repo, client = self.setup_fake_client_and_repository('quack')
2221
2937
        repo._format = format
2222
2938
        fallback_repo, ignored = self.setup_fake_client_and_repository(
2226
2942
        repo.add_fallback_repository(fallback_repo)
2227
2943
        # First the client should ask the primary repo
2228
2944
        client.add_expected_call(
2229
 
            'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2230
 
            'success', ('history-incomplete', 2, 'rev-two'))
 
2945
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2946
                                                 1, (42, b'rev-foo')),
 
2947
            b'success', (b'history-incomplete', 2, b'rev-two'))
2231
2948
        # Then it should ask the fallback, using revno/revid from the
2232
2949
        # history-incomplete response as the known revno/revid.
2233
2950
        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)
 
2951
            b'Repository.get_rev_id_for_revno', (
 
2952
                b'fallback/', 1, (2, b'rev-two')),
 
2953
            b'success', (b'ok', b'rev-one'))
 
2954
        result = repo.get_rev_id_for_revno(1, (42, b'rev-foo'))
 
2955
        self.assertEqual((True, b'rev-one'), result)
2238
2956
        self.assertFinished(client)
2239
2957
 
2240
2958
    def test_nosuchrevision(self):
2242
2960
        # remote repo.  The client translates that response to NoSuchRevision.
2243
2961
        repo, client = self.setup_fake_client_and_repository('quack')
2244
2962
        client.add_expected_call(
2245
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2246
 
            'error', ('nosuchrevision', 'rev-foo'))
 
2963
            b'Repository.get_rev_id_for_revno', (b'quack/',
 
2964
                                                 5, (42, b'rev-foo')),
 
2965
            b'error', (b'nosuchrevision', b'rev-foo'))
2247
2966
        self.assertRaises(
2248
2967
            errors.NoSuchRevision,
2249
 
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
 
2968
            repo.get_rev_id_for_revno, 5, (42, b'rev-foo'))
2250
2969
        self.assertFinished(client)
2251
2970
 
2252
2971
    def test_branch_fallback_locking(self):
2257
2976
        self.setup_smart_server_with_call_log()
2258
2977
        tree = self.make_branch_and_memory_tree('.')
2259
2978
        tree.lock_write()
 
2979
        tree.add('')
2260
2980
        rev1 = tree.commit('First')
2261
 
        rev2 = tree.commit('Second')
 
2981
        tree.commit('Second')
2262
2982
        tree.unlock()
2263
2983
        branch = tree.branch
2264
2984
        self.assertFalse(branch.is_locked())
2265
2985
        self.reset_smart_call_log()
2266
 
        verb = 'Repository.get_rev_id_for_revno'
 
2986
        verb = b'Repository.get_rev_id_for_revno'
2267
2987
        self.disable_verb(verb)
2268
2988
        self.assertEqual(rev1, branch.get_rev_id(1))
2269
2989
        self.assertLength(1, [call for call in self.hpss_calls if
2270
2990
                              call.call.method == verb])
2271
2991
 
2272
2992
 
 
2993
class TestRepositoryHasSignatureForRevisionId(TestRemoteRepository):
 
2994
 
 
2995
    def test_has_signature_for_revision_id(self):
 
2996
        # ('yes', ) for Repository.has_signature_for_revision_id -> 'True'.
 
2997
        transport_path = 'quack'
 
2998
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2999
        client.add_success_response(b'yes')
 
3000
        result = repo.has_signature_for_revision_id(b'A')
 
3001
        self.assertEqual(
 
3002
            [('call', b'Repository.has_signature_for_revision_id',
 
3003
              (b'quack/', b'A'))],
 
3004
            client._calls)
 
3005
        self.assertEqual(True, result)
 
3006
 
 
3007
    def test_is_not_shared(self):
 
3008
        # ('no', ) for Repository.has_signature_for_revision_id -> 'False'.
 
3009
        transport_path = 'qwack'
 
3010
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3011
        client.add_success_response(b'no')
 
3012
        result = repo.has_signature_for_revision_id(b'A')
 
3013
        self.assertEqual(
 
3014
            [('call', b'Repository.has_signature_for_revision_id',
 
3015
              (b'qwack/', b'A'))],
 
3016
            client._calls)
 
3017
        self.assertEqual(False, result)
 
3018
 
 
3019
 
 
3020
class TestRepositoryPhysicalLockStatus(TestRemoteRepository):
 
3021
 
 
3022
    def test_get_physical_lock_status_yes(self):
 
3023
        transport_path = 'qwack'
 
3024
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3025
        client.add_success_response(b'yes')
 
3026
        result = repo.get_physical_lock_status()
 
3027
        self.assertEqual(
 
3028
            [('call', b'Repository.get_physical_lock_status',
 
3029
              (b'qwack/', ))],
 
3030
            client._calls)
 
3031
        self.assertEqual(True, result)
 
3032
 
 
3033
    def test_get_physical_lock_status_no(self):
 
3034
        transport_path = 'qwack'
 
3035
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3036
        client.add_success_response(b'no')
 
3037
        result = repo.get_physical_lock_status()
 
3038
        self.assertEqual(
 
3039
            [('call', b'Repository.get_physical_lock_status',
 
3040
              (b'qwack/', ))],
 
3041
            client._calls)
 
3042
        self.assertEqual(False, result)
 
3043
 
 
3044
 
2273
3045
class TestRepositoryIsShared(TestRemoteRepository):
2274
3046
 
2275
3047
    def test_is_shared(self):
2276
3048
        # ('yes', ) for Repository.is_shared -> 'True'.
2277
3049
        transport_path = 'quack'
2278
3050
        repo, client = self.setup_fake_client_and_repository(transport_path)
2279
 
        client.add_success_response('yes')
 
3051
        client.add_success_response(b'yes')
2280
3052
        result = repo.is_shared()
2281
3053
        self.assertEqual(
2282
 
            [('call', 'Repository.is_shared', ('quack/',))],
 
3054
            [('call', b'Repository.is_shared', (b'quack/',))],
2283
3055
            client._calls)
2284
3056
        self.assertEqual(True, result)
2285
3057
 
2287
3059
        # ('no', ) for Repository.is_shared -> 'False'.
2288
3060
        transport_path = 'qwack'
2289
3061
        repo, client = self.setup_fake_client_and_repository(transport_path)
2290
 
        client.add_success_response('no')
 
3062
        client.add_success_response(b'no')
2291
3063
        result = repo.is_shared()
2292
3064
        self.assertEqual(
2293
 
            [('call', 'Repository.is_shared', ('qwack/',))],
 
3065
            [('call', b'Repository.is_shared', (b'qwack/',))],
 
3066
            client._calls)
 
3067
        self.assertEqual(False, result)
 
3068
 
 
3069
 
 
3070
class TestRepositoryMakeWorkingTrees(TestRemoteRepository):
 
3071
 
 
3072
    def test_make_working_trees(self):
 
3073
        # ('yes', ) for Repository.make_working_trees -> 'True'.
 
3074
        transport_path = 'quack'
 
3075
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3076
        client.add_success_response(b'yes')
 
3077
        result = repo.make_working_trees()
 
3078
        self.assertEqual(
 
3079
            [('call', b'Repository.make_working_trees', (b'quack/',))],
 
3080
            client._calls)
 
3081
        self.assertEqual(True, result)
 
3082
 
 
3083
    def test_no_working_trees(self):
 
3084
        # ('no', ) for Repository.make_working_trees -> 'False'.
 
3085
        transport_path = 'qwack'
 
3086
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3087
        client.add_success_response(b'no')
 
3088
        result = repo.make_working_trees()
 
3089
        self.assertEqual(
 
3090
            [('call', b'Repository.make_working_trees', (b'qwack/',))],
2294
3091
            client._calls)
2295
3092
        self.assertEqual(False, result)
2296
3093
 
2300
3097
    def test_lock_write(self):
2301
3098
        transport_path = 'quack'
2302
3099
        repo, client = self.setup_fake_client_and_repository(transport_path)
2303
 
        client.add_success_response('ok', 'a token')
2304
 
        result = repo.lock_write()
 
3100
        client.add_success_response(b'ok', b'a token')
 
3101
        token = repo.lock_write().repository_token
2305
3102
        self.assertEqual(
2306
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3103
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2307
3104
            client._calls)
2308
 
        self.assertEqual('a token', result)
 
3105
        self.assertEqual(b'a token', token)
2309
3106
 
2310
3107
    def test_lock_write_already_locked(self):
2311
3108
        transport_path = 'quack'
2312
3109
        repo, client = self.setup_fake_client_and_repository(transport_path)
2313
 
        client.add_error_response('LockContention')
 
3110
        client.add_error_response(b'LockContention')
2314
3111
        self.assertRaises(errors.LockContention, repo.lock_write)
2315
3112
        self.assertEqual(
2316
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3113
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2317
3114
            client._calls)
2318
3115
 
2319
3116
    def test_lock_write_unlockable(self):
2320
3117
        transport_path = 'quack'
2321
3118
        repo, client = self.setup_fake_client_and_repository(transport_path)
2322
 
        client.add_error_response('UnlockableTransport')
 
3119
        client.add_error_response(b'UnlockableTransport')
2323
3120
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
2324
3121
        self.assertEqual(
2325
 
            [('call', 'Repository.lock_write', ('quack/', ''))],
 
3122
            [('call', b'Repository.lock_write', (b'quack/', b''))],
2326
3123
            client._calls)
2327
3124
 
2328
3125
 
 
3126
class TestRepositoryWriteGroups(TestRemoteRepository):
 
3127
 
 
3128
    def test_start_write_group(self):
 
3129
        transport_path = 'quack'
 
3130
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3131
        client.add_expected_call(
 
3132
            b'Repository.lock_write', (b'quack/', b''),
 
3133
            b'success', (b'ok', b'a token'))
 
3134
        client.add_expected_call(
 
3135
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3136
            b'success', (b'ok', (b'token1', )))
 
3137
        repo.lock_write()
 
3138
        repo.start_write_group()
 
3139
 
 
3140
    def test_start_write_group_unsuspendable(self):
 
3141
        # Some repositories do not support suspending write
 
3142
        # groups. For those, fall back to the "real" repository.
 
3143
        transport_path = 'quack'
 
3144
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3145
 
 
3146
        def stub_ensure_real():
 
3147
            client._calls.append(('_ensure_real',))
 
3148
            repo._real_repository = _StubRealPackRepository(client._calls)
 
3149
        repo._ensure_real = stub_ensure_real
 
3150
        client.add_expected_call(
 
3151
            b'Repository.lock_write', (b'quack/', b''),
 
3152
            b'success', (b'ok', b'a token'))
 
3153
        client.add_expected_call(
 
3154
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3155
            b'error', (b'UnsuspendableWriteGroup',))
 
3156
        repo.lock_write()
 
3157
        repo.start_write_group()
 
3158
        self.assertEqual(client._calls[-2:], [
 
3159
            ('_ensure_real',),
 
3160
            ('start_write_group',)])
 
3161
 
 
3162
    def test_commit_write_group(self):
 
3163
        transport_path = 'quack'
 
3164
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3165
        client.add_expected_call(
 
3166
            b'Repository.lock_write', (b'quack/', b''),
 
3167
            b'success', (b'ok', b'a token'))
 
3168
        client.add_expected_call(
 
3169
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3170
            b'success', (b'ok', [b'token1']))
 
3171
        client.add_expected_call(
 
3172
            b'Repository.commit_write_group', (b'quack/',
 
3173
                                               b'a token', [b'token1']),
 
3174
            b'success', (b'ok',))
 
3175
        repo.lock_write()
 
3176
        repo.start_write_group()
 
3177
        repo.commit_write_group()
 
3178
 
 
3179
    def test_abort_write_group(self):
 
3180
        transport_path = 'quack'
 
3181
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3182
        client.add_expected_call(
 
3183
            b'Repository.lock_write', (b'quack/', b''),
 
3184
            b'success', (b'ok', b'a token'))
 
3185
        client.add_expected_call(
 
3186
            b'Repository.start_write_group', (b'quack/', b'a token'),
 
3187
            b'success', (b'ok', [b'token1']))
 
3188
        client.add_expected_call(
 
3189
            b'Repository.abort_write_group', (b'quack/',
 
3190
                                              b'a token', [b'token1']),
 
3191
            b'success', (b'ok',))
 
3192
        repo.lock_write()
 
3193
        repo.start_write_group()
 
3194
        repo.abort_write_group(False)
 
3195
 
 
3196
    def test_suspend_write_group(self):
 
3197
        transport_path = 'quack'
 
3198
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3199
        self.assertEqual([], repo.suspend_write_group())
 
3200
 
 
3201
    def test_resume_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.check_write_group', (b'quack/',
 
3209
                                              b'a token', [b'token1']),
 
3210
            b'success', (b'ok',))
 
3211
        repo.lock_write()
 
3212
        repo.resume_write_group(['token1'])
 
3213
 
 
3214
 
2329
3215
class TestRepositorySetMakeWorkingTrees(TestRemoteRepository):
2330
3216
 
2331
3217
    def test_backwards_compat(self):
2332
3218
        self.setup_smart_server_with_call_log()
2333
3219
        repo = self.make_repository('.')
2334
3220
        self.reset_smart_call_log()
2335
 
        verb = 'Repository.set_make_working_trees'
 
3221
        verb = b'Repository.set_make_working_trees'
2336
3222
        self.disable_verb(verb)
2337
3223
        repo.set_make_working_trees(True)
2338
3224
        call_count = len([call for call in self.hpss_calls if
2339
 
            call.call.method == verb])
 
3225
                          call.call.method == verb])
2340
3226
        self.assertEqual(1, call_count)
2341
3227
 
2342
3228
    def test_current(self):
2343
3229
        transport_path = 'quack'
2344
3230
        repo, client = self.setup_fake_client_and_repository(transport_path)
2345
3231
        client.add_expected_call(
2346
 
            'Repository.set_make_working_trees', ('quack/', 'True'),
2347
 
            'success', ('ok',))
 
3232
            b'Repository.set_make_working_trees', (b'quack/', b'True'),
 
3233
            b'success', (b'ok',))
2348
3234
        client.add_expected_call(
2349
 
            'Repository.set_make_working_trees', ('quack/', 'False'),
2350
 
            'success', ('ok',))
 
3235
            b'Repository.set_make_working_trees', (b'quack/', b'False'),
 
3236
            b'success', (b'ok',))
2351
3237
        repo.set_make_working_trees(True)
2352
3238
        repo.set_make_working_trees(False)
2353
3239
 
2357
3243
    def test_unlock(self):
2358
3244
        transport_path = 'quack'
2359
3245
        repo, client = self.setup_fake_client_and_repository(transport_path)
2360
 
        client.add_success_response('ok', 'a token')
2361
 
        client.add_success_response('ok')
 
3246
        client.add_success_response(b'ok', b'a token')
 
3247
        client.add_success_response(b'ok')
2362
3248
        repo.lock_write()
2363
3249
        repo.unlock()
2364
3250
        self.assertEqual(
2365
 
            [('call', 'Repository.lock_write', ('quack/', '')),
2366
 
             ('call', 'Repository.unlock', ('quack/', 'a token'))],
 
3251
            [('call', b'Repository.lock_write', (b'quack/', b'')),
 
3252
             ('call', b'Repository.unlock', (b'quack/', b'a token'))],
2367
3253
            client._calls)
2368
3254
 
2369
3255
    def test_unlock_wrong_token(self):
2370
3256
        # If somehow the token is wrong, unlock will raise TokenMismatch.
2371
3257
        transport_path = 'quack'
2372
3258
        repo, client = self.setup_fake_client_and_repository(transport_path)
2373
 
        client.add_success_response('ok', 'a token')
2374
 
        client.add_error_response('TokenMismatch')
 
3259
        client.add_success_response(b'ok', b'a token')
 
3260
        client.add_error_response(b'TokenMismatch')
2375
3261
        repo.lock_write()
2376
3262
        self.assertRaises(errors.TokenMismatch, repo.unlock)
2377
3263
 
2390
3276
        self.assertEqual([], client._calls)
2391
3277
 
2392
3278
 
 
3279
class TestRepositoryIterFilesBytes(TestRemoteRepository):
 
3280
    """Test Repository.iter_file_bytes."""
 
3281
 
 
3282
    def test_single(self):
 
3283
        transport_path = 'quack'
 
3284
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3285
        client.add_expected_call(
 
3286
            b'Repository.iter_files_bytes', (b'quack/', ),
 
3287
            b'success', (b'ok',), iter([b"ok\x000", b"\n", zlib.compress(b"mydata" * 10)]))
 
3288
        for (identifier, byte_stream) in repo.iter_files_bytes([(b"somefile",
 
3289
                                                                 b"somerev", b"myid")]):
 
3290
            self.assertEqual(b"myid", identifier)
 
3291
            self.assertEqual(b"".join(byte_stream), b"mydata" * 10)
 
3292
 
 
3293
    def test_missing(self):
 
3294
        transport_path = 'quack'
 
3295
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3296
        client.add_expected_call(
 
3297
            b'Repository.iter_files_bytes',
 
3298
            (b'quack/', ),
 
3299
            b'error', (b'RevisionNotPresent', b'somefile', b'somerev'),
 
3300
            iter([b"absent\0somefile\0somerev\n"]))
 
3301
        self.assertRaises(errors.RevisionNotPresent, list,
 
3302
                          repo.iter_files_bytes(
 
3303
                              [(b"somefile", b"somerev", b"myid")]))
 
3304
 
 
3305
 
2393
3306
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2394
3307
    """Base class for Repository.insert_stream and .insert_stream_1.19
2395
3308
    tests.
2396
3309
    """
2397
 
    
 
3310
 
2398
3311
    def checkInsertEmptyStream(self, repo, client):
2399
3312
        """Insert an empty stream, checking the result.
2400
3313
 
2402
3315
        the client is finished.
2403
3316
        """
2404
3317
        sink = repo._get_sink()
2405
 
        fmt = repository.RepositoryFormat.get_default_format()
 
3318
        fmt = repository.format_registry.get_default()
2406
3319
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
3320
        self.assertEqual([], resume_tokens)
2408
3321
        self.assertEqual(set(), missing_keys)
2417
3330
    """
2418
3331
 
2419
3332
    def setUp(self):
2420
 
        TestRemoteRepository.setUp(self)
2421
 
        self.disable_verb('Repository.insert_stream_1.19')
 
3333
        super(TestRepositoryInsertStream, self).setUp()
 
3334
        self.disable_verb(b'Repository.insert_stream_1.19')
2422
3335
 
2423
3336
    def test_unlocked_repo(self):
2424
3337
        transport_path = 'quack'
2425
3338
        repo, client = self.setup_fake_client_and_repository(transport_path)
2426
3339
        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',))
 
3340
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3341
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3342
        client.add_expected_call(
 
3343
            b'Repository.insert_stream', (b'quack/', b''),
 
3344
            b'success', (b'ok',))
 
3345
        client.add_expected_call(
 
3346
            b'Repository.insert_stream', (b'quack/', b''),
 
3347
            b'success', (b'ok',))
2435
3348
        self.checkInsertEmptyStream(repo, client)
2436
3349
 
2437
3350
    def test_locked_repo_with_no_lock_token(self):
2438
3351
        transport_path = 'quack'
2439
3352
        repo, client = self.setup_fake_client_and_repository(transport_path)
2440
3353
        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',))
 
3354
            b'Repository.lock_write', (b'quack/', b''),
 
3355
            b'success', (b'ok', b''))
 
3356
        client.add_expected_call(
 
3357
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3358
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3359
        client.add_expected_call(
 
3360
            b'Repository.insert_stream', (b'quack/', b''),
 
3361
            b'success', (b'ok',))
 
3362
        client.add_expected_call(
 
3363
            b'Repository.insert_stream', (b'quack/', b''),
 
3364
            b'success', (b'ok',))
2452
3365
        repo.lock_write()
2453
3366
        self.checkInsertEmptyStream(repo, client)
2454
3367
 
2456
3369
        transport_path = 'quack'
2457
3370
        repo, client = self.setup_fake_client_and_repository(transport_path)
2458
3371
        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',))
 
3372
            b'Repository.lock_write', (b'quack/', b''),
 
3373
            b'success', (b'ok', b'a token'))
 
3374
        client.add_expected_call(
 
3375
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3376
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3377
        client.add_expected_call(
 
3378
            b'Repository.insert_stream_locked', (b'quack/', b'', b'a token'),
 
3379
            b'success', (b'ok',))
 
3380
        client.add_expected_call(
 
3381
            b'Repository.insert_stream_locked', (b'quack/', b'', b'a token'),
 
3382
            b'success', (b'ok',))
2470
3383
        repo.lock_write()
2471
3384
        self.checkInsertEmptyStream(repo, client)
2472
3385
 
2479
3392
        transport_path = 'quack'
2480
3393
        repo, client = self.setup_fake_client_and_repository(transport_path)
2481
3394
        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',))
 
3395
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3396
            b'unknown', (b'Repository.insert_stream_1.19',))
 
3397
        client.add_expected_call(
 
3398
            b'Repository.insert_stream', (b'quack/', b''),
 
3399
            b'success', (b'ok',))
 
3400
        client.add_expected_call(
 
3401
            b'Repository.insert_stream', (b'quack/', b''),
 
3402
            b'success', (b'ok',))
2490
3403
        # Create a fake real repository for insert_stream to fall back on, so
2491
3404
        # that we can directly see the records the RemoteSink passes to the
2492
3405
        # real sink.
 
3406
 
2493
3407
        class FakeRealSink:
2494
3408
            def __init__(self):
2495
3409
                self.records = []
 
3410
 
2496
3411
            def insert_stream(self, stream, src_format, resume_tokens):
2497
3412
                for substream_kind, substream in stream:
2498
3413
                    self.records.append(
2499
3414
                        (substream_kind, [record.key for record in substream]))
2500
 
                return ['fake tokens'], ['fake missing keys']
 
3415
                return [b'fake tokens'], [b'fake missing keys']
2501
3416
        fake_real_sink = FakeRealSink()
 
3417
 
2502
3418
        class FakeRealRepository:
2503
3419
            def _get_sink(self):
2504
3420
                return fake_real_sink
 
3421
 
2505
3422
            def is_in_write_group(self):
2506
3423
                return False
 
3424
 
2507
3425
            def refresh_data(self):
2508
3426
                return True
2509
3427
        repo._real_repository = FakeRealRepository()
2510
3428
        sink = repo._get_sink()
2511
 
        fmt = repository.RepositoryFormat.get_default_format()
 
3429
        fmt = repository.format_registry.get_default()
2512
3430
        stream = self.make_stream_with_inv_deltas(fmt)
2513
3431
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
3432
        # Every record from the first inventory delta should have been sent to
2515
3433
        # the VFS sink.
2516
3434
        expected_records = [
2517
 
            ('inventory-deltas', [('rev2',), ('rev3',)]),
2518
 
            ('texts', [('some-rev', 'some-file')])]
 
3435
            ('inventory-deltas', [(b'rev2',), (b'rev3',)]),
 
3436
            ('texts', [(b'some-rev', b'some-file')])]
2519
3437
        self.assertEqual(expected_records, fake_real_sink.records)
2520
3438
        # The return values from the real sink's insert_stream are propagated
2521
3439
        # back to the original caller.
2522
 
        self.assertEqual(['fake tokens'], resume_tokens)
2523
 
        self.assertEqual(['fake missing keys'], missing_keys)
 
3440
        self.assertEqual([b'fake tokens'], resume_tokens)
 
3441
        self.assertEqual([b'fake missing keys'], missing_keys)
2524
3442
        self.assertFinished(client)
2525
3443
 
2526
3444
    def make_stream_with_inv_deltas(self, fmt):
2534
3452
           * texts substream: (some-rev, some-file)
2535
3453
        """
2536
3454
        # Define a stream using generators so that it isn't rewindable.
2537
 
        inv = inventory.Inventory(revision_id='rev1')
2538
 
        inv.root.revision = 'rev1'
 
3455
        inv = inventory.Inventory(revision_id=b'rev1')
 
3456
        inv.root.revision = b'rev1'
 
3457
 
2539
3458
        def stream_with_inv_delta():
2540
3459
            yield ('inventories', inventories_substream())
2541
3460
            yield ('inventory-deltas', inventory_delta_substream())
2542
3461
            yield ('texts', [
2543
3462
                versionedfile.FulltextContentFactory(
2544
 
                    ('some-rev', 'some-file'), (), None, 'content')])
 
3463
                    (b'some-rev', b'some-file'), (), None, b'content')])
 
3464
 
2545
3465
        def inventories_substream():
2546
3466
            # An empty inventory fulltext.  This will be streamed normally.
2547
3467
            text = fmt._serializer.write_inventory_to_string(inv)
2548
3468
            yield versionedfile.FulltextContentFactory(
2549
 
                ('rev1',), (), None, text)
 
3469
                (b'rev1',), (), None, text)
 
3470
 
2550
3471
        def inventory_delta_substream():
2551
3472
            # An inventory delta.  This can't be streamed via this verb, so it
2552
3473
            # will trigger a fallback to VFS insert_stream.
2553
3474
            entry = inv.make_entry(
2554
 
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
2555
 
            entry.revision = 'ghost'
2556
 
            delta = [(None, 'newdir', 'newdir-id', entry)]
 
3475
                'directory', 'newdir', inv.root.file_id, b'newdir-id')
 
3476
            entry.revision = b'ghost'
 
3477
            delta = [(None, 'newdir', b'newdir-id', entry)]
2557
3478
            serializer = inventory_delta.InventoryDeltaSerializer(
2558
3479
                versioned_root=True, tree_references=False)
2559
 
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
 
3480
            lines = serializer.delta_to_lines(b'rev1', b'rev2', delta)
2560
3481
            yield versionedfile.ChunkedContentFactory(
2561
 
                ('rev2',), (('rev1',)), None, lines)
 
3482
                (b'rev2',), ((b'rev1',)), None, lines)
2562
3483
            # Another delta.
2563
 
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
 
3484
            lines = serializer.delta_to_lines(b'rev1', b'rev3', delta)
2564
3485
            yield versionedfile.ChunkedContentFactory(
2565
 
                ('rev3',), (('rev1',)), None, lines)
 
3486
                (b'rev3',), ((b'rev1',)), None, lines)
2566
3487
        return stream_with_inv_delta()
2567
3488
 
2568
3489
 
2572
3493
        transport_path = 'quack'
2573
3494
        repo, client = self.setup_fake_client_and_repository(transport_path)
2574
3495
        client.add_expected_call(
2575
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2576
 
            'success', ('ok',))
 
3496
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3497
            b'success', (b'ok',))
2577
3498
        client.add_expected_call(
2578
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2579
 
            'success', ('ok',))
 
3499
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3500
            b'success', (b'ok',))
2580
3501
        self.checkInsertEmptyStream(repo, client)
2581
3502
 
2582
3503
    def test_locked_repo_with_no_lock_token(self):
2583
3504
        transport_path = 'quack'
2584
3505
        repo, client = self.setup_fake_client_and_repository(transport_path)
2585
3506
        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',))
 
3507
            b'Repository.lock_write', (b'quack/', b''),
 
3508
            b'success', (b'ok', b''))
 
3509
        client.add_expected_call(
 
3510
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3511
            b'success', (b'ok',))
 
3512
        client.add_expected_call(
 
3513
            b'Repository.insert_stream_1.19', (b'quack/', b''),
 
3514
            b'success', (b'ok',))
2594
3515
        repo.lock_write()
2595
3516
        self.checkInsertEmptyStream(repo, client)
2596
3517
 
2598
3519
        transport_path = 'quack'
2599
3520
        repo, client = self.setup_fake_client_and_repository(transport_path)
2600
3521
        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',))
 
3522
            b'Repository.lock_write', (b'quack/', b''),
 
3523
            b'success', (b'ok', b'a token'))
 
3524
        client.add_expected_call(
 
3525
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3526
            b'success', (b'ok',))
 
3527
        client.add_expected_call(
 
3528
            b'Repository.insert_stream_1.19', (b'quack/', b'', b'a token'),
 
3529
            b'success', (b'ok',))
2609
3530
        repo.lock_write()
2610
3531
        self.checkInsertEmptyStream(repo, client)
2611
3532
 
2613
3534
class TestRepositoryTarball(TestRemoteRepository):
2614
3535
 
2615
3536
    # This is a canned tarball reponse we can validate against
2616
 
    tarball_content = (
 
3537
    tarball_content = base64.b64decode(
2617
3538
        'QlpoOTFBWSZTWdGkj3wAAWF/k8aQACBIB//A9+8cIX/v33AACEAYABAECEACNz'
2618
3539
        'JqsgJJFPTSnk1A3qh6mTQAAAANPUHkagkSTEkaA09QaNAAAGgAAAcwCYCZGAEY'
2619
3540
        'mJhMJghpiaYBUkKammSHqNMZQ0NABkNAeo0AGneAevnlwQoGzEzNVzaYxp/1Uk'
2626
3547
        'QplPKp2nqBWAfwBGaOwVrz3y1T+UZZNismXHsb2Jq18T+VaD9k4P8DqE3g70qV'
2627
3548
        'JLurpnDI6VS5oqDDPVbtVjMxMxMg4rzQVipn2Bv1fVNK0iq3Gl0hhnnHKm/egy'
2628
3549
        'nWQ7QH/F3JFOFCQ0aSPfA='
2629
 
        ).decode('base64')
 
3550
        )
2630
3551
 
2631
3552
    def test_repository_tarball(self):
2632
3553
        # Test that Repository.tarball generates the right operations
2633
3554
        transport_path = 'repo'
2634
 
        expected_calls = [('call_expecting_body', 'Repository.tarball',
2635
 
                           ('repo/', 'bz2',),),
2636
 
            ]
 
3555
        expected_calls = [('call_expecting_body', b'Repository.tarball',
 
3556
                           (b'repo/', b'bz2',),),
 
3557
                          ]
2637
3558
        repo, client = self.setup_fake_client_and_repository(transport_path)
2638
 
        client.add_success_response_with_body(self.tarball_content, 'ok')
 
3559
        client.add_success_response_with_body(self.tarball_content, b'ok')
2639
3560
        # Now actually ask for the tarball
2640
3561
        tarball_file = repo._get_tarball('bz2')
2641
3562
        try:
2670
3591
        self.calls = calls
2671
3592
        self._pack_collection = _StubPackCollection(calls)
2672
3593
 
 
3594
    def start_write_group(self):
 
3595
        self.calls.append(('start_write_group',))
 
3596
 
2673
3597
    def is_in_write_group(self):
2674
3598
        return False
2675
3599
 
2696
3620
        transport_path = 'quack'
2697
3621
        repo, client = self.setup_fake_client_and_repository(transport_path)
2698
3622
        client.add_expected_call(
2699
 
            'PackRepository.autopack', ('quack/',), 'success', ('ok',))
 
3623
            b'PackRepository.autopack', (b'quack/',), b'success', (b'ok',))
2700
3624
        repo.autopack()
2701
3625
        self.assertFinished(client)
2702
3626
 
2707
3631
        transport_path = 'quack'
2708
3632
        repo, client = self.setup_fake_client_and_repository(transport_path)
2709
3633
        client.add_expected_call(
2710
 
            'PackRepository.autopack', ('quack/',),
2711
 
            'success', ('ok',))
 
3634
            b'PackRepository.autopack', (b'quack/',),
 
3635
            b'success', (b'ok',))
2712
3636
        repo._real_repository = _StubRealPackRepository(client._calls)
2713
3637
        repo.autopack()
2714
3638
        self.assertEqual(
2715
 
            [('call', 'PackRepository.autopack', ('quack/',)),
 
3639
            [('call', b'PackRepository.autopack', (b'quack/',)),
2716
3640
             ('pack collection reload_pack_names',)],
2717
3641
            client._calls)
2718
3642
 
2722
3646
        """
2723
3647
        transport_path = 'quack'
2724
3648
        repo, client = self.setup_fake_client_and_repository(transport_path)
2725
 
        client.add_unknown_method_response('PackRepository.autopack')
 
3649
        client.add_unknown_method_response(b'PackRepository.autopack')
 
3650
 
2726
3651
        def stub_ensure_real():
2727
3652
            client._calls.append(('_ensure_real',))
2728
3653
            repo._real_repository = _StubRealPackRepository(client._calls)
2729
3654
        repo._ensure_real = stub_ensure_real
2730
3655
        repo.autopack()
2731
3656
        self.assertEqual(
2732
 
            [('call', 'PackRepository.autopack', ('quack/',)),
 
3657
            [('call', b'PackRepository.autopack', (b'quack/',)),
2733
3658
             ('_ensure_real',),
2734
3659
             ('pack collection autopack',)],
2735
3660
            client._calls)
2736
3661
 
 
3662
    def test_oom_error_reporting(self):
 
3663
        """An out-of-memory condition on the server is reported clearly"""
 
3664
        transport_path = 'quack'
 
3665
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
3666
        client.add_expected_call(
 
3667
            b'PackRepository.autopack', (b'quack/',),
 
3668
            b'error', (b'MemoryError',))
 
3669
        err = self.assertRaises(errors.BzrError, repo.autopack)
 
3670
        self.assertContainsRe(str(err), "^remote server out of mem")
 
3671
 
2737
3672
 
2738
3673
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2739
 
    """Base class for unit tests for bzrlib.remote._translate_error."""
 
3674
    """Base class for unit tests for breezy.bzr.remote._translate_error."""
2740
3675
 
2741
3676
    def translateTuple(self, error_tuple, **context):
2742
3677
        """Call _translate_error with an ErrorFromSmartServer built from the
2762
3697
        """
2763
3698
        try:
2764
3699
            raise error_object
2765
 
        except errors.ErrorFromSmartServer, server_error:
 
3700
        except errors.ErrorFromSmartServer as server_error:
2766
3701
            translated_error = self.assertRaises(
2767
3702
                errors.BzrError, remote._translate_error, server_error,
2768
3703
                **context)
2770
3705
 
2771
3706
 
2772
3707
class TestErrorTranslationSuccess(TestErrorTranslationBase):
2773
 
    """Unit tests for bzrlib.remote._translate_error.
 
3708
    """Unit tests for breezy.bzr.remote._translate_error.
2774
3709
 
2775
3710
    Given an ErrorFromSmartServer (which has an error tuple from a smart
2776
3711
    server) and some context, _translate_error raises more specific errors from
2777
 
    bzrlib.errors.
 
3712
    breezy.errors.
2778
3713
 
2779
3714
    This test case covers the cases where _translate_error succeeds in
2780
3715
    translating an ErrorFromSmartServer to something better.  See
2783
3718
 
2784
3719
    def test_NoSuchRevision(self):
2785
3720
        branch = self.make_branch('')
2786
 
        revid = 'revid'
 
3721
        revid = b'revid'
2787
3722
        translated_error = self.translateTuple(
2788
 
            ('NoSuchRevision', revid), branch=branch)
 
3723
            (b'NoSuchRevision', revid), branch=branch)
2789
3724
        expected_error = errors.NoSuchRevision(branch, revid)
2790
3725
        self.assertEqual(expected_error, translated_error)
2791
3726
 
2792
3727
    def test_nosuchrevision(self):
2793
3728
        repository = self.make_repository('')
2794
 
        revid = 'revid'
 
3729
        revid = b'revid'
2795
3730
        translated_error = self.translateTuple(
2796
 
            ('nosuchrevision', revid), repository=repository)
 
3731
            (b'nosuchrevision', revid), repository=repository)
2797
3732
        expected_error = errors.NoSuchRevision(repository, revid)
2798
3733
        self.assertEqual(expected_error, translated_error)
2799
3734
 
2800
3735
    def test_nobranch(self):
2801
 
        bzrdir = self.make_bzrdir('')
2802
 
        translated_error = self.translateTuple(('nobranch',), bzrdir=bzrdir)
 
3736
        bzrdir = self.make_controldir('')
 
3737
        translated_error = self.translateTuple((b'nobranch',), bzrdir=bzrdir)
2803
3738
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
2804
3739
        self.assertEqual(expected_error, translated_error)
2805
3740
 
2806
3741
    def test_nobranch_one_arg(self):
2807
 
        bzrdir = self.make_bzrdir('')
 
3742
        bzrdir = self.make_controldir('')
2808
3743
        translated_error = self.translateTuple(
2809
 
            ('nobranch', 'extra detail'), bzrdir=bzrdir)
 
3744
            (b'nobranch', b'extra detail'), bzrdir=bzrdir)
2810
3745
        expected_error = errors.NotBranchError(
2811
3746
            path=bzrdir.root_transport.base,
2812
3747
            detail='extra detail')
2813
3748
        self.assertEqual(expected_error, translated_error)
2814
3749
 
 
3750
    def test_norepository(self):
 
3751
        bzrdir = self.make_controldir('')
 
3752
        translated_error = self.translateTuple((b'norepository',),
 
3753
                                               bzrdir=bzrdir)
 
3754
        expected_error = errors.NoRepositoryPresent(bzrdir)
 
3755
        self.assertEqual(expected_error, translated_error)
 
3756
 
2815
3757
    def test_LockContention(self):
2816
 
        translated_error = self.translateTuple(('LockContention',))
 
3758
        translated_error = self.translateTuple((b'LockContention',))
2817
3759
        expected_error = errors.LockContention('(remote lock)')
2818
3760
        self.assertEqual(expected_error, translated_error)
2819
3761
 
2820
3762
    def test_UnlockableTransport(self):
2821
 
        bzrdir = self.make_bzrdir('')
 
3763
        bzrdir = self.make_controldir('')
2822
3764
        translated_error = self.translateTuple(
2823
 
            ('UnlockableTransport',), bzrdir=bzrdir)
 
3765
            (b'UnlockableTransport',), bzrdir=bzrdir)
2824
3766
        expected_error = errors.UnlockableTransport(bzrdir.root_transport)
2825
3767
        self.assertEqual(expected_error, translated_error)
2826
3768
 
2827
3769
    def test_LockFailed(self):
2828
3770
        lock = 'str() of a server lock'
2829
3771
        why = 'str() of why'
2830
 
        translated_error = self.translateTuple(('LockFailed', lock, why))
 
3772
        translated_error = self.translateTuple(
 
3773
            (b'LockFailed', lock.encode('ascii'), why.encode('ascii')))
2831
3774
        expected_error = errors.LockFailed(lock, why)
2832
3775
        self.assertEqual(expected_error, translated_error)
2833
3776
 
2834
3777
    def test_TokenMismatch(self):
2835
3778
        token = 'a lock token'
2836
 
        translated_error = self.translateTuple(('TokenMismatch',), token=token)
 
3779
        translated_error = self.translateTuple(
 
3780
            (b'TokenMismatch',), token=token)
2837
3781
        expected_error = errors.TokenMismatch(token, '(remote token)')
2838
3782
        self.assertEqual(expected_error, translated_error)
2839
3783
 
2841
3785
        branch = self.make_branch('a')
2842
3786
        other_branch = self.make_branch('b')
2843
3787
        translated_error = self.translateTuple(
2844
 
            ('Diverged',), branch=branch, other_branch=other_branch)
 
3788
            (b'Diverged',), branch=branch, other_branch=other_branch)
2845
3789
        expected_error = errors.DivergedBranches(branch, other_branch)
2846
3790
        self.assertEqual(expected_error, translated_error)
2847
3791
 
 
3792
    def test_NotStacked(self):
 
3793
        branch = self.make_branch('')
 
3794
        translated_error = self.translateTuple((b'NotStacked',), branch=branch)
 
3795
        expected_error = errors.NotStacked(branch)
 
3796
        self.assertEqual(expected_error, translated_error)
 
3797
 
2848
3798
    def test_ReadError_no_args(self):
2849
3799
        path = 'a path'
2850
 
        translated_error = self.translateTuple(('ReadError',), path=path)
 
3800
        translated_error = self.translateTuple((b'ReadError',), path=path)
2851
3801
        expected_error = errors.ReadError(path)
2852
3802
        self.assertEqual(expected_error, translated_error)
2853
3803
 
2854
3804
    def test_ReadError(self):
2855
3805
        path = 'a path'
2856
 
        translated_error = self.translateTuple(('ReadError', path))
 
3806
        translated_error = self.translateTuple(
 
3807
            (b'ReadError', path.encode('utf-8')))
2857
3808
        expected_error = errors.ReadError(path)
2858
3809
        self.assertEqual(expected_error, translated_error)
2859
3810
 
2860
3811
    def test_IncompatibleRepositories(self):
2861
 
        translated_error = self.translateTuple(('IncompatibleRepositories',
2862
 
            "repo1", "repo2", "details here"))
 
3812
        translated_error = self.translateTuple((b'IncompatibleRepositories',
 
3813
                                                b"repo1", b"repo2", b"details here"))
2863
3814
        expected_error = errors.IncompatibleRepositories("repo1", "repo2",
2864
 
            "details here")
 
3815
                                                         "details here")
 
3816
        self.assertEqual(expected_error, translated_error)
 
3817
 
 
3818
    def test_GhostRevisionsHaveNoRevno(self):
 
3819
        translated_error = self.translateTuple((b'GhostRevisionsHaveNoRevno',
 
3820
                                                b"revid1", b"revid2"))
 
3821
        expected_error = errors.GhostRevisionsHaveNoRevno(b"revid1", b"revid2")
2865
3822
        self.assertEqual(expected_error, translated_error)
2866
3823
 
2867
3824
    def test_PermissionDenied_no_args(self):
2868
3825
        path = 'a path'
2869
 
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
 
3826
        translated_error = self.translateTuple((b'PermissionDenied',),
 
3827
                                               path=path)
2870
3828
        expected_error = errors.PermissionDenied(path)
2871
3829
        self.assertEqual(expected_error, translated_error)
2872
3830
 
2873
3831
    def test_PermissionDenied_one_arg(self):
2874
3832
        path = 'a path'
2875
 
        translated_error = self.translateTuple(('PermissionDenied', path))
 
3833
        translated_error = self.translateTuple(
 
3834
            (b'PermissionDenied', path.encode('utf-8')))
2876
3835
        expected_error = errors.PermissionDenied(path)
2877
3836
        self.assertEqual(expected_error, translated_error)
2878
3837
 
2883
3842
        local_path = 'local path'
2884
3843
        remote_path = 'remote path'
2885
3844
        translated_error = self.translateTuple(
2886
 
            ('PermissionDenied', remote_path), path=local_path)
 
3845
            (b'PermissionDenied', remote_path.encode('utf-8')), path=local_path)
2887
3846
        expected_error = errors.PermissionDenied(local_path)
2888
3847
        self.assertEqual(expected_error, translated_error)
2889
3848
 
2891
3850
        path = 'a path'
2892
3851
        extra = 'a string with extra info'
2893
3852
        translated_error = self.translateTuple(
2894
 
            ('PermissionDenied', path, extra))
 
3853
            (b'PermissionDenied', path.encode('utf-8'), extra.encode('utf-8')))
2895
3854
        expected_error = errors.PermissionDenied(path, extra)
2896
3855
        self.assertEqual(expected_error, translated_error)
2897
3856
 
 
3857
    # GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
 
3858
 
 
3859
    def test_NoSuchFile_context_path(self):
 
3860
        local_path = "local path"
 
3861
        translated_error = self.translateTuple((b'ReadError', b"remote path"),
 
3862
                                               path=local_path)
 
3863
        expected_error = errors.ReadError(local_path)
 
3864
        self.assertEqual(expected_error, translated_error)
 
3865
 
 
3866
    def test_NoSuchFile_without_context(self):
 
3867
        remote_path = "remote path"
 
3868
        translated_error = self.translateTuple(
 
3869
            (b'ReadError', remote_path.encode('utf-8')))
 
3870
        expected_error = errors.ReadError(remote_path)
 
3871
        self.assertEqual(expected_error, translated_error)
 
3872
 
 
3873
    def test_ReadOnlyError(self):
 
3874
        translated_error = self.translateTuple((b'ReadOnlyError',))
 
3875
        expected_error = errors.TransportNotPossible("readonly transport")
 
3876
        self.assertEqual(expected_error, translated_error)
 
3877
 
 
3878
    def test_MemoryError(self):
 
3879
        translated_error = self.translateTuple((b'MemoryError',))
 
3880
        self.assertStartsWith(str(translated_error),
 
3881
                              "remote server out of memory")
 
3882
 
 
3883
    def test_generic_IndexError_no_classname(self):
 
3884
        err = errors.ErrorFromSmartServer(
 
3885
            (b'error', b"list index out of range"))
 
3886
        translated_error = self.translateErrorFromSmartServer(err)
 
3887
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3888
        self.assertEqual(expected_error, translated_error)
 
3889
 
 
3890
    # GZ 2011-03-02: TODO test generic non-ascii error string
 
3891
 
 
3892
    def test_generic_KeyError(self):
 
3893
        err = errors.ErrorFromSmartServer((b'error', b'KeyError', b"1"))
 
3894
        translated_error = self.translateErrorFromSmartServer(err)
 
3895
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3896
        self.assertEqual(expected_error, translated_error)
 
3897
 
2898
3898
 
2899
3899
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2900
 
    """Unit tests for bzrlib.remote._translate_error's robustness.
 
3900
    """Unit tests for breezy.bzr.remote._translate_error's robustness.
2901
3901
 
2902
3902
    TestErrorTranslationSuccess is for cases where _translate_error can
2903
3903
    translate successfully.  This class about how _translate_err behaves when
2908
3908
        """If the error code from the server is not recognised, the original
2909
3909
        ErrorFromSmartServer is propagated unmodified.
2910
3910
        """
2911
 
        error_tuple = ('An unknown error tuple',)
 
3911
        error_tuple = (b'An unknown error tuple',)
2912
3912
        server_error = errors.ErrorFromSmartServer(error_tuple)
2913
3913
        translated_error = self.translateErrorFromSmartServer(server_error)
2914
3914
        expected_error = errors.UnknownErrorFromSmartServer(server_error)
2922
3922
        # To translate a NoSuchRevision error _translate_error needs a 'branch'
2923
3923
        # in the context dict.  So let's give it an empty context dict instead
2924
3924
        # to exercise its error recovery.
2925
 
        empty_context = {}
2926
 
        error_tuple = ('NoSuchRevision', 'revid')
 
3925
        error_tuple = (b'NoSuchRevision', b'revid')
2927
3926
        server_error = errors.ErrorFromSmartServer(error_tuple)
2928
3927
        translated_error = self.translateErrorFromSmartServer(server_error)
2929
3928
        self.assertEqual(server_error, translated_error)
2938
3937
        'path' variable from either the wire or the local context.  If neither
2939
3938
        has it, then an error is raised.
2940
3939
        """
2941
 
        error_tuple = ('ReadError',)
 
3940
        error_tuple = (b'ReadError',)
2942
3941
        server_error = errors.ErrorFromSmartServer(error_tuple)
2943
3942
        translated_error = self.translateErrorFromSmartServer(server_error)
2944
3943
        self.assertEqual(server_error, translated_error)
2958
3957
        # make a branch stacked on another repository containing an empty
2959
3958
        # revision, then open it over hpss - we should be able to see that
2960
3959
        # revision.
2961
 
        base_transport = self.get_transport()
2962
3960
        base_builder = self.make_branch_builder('base', format='1.9')
2963
3961
        base_builder.start_series()
2964
 
        base_revid = base_builder.build_snapshot('rev-id', None,
2965
 
            [('add', ('', None, 'directory', None))],
2966
 
            'message')
 
3962
        base_revid = base_builder.build_snapshot(None,
 
3963
                                                 [('add', ('', None, 'directory', None))],
 
3964
                                                 'message', revision_id=b'rev-id')
2967
3965
        base_builder.finish_series()
2968
3966
        stacked_branch = self.make_branch('stacked', format='1.9')
2969
3967
        stacked_branch.set_stacked_on_url('../base')
2980
3978
            # be a RemoteRepository
2981
3979
            self.assertLength(1, remote_repo._fallback_repositories)
2982
3980
            self.assertIsInstance(remote_repo._fallback_repositories[0],
2983
 
                RemoteRepository)
 
3981
                                  RemoteRepository)
2984
3982
            # and it has the revision committed to the underlying repository;
2985
3983
            # these have varying implementations so we try several of them
2986
3984
            self.assertTrue(remote_repo.has_revisions([base_revid]))
2987
3985
            self.assertTrue(remote_repo.has_revision(base_revid))
2988
3986
            self.assertEqual(remote_repo.get_revision(base_revid).message,
2989
 
                'message')
 
3987
                             'message')
2990
3988
        finally:
2991
3989
            remote_repo.unlock()
2992
3990
 
2994
3992
        """Get stacked_upon and stacked branches with content in each."""
2995
3993
        self.setup_smart_server_with_call_log()
2996
3994
        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()
 
3995
        tree1.commit('rev1', rev_id=b'rev1')
 
3996
        tree2 = tree1.branch.controldir.sprout('tree2', stacked=True
 
3997
                                               ).open_workingtree()
3000
3998
        local_tree = tree2.branch.create_checkout('local')
3001
3999
        local_tree.commit('local changes make me feel good.')
3002
4000
        branch2 = Branch.open(self.get_url('tree2'))
3008
4006
        # the public implementation of get_parent_map obeys stacking
3009
4007
        _, branch = self.prepare_stacked_remote_branch()
3010
4008
        repo = branch.repository
3011
 
        self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
 
4009
        self.assertEqual({b'rev1'}, set(repo.get_parent_map([b'rev1'])))
3012
4010
 
3013
4011
    def test_unstacked_get_parent_map(self):
3014
4012
        # _unstacked_provider.get_parent_map ignores stacking
3015
4013
        _, branch = self.prepare_stacked_remote_branch()
3016
4014
        provider = branch.repository._unstacked_provider
3017
 
        self.assertEqual([], provider.get_parent_map(['rev1']).keys())
 
4015
        self.assertEqual(set(), set(provider.get_parent_map([b'rev1'])))
3018
4016
 
3019
4017
    def fetch_stream_to_rev_order(self, stream):
3020
4018
        result = []
3036
4034
        :result: The revision ids in the stream, in the order seen,
3037
4035
            the topological order of revisions in the source.
3038
4036
        """
3039
 
        unordered_format = bzrdir.format_registry.get(format)()
 
4037
        unordered_format = controldir.format_registry.get(format)()
3040
4038
        target_repository_format = unordered_format.repository_format
3041
4039
        # Cross check
3042
4040
        self.assertEqual(order, target_repository_format._fetch_order)
3045
4043
        _, stacked = branch_factory()
3046
4044
        source = stacked.repository._get_source(target_repository_format)
3047
4045
        tip = stacked.last_revision()
3048
 
        revs = stacked.repository.get_ancestry(tip)
3049
 
        search = graph.PendingAncestryResult([tip], stacked.repository)
 
4046
        stacked.repository._ensure_real()
 
4047
        graph = stacked.repository.get_graph()
 
4048
        revs = [r for (r, ps) in graph.iter_ancestry([tip])
 
4049
                if r != NULL_REVISION]
 
4050
        revs.reverse()
 
4051
        search = vf_search.PendingAncestryResult([tip], stacked.repository)
3050
4052
        self.reset_smart_call_log()
3051
4053
        stream = source.get_stream(search)
3052
 
        if None in revs:
3053
 
            revs.remove(None)
3054
4054
        # We trust that if a revision is in the stream the rest of the new
3055
4055
        # content for it is too, as per our main fetch tests; here we are
3056
4056
        # checking that the revisions are actually included at all, and their
3072
4072
        # is itself stacked yields the full data from all three sources.
3073
4073
        def make_stacked_stacked():
3074
4074
            _, stacked = self.prepare_stacked_remote_branch()
3075
 
            tree = stacked.bzrdir.sprout('tree3', stacked=True
3076
 
                ).open_workingtree()
 
4075
            tree = stacked.controldir.sprout('tree3', stacked=True
 
4076
                                             ).open_workingtree()
3077
4077
            local_tree = tree.branch.create_checkout('local-tree3')
3078
4078
            local_tree.commit('more local changes are better')
3079
4079
            branch = Branch.open(self.get_url('tree3'))
3080
4080
            branch.lock_read()
3081
4081
            self.addCleanup(branch.unlock)
3082
4082
            return None, branch
3083
 
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3084
 
            branch_factory=make_stacked_stacked)
 
4083
        rev_ord, expected_revs = self.get_ordered_revs(
 
4084
            '1.9', 'unordered', branch_factory=make_stacked_stacked)
3085
4085
        self.assertEqual(set(expected_revs), set(rev_ord))
3086
4086
        # Getting unordered results should have made a streaming data request
3087
4087
        # from the server, and one from each backing repo
3095
4095
        self.assertEqual(expected_revs, rev_ord)
3096
4096
        # Getting topological sort requires VFS calls still - one of which is
3097
4097
        # pushing up from the bound branch.
3098
 
        self.assertLength(13, self.hpss_calls)
 
4098
        self.assertLength(14, self.hpss_calls)
3099
4099
 
3100
4100
    def test_stacked_get_stream_groupcompress(self):
3101
4101
        # Repository._get_source.get_stream() from a stacked repository with
3116
4116
        # branch pulling content from stacked and trunk.
3117
4117
        self.setup_smart_server_with_call_log()
3118
4118
        trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3119
 
        r1 = trunk.commit('start')
 
4119
        trunk.commit('start')
3120
4120
        stacked_branch = trunk.branch.create_clone_on_transport(
3121
4121
            self.get_transport('stacked'), stacked_on=trunk.branch.base)
3122
4122
        local = self.make_branch('local', format='1.9-rich-root')
3123
4123
        local.repository.fetch(stacked_branch.repository,
3124
 
            stacked_branch.last_revision())
 
4124
                               stacked_branch.last_revision())
3125
4125
 
3126
4126
 
3127
4127
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
3142
4142
 
3143
4143
    def test_copy_content_into_avoids_revision_history(self):
3144
4144
        local = self.make_branch('local')
3145
 
        remote_backing_tree = self.make_branch_and_tree('remote')
3146
 
        remote_backing_tree.commit("Commit.")
 
4145
        builder = self.make_branch_builder('remote')
 
4146
        builder.build_commit(message="Commit.")
3147
4147
        remote_branch_url = self.smart_server.get_url() + 'remote'
3148
4148
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3149
4149
        local.repository.fetch(remote_branch.repository)
3150
4150
        self.hpss_calls = []
3151
4151
        remote_branch.copy_content_into(local)
3152
 
        self.assertFalse('Branch.revision_history' in self.hpss_calls)
 
4152
        self.assertFalse(b'Branch.revision_history' in self.hpss_calls)
 
4153
 
 
4154
    def test_fetch_everything_needs_just_one_call(self):
 
4155
        local = self.make_branch('local')
 
4156
        builder = self.make_branch_builder('remote')
 
4157
        builder.build_commit(message="Commit.")
 
4158
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
4159
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
4160
        self.hpss_calls = []
 
4161
        local.repository.fetch(
 
4162
            remote_branch.repository,
 
4163
            fetch_spec=vf_search.EverythingResult(remote_branch.repository))
 
4164
        self.assertEqual([b'Repository.get_stream_1.19'], self.hpss_calls)
 
4165
 
 
4166
    def override_verb(self, verb_name, verb):
 
4167
        request_handlers = request.request_handlers
 
4168
        orig_verb = request_handlers.get(verb_name)
 
4169
        orig_info = request_handlers.get_info(verb_name)
 
4170
        request_handlers.register(verb_name, verb, override_existing=True)
 
4171
        self.addCleanup(request_handlers.register, verb_name, orig_verb,
 
4172
                        override_existing=True, info=orig_info)
 
4173
 
 
4174
    def test_fetch_everything_backwards_compat(self):
 
4175
        """Can fetch with EverythingResult even with pre 2.4 servers.
 
4176
 
 
4177
        Pre-2.4 do not support 'everything' searches with the
 
4178
        Repository.get_stream_1.19 verb.
 
4179
        """
 
4180
        verb_log = []
 
4181
 
 
4182
        class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
 
4183
            """A version of the Repository.get_stream_1.19 verb patched to
 
4184
            reject 'everything' searches the way 2.3 and earlier do.
 
4185
            """
 
4186
 
 
4187
            def recreate_search(self, repository, search_bytes,
 
4188
                                discard_excess=False):
 
4189
                verb_log.append(search_bytes.split(b'\n', 1)[0])
 
4190
                if search_bytes == b'everything':
 
4191
                    return (None,
 
4192
                            request.FailedSmartServerResponse((b'BadSearch',)))
 
4193
                return super(OldGetStreamVerb,
 
4194
                             self).recreate_search(repository, search_bytes,
 
4195
                                                   discard_excess=discard_excess)
 
4196
        self.override_verb(b'Repository.get_stream_1.19', OldGetStreamVerb)
 
4197
        local = self.make_branch('local')
 
4198
        builder = self.make_branch_builder('remote')
 
4199
        builder.build_commit(message="Commit.")
 
4200
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
4201
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
4202
        self.hpss_calls = []
 
4203
        local.repository.fetch(
 
4204
            remote_branch.repository,
 
4205
            fetch_spec=vf_search.EverythingResult(remote_branch.repository))
 
4206
        # make sure the overridden verb was used
 
4207
        self.assertLength(1, verb_log)
 
4208
        # more than one HPSS call is needed, but because it's a VFS callback
 
4209
        # its hard to predict exactly how many.
 
4210
        self.assertTrue(len(self.hpss_calls) > 1)
 
4211
 
 
4212
 
 
4213
class TestUpdateBoundBranchWithModifiedBoundLocation(
 
4214
        tests.TestCaseWithTransport):
 
4215
    """Ensure correct handling of bound_location modifications.
 
4216
 
 
4217
    This is tested against a smart server as http://pad.lv/786980 was about a
 
4218
    ReadOnlyError (write attempt during a read-only transaction) which can only
 
4219
    happen in this context.
 
4220
    """
 
4221
 
 
4222
    def setUp(self):
 
4223
        super(TestUpdateBoundBranchWithModifiedBoundLocation, self).setUp()
 
4224
        self.transport_server = test_server.SmartTCPServer_for_testing
 
4225
 
 
4226
    def make_master_and_checkout(self, master_name, checkout_name):
 
4227
        # Create the master branch and its associated checkout
 
4228
        self.master = self.make_branch_and_tree(master_name)
 
4229
        self.checkout = self.master.branch.create_checkout(checkout_name)
 
4230
        # Modify the master branch so there is something to update
 
4231
        self.master.commit('add stuff')
 
4232
        self.last_revid = self.master.commit('even more stuff')
 
4233
        self.bound_location = self.checkout.branch.get_bound_location()
 
4234
 
 
4235
    def assertUpdateSucceeds(self, new_location):
 
4236
        self.checkout.branch.set_bound_location(new_location)
 
4237
        self.checkout.update()
 
4238
        self.assertEqual(self.last_revid, self.checkout.last_revision())
 
4239
 
 
4240
    def test_without_final_slash(self):
 
4241
        self.make_master_and_checkout('master', 'checkout')
 
4242
        # For unclear reasons some users have a bound_location without a final
 
4243
        # '/', simulate that by forcing such a value
 
4244
        self.assertEndsWith(self.bound_location, '/')
 
4245
        self.assertUpdateSucceeds(self.bound_location.rstrip('/'))
 
4246
 
 
4247
    def test_plus_sign(self):
 
4248
        self.make_master_and_checkout('+master', 'checkout')
 
4249
        self.assertUpdateSucceeds(self.bound_location.replace('%2B', '+', 1))
 
4250
 
 
4251
    def test_tilda(self):
 
4252
        # Embed ~ in the middle of the path just to avoid any $HOME
 
4253
        # interpretation
 
4254
        self.make_master_and_checkout('mas~ter', 'checkout')
 
4255
        self.assertUpdateSucceeds(self.bound_location.replace('%2E', '~', 1))
 
4256
 
 
4257
 
 
4258
class TestWithCustomErrorHandler(RemoteBranchTestCase):
 
4259
 
 
4260
    def test_no_context(self):
 
4261
        class OutOfCoffee(errors.BzrError):
 
4262
            """A dummy exception for testing."""
 
4263
 
 
4264
            def __init__(self, urgency):
 
4265
                self.urgency = urgency
 
4266
        remote.no_context_error_translators.register(b"OutOfCoffee",
 
4267
                                                     lambda err: OutOfCoffee(err.error_args[0]))
 
4268
        transport = MemoryTransport()
 
4269
        client = FakeClient(transport.base)
 
4270
        client.add_expected_call(
 
4271
            b'Branch.get_stacked_on_url', (b'quack/',),
 
4272
            b'error', (b'NotStacked',))
 
4273
        client.add_expected_call(
 
4274
            b'Branch.last_revision_info',
 
4275
            (b'quack/',),
 
4276
            b'error', (b'OutOfCoffee', b'low'))
 
4277
        transport.mkdir('quack')
 
4278
        transport = transport.clone('quack')
 
4279
        branch = self.make_remote_branch(transport, client)
 
4280
        self.assertRaises(OutOfCoffee, branch.last_revision_info)
 
4281
        self.assertFinished(client)
 
4282
 
 
4283
    def test_with_context(self):
 
4284
        class OutOfTea(errors.BzrError):
 
4285
            def __init__(self, branch, urgency):
 
4286
                self.branch = branch
 
4287
                self.urgency = urgency
 
4288
        remote.error_translators.register(b"OutOfTea",
 
4289
                                          lambda err, find, path: OutOfTea(
 
4290
                                              err.error_args[0].decode(
 
4291
                                                  'utf-8'),
 
4292
                                              find("branch")))
 
4293
        transport = MemoryTransport()
 
4294
        client = FakeClient(transport.base)
 
4295
        client.add_expected_call(
 
4296
            b'Branch.get_stacked_on_url', (b'quack/',),
 
4297
            b'error', (b'NotStacked',))
 
4298
        client.add_expected_call(
 
4299
            b'Branch.last_revision_info',
 
4300
            (b'quack/',),
 
4301
            b'error', (b'OutOfTea', b'low'))
 
4302
        transport.mkdir('quack')
 
4303
        transport = transport.clone('quack')
 
4304
        branch = self.make_remote_branch(transport, client)
 
4305
        self.assertRaises(OutOfTea, branch.last_revision_info)
 
4306
        self.assertFinished(client)
 
4307
 
 
4308
 
 
4309
class TestRepositoryPack(TestRemoteRepository):
 
4310
 
 
4311
    def test_pack(self):
 
4312
        transport_path = 'quack'
 
4313
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4314
        client.add_expected_call(
 
4315
            b'Repository.lock_write', (b'quack/', b''),
 
4316
            b'success', (b'ok', b'token'))
 
4317
        client.add_expected_call(
 
4318
            b'Repository.pack', (b'quack/', b'token', b'False'),
 
4319
            b'success', (b'ok',), )
 
4320
        client.add_expected_call(
 
4321
            b'Repository.unlock', (b'quack/', b'token'),
 
4322
            b'success', (b'ok', ))
 
4323
        repo.pack()
 
4324
 
 
4325
    def test_pack_with_hint(self):
 
4326
        transport_path = 'quack'
 
4327
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4328
        client.add_expected_call(
 
4329
            b'Repository.lock_write', (b'quack/', b''),
 
4330
            b'success', (b'ok', b'token'))
 
4331
        client.add_expected_call(
 
4332
            b'Repository.pack', (b'quack/', b'token', b'False'),
 
4333
            b'success', (b'ok',), )
 
4334
        client.add_expected_call(
 
4335
            b'Repository.unlock', (b'quack/', b'token', b'False'),
 
4336
            b'success', (b'ok', ))
 
4337
        repo.pack(['hinta', 'hintb'])
 
4338
 
 
4339
 
 
4340
class TestRepositoryIterInventories(TestRemoteRepository):
 
4341
    """Test Repository.iter_inventories."""
 
4342
 
 
4343
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4344
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4345
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4346
 
 
4347
    def test_single_empty(self):
 
4348
        transport_path = 'quack'
 
4349
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4350
        fmt = controldir.format_registry.get('2a')().repository_format
 
4351
        repo._format = fmt
 
4352
        stream = [('inventory-deltas', [
 
4353
            versionedfile.FulltextContentFactory(b'somerevid', None, None,
 
4354
                                                 self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4355
        client.add_expected_call(
 
4356
            b'VersionedFileRepository.get_inventories', (
 
4357
                b'quack/', b'unordered'),
 
4358
            b'success', (b'ok', ),
 
4359
            _stream_to_byte_stream(stream, fmt))
 
4360
        ret = list(repo.iter_inventories([b"somerevid"]))
 
4361
        self.assertLength(1, ret)
 
4362
        inv = ret[0]
 
4363
        self.assertEqual(b"somerevid", inv.revision_id)
 
4364
 
 
4365
    def test_empty(self):
 
4366
        transport_path = 'quack'
 
4367
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4368
        ret = list(repo.iter_inventories([]))
 
4369
        self.assertEqual(ret, [])
 
4370
 
 
4371
    def test_missing(self):
 
4372
        transport_path = 'quack'
 
4373
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4374
        client.add_expected_call(
 
4375
            b'VersionedFileRepository.get_inventories', (
 
4376
                b'quack/', b'unordered'),
 
4377
            b'success', (b'ok', ), iter([]))
 
4378
        self.assertRaises(errors.NoSuchRevision, list, repo.iter_inventories(
 
4379
            [b"somerevid"]))
 
4380
 
 
4381
 
 
4382
class TestRepositoryRevisionTreeArchive(TestRemoteRepository):
 
4383
    """Test Repository.iter_inventories."""
 
4384
 
 
4385
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4386
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4387
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4388
 
 
4389
    def test_simple(self):
 
4390
        transport_path = 'quack'
 
4391
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4392
        fmt = controldir.format_registry.get('2a')().repository_format
 
4393
        repo._format = fmt
 
4394
        stream = [('inventory-deltas', [
 
4395
            versionedfile.FulltextContentFactory(b'somerevid', None, None,
 
4396
                                                 self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4397
        client.add_expected_call(
 
4398
            b'VersionedFileRepository.get_inventories', (
 
4399
                b'quack/', b'unordered'),
 
4400
            b'success', (b'ok', ),
 
4401
            _stream_to_byte_stream(stream, fmt))
 
4402
        f = BytesIO()
 
4403
        with tarfile.open(mode='w', fileobj=f) as tf:
 
4404
            info = tarfile.TarInfo('somefile')
 
4405
            info.mtime = 432432
 
4406
            contents = b'some data'
 
4407
            info.type = tarfile.REGTYPE
 
4408
            info.mode = 0o644
 
4409
            info.size = len(contents)
 
4410
            tf.addfile(info, BytesIO(contents))
 
4411
        client.add_expected_call(
 
4412
            b'Repository.revision_archive', (b'quack/',
 
4413
                                             b'somerevid', b'tar', b'foo.tar', b'', b'', None),
 
4414
            b'success', (b'ok', ),
 
4415
            f.getvalue())
 
4416
        tree = repo.revision_tree(b'somerevid')
 
4417
        self.assertEqual(f.getvalue(), b''.join(
 
4418
            tree.archive('tar', 'foo.tar')))
 
4419
 
 
4420
 
 
4421
class TestRepositoryAnnotate(TestRemoteRepository):
 
4422
    """Test RemoteRevisionTree.annotate.."""
 
4423
 
 
4424
    def _serialize_inv_delta(self, old_name, new_name, delta):
 
4425
        serializer = inventory_delta.InventoryDeltaSerializer(True, False)
 
4426
        return b"".join(serializer.delta_to_lines(old_name, new_name, delta))
 
4427
 
 
4428
    def test_simple(self):
 
4429
        transport_path = 'quack'
 
4430
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
4431
        fmt = controldir.format_registry.get('2a')().repository_format
 
4432
        repo._format = fmt
 
4433
        stream = [
 
4434
            ('inventory-deltas', [
 
4435
                versionedfile.FulltextContentFactory(
 
4436
                    b'somerevid', None, None,
 
4437
                    self._serialize_inv_delta(b'null:', b'somerevid', []))])]
 
4438
        client.add_expected_call(
 
4439
            b'VersionedFileRepository.get_inventories', (
 
4440
                b'quack/', b'unordered'),
 
4441
            b'success', (b'ok', ),
 
4442
            _stream_to_byte_stream(stream, fmt))
 
4443
        client.add_expected_call(
 
4444
            b'Repository.annotate_file_revision',
 
4445
            (b'quack/', b'somerevid', b'filename', b'', b'current:'),
 
4446
            b'success', (b'ok', ),
 
4447
            bencode.bencode([[b'baserevid', b'line 1\n'],
 
4448
                             [b'somerevid', b'line2\n']]))
 
4449
        tree = repo.revision_tree(b'somerevid')
 
4450
        self.assertEqual([
 
4451
            (b'baserevid', b'line 1\n'),
 
4452
            (b'somerevid', b'line2\n')],
 
4453
            list(tree.annotate_iter('filename')))