~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Johan Walles
  • Date: 2009-05-07 05:08:46 UTC
  • mfrom: (4342 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4343.
  • Revision ID: johan.walles@gmail.com-20090507050846-nkwvcyauf1eh653q
Merge from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007 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
27
27
    inventory,
28
28
    merge,
29
29
    osutils,
 
30
    repository,
30
31
    revision as _mod_revision,
31
 
    symbol_versioning,
32
32
    tests,
33
33
    treebuilder,
34
34
    )
35
35
from bzrlib.bundle import read_mergeable_from_url
36
36
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
37
37
from bzrlib.bundle.bundle_data import BundleTree
 
38
from bzrlib.bzrdir import BzrDir
38
39
from bzrlib.directory_service import directories
39
40
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
40
41
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
41
42
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
42
43
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
 
44
from bzrlib.branch import Branch
43
45
from bzrlib.repofmt import knitrepo
44
46
from bzrlib.tests import (
45
47
    test_read_bundle,
48
50
from bzrlib.transform import TreeTransform
49
51
 
50
52
 
51
 
def get_text(vf, key):
52
 
    """Get the fulltext for a given revision id that is present in the vf"""
53
 
    stream = vf.get_record_stream([key], 'unordered', True)
54
 
    record = stream.next()
55
 
    return record.get_bytes_as('fulltext')
56
 
 
57
 
 
58
 
def get_inventory_text(repo, revision_id):
59
 
    """Get the fulltext for the inventory at revision id"""
60
 
    repo.lock_read()
61
 
    try:
62
 
        return get_text(repo.inventories, (revision_id,))
63
 
    finally:
64
 
        repo.unlock()
65
 
 
66
 
 
67
53
class MockTree(object):
68
 
 
69
54
    def __init__(self):
70
55
        from bzrlib.inventory import InventoryDirectory, ROOT_ID
71
56
        object.__init__(self)
76
61
 
77
62
    inventory = property(lambda x:x)
78
63
 
79
 
    def all_file_ids(self):
80
 
        return set(self.paths.keys())
 
64
    def __iter__(self):
 
65
        return self.paths.iterkeys()
81
66
 
82
67
    def __getitem__(self, file_id):
83
68
        if file_id == self.root.file_id:
113
98
            ie = InventoryDirectory(file_id, name, parent_id)
114
99
        elif kind == 'file':
115
100
            ie = InventoryFile(file_id, name, parent_id)
116
 
            ie.text_sha1 = text_sha_1
117
 
            ie.text_size = text_size
118
101
        elif kind == 'symlink':
119
102
            ie = InventoryLink(file_id, name, parent_id)
120
103
        else:
121
104
            raise errors.BzrError('unknown kind %r' % kind)
 
105
        ie.text_sha1 = text_sha_1
 
106
        ie.text_size = text_size
122
107
        return ie
123
108
 
124
109
    def add_dir(self, file_id, path):
144
129
        result.seek(0,0)
145
130
        return result
146
131
 
147
 
    def get_file_revision(self, file_id):
148
 
        return self.inventory[file_id].revision
149
 
 
150
132
    def contents_stats(self, file_id):
151
133
        if file_id not in self.contents:
152
134
            return None, None
494
476
                                 % (ancestor,))
495
477
 
496
478
                # Now check that the file contents are all correct
497
 
                for inventory_id in old.all_file_ids():
 
479
                for inventory_id in old:
498
480
                    try:
499
481
                        old_file = old.get_file(inventory_id)
500
482
                    except errors.NoSuchFile:
508
490
                old.unlock()
509
491
        if not _mod_revision.is_null(rev_id):
510
492
            rh = self.b1.revision_history()
511
 
            self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
512
 
                tree.branch.set_revision_history, rh[:rh.index(rev_id)+1])
 
493
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
513
494
            tree.update()
514
495
            delta = tree.changes_from(self.b1.repository.revision_tree(rev_id))
