~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-06-05 04:05:05 UTC
  • mfrom: (3473.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080605040505-i9kqxg2fps2qjdi0
Add the 'alias' command (Tim Penhey)

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from cStringIO import StringIO
28
28
 
29
29
from bzrlib import (
30
 
    config,
31
30
    errors,
32
31
    graph,
33
32
    pack,
34
33
    remote,
35
34
    repository,
36
35
    tests,
37
 
    urlutils,
38
36
    )
39
37
from bzrlib.branch import Branch
40
38
from bzrlib.bzrdir import BzrDir, BzrDirFormat
50
48
from bzrlib.symbol_versioning import one_four
51
49
from bzrlib.transport import get_transport, http
52
50
from bzrlib.transport.memory import MemoryTransport
53
 
from bzrlib.transport.remote import (
54
 
    RemoteTransport,
55
 
    RemoteSSHTransport,
56
 
    RemoteTCPTransport,
57
 
)
 
51
from bzrlib.transport.remote import RemoteTransport, RemoteTCPTransport
58
52
 
59
53
 
60
54
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
112
106
        self.assertStartsWith(str(b), 'RemoteBranch(')
113
107
 
114
108
 
115
 
class FakeRemoteTransport(object):
116
 
    """This class provides the minimum support for use in place of a RemoteTransport.
117
 
    
118
 
    It doesn't actually transmit requests, but rather expects them to be
119
 
    handled by a FakeClient which holds canned responses.  It does not allow
120
 
    any vfs access, therefore is not suitable for testing any operation that
121
 
    will fallback to vfs access.  Backing the test by an instance of this
122
 
    class guarantees that it's - done using non-vfs operations.
123
 
    """
124
 
 
125
 
    _default_url = 'fakeremotetransport://host/path/'
126
 
 
127
 
    def __init__(self, url=None):
128
 
        if url is None:
129
 
            url = self._default_url
130
 
        self.base = url
131
 
 
132
 
    def __repr__(self):
133
 
        return "%r(%r)" % (self.__class__.__name__,
134
 
            self.base)
135
 
 
136
 
    def clone(self, relpath):
137
 
        return FakeRemoteTransport(urlutils.join(self.base, relpath))
138
 
 
139
 
    def get(self, relpath):
140
 
        # only get is specifically stubbed out, because it's usually the first
141
 
        # thing we do.  anything else will fail with an AttributeError.
142
 
        raise AssertionError("%r doesn't support file access to %r"
143
 
            % (self, relpath))
144
 
 
145
 
 
146
 
 
147
109
class FakeProtocol(object):
148
110
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
149
111
 
182
144
        self.responses = []
183
145
        self._calls = []
184
146
        self.expecting_body = False
185
 
        # if non-None, this is the list of expected calls, with only the
186
 
        # method name and arguments included.  the body might be hard to
187
 
        # compute so is not included
188
 
        self._expected_calls = None
189
147
        _SmartClient.__init__(self, FakeMedium(self._calls, fake_medium_base))
190
148
 
191
 
    def add_expected_call(self, call_name, call_args, response_type,
192
 
        response_args, response_body=None):
193
 
        if self._expected_calls is None:
194
 
            self._expected_calls = []
195
 
        self._expected_calls.append((call_name, call_args))
196
 
        self.responses.append((response_type, response_args, response_body))
197
 
 
198
149
    def add_success_response(self, *args):
199
150
        self.responses.append(('success', args, None))
200
151
 
207
158
    def add_unknown_method_response(self, verb):
208
159
        self.responses.append(('unknown', verb))
209
160
 
210
 
    def finished_test(self):
211
 
        if self._expected_calls:
212
 
            raise AssertionError("%r finished but was still expecting %r"
213
 
                % (self, self._expected_calls[0]))
214
 
 
215
161
    def _get_next_response(self):
216
 
        try:
217
 
            response_tuple = self.responses.pop(0)
218
 
        except IndexError, e:
219
 
            raise AssertionError("%r didn't expect any more calls"
220
 
                % (self,))
 
162
        response_tuple = self.responses.pop(0)
221
163
        if response_tuple[0] == 'unknown':
222
164
            raise errors.UnknownSmartMethod(response_tuple[1])
223
165
        elif response_tuple[0] == 'error':
224
166
            raise errors.ErrorFromSmartServer(response_tuple[1])
225
167
        return response_tuple
226
168
 
227
 
    def _check_call(self, method, args):
228
 
        if self._expected_calls is None:
229
 
            # the test should be updated to say what it expects
230
 
            return
231
 
        try:
232
 
            next_call = self._expected_calls.pop(0)
233
 
        except IndexError:
234
 
            raise AssertionError("%r didn't expect any more calls "
235
 
                "but got %r%r"
236
 
                % (self, method, args,))
237
 
        if method != next_call[0] or args != next_call[1]:
238
 
            raise AssertionError("%r expected %r%r "
239
 
                "but got %r%r"
240
 
                % (self, next_call[0], next_call[1], method, args,))
241
 
 
242
169
    def call(self, method, *args):
243
 
        self._check_call(method, args)
244
170
        self._calls.append(('call', method, args))
245
171
        return self._get_next_response()[1]
246
172
 
247
173
    def call_expecting_body(self, method, *args):
248
 
        self._check_call(method, args)
249
174
        self._calls.append(('call_expecting_body', method, args))
250
175
        result = self._get_next_response()
251
176
        self.expecting_body = True
252
177
        return result[1], FakeProtocol(result[2], self)
253
178
 
254
179
    def call_with_body_bytes_expecting_body(self, method, args, body):
255
 
        self._check_call(method, args)
256
180
        self._calls.append(('call_with_body_bytes_expecting_body', method,
257
181
            args, body))
258
182
        result = self._get_next_response()
263
187
class FakeMedium(medium.SmartClientMedium):
264
188
 
265
189
    def __init__(self, client_calls, base):
266
 
        medium.SmartClientMedium.__init__(self, base)
 
190
        self._remote_is_at_least_1_2 = True
267
191
        self._client_calls = client_calls
 
192
        self.base = base
268
193
 
269
194
    def disconnect(self):
270
195
        self._client_calls.append(('disconnect medium',))
329
254
                'xyz/', scheme + '//host/path', 'xyz/')
