/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_fetch.py

  • Committer: Ian Clatworthy
  • Date: 2008-12-15 06:18:29 UTC
  • mfrom: (3905 +trunk)
  • mto: (3586.1.23 views-ui)
  • mto: This revision was merged to the branch mainline in revision 4030.
  • Revision ID: ian.clatworthy@canonical.com-20081215061829-c8qwa93g71u9fsh5
merge bzr.dev 3905

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
from bzrlib import (
23
23
    bzrdir,
24
24
    errors,
 
25
    osutils,
25
26
    merge,
26
27
    repository,
 
28
    versionedfile,
27
29
    )
28
30
from bzrlib.branch import Branch
29
31
from bzrlib.bzrdir import BzrDir
329
331
        self.assertEqual(4, len(http_logs))
330
332
 
331
333
 
 
334
class TestKnitToPackFetch(TestCaseWithTransport):
 
335
 
 
336
    def find_get_record_stream(self, calls):
 
337
        """In a list of calls, find 'get_record_stream' calls.
 
338
 
 
339
        This also ensures that there is only one get_record_stream call.
 
340
        """
 
341
        get_record_call = None
 
342
        for call in calls:
 
343
            if call[0] == 'get_record_stream':
 
344
                self.assertIs(None, get_record_call,
 
345
                              "there should only be one call to"
 
346
                              " get_record_stream")
 
347
                get_record_call = call
 
348
        self.assertIsNot(None, get_record_call,
 
349
                         "there should be exactly one call to "
 
350
                         " get_record_stream")
 
351
        return get_record_call
 
352
 
 
353
    def test_fetch_with_deltas_no_delta_closure(self):
 
354
        tree = self.make_branch_and_tree('source', format='dirstate')
 
355
        target = self.make_repository('target', format='pack-0.92')
 
356
        self.build_tree(['source/file'])
 
357
        tree.set_root_id('root-id')
 
358
        tree.add('file', 'file-id')
 
359
        tree.commit('one', rev_id='rev-one')
 
360
        source = tree.branch.repository
 
361
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
 
362
                        source.texts)
 
363
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
 
364
                        source.signatures)
 
365
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
 
366
                        source.revisions)
 
367
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
 
368
                        source.inventories)
 
369
        # precondition
 
370
        self.assertTrue(target._fetch_uses_deltas)
 
371
        target.fetch(source, revision_id='rev-one')
 
372
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
 
373
                          target._fetch_order, False),
 
374
                         self.find_get_record_stream(source.texts.calls))
 
375
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
376
                          target._fetch_order, False),
 
377
                         self.find_get_record_stream(source.inventories.calls))
 
378
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
379
                          target._fetch_order, False),
 
380
                         self.find_get_record_stream(source.revisions.calls))
 
381
        # XXX: Signatures is special, and slightly broken. The
 
382
        # standard item_keys_introduced_by actually does a lookup for every
 
383
        # signature to see if it exists, rather than waiting to do them all at
 
384
        # once at the end. The fetch code then does an all-at-once and just
 
385
        # allows for some of them to be missing.
 
386
        # So we know there will be extra calls, but the *last* one is the one
 
387
        # we care about.
 
388
        signature_calls = source.signatures.calls[-1:]
 
389
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
390
                          target._fetch_order, False),
 
391
                         self.find_get_record_stream(signature_calls))
 
392
 
 
393
    def test_fetch_no_deltas_with_delta_closure(self):
 
394
        tree = self.make_branch_and_tree('source', format='dirstate')
 
395
        target = self.make_repository('target', format='pack-0.92')
 
396
        self.build_tree(['source/file'])
 
397
        tree.set_root_id('root-id')
 
398
        tree.add('file', 'file-id')
 
399
        tree.commit('one', rev_id='rev-one')
 
400
        source = tree.branch.repository
 
401
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
 
402
                        source.texts)
 
403
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
 
404
                        source.signatures)
 
405
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
 
406
                        source.revisions)
 
407
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
 
408
                        source.inventories)
 
409
        target._fetch_uses_deltas = False
 
410
        target.fetch(source, revision_id='rev-one')
 
411
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
 
412
                          target._fetch_order, True),
 
413
                         self.find_get_record_stream(source.texts.calls))
 
414
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
415
                          target._fetch_order, True),
 
416
                         self.find_get_record_stream(source.inventories.calls))
 
417
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
418
                          target._fetch_order, True),
 
419
                         self.find_get_record_stream(source.revisions.calls))
 
420
        # XXX: Signatures is special, and slightly broken. The
 
421
        # standard item_keys_introduced_by actually does a lookup for every
 
422
        # signature to see if it exists, rather than waiting to do them all at
 
423
        # once at the end. The fetch code then does an all-at-once and just
 
424
        # allows for some of them to be missing.
 
425
        # So we know there will be extra calls, but the *last* one is the one
 
426
        # we care about.
 
427
        signature_calls = source.signatures.calls[-1:]
 
428
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
429
                          target._fetch_order, True),
 
430
                         self.find_get_record_stream(signature_calls))
 
431
 
 
432
    def test_fetch_revisions_with_deltas_into_pack(self):
 
433
        # See BUG #261339, dev versions of bzr could accidentally create deltas
 
