/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 bzrlib/tests/test_remote.py

  • Committer: Andrew Bennetts
  • Date: 2008-01-21 00:46:32 UTC
  • mfrom: (3193 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3194.
  • Revision ID: andrew.bennetts@canonical.com-20080121004632-wvpox6g9j7czn4vk
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from bzrlib import (
29
29
    bzrdir,
30
30
    errors,
 
31
    graph,
31
32
    pack,
32
33
    remote,
33
34
    repository,
108
109
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
109
110
 
110
111
    def __init__(self, body, fake_client):
111
 
        self._body_buffer = StringIO(body)
 
112
        self.body = body
 
113
        self._body_buffer = None
112
114
        self._fake_client = fake_client
113
115
 
114
116
    def read_body_bytes(self, count=-1):
 
117
        if self._body_buffer is None:
 
118
            self._body_buffer = StringIO(self.body)
115
119
        bytes = self._body_buffer.read(count)
116
120
        if self._body_buffer.tell() == len(self._body_buffer.getvalue()):
117
121
            self._fake_client.expecting_body = False
120
124
    def cancel_read_body(self):
121
125
        self._fake_client.expecting_body = False
122
126
 
 
127
    def read_streamed_body(self):
 
128
        return self.body
 
129
 
123
130
 
124
131
class FakeClient(_SmartClient):
125
132
    """Lookalike for _SmartClient allowing testing."""
126
133
    
127
 
    def __init__(self, responses):
128
 
        # We don't call the super init because there is no medium.
 
134
    def __init__(self, responses, fake_medium_base='fake base'):
129
135
        """Create a FakeClient.
130
136
 
131
 
        :param respones: A list of response-tuple, body-data pairs to be sent
 
137
        :param responses: A list of response-tuple, body-data pairs to be sent
132
138
            back to callers.
133
139
        """
134
140
        self.responses = responses
135
141
        self._calls = []
136
142
        self.expecting_body = False
 
143
        _SmartClient.__init__(self, FakeMedium(fake_medium_base))
137
144
 
138
145
    def call(self, method, *args):
139
146
        self._calls.append(('call', method, args))
145
152
        self.expecting_body = True
146
153
        return result[0], FakeProtocol(result[1], self)
147
154
 
 
155
    def call_with_body_bytes_expecting_body(self, method, args, body):
 
156
        self._calls.append(('call_with_body_bytes_expecting_body', method,
 
157
            args, body))
 
158
        result = self.responses.pop(0)
 
159
        self.expecting_body = True
 
160
        return result[0], FakeProtocol(result[1], self)
 
161
 
 
162
 
 
163
class FakeMedium(object):
 
164
 
 
165
    def __init__(self, base):
 
166
        self.base = base
 
167
 
148
168
 
149
169
class TestBzrDirOpenBranch(tests.TestCase):
150
170
 
151
171
    def test_branch_present(self):
152
 
        client = FakeClient([(('ok', ''), ), (('ok', '', 'no', 'no'), )])
153
172
        transport = MemoryTransport()
154
173
        transport.mkdir('quack')
155
174
        transport = transport.clone('quack')
 
175
        client = FakeClient([(('ok', ''), ), (('ok', '', 'no', 'no'), )],
 
176
                            transport.base)
156
177
        bzrdir = RemoteBzrDir(transport, _client=client)
157
178
        result = bzrdir.open_branch()
158
179
        self.assertEqual(
159
 
            [('call', 'BzrDir.open_branch', ('///quack/',)),
160
 
             ('call', 'BzrDir.find_repository', ('///quack/',))],
 
180
            [('call', 'BzrDir.open_branch', ('quack/',)),
 
181
             ('call', 'BzrDir.find_repository', ('quack/',))],
161
182
            client._calls)
162
183
        self.assertIsInstance(result, RemoteBranch)
163
184
        self.assertEqual(bzrdir, result.bzrdir)
164
185
 
165
186
    def test_branch_missing(self):
166
 
        client = FakeClient([(('nobranch',), )])
167
187
        transport = MemoryTransport()
168
188
        transport.mkdir('quack')
169
189
        transport = transport.clone('quack')
 
190
        client = FakeClient([(('nobranch',), )], transport.base)
170
191
        bzrdir = RemoteBzrDir(transport, _client=client)
171
192
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
172
193
        self.assertEqual(
173
 
            [('call', 'BzrDir.open_branch', ('///quack/',))],
 
194
            [('call', 'BzrDir.open_branch', ('quack/',))],
174
195
            client._calls)
175
196
 
176
197
    def check_open_repository(self, rich_root, subtrees):
 
198
        transport = MemoryTransport()
 
199
        transport.mkdir('quack')
 
200
        transport = transport.clone('quack')
177
201
        if rich_root:
178
202
            rich_response = 'yes'
179
203
        else:
182
206
            subtree_response = 'yes'
183
207
        else:
184
208
            subtree_response = 'no'
185
 
        client = FakeClient([(('ok', '', rich_response, subtree_response), ),])
186
 
        transport = MemoryTransport()
187
 
        transport.mkdir('quack')
188
 
        transport = transport.clone('quack')
 
209
        client = FakeClient([(('ok', '', rich_response, subtree_response), ),],
 
210
                            transport.base)
189
211
        bzrdir = RemoteBzrDir(transport, _client=client)
190
212
        result = bzrdir.open_repository()
191
213
        self.assertEqual(
192
 
            [('call', 'BzrDir.find_repository', ('///quack/',))],
 
214
            [('call', 'BzrDir.find_repository', ('quack/',))],
193
215
            client._calls)
194
216
        self.assertIsInstance(result, RemoteRepository)
195
217
        self.assertEqual(bzrdir, result.bzrdir)
239
261
 
240
262
    def test_empty_branch(self):
241
263
        # in an empty branch we decode the response properly
242
 
        client = FakeClient([(('ok', '0', 'null:'), )])
243
264
        transport = MemoryTransport()
 
265
        client = FakeClient([(('ok', '0', 'null:'), )], transport.base)
244
266
        transport.mkdir('quack')
245
267
        transport = transport.clone('quack')
246
268
        # we do not want bzrdir to make any remote calls
249
271
        result = branch.last_revision_info()
250
272
 
251
273
        self.assertEqual(
252
 
            [('call', 'Branch.last_revision_info', ('///quack/',))],
 
274
            [('call', 'Branch.last_revision_info', ('quack/',))],
253
275
            client._calls)
254
276
        self.assertEqual((0, NULL_REVISION), result)
255
277
 
256
278
    def test_non_empty_branch(self):
257
279
        # in a non-empty branch we also decode the response properly
258
280
        revid = u'\xc8'.encode('utf8')
259
 
        client = FakeClient([(('ok', '2', revid), )])
260
281
        transport = MemoryTransport()
 
282
        client = FakeClient([(('ok', '2', revid), )], transport.base)
261
283
        transport.mkdir('kwaak')
262
284
        transport = transport.clone('kwaak')
263
285
        # we do not want bzrdir to make any remote calls
266
288
        result = branch.last_revision_info()
267
289
 
268
290
        self.assertEqual(
269
 
            [('call', 'Branch.last_revision_info', ('///kwaak/',))],
 
291
            [('call', 'Branch.last_revision_info', ('kwaak/',))],
270
292
            client._calls)
271
293
        self.assertEqual((2, revid), result)
272
294
 
276
298
    def test_set_empty(self):
277
299
        # set_revision_history([]) is translated to calling
278
300
        # Branch.set_last_revision(path, '') on the wire.
 
301
        transport = MemoryTransport()
 
302
        transport.mkdir('branch')
 
303
        transport = transport.clone('branch')
 
304
 
279
305
        client = FakeClient([
280
306
            # lock_write
281
307
            (('ok', 'branch token', 'repo token'), ),
282
308
            # set_last_revision
283
309
            (('ok',), ),
284
310
            # unlock
285
 
            (('ok',), )])
286
 
        transport = MemoryTransport()
287
 
        transport.mkdir('branch')
288
 
        transport = transport.clone('branch')
289
 
 
 
311
            (('ok',), )],
 
312
            transport.base)