515
496
            self.assertFalse(delta.has_changed(),
577
558
        self.tree1 = self.make_branch_and_tree('b1')
578
559
        self.b1 = self.tree1.branch
579
560
 
580
 
        self.build_tree_contents([('b1/one', 'one\n')])
581
 
        self.tree1.add('one', 'one-id')
582
 
        self.tree1.set_root_id('root-id')
 
561
        open('b1/one', 'wb').write('one\n')
 
562
        self.tree1.add('one')
583
563
        self.tree1.commit('add one', rev_id='a@cset-0-1')
584
564
 
585
565
        bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
596
576
                , 'b1/sub/sub/'
597
577
                , 'b1/sub/sub/nonempty.txt'
598
578
                ])
599
 
        self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
600
 
                                  ('b1/dir/nolastnewline.txt', 'bloop')])
 
579
        open('b1/sub/sub/emptyfile.txt', 'wb').close()
 
580
        open('b1/dir/nolastnewline.txt', 'wb').write('bloop')
601
581
        tt = TreeTransform(self.tree1)
602
582
        tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
603
583
        tt.apply()
636
616
 
637
617
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
638
618
        self.assertRaises((errors.TestamentMismatch,
639
 
            errors.VersionedFileInvalidChecksum,
640
 
            errors.BadBundle), self.get_invalid_bundle,
 
619
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
641
620
            'a@cset-0-2', 'a@cset-0-3')
642
621
        # Check a rollup bundle
643
622
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
667
646
                          verbose=False)
668
647
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
669
648
        other = self.get_checkout('a@cset-0-5')
670
 
        tree1_inv = get_inventory_text(self.tree1.branch.repository,
671
 
                                       'a@cset-0-5')
672
 
        tree2_inv = get_inventory_text(other.branch.repository,
673
 
                                       'a@cset-0-5')
 
649
        tree1_inv = self.tree1.branch.repository.get_inventory_xml(
 
650
            'a@cset-0-5')
 
651
        tree2_inv = other.branch.repository.get_inventory_xml('a@cset-0-5')
674
652
        self.assertEqualDiff(tree1_inv, tree2_inv)
675
653
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
676
654
        other.commit('rename file', rev_id='a@cset-0-6b')
1042
1020
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1043
1021
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1044
1022
        bundle.install_revisions(repo)
1045
 
        inv_text = repo._get_inventory_xml('rev2')
 
1023
        inv_text = repo.get_inventory_xml('rev2')
1046
1024
        self.assertNotContainsRe(inv_text, 'format="5"')
1047
1025
        self.assertContainsRe(inv_text, 'format="7"')
1048
1026
 
1068
1046
 
1069
1047
    def test_inv_hash_across_serializers(self):
1070
1048
        repo = self.make_repo_with_installed_revisions()
1071
 
        recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1072
 
        xml = repo._get_inventory_xml('rev2')
 
1049
        recorded_inv_sha1 = repo.get_inventory_sha1('rev2')
 
1050
        xml = repo.get_inventory_xml('rev2')
1073
1051
        self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1074
1052
 
1075
1053
    def test_across_models_incompatible(self):
1339
1317
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1340
1318
        new_text = new_text.replace('<file file_id="exe-1"',
1341
1319
                                    '<file executable="y" file_id="exe-1"')
1342
 
        new_text = new_text.replace('B260', 'B275')
 
1320
        new_text = new_text.replace('B222', 'B237')
1343
1321
        bundle_txt = StringIO()
1344
1322
        bundle_txt.write(serializer._get_bundle_header('4'))
1345
1323
        bundle_txt.write('\n')
1415
1393
        branch = tree_a.branch
1416
1394
        repo_a = branch.repository
1417
1395
        tree_a.commit("base", allow_pointless=True, rev_id='A')
1418
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
1396
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1419
1397
        try:
1420
1398
            from bzrlib.testament import Testament
1421
1399
            # monkey patch gpg signing mechanism
1445
1423
        install_bundle(repo_b, serializer.read(s))