330
255
 
331
256
 
332
 
class Test_ClientMedium_remote_is_at_least(tests.TestCase):
333
 
    """Tests for the behaviour of client_medium.remote_is_at_least."""
334
 
 
335
 
    def test_initially_unlimited(self):
336
 
        """A fresh medium assumes that the remote side supports all
337
 
        versions.
338
 
        """
339
 
        client_medium = medium.SmartClientMedium('dummy base')
340
 
        self.assertFalse(client_medium._is_remote_before((99, 99)))
341
 
    
342
 
    def test__remember_remote_is_before(self):
343
 
        """Calling _remember_remote_is_before ratchets down the known remote
344
 
        version.
345
 
        """
346
 
        client_medium = medium.SmartClientMedium('dummy base')
347
 
        # Mark the remote side as being less than 1.6.  The remote side may
348
 
        # still be 1.5.
349
 
        client_medium._remember_remote_is_before((1, 6))
350
 
        self.assertTrue(client_medium._is_remote_before((1, 6)))
351
 
        self.assertFalse(client_medium._is_remote_before((1, 5)))
352
 
        # Calling _remember_remote_is_before again with a lower value works.
353
 
        client_medium._remember_remote_is_before((1, 5))
354
 
        self.assertTrue(client_medium._is_remote_before((1, 5)))
355
 
        # You cannot call _remember_remote_is_before with a larger value.