290
313
        bzrdir = RemoteBzrDir(transport, _client=False)
291
314
        branch = RemoteBranch(bzrdir, None, _client=client)
292
315
        # This is a hack to work around the problem that RemoteBranch currently
297
320
        result = branch.set_revision_history([])
298
321
        self.assertEqual(
299
322
            [('call', 'Branch.set_last_revision',
300
 
                ('///branch/', 'branch token', 'repo token', 'null:'))],
 
323
                ('branch/', 'branch token', 'repo token', 'null:'))],
301
324
            client._calls)
302
325
        branch.unlock()
303
326
        self.assertEqual(None, result)
305
328
    def test_set_nonempty(self):
306
329
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
307
330
        # Branch.set_last_revision(path, rev-idN) on the wire.
 
331
        transport = MemoryTransport()
 
332
        transport.mkdir('branch')
 
333
        transport = transport.clone('branch')
 
334
 
308
335
        client = FakeClient([
309
336
            # lock_write
310
337
            (('ok', 'branch token', 'repo token'), ),
311
338
            # set_last_revision
312
339
            (('ok',), ),
313
340
            # unlock
314
 
            (('ok',), )])
315
 
        transport = MemoryTransport()
316
 
        transport.mkdir('branch')
317
 
        transport = transport.clone('branch')