1446
1424
 
1447
1425
 
1448
 
class V4_2aBundleTester(V4BundleTester):
 
1426
class V4WeaveBundleTester(V4BundleTester):
1449
1427
 
1450
1428
    def bzrdir_format(self):
1451
 
        return '2a'
1452
 
 
1453
 
    def get_invalid_bundle(self, base_rev_id, rev_id):
1454
 
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1455
 
        Munge the text so that it's invalid.
1456
 
 
1457
 
        :return: The in-memory bundle
1458
 
        """
1459
 
        from bzrlib.bundle import serializer
1460
 
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1461
 
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1462
 
        # We are going to be replacing some text to set the executable bit on a
1463
 
        # file. Make sure the text replacement actually works correctly.
1464
 
        self.assertContainsRe(new_text, '(?m)B244\n\ni 1\n<inventory')
1465
 
        new_text = new_text.replace('<file file_id="exe-1"',
1466
 
                                    '<file executable="y" file_id="exe-1"')
1467
 
        new_text = new_text.replace('B244', 'B259')
1468
 
        bundle_txt = StringIO()
1469
 
        bundle_txt.write(serializer._get_bundle_header('4'))
1470
 
        bundle_txt.write('\n')
1471
 
        bundle_txt.write(new_text.encode('bz2'))
1472
 
        bundle_txt.seek(0)
1473
 
        bundle = read_bundle(bundle_txt)
1474
 
        self.valid_apply_bundle(base_rev_id, bundle)
1475
 
        return bundle
1476
 
 
1477
 
    def make_merged_branch(self):
1478
 
        builder = self.make_branch_builder('source')
1479
 
        builder.start_series()
1480
 
        builder.build_snapshot('a@cset-0-1', None, [
1481
 
            ('add', ('', 'root-id', 'directory', None)),
1482
 
            ('add', ('file', 'file-id', 'file', 'original content\n')),
1483
 
            ])
1484
 
        builder.build_snapshot('a@cset-0-2a', ['a@cset-0-1'], [
1485
 
            ('modify', ('file-id', 'new-content\n')),
1486
 
            ])
1487
 
        builder.build_snapshot('a@cset-0-2b', ['a@cset-0-1'], [
1488
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1489
 
            ])
1490
 
        builder.build_snapshot('a@cset-0-3', ['a@cset-0-2a', 'a@cset-0-2b'], [
1491
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1492
 
            ])
1493
 
        builder.finish_series()
1494
 
        self.b1 = builder.get_branch()
1495
 
        self.b1.lock_read()
1496
 
        self.addCleanup(self.b1.unlock)
1497
 
 
1498
 
    def make_bundle_just_inventories(self, base_revision_id,
1499
 
                                     target_revision_id,
1500
 
                                     revision_ids):
1501
 
        sio = StringIO()
1502
 
        writer = v4.BundleWriteOperation(base_revision_id, target_revision_id,
1503
 
                                         self.b1.repository, sio)
1504
 
        writer.bundle.begin()
1505
 
        writer._add_inventory_mpdiffs_from_serializer(revision_ids)
1506
 
        writer.bundle.end()
1507
 
        sio.seek(0)
1508
 
        return sio
1509
 
 
1510
 
    def test_single_inventory_multiple_parents_as_xml(self):
1511
 
        self.make_merged_branch()
1512
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1513
 
                                                ['a@cset-0-3'])
1514
 
        reader = v4.BundleReader(sio, stream_input=False)
1515
 
        records = list(reader.iter_records())
1516
 
        self.assertEqual(1, len(records))
1517
 
        (bytes, metadata, repo_kind, revision_id,
1518
 
         file_id) = records[0]
1519
 
        self.assertIs(None, file_id)
1520
 
        self.assertEqual('a@cset-0-3', revision_id)
1521
 
        self.assertEqual('inventory', repo_kind)
1522
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1523
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1524
 
                          'storage_kind': 'mpdiff',
1525
 
                         }, metadata)
1526
 
        # We should have an mpdiff that takes some lines from both parents.
1527
 
        self.assertEqualDiff(
1528
 
            'i 1\n'
1529
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1530
 
            '\n'
1531
 
            'c 0 1 1 2\n'
1532
 
            'c 1 3 3 2\n', bytes)
1533
 
 
1534
 
    def test_single_inv_no_parents_as_xml(self):
1535
 
        self.make_merged_branch()
1536
 
        sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1537
 
                                                ['a@cset-0-1'])
1538
 
        reader = v4.BundleReader(sio, stream_input=False)
1539
 
        records = list(reader.iter_records())
1540
 
        self.assertEqual(1, len(records))
1541
 
        (bytes, metadata, repo_kind, revision_id,
1542
 
         file_id) = records[0]
1543
 
        self.assertIs(None, file_id)
1544
 
        self.assertEqual('a@cset-0-1', revision_id)
1545
 
        self.assertEqual('inventory', repo_kind)
1546
 
        self.assertEqual({'parents': [],
1547
 
                          'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1548
 
                          'storage_kind': 'mpdiff',
1549
 
                         }, metadata)
1550
 
        # We should have an mpdiff that takes some lines from both parents.
1551
 
        self.assertEqualDiff(
1552
 
            'i 4\n'
1553
 
            '<inventory format="10" revision_id="a@cset-0-1">\n'
1554
 
            '<directory file_id="root-id" name=""'
1555
 
                ' revision="a@cset-0-1" />\n'
1556
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1557
 
                ' revision="a@cset-0-1"'
1558
 
                ' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1559
 
                ' text_size="17" />\n'
1560
 
            '</inventory>\n'
1561
 
            '\n', bytes)
1562
 
 
1563
 
    def test_multiple_inventories_as_xml(self):
1564
 
        self.make_merged_branch()
1565
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1566
 
            ['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'])
1567
 
        reader = v4.BundleReader(sio, stream_input=False)
1568
 
        records = list(reader.iter_records())
1569
 
        self.assertEqual(3, len(records))
1570
 
        revision_ids = [rev_id for b, m, k, rev_id, f in records]
1571
 
        self.assertEqual(['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'],
1572
 
                         revision_ids)
1573
 
        metadata_2a = records[0][1]
1574
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1575
 
                          'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1576
 
                          'storage_kind': 'mpdiff',
1577
 
                         }, metadata_2a)
1578
 
        metadata_2b = records[1][1]
1579
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1580
 
                          'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1581
 
                          'storage_kind': 'mpdiff',
1582
 
                         }, metadata_2b)
1583
 
        metadata_3 = records[2][1]
1584
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1585
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1586
 
                          'storage_kind': 'mpdiff',
1587
 
                         }, metadata_3)
1588
 
        bytes_2a = records[0][0]
1589
 
        self.assertEqualDiff(
1590
 
            'i 1\n'
1591
 
            '<inventory format="10" revision_id="a@cset-0-2a">\n'
1592
 
            '\n'
1593
 
            'c 0 1 1 1\n'
1594
 
            'i 1\n'
1595
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1596
 
                ' revision="a@cset-0-2a"'
1597
 
                ' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1598
 
                ' text_size="12" />\n'
1599
 
            '\n'
1600
 
            'c 0 3 3 1\n', bytes_2a)
1601
 
        bytes_2b = records[1][0]
1602
 
        self.assertEqualDiff(
1603
 
            'i 1\n'
1604
 
            '<inventory format="10" revision_id="a@cset-0-2b">\n'
1605
 
            '\n'
1606
 
            'c 0 1 1 2\n'
1607
 
            'i 1\n'
1608
 
            '<file file_id="file2-id" name="other-file" parent_id="root-id"'
1609
 
                ' revision="a@cset-0-2b"'
1610
 
                ' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1611
 
                ' text_size="14" />\n'
1612
 
            '\n'
1613
 
            'c 0 3 4 1\n', bytes_2b)
1614
 
        bytes_3 = records[2][0]
1615
 
        self.assertEqualDiff(
1616
 
            'i 1\n'
1617
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1618
 
            '\n'
1619
 
            'c 0 1 1 2\n'
1620
 
            'c 1 3 3 2\n', bytes_3)
1621
 
 
1622
 
    def test_creating_bundle_preserves_chk_pages(self):
1623
 
        self.make_merged_branch()
1624
 
        target = self.b1.bzrdir.sprout('target',
1625
 
                                       revision_id='a@cset-0-2a').open_branch()
1626
 
        bundle_txt, rev_ids = self.create_bundle_text('a@cset-0-2a',
1627
 
                                                      'a@cset-0-3')
1628
 
        self.assertEqual(['a@cset-0-2b', 'a@cset-0-3'], rev_ids)
1629
 
        bundle = read_bundle(bundle_txt)
1630
 
        target.lock_write()
1631
 
        self.addCleanup(target.unlock)
1632
 
        install_bundle(target.repository, bundle)
1633
 
        inv1 = self.b1.repository.inventories.get_record_stream([
1634
 
            ('a@cset-0-3',)], 'unordered',
1635
 
            True).next().get_bytes_as('fulltext')
1636
 
        inv2 = target.repository.inventories.get_record_stream([
1637
 
            ('a@cset-0-3',)], 'unordered',
1638
 
            True).next().get_bytes_as('fulltext')
1639
 
        self.assertEqualDiff(inv1, inv2)
 
1429
        return 'metaweave'
1640
1430
 
1641
1431
 
1642
1432
class MungedBundleTester(object):
1817
1607
            def look_up(self, name, url):
1818
1608
                return 'source'
1819
1609
        directories.register('foo:', FooService, 'Testing directory service')
1820
 
        self.addCleanup(directories.remove, 'foo:')
 
1610
        self.addCleanup(lambda: directories.remove('foo:'))
1821
1611
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1822
1612
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1823
1613
                          'foo:bar')
1824
1614
 
1825
 
    def test_infinite_redirects_are_not_a_bundle(self):
1826
 
        """If a URL causes TooManyRedirections then NotABundle is raised.