356
 
        self.assertRaises(
357
 
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
358
 
 
359
 
 
360
257
class TestBzrDirOpenBranch(tests.TestCase):
361
258
 
362
259
    def test_branch_present(self):
364
261
        transport.mkdir('quack')
365
262
        transport = transport.clone('quack')
366
263
        client = FakeClient(transport.base)
367
 
        client.add_expected_call(
368
 
            'BzrDir.open_branch', ('quack/',),
369
 
            'success', ('ok', ''))
370
 
        client.add_expected_call(
371
 
            'BzrDir.find_repositoryV2', ('quack/',),
372
 
            'success', ('ok', '', 'no', 'no', 'no'))
373
 
        client.add_expected_call(
374
 
            'Branch.get_stacked_on_url', ('quack/',),
375
 
            'error', ('NotStacked',))
 
264
        client.add_success_response('ok', '')
 
265
        client.add_success_response('ok', '', 'no', 'no', 'no')
376
266
        bzrdir = RemoteBzrDir(transport, _client=client)
377
267
        result = bzrdir.open_branch()
 
268
        self.assertEqual(
 
269
            [('call', 'BzrDir.open_branch', ('quack/',)),
 
270
             ('call', 'BzrDir.find_repositoryV2', ('quack/',))],
 
271
            client._calls)
378
272
        self.assertIsInstance(result, RemoteBranch)
379
273
        self.assertEqual(bzrdir, result.bzrdir)
380
 
        client.finished_test()
381
274
 
382
275
    def test_branch_missing(self):
383
276
        transport = MemoryTransport()
413
306
        # transmitted as "~", not "%7E".
414
307
        transport = RemoteTCPTransport('bzr://localhost/~hello/')
415
308
        client = FakeClient(transport.base)
416
 
        client.add_expected_call(
417
 
            'BzrDir.open_branch', ('~hello/',),
418
 
            'success', ('ok', ''))
419
 
        client.add_expected_call(
420
 
            'BzrDir.find_repositoryV2', ('~hello/',),
421
 
            'success', ('ok', '', 'no', 'no', 'no'))
422
 
        client.add_expected_call(
423
 
            'Branch.get_stacked_on_url', ('~hello/',),
424
 
            'error', ('NotStacked',))
 
309
        client.add_success_response('ok', '')
 
310
        client.add_success_response('ok', '', 'no', 'no', 'no')
425
311
        bzrdir = RemoteBzrDir(transport, _client=client)
426
312
        result = bzrdir.open_branch()
427
 
        client.finished_test()
 
313
        self.assertEqual(
 
314
            [('call', 'BzrDir.open_branch', ('~hello/',)),
 
315
             ('call', 'BzrDir.find_repositoryV2', ('~hello/',))],
 
316
            client._calls)
428
317
 
429
318
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
430
319
        transport = MemoryTransport()
511
400
        return OldSmartClient()
512
401
 
513
402
 
514
 
class RemoteBranchTestCase(tests.TestCase):
515
 
 
516
 
    def make_remote_branch(self, transport, client):
517
 
        """Make a RemoteBranch using 'client' as its _SmartClient.
518
 
        
519
 
        A RemoteBzrDir and RemoteRepository will also be created to fill out
520
 
        the RemoteBranch, albeit with stub values for some of their attributes.
521
 
        """
522
 
        # we do not want bzrdir to make any remote calls, so use False as its
523
 
        # _client.  If it tries to make a remote call, this will fail
524
 
        # immediately.
525
 
        bzrdir = RemoteBzrDir(transport, _client=False)
526
 
        repo = RemoteRepository(bzrdir, None, _client=client)
527
 
        return RemoteBranch(bzrdir, repo, _client=client)
528
 
 
529
 
 
530
 
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
 
403
class TestBranchLastRevisionInfo(tests.TestCase):
531
404
 
532
405
    def test_empty_branch(self):
533
406
        # in an empty branch we decode the response properly
534
407
        transport = MemoryTransport()
535
408
        client = FakeClient(transport.base)
536
 
        client.add_expected_call(
537
 
            'Branch.get_stacked_on_url', ('quack/',),
538
 
            'error', ('NotStacked',))
539
 
        client.add_expected_call(
540
 
            'Branch.last_revision_info', ('quack/',),
541
 
            'success', ('ok', '0', 'null:'))
 
409
        client.add_success_response('ok', '0', 'null:')
542
410
        transport.mkdir('quack')
543
411
        transport = transport.clone('quack')
544
 
        branch = self.make_remote_branch(transport, client)
 
412
        # we do not want bzrdir to make any remote calls
 
413
        bzrdir = RemoteBzrDir(transport, _client=False)
 
414
        branch = RemoteBranch(bzrdir, None, _client=client)
545
415
        result = branch.last_revision_info()
546
 
        client.finished_test()
 
416
 
 
417
        self.assertEqual(
 
418
            [('call', 'Branch.last_revision_info', ('quack/',))],
 
419
            client._calls)
547
420
        self.assertEqual((0, NULL_REVISION), result)
548
421
 
549
422
    def test_non_empty_branch(self):
551
424
        revid = u'\xc8'.encode('utf8')
552
425
        transport = MemoryTransport()
553
426
        client = FakeClient(transport.base)
554
 
        client.add_expected_call(
555
 
            'Branch.get_stacked_on_url', ('kwaak/',),
556
 
            'error', ('NotStacked',))
557
 
        client.add_expected_call(
558
 
            'Branch.last_revision_info', ('kwaak/',),
559
 
            'success', ('ok', '2', revid))
 
427
        client.add_success_response('ok', '2', revid)
560
428
        transport.mkdir('kwaak')
561
429
        transport = transport.clone('kwaak')
562
 
        branch = self.make_remote_branch(transport, client)
 
430
        # we do not want bzrdir to make any remote calls
 
431
        bzrdir = RemoteBzrDir(transport, _client=False)
 
432
        branch = RemoteBranch(bzrdir, None, _client=client)
563
433
        result = branch.last_revision_info()
 
434
 
 
435
        self.assertEqual(
 
436
            [('call', 'Branch.last_revision_info', ('kwaak/',))],
 
437
            client._calls)
564
438
        self.assertEqual((2, revid), result)
565
439
 
566
440
 
567
 
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
568
 
    """Test Branch._get_stacked_on_url rpc"""
569
 
 
570
 
    def test_get_stacked_on_invalid_url(self):
571
 
        raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
572
 
        transport = FakeRemoteTransport('fakeremotetransport:///')
573
 
        client = FakeClient(transport.base)
574
 
        client.add_expected_call(
575
 
            'Branch.get_stacked_on_url', ('.',),
576
 
            'success', ('ok', 'file:///stacked/on'))
577
 
        bzrdir = RemoteBzrDir(transport, _client=client)
578
 
        branch = RemoteBranch(bzrdir, None, _client=client)
579
 
        result = branch.get_stacked_on_url()
580
 
        self.assertEqual(
581
 
            'file:///stacked/on', result)
582
 
 
583
 
    def test_backwards_compatible(self):
584
 
        # like with bzr1.6 with no Branch.get_stacked_on_url rpc
585
 
        base_branch = self.make_branch('base', format='1.6')
586
 
        stacked_branch = self.make_branch('stacked', format='1.6')
587
 
        stacked_branch.set_stacked_on_url('../base')
588
 
        client = FakeClient(self.get_url())
589
 
        client.add_expected_call(
590
 
            'BzrDir.open_branch', ('stacked/',),
591
 
            'success', ('ok', ''))
592
 
        client.add_expected_call(
593
 
            'BzrDir.find_repositoryV2', ('stacked/',),
594
 
            'success', ('ok', '', 'no', 'no', 'no'))
595
 
        # called twice, once from constructor and then again by us
596
 
        client.add_expected_call(
597
 
            'Branch.get_stacked_on_url', ('stacked/',),
598
 
            'unknown', ('Branch.get_stacked_on_url',))
599
 
        client.add_expected_call(
600
 
            'Branch.get_stacked_on_url', ('stacked/',),
601
 
            'unknown', ('Branch.get_stacked_on_url',))
602
 
        # this will also do vfs access, but that goes direct to the transport
603
 
        # and isn't seen by the FakeClient.
604
 
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
605
 
        branch = bzrdir.open_branch()
606
 
        result = branch.get_stacked_on_url()
607
 
        self.assertEqual('../base', result)
608
 
        client.finished_test()
609
 
        # it's in the fallback list both for the RemoteRepository and its vfs
610
 
        # repository
611
 
        self.assertEqual(1, len(branch.repository._fallback_repositories))
612
 
        self.assertEqual(1,
613
 
            len(branch.repository._real_repository._fallback_repositories))
614
 
 
615
 
    def test_get_stacked_on_real_branch(self):
616
 
        base_branch = self.make_branch('base', format='1.6')
617
 
        stacked_branch = self.make_branch('stacked', format='1.6')
618
 
        stacked_branch.set_stacked_on_url('../base')
619
 
        client = FakeClient(self.get_url())
620
 
        client.add_expected_call(
621
 
            'BzrDir.open_branch', ('stacked/',),
622
 
            'success', ('ok', ''))
623
 
        client.add_expected_call(
624
 
            'BzrDir.find_repositoryV2', ('stacked/',),
625
 
            'success', ('ok', '', 'no', 'no', 'no'))
626
 
        # called twice, once from constructor and then again by us
627
 
        client.add_expected_call(
628
 
            'Branch.get_stacked_on_url', ('stacked/',),
629
 
            'success', ('ok', '../base'))
630
 
        client.add_expected_call(
631
 
            'Branch.get_stacked_on_url', ('stacked/',),
632
 
            'success', ('ok', '../base'))
633
 
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
634
 
        branch = bzrdir.open_branch()
635
 
        result = branch.get_stacked_on_url()
636
 
        self.assertEqual('../base', result)
637
 
        client.finished_test()
638
 
        # it's in the fallback list both for the RemoteRepository and its vfs
639
 
        # repository
640
 
        self.assertEqual(1, len(branch.repository._fallback_repositories))
641
 
        self.assertEqual(1,
642
 
            len(branch.repository._real_repository._fallback_repositories))
643
 
 
644
 
 
645
 
class TestBranchSetLastRevision(RemoteBranchTestCase):
 
441
class TestBranchSetLastRevision(tests.TestCase):
646
442
 
647
443
    def test_set_empty(self):
648
444
        # set_revision_history([]) is translated to calling
652
448
        transport = transport.clone('branch')
653
449
 
654
450
        client = FakeClient(transport.base)
655
 
        client.add_expected_call(
656
 
            'Branch.get_stacked_on_url', ('branch/',),
657
 
            'error', ('NotStacked',))
658
 
        client.add_expected_call(
659
 
            'Branch.lock_write', ('branch/', '', ''),
660
 
            'success', ('ok', 'branch token', 'repo token'))
661
 
        client.add_expected_call(
662
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
663
 
            'success', ('ok',))
664
 
        client.add_expected_call(
665
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
666
 
            'success', ('ok',))
667
 
        branch = self.make_remote_branch(transport, client)
 
451
        # lock_write
 
452
        client.add_success_response('ok', 'branch token', 'repo token')
 
453
        # set_last_revision
 
454
        client.add_success_response('ok')
 
455
        # unlock
 
456
        client.add_success_response('ok')
 
457
        bzrdir = RemoteBzrDir(transport, _client=False)
 
458
        branch = RemoteBranch(bzrdir, None, _client=client)
668
459
        # This is a hack to work around the problem that RemoteBranch currently
669
460
        # unnecessarily invokes _ensure_real upon a call to lock_write.
670
461
        branch._ensure_real = lambda: None
671
462
        branch.lock_write()
 
463
        client._calls = []
672
464
        result = branch.set_revision_history([])
 
465
        self.assertEqual(
 
466
            [('call', 'Branch.set_last_revision',
 
467
                ('branch/', 'branch token', 'repo token', 'null:'))],
 
468
            client._calls)
673
469
        branch.unlock()
674
470
        self.assertEqual(None, result)
675
 
        client.finished_test()
676
471
 
677
472
    def test_set_nonempty(self):
678
473
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
682
477
        transport = transport.clone('branch')
683
478
 
684
479
        client = FakeClient(transport.base)
685
 
        client.add_expected_call(
686
 
            'Branch.get_stacked_on_url', ('branch/',),
687
 
            'error', ('NotStacked',))
688
 
        client.add_expected_call(
689
 
            'Branch.lock_write', ('branch/', '', ''),
690
 
            'success', ('ok', 'branch token', 'repo token'))
691
 
        client.add_expected_call(
692
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
693
 
            'success', ('ok',))
694
 
        client.add_expected_call(
695
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
696
 
            'success', ('ok',))
697
 
        branch = self.make_remote_branch(transport, client)
 
480
        # lock_write
 
481
        client.add_success_response('ok', 'branch token', 'repo token')
 
482
        # set_last_revision
 
483
        client.add_success_response('ok')
 
484
        # unlock
 
485
        client.add_success_response('ok')
 
486
        bzrdir = RemoteBzrDir(transport, _client=False)
 
487
        branch = RemoteBranch(bzrdir, None, _client=client)
698
488
        # This is a hack to work around the problem that RemoteBranch currently
699
489
        # unnecessarily invokes _ensure_real upon a call to lock_write.
700
490
        branch._ensure_real = lambda: None
701
491
        # Lock the branch, reset the record of remote calls.
702
492
        branch.lock_write()
 
493
        client._calls = []
 
494
 
703
495
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
 
496
        self.assertEqual(
 
497
            [('call', 'Branch.set_last_revision',
 
498
                ('branch/', 'branch token', 'repo token', 'rev-id2'))],
 
499
            client._calls)
704
500
        branch.unlock()
705
501
        self.assertEqual(None, result)
706
 
        client.finished_test()
707
502
 
708
503
    def test_no_such_revision(self):
709
504
        transport = MemoryTransport()
711
506
        transport = transport.clone('branch')
712
507
        # A response of 'NoSuchRevision' is translated into an exception.
713
508
        client = FakeClient(transport.base)
714
 
        client.add_expected_call(
715
 
            'Branch.get_stacked_on_url', ('branch/',),
716
 
            'error', ('NotStacked',))
717
 
        client.add_expected_call(
718
 
            'Branch.lock_write', ('branch/', '', ''),
719
 
            'success', ('ok', 'branch token', 'repo token'))
720
 
        client.add_expected_call(
721
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
722
 
            'error', ('NoSuchRevision', 'rev-id'))
723
 
        client.add_expected_call(
724
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
725
 
            'success', ('ok',))
 
509
        # lock_write
 
510
        client.add_success_response('ok', 'branch token', 'repo token')
 
511
        # set_last_revision
 
512
        client.add_error_response('NoSuchRevision', 'rev-id')
 
513
        # unlock
 
514
        client.add_success_response('ok')
726
515
 
727
 
        branch = self.make_remote_branch(transport, client)
 
516
        bzrdir = RemoteBzrDir(transport, _client=False)
 
517
        branch = RemoteBranch(bzrdir, None, _client=client)
 
518
        branch._ensure_real = lambda: None
728
519
        branch.lock_write()
 
520
        client._calls = []
 
521
 
729
522
        self.assertRaises(
730
523
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
731
524
        branch.unlock()
732
 
        client.finished_test()
733
 
 
734
 
    def test_tip_change_rejected(self):
735
 
        """TipChangeRejected responses cause a TipChangeRejected exception to
736
 
        be raised.
737
 
        """
738
 
        transport = MemoryTransport()
739
 
        transport.mkdir('branch')
740
 
        transport = transport.clone('branch')
741
 
        client = FakeClient(transport.base)
742
 
        rejection_msg_unicode = u'rejection message\N{INTERROBANG}'
743
 
        rejection_msg_utf8 = rejection_msg_unicode.encode('utf8')
744
 
        client.add_expected_call(
745
 
            'Branch.get_stacked_on_url', ('branch/',),
746
 
            'error', ('NotStacked',))
747
 
        client.add_expected_call(
748
 
            'Branch.lock_write', ('branch/', '', ''),
749
 
            'success', ('ok', 'branch token', 'repo token'))
750
 
        client.add_expected_call(
751
 
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
752
 
            'error', ('TipChangeRejected', rejection_msg_utf8))
753
 
        client.add_expected_call(
754
 
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
755
 
            'success', ('ok',))
756
 
        branch = self.make_remote_branch(transport, client)
757
 
        branch._ensure_real = lambda: None
758
 
        branch.lock_write()
759
 
        self.addCleanup(branch.unlock)
760
 
        # The 'TipChangeRejected' error response triggered by calling
761
 
        # set_revision_history causes a TipChangeRejected exception.
762
 
        err = self.assertRaises(
763
 
            errors.TipChangeRejected, branch.set_revision_history, ['rev-id'])
764
 
        # The UTF-8 message from the response has been decoded into a unicode
765
 
        # object.
766
 
        self.assertIsInstance(err.msg, unicode)
767
 
        self.assertEqual(rejection_msg_unicode, err.msg)
768
 
        branch.unlock()
769
 
        client.finished_test()
770
 
 
771
 
 
772
 
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
 
525
 
 
526
 
 
527
class TestBranchSetLastRevisionInfo(tests.TestCase):
773
528
 
774
529
    def test_set_last_revision_info(self):
775
530
        # set_last_revision_info(num, 'rev-id') is translated to calling
778
533
        transport.mkdir('branch')
779
534
        transport = transport.clone('branch')
780
535
        client = FakeClient(transport.base)
781
 
        # get_stacked_on_url
782
 
        client.add_error_response('NotStacked')
783
536
        # lock_write
784
537
        client.add_success_response('ok', 'branch token', 'repo token')
785
538
        # set_last_revision
787
540
        # unlock
788
541
        client.add_success_response('ok')
789
542
 
790
 
        branch = self.make_remote_branch(transport, client)
 
543
        bzrdir = RemoteBzrDir(transport, _client=False)
 
544
        branch = RemoteBranch(bzrdir, None, _client=client)
 
545
        # This is a hack to work around the problem that RemoteBranch currently
 
546
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
547
        branch._ensure_real = lambda: None
791
548
        # Lock the branch, reset the record of remote calls.
792
549
        branch.lock_write()
793
550
        client._calls = []
805
562
        transport.mkdir('branch')
806
563
        transport = transport.clone('branch')
807
564
        client = FakeClient(transport.base)
808
 
        # get_stacked_on_url
809
 
        client.add_error_response('NotStacked')
810
565
        # lock_write
811
566
        client.add_success_response('ok', 'branch token', 'repo token')
812
567
        # set_last_revision
814
569
        # unlock
815
570
        client.add_success_response('ok')
816
571
 
817
 
        branch = self.make_remote_branch(transport, client)
 
572
        bzrdir = RemoteBzrDir(transport, _client=False)
 
573
        branch = RemoteBranch(bzrdir, None, _client=client)
 
574
        # This is a hack to work around the problem that RemoteBranch currently
 
575
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
576
        branch._ensure_real = lambda: None
818
577
        # Lock the branch, reset the record of remote calls.
819
578
        branch.lock_write()
820
579
        client._calls = []
829
588
        branch._lock_count = 2
830
589
        branch._lock_token = 'branch token'
831
590
        branch._repo_lock_token = 'repo token'
832
 
        branch.repository._lock_mode = 'w'
833
 
        branch.repository._lock_count = 2
834
 
        branch.repository._lock_token = 'repo token'
835
591
 
836
592
    def test_backwards_compatibility(self):
837
593
        """If the server does not support the Branch.set_last_revision_info
849
605
        transport.mkdir('branch')
850
606
        transport = transport.clone('branch')
851
607
        client = FakeClient(transport.base)
852
 
        client.add_expected_call(
853
 
            'Branch.get_stacked_on_url', ('branch/',),
854
 
            'error', ('NotStacked',))
855
 
        client.add_expected_call(
856
 
            'Branch.set_last_revision_info',
857
 
            ('branch/', 'branch token', 'repo token', '1234', 'a-revision-id',),
858
 
            'unknown', 'Branch.set_last_revision_info')
859
 
 
860
 
        branch = self.make_remote_branch(transport, client)
 
608
        client.add_unknown_method_response('Branch.set_last_revision_info')
 
609
        bzrdir = RemoteBzrDir(transport, _client=False)
 
610
        branch = RemoteBranch(bzrdir, None, _client=client)
861
611
        class StubRealBranch(object):
862
612
            def __init__(self):
863
613
                self.calls = []
864
614
            def set_last_revision_info(self, revno, revision_id):
865
615
                self.calls.append(
866
616
                    ('set_last_revision_info', revno, revision_id))
867
 
            def _clear_cached_state(self):
868
 
                pass
869
617
        real_branch = StubRealBranch()
870
618
        branch._real_branch = real_branch
871
619
        self.lock_remote_branch(branch)
873
621
        # Call set_last_revision_info, and verify it behaved as expected.
874
622
        result = branch.set_last_revision_info(1234, 'a-revision-id')
875
623
        self.assertEqual(
 
624
            [('call', 'Branch.set_last_revision_info',
 
625
                ('branch/', 'branch token', 'repo token',
 
626
                 '1234', 'a-revision-id')),],
 
627
            client._calls)
 
628
        self.assertEqual(
876
629
            [('set_last_revision_info', 1234, 'a-revision-id')],
877
630
            real_branch.calls)
878
 
        client.finished_test()
879
631
 
880
632
    def test_unexpected_error(self):
881
 
        # If the server sends an error the client doesn't understand, it gets
882
 
        # turned into an UnknownErrorFromSmartServer, which is presented as a
883
 
        # non-internal error to the user.
 
633
        # A response of 'NoSuchRevision' is translated into an exception.
884
634
        transport = MemoryTransport()
885
635
        transport.mkdir('branch')
886
636
        transport = transport.clone('branch')
887
637
        client = FakeClient(transport.base)
888
 
        # get_stacked_on_url
889
 
        client.add_error_response('NotStacked')
890
638
        # lock_write
891
639
        client.add_success_response('ok', 'branch token', 'repo token')
892
640
        # set_last_revision
894
642
        # unlock
895
643
        client.add_success_response('ok')
896
644
 
897
 
        branch = self.make_remote_branch(transport, client)
 
645
        bzrdir = RemoteBzrDir(transport, _client=False)
 
646
        branch = RemoteBranch(bzrdir, None, _client=client)
 
647
        # This is a hack to work around the problem that RemoteBranch currently
 
648
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
649
        branch._ensure_real = lambda: None
898
650
        # Lock the branch, reset the record of remote calls.
899
651
        branch.lock_write()
900
652
        client._calls = []
901
653
 
902
654
        err = self.assertRaises(
903
 
            errors.UnknownErrorFromSmartServer,
 
655
            errors.ErrorFromSmartServer,
904
656
            branch.set_last_revision_info, 123, 'revid')
905
657
        self.assertEqual(('UnexpectedError',), err.error_tuple)
906
658
        branch.unlock()
907
659
 
908
 
    def test_tip_change_rejected(self):
909
 
        """TipChangeRejected responses cause a TipChangeRejected exception to
910
 
        be raised.
911
 
        """
912
 
        transport = MemoryTransport()
913
 
        transport.mkdir('branch')
914
 
        transport = transport.clone('branch')
915
 
        client = FakeClient(transport.base)
916
 
        # get_stacked_on_url
917
 
        client.add_error_response('NotStacked')
918
 
        # lock_write
919
 
        client.add_success_response('ok', 'branch token', 'repo token')
920
 
        # set_last_revision
921
 
        client.add_error_response('TipChangeRejected', 'rejection message')
922
 
        # unlock
923
 
        client.add_success_response('ok')
924
 
 
925
 
        branch = self.make_remote_branch(transport, client)
926
 
        # Lock the branch, reset the record of remote calls.
927
 
        branch.lock_write()
928
 
        self.addCleanup(branch.unlock)
929
 
        client._calls = []
930
 
 
931
 
        # The 'TipChangeRejected' error response triggered by calling
932
 
        # set_last_revision_info causes a TipChangeRejected exception.
933
 
        err = self.assertRaises(
934
 
            errors.TipChangeRejected,
935
 
            branch.set_last_revision_info, 123, 'revid')
936
 
        self.assertEqual('rejection message', err.msg)
937
 
 
938
660
 
939
661
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
940
662
    """Getting the branch configuration should use an abstract method not vfs.
962
684
        ##     client._calls)
963
685
 
964
686
 
965
 
class TestBranchLockWrite(RemoteBranchTestCase):
 
687
class TestBranchLockWrite(tests.TestCase):
966
688
 
967
689
    def test_lock_write_unlockable(self):
968
690
        transport = MemoryTransport()
969
691
        client = FakeClient(transport.base)
970
 
        client.add_expected_call(
971
 
            'Branch.get_stacked_on_url', ('quack/',),
972
 
            'error', ('NotStacked',),)
973
 
        client.add_expected_call(
974
 
            'Branch.lock_write', ('quack/', '', ''),
975
 
            'error', ('UnlockableTransport',))
 
692
        client.add_error_response('UnlockableTransport')
976
693
        transport.mkdir('quack')
977
694
        transport = transport.clone('quack')
978
 
        branch = self.make_remote_branch(transport, client)
 
695
        # we do not want bzrdir to make any remote calls
 
696
        bzrdir = RemoteBzrDir(transport, _client=False)
 
697
        branch = RemoteBranch(bzrdir, None, _client=client)
979
698
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
980
 
        client.finished_test()
 
699
        self.assertEqual(
 
700
            [('call', 'Branch.lock_write', ('quack/', '', ''))],
 
701
            client._calls)
981
702
 
982
703
 
983
704
class TestTransportIsReadonly(tests.TestCase):
1019
740
            client._calls)
1020
741
 
1021
742
 
1022
 
class TestRemoteSSHTransportAuthentication(tests.TestCaseInTempDir):
1023
 
 
1024
 
    def test_defaults_to_none(self):
1025
 
        t = RemoteSSHTransport('bzr+ssh://example.com')
1026
 
        self.assertIs(None, t._get_credentials()[0])
1027
 
 
1028
 
    def test_uses_authentication_config(self):
1029
 
        conf = config.AuthenticationConfig()
1030
 
        conf._get_config().update(
1031
 
            {'bzr+sshtest': {'scheme': 'ssh', 'user': 'bar', 'host':
1032
 
            'example.com'}})
1033
 
        conf._save()
1034
 
        t = RemoteSSHTransport('bzr+ssh://example.com')
1035
 
        self.assertEqual('bar', t._get_credentials()[0])
1036
 
 
1037
 
 
1038
743
class TestRemoteRepository(tests.TestCase):
1039
744
    """Base for testing RemoteRepository protocol usage.
1040
745
    
1178
883
        repo, client = self.setup_fake_client_and_repository(transport_path)
1179
884
        client.add_unknown_method_response('Repository,get_parent_map')
1180
885
        client.add_success_response_with_body('', 'ok')
1181
 
        self.assertFalse(client._medium._is_remote_before((1, 2)))
 
886
        self.assertTrue(client._medium._remote_is_at_least_1_2)
1182
887
        rev_id = 'revision-id'
1183
888
        expected_deprecations = [
1184
889
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1193
898
              ('quack/', ''))],
1194
899
            client._calls)
1195
900
        # The medium is now marked as being connected to an older server
1196
 
        self.assertTrue(client._medium._is_remote_before((1, 2)))
 
901
        self.assertFalse(client._medium._remote_is_at_least_1_2)
1197
902
 
1198
903
    def test_get_parent_map_fallback_parentless_node(self):
1199
904
        """get_parent_map falls back to get_revision_graph on old servers.  The
1210
915
        transport_path = 'quack'
1211
916
        repo, client = self.setup_fake_client_and_repository(transport_path)
1212
917
        client.add_success_response_with_body(rev_id, 'ok')
1213
 
        client._medium._remember_remote_is_before((1, 2))
 
918
        client._medium._remote_is_at_least_1_2 = False
1214
919
        expected_deprecations = [
1215
920
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1216
921
            'in version 1.4.']
1297
1002
        transport_path = 'sinhala'
1298
1003
        repo, client = self.setup_fake_client_and_repository(transport_path)
1299
1004
        client.add_error_response('AnUnexpectedError')
1300
 
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
 
1005
        e = self.assertRaises(errors.ErrorFromSmartServer,
1301
1006
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
1302
1007
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
1303
1008
 
1450
1155
        src_repo.copy_content_into(dest_repo)
1451
1156
 
1452
1157
 
1453
 
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
1454
 
    """Base class for unit tests for bzrlib.remote._translate_error."""
1455
 
 
1456
 
    def translateTuple(self, error_tuple, **context):
1457
 
        """Call _translate_error with an ErrorFromSmartServer built from the
1458
 
        given error_tuple.
1459
 
 
1460
 
        :param error_tuple: A tuple of a smart server response, as would be
1461
 
            passed to an ErrorFromSmartServer.
1462
 
        :kwargs context: context items to call _translate_error with.
1463
 
 
1464
 
        :returns: The error raised by _translate_error.
1465
 
        """
1466
 
        # Raise the ErrorFromSmartServer before passing it as an argument,
1467
 
        # because _translate_error may need to re-raise it with a bare 'raise'
1468
 
        # statement.
1469
 
        server_error = errors.ErrorFromSmartServer(error_tuple)
1470
 
        translated_error = self.translateErrorFromSmartServer(
1471
 
            server_error, **context)
1472
 
        return translated_error
1473
 
 
1474
 
    def translateErrorFromSmartServer(self, error_object, **context):
1475
 
        """Like translateTuple, but takes an already constructed
1476
 
        ErrorFromSmartServer rather than a tuple.
1477
 
        """
1478
 
        try:
1479
 
            raise error_object
1480
 
        except errors.ErrorFromSmartServer, server_error:
1481
 
            translated_error = self.assertRaises(
1482
 
                errors.BzrError, remote._translate_error, server_error,
1483
 
                **context)
1484
 
        return translated_error
1485
 
 
1486
 
    
1487
 
class TestErrorTranslationSuccess(TestErrorTranslationBase):
1488
 
    """Unit tests for bzrlib.remote._translate_error.
1489
 
    
1490
 
    Given an ErrorFromSmartServer (which has an error tuple from a smart
1491
 
    server) and some context, _translate_error raises more specific errors from
1492
 
    bzrlib.errors.
1493
 
 
1494
 
    This test case covers the cases where _translate_error succeeds in
1495
 
    translating an ErrorFromSmartServer to something better.  See
1496
 
    TestErrorTranslationRobustness for other cases.
1497
 
    """
1498
 
 
1499
 
    def test_NoSuchRevision(self):
1500
 
        branch = self.make_branch('')
1501
 
        revid = 'revid'
1502
 
        translated_error = self.translateTuple(
1503
 
            ('NoSuchRevision', revid), branch=branch)
1504
 
        expected_error = errors.NoSuchRevision(branch, revid)
1505
 
        self.assertEqual(expected_error, translated_error)
1506
 
 
1507
 
    def test_nosuchrevision(self):
1508
 
        repository = self.make_repository('')
1509
 
        revid = 'revid'
1510
 
        translated_error = self.translateTuple(
1511
 
            ('nosuchrevision', revid), repository=repository)
1512
 
        expected_error = errors.NoSuchRevision(repository, revid)
1513
 
        self.assertEqual(expected_error, translated_error)
1514
 
 
1515
 
    def test_nobranch(self):
1516
 
        bzrdir = self.make_bzrdir('')
1517
 
        translated_error = self.translateTuple(('nobranch',), bzrdir=bzrdir)
1518
 
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
1519
 
        self.assertEqual(expected_error, translated_error)
1520
 
 
1521
 
    def test_LockContention(self):
1522
 
        translated_error = self.translateTuple(('LockContention',))
1523
 
        expected_error = errors.LockContention('(remote lock)')
1524
 
        self.assertEqual(expected_error, translated_error)
1525
 
 
1526
 
    def test_UnlockableTransport(self):
1527
 
        bzrdir = self.make_bzrdir('')
1528
 
        translated_error = self.translateTuple(
1529
 
            ('UnlockableTransport',), bzrdir=bzrdir)
1530
 
        expected_error = errors.UnlockableTransport(bzrdir.root_transport)
1531
 
        self.assertEqual(expected_error, translated_error)
1532
 
 
1533
 
    def test_LockFailed(self):
1534
 
        lock = 'str() of a server lock'
1535
 
        why = 'str() of why'
1536
 
        translated_error = self.translateTuple(('LockFailed', lock, why))
1537
 
        expected_error = errors.LockFailed(lock, why)
1538
 
        self.assertEqual(expected_error, translated_error)
1539
 
 
1540
 
    def test_TokenMismatch(self):
1541
 
        token = 'a lock token'
1542
 
        translated_error = self.translateTuple(('TokenMismatch',), token=token)
1543
 
        expected_error = errors.TokenMismatch(token, '(remote token)')
1544
 
        self.assertEqual(expected_error, translated_error)
1545
 
 
1546
 
    def test_Diverged(self):
1547
 
        branch = self.make_branch('a')
1548
 
        other_branch = self.make_branch('b')
1549
 
        translated_error = self.translateTuple(
1550
 
            ('Diverged',), branch=branch, other_branch=other_branch)
1551
 
        expected_error = errors.DivergedBranches(branch, other_branch)
1552
 
        self.assertEqual(expected_error, translated_error)
1553
 
 
1554
 
 
1555
 
class TestErrorTranslationRobustness(TestErrorTranslationBase):
1556
 
    """Unit tests for bzrlib.remote._translate_error's robustness.
1557
 
    
1558
 
    TestErrorTranslationSuccess is for cases where _translate_error can
1559
 
    translate successfully.  This class about how _translate_err behaves when
1560
 
    it fails to translate: it re-raises the original error.
1561
 
    """
1562
 
 
1563
 
    def test_unrecognised_server_error(self):
1564
 
        """If the error code from the server is not recognised, the original
1565
 
        ErrorFromSmartServer is propagated unmodified.
1566
 
        """
1567
 
        error_tuple = ('An unknown error tuple',)
1568
 
        server_error = errors.ErrorFromSmartServer(error_tuple)
1569
 
        translated_error = self.translateErrorFromSmartServer(server_error)
1570
 
        expected_error = errors.UnknownErrorFromSmartServer(server_error)
1571
 
        self.assertEqual(expected_error, translated_error)
1572
 
 
1573
 
    def test_context_missing_a_key(self):
1574
 
        """In case of a bug in the client, or perhaps an unexpected response
1575
 
        from a server, _translate_error returns the original error tuple from
1576
 
        the server and mutters a warning.
1577
 
        """
1578
 
        # To translate a NoSuchRevision error _translate_error needs a 'branch'
1579
 
        # in the context dict.  So let's give it an empty context dict instead
1580
 
        # to exercise its error recovery.
1581
 
        empty_context = {}
1582
 
        error_tuple = ('NoSuchRevision', 'revid')
1583
 
        server_error = errors.ErrorFromSmartServer(error_tuple)
1584
 
        translated_error = self.translateErrorFromSmartServer(server_error)
1585
 
        self.assertEqual(server_error, translated_error)
1586
 
        # In addition to re-raising ErrorFromSmartServer, some debug info has
1587
 
        # been muttered to the log file for developer to look at.
1588
 
        self.assertContainsRe(
1589
 
            self._get_log(keep_log_file=True),
1590
 
            "Missing key 'branch' in context")
 
1158
class TestRepositoryStreamKnitData(TestRemoteRepository):
 
1159
 
 
1160
    def make_pack_file(self, records):
 
1161
        pack_file = StringIO()
 
1162
        pack_writer = pack.ContainerWriter(pack_file.write)
 
1163
        pack_writer.begin()
 
1164
        for bytes, names in records:
 
1165
            pack_writer.add_bytes_record(bytes, names)
 
1166
        pack_writer.end()
 
1167
        pack_file.seek(0)
 
1168
        return pack_file
 
1169
 
 
1170
    def make_pack_stream(self, records):
 
1171
        pack_serialiser = pack.ContainerSerialiser()
 
1172
        yield pack_serialiser.begin()
 
1173
        for bytes, names in records:
 
1174
            yield pack_serialiser.bytes_record(bytes, names)
 
1175
        yield pack_serialiser.end()
 
1176
 
 
1177
    def test_bad_pack_from_server(self):
 
1178
        """A response with invalid data (e.g. it has a record with multiple
 
1179
        names) triggers an exception.
1591
1180
        
1592
 
 
1593
 
class TestStacking(tests.TestCaseWithTransport):
1594
 
    """Tests for operations on stacked remote repositories.
 
1181
        Not all possible errors will be caught at this stage, but obviously
 
1182
        malformed data should be.
 
1183
        """
 
1184
        record = ('bytes', [('name1',), ('name2',)])
 
1185
        pack_stream = self.make_pack_stream([record])
 
1186
        transport_path = 'quack'
 
1187
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
1188
        client.add_success_response_with_body(pack_stream, 'ok')
 
1189
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
 
1190
        stream = repo.get_data_stream_for_search(search)
 
1191
        self.assertRaises(errors.SmartProtocolError, list, stream)
1595
1192
    
1596
 
    The underlying format type must support stacking.
1597
 
    """
1598
 
 
1599
 
    def test_access_stacked_remote(self):
1600
 
        # based on <http://launchpad.net/bugs/261315>
1601
 
        # make a branch stacked on another repository containing an empty
1602
 
        # revision, then open it over hpss - we should be able to see that
1603
 
        # revision.
1604
 
        base_transport = self.get_transport()
1605
 
        base_builder = self.make_branch_builder('base', format='1.6')
1606
 
        base_builder.start_series()
1607
 
        base_revid = base_builder.build_snapshot('rev-id', None,
1608
 
            [('add', ('', None, 'directory', None))],
1609
 
            'message')
1610
 
        base_builder.finish_series()
1611
 
        stacked_branch = self.make_branch('stacked', format='1.6')
1612
 
        stacked_branch.set_stacked_on_url('../base')
1613
 
        # start a server looking at this
1614
 
        smart_server = server.SmartTCPServer_for_testing()
1615
 
        smart_server.setUp()
1616
 
        self.addCleanup(smart_server.tearDown)
1617
 
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
1618
 
        # can get its branch and repository
1619
 
        remote_branch = remote_bzrdir.open_branch()
1620
 
        remote_repo = remote_branch.repository
1621
 
        remote_repo.lock_read()
1622
 
        try:
1623
 
            # it should have an appropriate fallback repository, which should also
1624
 
            # be a RemoteRepository
1625
 
            self.assertEquals(len(remote_repo._fallback_repositories), 1)
1626
 
            self.assertIsInstance(remote_repo._fallback_repositories[0],
1627
 
                RemoteRepository)
1628
 
            # and it has the revision committed to the underlying repository;
1629
 
            # these have varying implementations so we try several of them
1630
 
            self.assertTrue(remote_repo.has_revisions([base_revid]))
1631
 
            self.assertTrue(remote_repo.has_revision(base_revid))
1632
 
            self.assertEqual(remote_repo.get_revision(base_revid).message,
1633
 
                'message')
1634
 
        finally:
1635
 
            remote_repo.unlock()
 
1193
    def test_backwards_compatibility(self):
 
1194
        """If the server doesn't recognise this request, fallback to VFS."""
 
1195
        repo, client = self.setup_fake_client_and_repository('path')
 
1196
        client.add_unknown_method_response(
 
1197
            'Repository.stream_revisions_chunked')
 
1198
        self.mock_called = False
 
1199
        repo._real_repository = MockRealRepository(self)
 
1200
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
 
1201
        repo.get_data_stream_for_search(search)
 
1202
        self.assertTrue(self.mock_called)
 
1203
        self.failIf(client.expecting_body,
 
1204
            "The protocol has been left in an unclean state that will cause "
 
1205
            "TooManyConcurrentRequests errors.")
 
1206
 
 
1207
 
 
1208
class MockRealRepository(object):
 
1209
    """Helper class for TestRepositoryStreamKnitData.test_unknown_method."""
 
1210
 
 
1211
    def __init__(self, test):
 
1212
        self.test = test
 
1213
 
 
1214
    def get_data_stream_for_search(self, search):
 
1215
        self.test.assertEqual(set(['revid']), search.get_keys())
 
1216
        self.test.mock_called = True
 
1217
 
 
1218