318
 
 
 
341
            (('ok',), )],
 
342
            transport.base)
319
343
        bzrdir = RemoteBzrDir(transport, _client=False)
320
344
        branch = RemoteBranch(bzrdir, None, _client=client)
321
345
        # This is a hack to work around the problem that RemoteBranch currently
328
352
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
329
353
        self.assertEqual(
330
354
            [('call', 'Branch.set_last_revision',
331
 
                ('///branch/', 'branch token', 'repo token', 'rev-id2'))],
 
355
                ('branch/', 'branch token', 'repo token', 'rev-id2'))],
332
356
            client._calls)
333
357
        branch.unlock()
334
358
        self.assertEqual(None, result)
368
392
 
369
393
    def test_get_branch_conf(self):
370
394
        # in an empty branch we decode the response properly
371
 
        client = FakeClient([(('ok', ), 'config file body')])
 
395
        client = FakeClient([(('ok', ), 'config file body')], self.get_url())
372
396
        # we need to make a real branch because the remote_branch.control_files
373
397
        # will trigger _ensure_real.
374
398
        branch = self.make_branch('quack')
378
402
        branch = RemoteBranch(bzrdir, None, _client=client)
379
403
        result = branch.control_files.get('branch.conf')
380
404
        self.assertEqual(
381
 
            [('call_expecting_body', 'Branch.get_config_file', ('///quack/',))],
 
405
            [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
382
406
            client._calls)
383
407
        self.assertEqual('config file body', result.read())
384
408
 
386
410
class TestBranchLockWrite(tests.TestCase):
387
411
 
388
412
    def test_lock_write_unlockable(self):
389
 
        client = FakeClient([(('UnlockableTransport', ), '')])
390
413
        transport = MemoryTransport()
 
414
        client = FakeClient([(('UnlockableTransport', ), '')], transport.base)
391
415
        transport.mkdir('quack')
392
416
        transport = transport.clone('quack')
393
417
        # we do not want bzrdir to make any remote calls
395
419
        branch = RemoteBranch(bzrdir, None, _client=client)
396
420
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
397
421
        self.assertEqual(
398
 
            [('call', 'Branch.lock_write', ('///quack/', '', ''))],
 
422
            [('call', 'Branch.lock_write', ('quack/', '', ''))],
399
423
            client._calls)
400
424
 
401
425
 
468
492
        :param transport_path: Path below the root of the MemoryTransport
469
493
            where the repository will be created.
470
494
        """
471
 
        client = FakeClient(responses)
472
495
        transport = MemoryTransport()
473
496
        transport.mkdir(transport_path)
 
497
        client = FakeClient(responses, transport.base)
474
498
        transport = transport.clone(transport_path)
475
499
        # we do not want bzrdir to make any remote calls
476
500
        bzrdir = RemoteBzrDir(transport, _client=False)
489
513
        result = repo.gather_stats(None)
490
514
        self.assertEqual(
491
515
            [('call_expecting_body', 'Repository.gather_stats',
492
 
             ('///quack/','','no'))],
 
516
             ('quack/','','no'))],
493
517
            client._calls)
494
518
        self.assertEqual({'revisions': 2, 'size': 18}, result)
495
519
 
507
531
        result = repo.gather_stats(revid)
508
532
        self.assertEqual(
509
533
            [('call_expecting_body', 'Repository.gather_stats',
510
 
              ('///quick/', revid, 'no'))],
 
534
              ('quick/', revid, 'no'))],
511
535
            client._calls)
512
536
        self.assertEqual({'revisions': 2, 'size': 18,
513
537
                          'firstrev': (123456.300, 3600),
529
553
        result = repo.gather_stats(revid, True)
530
554
        self.assertEqual(
531
555
            [('call_expecting_body', 'Repository.gather_stats',
532
 
              ('///buick/', revid, 'yes'))],
 
556
              ('buick/', revid, 'yes'))],
533
557
            client._calls)
534
558
        self.assertEqual({'revisions': 2, 'size': 18,
535
559
                          'committers': 128,
538
562
                         result)
539
563
 
540
564
 
 
565
class TestRepositoryGetGraph(TestRemoteRepository):
 
566
 
 
567
    def test_get_graph(self):
 
568
        # get_graph returns a graph with the repository as the
 
569
        # parents_provider.
 
570
        responses = []
 
571
        transport_path = 'quack'
 
572
        repo, client = self.setup_fake_client_and_repository(
 
573
            responses, transport_path)
 
574
        graph = repo.get_graph()
 
575
        self.assertEqual(graph._parents_provider, repo)
 
576
 
 
577
 
 
578
class TestRepositoryGetParentMap(TestRemoteRepository):
 
579
 
 
580
    def test_get_parent_map_caching(self):
 
581
        # get_parent_map returns from cache until unlock()
 
582
        # setup a reponse with two revisions
 
583
        r1 = u'\u0e33'.encode('utf8')
 
584
        r2 = u'\u0dab'.encode('utf8')
 
585
        lines = [' '.join([r2, r1]), r1]
 
586
        encoded_body = '\n'.join(lines)
 
587
        responses = [(('ok', ), encoded_body), (('ok', ), encoded_body)]
 
588
 
 
589
        transport_path = 'quack'
 
590
        repo, client = self.setup_fake_client_and_repository(
 
591
            responses, transport_path)
 
592
        repo.lock_read()
 
593
        graph = repo.get_graph()
 
594
        parents = graph.get_parent_map([r2])
 
595
        self.assertEqual({r2: (r1,)}, parents)
 
596
        # locking and unlocking deeper should not reset
 
597
        repo.lock_read()
 
598
        repo.unlock()
 
599
        parents = graph.get_parent_map([r1])
 
600
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
 
601
        self.assertEqual(
 
602
            [('call_expecting_body', 'Repository.get_parent_map',
 
603
             ('quack/', r2))],
 
604
            client._calls)
 
605
        repo.unlock()
 
606
        # now we call again, and it should use the second response.
 
607
        repo.lock_read()
 
608
        graph = repo.get_graph()
 
609
        parents = graph.get_parent_map([r1])
 
610
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
 
611
        self.assertEqual(
 
612
            [('call_expecting_body', 'Repository.get_parent_map',
 
613
              ('quack/', r2)),
 
614
             ('call_expecting_body', 'Repository.get_parent_map',
 
615
              ('quack/', r1))
 
616
            ],
 
617
            client._calls)
 
618
        repo.unlock()
 
619
 
 
620
 
541
621
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
542
622
    
543
623
    def test_null_revision(self):
565
645
        result = repo.get_revision_graph()
566
646
        self.assertEqual(
567
647
            [('call_expecting_body', 'Repository.get_revision_graph',
568
 
             ('///sinhala/', ''))],
 
648
             ('sinhala/', ''))],
569
649
            client._calls)
570
650
        self.assertEqual({r1: (), r2: (r1, )}, result)
571
651
 
585
665
        result = repo.get_revision_graph(r2)
586
666
        self.assertEqual(
587
667
            [('call_expecting_body', 'Repository.get_revision_graph',
588
 
             ('///sinhala/', r2))],
 
668
             ('sinhala/', r2))],
589
669
            client._calls)
590
670
        self.assertEqual({r11: (), r12: (), r2: (r11, r12), }, result)
591
671
 
600
680
            repo.get_revision_graph, revid)
601
681
        self.assertEqual(
602
682
            [('call_expecting_body', 'Repository.get_revision_graph',
603
 
             ('///sinhala/', revid))],
 
683
             ('sinhala/', revid))],
604
684
            client._calls)
605
685
 
606
686
        
614
694
            responses, transport_path)
615
695
        result = repo.is_shared()
616
696
        self.assertEqual(
617
 
            [('call', 'Repository.is_shared', ('///quack/',))],
 
697
            [('call', 'Repository.is_shared', ('quack/',))],
618
698
            client._calls)
619
699
        self.assertEqual(True, result)
620
700
 
626
706
            responses, transport_path)
627
707
        result = repo.is_shared()
628
708
        self.assertEqual(
629
 
            [('call', 'Repository.is_shared', ('///qwack/',))],
 
709
            [('call', 'Repository.is_shared', ('qwack/',))],
630
710
            client._calls)
631
711
        self.assertEqual(False, result)
632
712
 
640
720
            responses, transport_path)
641
721
        result = repo.lock_write()
642
722
        self.assertEqual(
643
 
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
723
            [('call', 'Repository.lock_write', ('quack/', ''))],
644
724
            client._calls)
645
725
        self.assertEqual('a token', result)
646
726
 
651
731
            responses, transport_path)
652
732
        self.assertRaises(errors.LockContention, repo.lock_write)
653
733
        self.assertEqual(
654
 
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
734
            [('call', 'Repository.lock_write', ('quack/', ''))],
655
735
            client._calls)
656
736
 
657
737
    def test_lock_write_unlockable(self):
661
741
            responses, transport_path)
662
742
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
663
743
        self.assertEqual(
664
 
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
744
            [('call', 'Repository.lock_write', ('quack/', ''))],
665
745
            client._calls)
666
746
 
667
747
 
676
756
        repo.lock_write()
677
757
        repo.unlock()
678
758
        self.assertEqual(
679
 
            [('call', 'Repository.lock_write', ('///quack/', '')),
680
 
             ('call', 'Repository.unlock', ('///quack/', 'a token'))],
 
759
            [('call', 'Repository.lock_write', ('quack/', '')),
 
760
             ('call', 'Repository.unlock', ('quack/', 'a token'))],
681
761
            client._calls)
682
762
 
683
763
    def test_unlock_wrong_token(self):
701
781
            responses, transport_path)
702
782
 
703
783
        # The null revision is always there, so has_revision(None) == True.
704
 
        self.assertEqual(True, repo.has_revision(None))
 
784
        self.assertEqual(True, repo.has_revision(NULL_REVISION))
705
785
 
706
786
        # The remote repo shouldn't be accessed.
707
787
        self.assertEqual([], client._calls)
731
811
        expected_responses = [(('ok',), self.tarball_content),
732
812
            ]
733
813
        expected_calls = [('call_expecting_body', 'Repository.tarball',
734
 
                           ('///repo/', 'bz2',),),
 
814
                           ('repo/', 'bz2',),),
735
815
            ]
736
816
        remote_repo, client = self.setup_fake_client_and_repository(
737
817
            expected_responses, transport_path)
775
855
        pack_file.seek(0)
776
856
        return pack_file
777
857
 
 
858
    def make_pack_stream(self, records):
 
859
        pack_serialiser = pack.ContainerSerialiser()
 
860
        yield pack_serialiser.begin()
 
861
        for bytes, names in records:
 
862
            yield pack_serialiser.bytes_record(bytes, names)
 
863
        yield pack_serialiser.end()
 
864
 
778
865
    def test_bad_pack_from_server(self):
779
866
        """A response with invalid data (e.g. it has a record with multiple
780
867
        names) triggers an exception.
783
870
        malformed data should be.
784
871
        """
785
872
        record = ('bytes', [('name1',), ('name2',)])
786
 
        pack_file = self.make_pack_file([record])
787
 
        responses = [(('ok',), pack_file.getvalue()), ]
 
873
        pack_stream = self.make_pack_stream([record])
 
874
        responses = [(('ok',), pack_stream), ]
788
875
        transport_path = 'quack'
789
876
        repo, client = self.setup_fake_client_and_repository(
790
877
            responses, transport_path)
791
 
        stream = repo.get_data_stream(['revid'])
 
878
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
 
879
        stream = repo.get_data_stream_for_search(search)
792
880
        self.assertRaises(errors.SmartProtocolError, list, stream)
793
881
    
794
882
    def test_backwards_compatibility(self):
795
883
        """If the server doesn't recognise this request, fallback to VFS."""
796
884
        error_msg = (
797
885
            "Generic bzr smart protocol error: "
798
 
            "bad request 'Repository.stream_knit_data_for_revisions'")
 
886
            "bad request 'Repository.stream_revisions_chunked'")
799
887
        responses = [
800
888
            (('error', error_msg), '')]
801
889
        repo, client = self.setup_fake_client_and_repository(
802
890
            responses, 'path')
803
891
        self.mock_called = False
804
892
        repo._real_repository = MockRealRepository(self)
805
 
        repo.get_data_stream(['revid'])
 
893
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
 
894
        repo.get_data_stream_for_search(search)
806
895
        self.assertTrue(self.mock_called)
807
896
        self.failIf(client.expecting_body,
808
897
            "The protocol has been left in an unclean state that will cause "
815
904
    def __init__(self, test):
816
905
        self.test = test
817
906
 
818
 
    def get_data_stream(self, revision_ids):
819
 
        self.test.assertEqual(['revid'], revision_ids)
 
907
    def get_data_stream_for_search(self, search):
 
908
        self.test.assertEqual(set(['revid']), search.get_keys())
820
909
        self.test.mock_called = True
821
910
 
822
911