1827
 
        """
1828
 
        from bzrlib.tests.blackbox.test_push import RedirectingMemoryServer
1829
 
        server = RedirectingMemoryServer()
1830
 
        self.start_server(server)
1831
 
        url = server.get_url() + 'infinite-loop'
1832
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1833
 
 
1834
1615
    def test_smart_server_connection_reset(self):
1835
1616
        """If a smart server connection fails during the attempt to read a
1836
1617
        bundle, then the ConnectionReset error should be propagated.
1837
1618
        """
1838
1619
        # Instantiate a server that will provoke a ConnectionReset
1839
1620
        sock_server = _DisconnectingTCPServer()
1840
 
        self.start_server(sock_server)
 
1621
        sock_server.setUp()
 
1622
        self.addCleanup(sock_server.tearDown)
1841
1623
        # We don't really care what the url is since the server will close the
1842
1624
        # connection without interpreting it
1843
1625
        url = sock_server.get_url()
1847
1629
class _DisconnectingTCPServer(object):
1848
1630
    """A TCP server that immediately closes any connection made to it."""
1849
1631
 
1850
 
    def start_server(self):
 
1632
    def setUp(self):
1851
1633
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1852
1634
        self.sock.bind(('127.0.0.1', 0))
1853
1635
        self.sock.listen(1)
1865
1647
    def get_url(self):
1866
1648
        return 'bzr://127.0.0.1:%d/' % (self.port,)
1867
1649
 
1868
 
    def stop_server(self):
 
1650
    def tearDown(self):
1869
1651
        try:
1870
1652
            # make sure the thread dies by connecting to the listening socket,
1871
1653
            # just in case the test failed to do so.
1876
1658
            pass
1877
1659
        self.sock.close()
1878
1660
        self.thread.join()
 
1661