434
        # in revision texts in knit branches (when fetching from packs). So we
 
435
        # ensure that *if* a knit repository has a delta in revisions, that it
 
436
        # gets properly expanded back into a fulltext when stored in the pack
 
437
        # file.
 
438
        tree = self.make_branch_and_tree('source', format='dirstate')
 
439
        target = self.make_repository('target', format='pack-0.92')
 
440
        self.build_tree(['source/file'])
 
441
        tree.set_root_id('root-id')
 
442
        tree.add('file', 'file-id')
 
443
        tree.commit('one', rev_id='rev-one')
 
444
        # Hack the KVF for revisions so that it "accidentally" allows a delta
 
445
        tree.branch.repository.revisions._max_delta_chain = 200
 
446
        tree.commit('two', rev_id='rev-two')
 
447
        source = tree.branch.repository
 
448
        # Ensure that we stored a delta
 
449
        source.lock_read()
 
450
        self.addCleanup(source.unlock)
 
451
        record = source.revisions.get_record_stream([('rev-two',)],
 
452
            'unordered', False).next()
 
453
        self.assertEqual('knit-delta-gz', record.storage_kind)
 
454
        target.fetch(tree.branch.repository, revision_id='rev-two')
 
455
        # The record should get expanded back to a fulltext
 
456
        target.lock_read()
 
457
        self.addCleanup(target.unlock)
 
458
        record = target.revisions.get_record_stream([('rev-two',)],
 
459
            'unordered', False).next()
 
460
        self.assertEqual('knit-ft-gz', record.storage_kind)
 
461
 
 
462
    def test_fetch_with_fallback_and_merge(self):
 
463
        builder = self.make_branch_builder('source', format='pack-0.92')
 
464
        builder.start_series()
 
465
        # graph
 
466
        #   A
 
467
        #   |\
 
468
        #   B C
 
469
        #   | |
 
470
        #   | D
 
471
        #   | |
 
472
        #   | E
 
473
        #    \|
 
474
        #     F
 
475
        # A & B are present in the base (stacked-on) repository, A-E are
 
476
        # present in the source.
 
477
        # This reproduces bug #304841
 
478
        # We need a large enough inventory that total size of compressed deltas
 
479
        # is shorter than the size of a compressed fulltext. We have to use
 
480
        # random ids because otherwise the inventory fulltext compresses too
 
481
        # well and the deltas get bigger.
 
482
        to_add = [
 
483
            ('add', ('', 'TREE_ROOT', 'directory', None))]
 
484
        for i in xrange(10):
 
485
            fname = 'file%03d' % (i,)
 
486
            fileid = '%s-%s' % (fname, osutils.rand_chars(64))
 
487
            to_add.append(('add', (fname, fileid, 'file', 'content\n')))
 
488
        builder.build_snapshot('A', None, to_add)
 
489
        builder.build_snapshot('B', ['A'], [])
 
490
        builder.build_snapshot('C', ['A'], [])
 
491
        builder.build_snapshot('D', ['C'], [])
 
492
        builder.build_snapshot('E', ['D'], [])
 
493
        builder.build_snapshot('F', ['E', 'B'], [])
 
494
        builder.finish_series()
 
495
        source_branch = builder.get_branch()
 
496
        source_branch.bzrdir.sprout('base', revision_id='B')
 
497
        target_branch = self.make_branch('target', format='1.6')
 
498
        target_branch.set_stacked_on_url('../base')
 
499
        source = source_branch.repository
 
500
        source.lock_read()
 
501
        self.addCleanup(source.unlock)
 
502
        source.inventories = versionedfile.OrderingVersionedFilesDecorator(
 
503
                        source.inventories,
 
504
                        key_priority={('E',): 1, ('D',): 2, ('C',): 4,
 
505
                                      ('F',): 3})
 
506
        # Ensure that the content is yielded in the proper order, and given as
 
507
        # the expected kinds
 
508
        records = [(record.key, record.storage_kind)
 
509
                   for record in source.inventories.get_record_stream(
 
510
                        [('D',), ('C',), ('E',), ('F',)], 'unordered', False)]
 
511
        self.assertEqual([(('E',), 'knit-delta-gz'), (('D',), 'knit-delta-gz'),
 
512
                          (('F',), 'knit-delta-gz'), (('C',), 'knit-delta-gz')],
 
513
                          records)
 
514
 
 
515
        target_branch.lock_write()
 
516
        self.addCleanup(target_branch.unlock)
 
517
        target = target_branch.repository
 
518
        target.fetch(source, revision_id='F')
 
519
        # 'C' should be expanded to a fulltext, but D and E should still be
 
520
        # deltas
 
521
        stream = target.inventories.get_record_stream(
 
522
            [('C',), ('D',), ('E',), ('F',)],
 
523
            'unordered', False)
 
524
        kinds = dict((record.key, record.storage_kind) for record in stream)
 
525
        self.assertEqual({('C',): 'knit-ft-gz', ('D',): 'knit-delta-gz',
 
526
                          ('E',): 'knit-delta-gz', ('F',): 'knit-delta-gz'},
 
527
                         kinds)
 
528
 
 
529
 
332
530
class Test1To2Fetch(TestCaseWithTransport):
333
531
    """Tests for Model1To2 failure modes"""
334
532