~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-07-01 04:21:23 UTC
  • mfrom: (3350.8.14 stacking-knits)
  • Revision ID: pqm@pqm.ubuntu.com-20080701042123-zcmqzb7o6znwjx34
(robertc) Teach KnitVersionedFiles how to stack on other
        VersionedFile objects. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
    errors,
27
27
    generate_ids,
28
28
    knit,
 
29
    multiparent,
29
30
    pack,
30
31
    )
31
32
from bzrlib.errors import (
51
52
from bzrlib.symbol_versioning import one_four
52
53
from bzrlib.tests import (
53
54
    Feature,
 
55
    KnownFailure,
54
56
    TestCase,
55
57
    TestCaseWithMemoryTransport,
56
58
    TestCaseWithTransport,
58
60
from bzrlib.transport import get_transport
59
61
from bzrlib.transport.memory import MemoryTransport
60
62
from bzrlib.tuned_gzip import GzipFile
61
 
from bzrlib.versionedfile import ConstantMapper
 
63
from bzrlib.versionedfile import (
 
64
    AbsentContentFactory,
 
65
    ConstantMapper,
 
66
    RecordingVersionedFilesDecorator,
 
67
    )
62
68
 
63
69
 
64
70
class _CompiledKnitFeature(Feature):
1371
1377
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), []),
1372
1378
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1373
1379
        self.assertEqual([], self.caught_entries)
 
1380
 
 
1381
 
 
1382
class TestStacking(KnitTests):
 
1383
 
 
1384
    def get_basis_and_test_knit(self):
 
1385
        basis = self.make_test_knit(name='basis')
 
1386
        basis = RecordingVersionedFilesDecorator(basis)
 
1387
        test = self.make_test_knit(name='test')
 
1388
        test.add_fallback_versioned_files(basis)
 
1389
        return basis, test
 
1390
 
 
1391
    def test_add_fallback_versioned_files(self):
 
1392
        basis = self.make_test_knit(name='basis')
 
1393
        test = self.make_test_knit(name='test')
 
1394
        # It must not error; other tests test that the fallback is referred to
 
1395
        # when accessing data.
 
1396
        test.add_fallback_versioned_files(basis)
 
1397
 
 
1398
    def test_add_lines(self):
 
1399
        # lines added to the test are not added to the basis
 
1400
        basis, test = self.get_basis_and_test_knit()
 
1401
        key = ('foo',)
 
1402
        key_basis = ('bar',)
 
1403
        key_cross_border = ('quux',)
 
1404
        key_delta = ('zaphod',)
 
1405
        test.add_lines(key, (), ['foo\n'])
 
1406
        self.assertEqual({}, basis.get_parent_map([key]))
 
1407
        # lines added to the test that reference across the stack do a
 
1408
        # fulltext.
 
1409
        basis.add_lines(key_basis, (), ['foo\n'])
 
1410
        basis.calls = []
 
1411
        test.add_lines(key_cross_border, (key_basis,), ['foo\n'])
 
1412
        self.assertEqual('fulltext', test._index.get_method(key_cross_border))
 
1413
        self.assertEqual([("get_parent_map", set([key_basis]))], basis.calls)
 
1414
        # Subsequent adds do delta.
 
1415
        basis.calls = []
 
1416
        test.add_lines(key_delta, (key_cross_border,), ['foo\n'])
 
1417
        self.assertEqual('line-delta', test._index.get_method(key_delta))
 
1418
        self.assertEqual([], basis.calls)
 
1419
 
 
1420
    def test_annotate(self):
 
1421
        # annotations from the test knit are answered without asking the basis
 
1422
        basis, test = self.get_basis_and_test_knit()
 
1423
        key = ('foo',)
 
1424
        key_basis = ('bar',)
 
1425
        key_missing = ('missing',)
 
1426
        test.add_lines(key, (), ['foo\n'])
 
1427
        details = test.annotate(key)
 
1428
        self.assertEqual([(key, 'foo\n')], details)
 
1429
        self.assertEqual([], basis.calls)
 
1430
        # But texts that are not in the test knit are looked for in the basis
 
1431
        # directly.
 
1432
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
 
1433
        basis.calls = []
 
1434
        self.assertRaises(RevisionNotPresent, test.annotate, key_basis)
 
1435
        raise KnownFailure("Annotation on stacked knits currently fails.")
 
1436
        details = test.annotate(key_basis)
 
1437
        self.assertEqual([(key_basis, 'foo\n'), (key_basis, 'bar\n')], details)
 
1438
        self.assertEqual([("annotate", key_basis)], basis.calls)
 
1439
 
 
1440
    def test_check(self):
 
1441
        # check() must not check the fallback files, it's none of its business.
 
1442
        basis, test = self.get_basis_and_test_knit()
 
1443
        basis.check = None
 
1444
        test.check()
 
1445
 
 
1446
    def test_get_parent_map(self):
 
1447
        # parents in the test knit are answered without asking the basis
 
1448
        basis, test = self.get_basis_and_test_knit()
 
1449
        key = ('foo',)
 
1450
        key_basis = ('bar',)
 
1451
        key_missing = ('missing',)
 
1452
        test.add_lines(key, (), [])
 
1453
        parent_map = test.get_parent_map([key])
 
1454
        self.assertEqual({key: ()}, parent_map)
 
1455
        self.assertEqual([], basis.calls)
 
1456
        # But parents that are not in the test knit are looked for in the basis
 
1457
        basis.add_lines(key_basis, (), [])
 
1458
        basis.calls = []
 
1459
        parent_map = test.get_parent_map([key, key_basis, key_missing])
 
1460
        self.assertEqual({key: (),
 
1461
            key_basis: ()}, parent_map)
 
1462
        self.assertEqual([("get_parent_map", set([key_basis, key_missing]))],
 
1463
            basis.calls)
 
1464
 
 
1465
    def test_get_record_stream_unordered_fulltexts(self):
 
1466
        # records from the test knit are answered without asking the basis:
 
1467
        basis, test = self.get_basis_and_test_knit()
 
1468
        key = ('foo',)
 
1469
        key_basis = ('bar',)
 
1470
        key_missing = ('missing',)
 
1471
        test.add_lines(key, (), ['foo\n'])
 
1472
        records = list(test.get_record_stream([key], 'unordered', True))
 
1473
        self.assertEqual(1, len(records))
 
1474
        self.assertEqual([], basis.calls)
 
1475
        # Missing (from test knit) objects are retrieved from the basis:
 
1476
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
 
1477
        basis.calls = []
 
1478
        records = list(test.get_record_stream([key_basis, key_missing],
 
1479
            'unordered', True))
 
1480
        self.assertEqual(2, len(records))
 
1481
        calls = list(basis.calls)
 
1482
        for record in records:
 
1483
            self.assertSubset([record.key], (key_basis, key_missing))
 
1484
            if record.key == key_missing:
 
1485
                self.assertIsInstance(record, AbsentContentFactory)
 
1486
            else:
 
1487
                reference = list(basis.get_record_stream([key_basis],
 
1488
                    'unordered', True))[0]
 
1489
                self.assertEqual(reference.key, record.key)
 
1490
                self.assertEqual(reference.sha1, record.sha1)
 
1491
                self.assertEqual(reference.storage_kind, record.storage_kind)
 
1492
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
 
1493
                    record.get_bytes_as(record.storage_kind))
 
1494
                self.assertEqual(reference.get_bytes_as('fulltext'),
 
1495
                    record.get_bytes_as('fulltext'))
 
1496
        # It's not strictly minimal, but it seems reasonable for now for it to
 
1497
        # ask which fallbacks have which parents.
 
1498
        self.assertEqual([
 
1499
            ("get_parent_map", set([key_basis, key_missing])),
 
1500
            ("get_record_stream", [key_basis], 'unordered', True)],
 
1501
            calls)
 
1502
 
 
1503
    def test_get_record_stream_ordered_fulltexts(self):
 
1504
        # ordering is preserved down into the fallback store.
 
1505
        basis, test = self.get_basis_and_test_knit()
 
1506
        key = ('foo',)
 
1507
        key_basis = ('bar',)
 
1508
        key_basis_2 = ('quux',)
 
1509
        key_missing = ('missing',)
 
1510
        test.add_lines(key, (key_basis,), ['foo\n'])
 
1511
        # Missing (from test knit) objects are retrieved from the basis:
 
1512
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
 
1513
        basis.add_lines(key_basis_2, (), ['quux\n'])
 
1514
        basis.calls = []
 
1515
        # ask for in non-topological order
 
1516
        records = list(test.get_record_stream(
 
1517
            [key, key_basis, key_missing, key_basis_2], 'topological', True))
 
1518
        self.assertEqual(4, len(records))
 
1519
        results = []
 
1520
        for record in records:
 
1521
            self.assertSubset([record.key],
 
1522
                (key_basis, key_missing, key_basis_2, key))
 
1523
            if record.key == key_missing:
 
1524
                self.assertIsInstance(record, AbsentContentFactory)
 
1525
            else:
 
1526
                results.append((record.key, record.sha1, record.storage_kind,
 
1527
                    record.get_bytes_as('fulltext')))
 
1528
        calls = list(basis.calls)
 
1529
        order = [record[0] for record in results]
 
1530
        self.assertEqual([key_basis_2, key_basis, key], order)
 
1531
        for result in results:
 
1532
            if result[0] == key:
 
1533
                source = test
 
1534
            else:
 
1535
                source = basis
 
1536
            record = source.get_record_stream([result[0]], 'unordered',
 
1537
                True).next()
 
1538
            self.assertEqual(record.key, result[0])
 
1539
            self.assertEqual(record.sha1, result[1])
 
1540
            self.assertEqual(record.storage_kind, result[2])
 
1541
            self.assertEqual(record.get_bytes_as('fulltext'), result[3])
 
1542
        # It's not strictly minimal, but it seems reasonable for now for it to
 
1543
        # ask which fallbacks have which parents.
 
1544
        self.assertEqual([
 
1545
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
 
1546
            # unordered is asked for by the underlying worker as it still
 
1547
            # buffers everything while answering - which is a problem!
 
1548
            ("get_record_stream", [key_basis_2, key_basis], 'unordered', True)],
 
1549
            calls)
 
1550
 
 
1551
    def test_get_record_stream_unordered_deltas(self):
 
1552
        # records from the test knit are answered without asking the basis:
 
1553
        basis, test = self.get_basis_and_test_knit()
 
1554
        key = ('foo',)
 
1555
        key_basis = ('bar',)
 
1556
        key_missing = ('missing',)
 
1557
        test.add_lines(key, (), ['foo\n'])
 
1558
        records = list(test.get_record_stream([key], 'unordered', False))
 
1559
        self.assertEqual(1, len(records))
 
1560
        self.assertEqual([], basis.calls)
 
1561
        # Missing (from test knit) objects are retrieved from the basis:
 
1562
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
 
1563
        basis.calls = []
 
1564
        records = list(test.get_record_stream([key_basis, key_missing],
 
1565
            'unordered', False))
 
1566
        self.assertEqual(2, len(records))
 
1567
        calls = list(basis.calls)
 
1568
        for record in records:
 
1569
            self.assertSubset([record.key], (key_basis, key_missing))
 
1570
            if record.key == key_missing:
 
1571
                self.assertIsInstance(record, AbsentContentFactory)
 
1572
            else:
 
1573
                reference = list(basis.get_record_stream([key_basis],
 
1574
                    'unordered', False))[0]
 
1575
                self.assertEqual(reference.key, record.key)
 
1576
                self.assertEqual(reference.sha1, record.sha1)
 
1577
                self.assertEqual(reference.storage_kind, record.storage_kind)
 
1578
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
 
1579
                    record.get_bytes_as(record.storage_kind))
 
1580
        # It's not strictly minimal, but it seems reasonable for now for it to
 
1581
        # ask which fallbacks have which parents.
 
1582
        self.assertEqual([
 
1583
            ("get_parent_map", set([key_basis, key_missing])),
 
1584
            ("get_record_stream", [key_basis], 'unordered', False)],
 
1585
            calls)
 
1586
 
 
1587
    def test_get_record_stream_ordered_deltas(self):
 
1588
        # ordering is preserved down into the fallback store.
 
1589
        basis, test = self.get_basis_and_test_knit()
 
1590
        key = ('foo',)
 
1591
        key_basis = ('bar',)
 
1592
        key_basis_2 = ('quux',)
 
1593
        key_missing = ('missing',)
 
1594
        test.add_lines(key, (key_basis,), ['foo\n'])
 
1595
        # Missing (from test knit) objects are retrieved from the basis:
 
1596
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
 
1597
        basis.add_lines(key_basis_2, (), ['quux\n'])
 
1598
        basis.calls = []
 
1599
        # ask for in non-topological order
 
1600
        records = list(test.get_record_stream(
 
1601
            [key, key_basis, key_missing, key_basis_2], 'topological', False))
 
1602
        self.assertEqual(4, len(records))
 
1603
        results = []
 
1604
        for record in records:
 
1605
            self.assertSubset([record.key],
 
1606
                (key_basis, key_missing, key_basis_2, key))
 
1607
            if record.key == key_missing:
 
1608
                self.assertIsInstance(record, AbsentContentFactory)
 
1609
            else:
 
1610
                results.append((record.key, record.sha1, record.storage_kind,
 
1611
                    record.get_bytes_as(record.storage_kind)))
 
1612
        calls = list(basis.calls)
 
1613
        order = [record[0] for record in results]
 
1614
        self.assertEqual([key_basis_2, key_basis, key], order)
 
1615
        for result in results:
 
1616
            if result[0] == key:
 
1617
                source = test
 
1618
            else:
 
1619
                source = basis
 
1620
            record = source.get_record_stream([result[0]], 'unordered',
 
1621
                False).next()
 
1622
            self.assertEqual(record.key, result[0])
 
1623
            self.assertEqual(record.sha1, result[1])
 
1624
            self.assertEqual(record.storage_kind, result[2])
 
1625
            self.assertEqual(record.get_bytes_as(record.storage_kind), result[3])
 
1626
        # It's not strictly minimal, but it seems reasonable for now for it to
 
1627
        # ask which fallbacks have which parents.
 
1628
        self.assertEqual([
 
1629
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
 
1630
            ("get_record_stream", [key_basis_2, key_basis], 'topological', False)],
 
1631
            calls)
 
1632
 
 
1633
    def test_get_sha1s(self):
 
1634
        # sha1's in the test knit are answered without asking the basis
 
1635
        basis, test = self.get_basis_and_test_knit()
 
1636
        key = ('foo',)
 
1637
        key_basis = ('bar',)
 
1638
        key_missing = ('missing',)
 
1639
        test.add_lines(key, (), ['foo\n'])
 
1640
        key_sha1sum = sha.new('foo\n').hexdigest()
 
1641
        sha1s = test.get_sha1s([key])
 
1642
        self.assertEqual({key: key_sha1sum}, sha1s)
 
1643
        self.assertEqual([], basis.calls)
 
1644
        # But texts that are not in the test knit are looked for in the basis
 
1645
        # directly (rather than via text reconstruction) so that remote servers
 
1646
        # etc don't have to answer with full content.
 
1647
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
 
1648
        basis_sha1sum = sha.new('foo\nbar\n').hexdigest()
 
1649
        basis.calls = []
 
1650
        sha1s = test.get_sha1s([key, key_missing, key_basis])
 
1651
        self.assertEqual({key: key_sha1sum,
 
1652
            key_basis: basis_sha1sum}, sha1s)
 
1653
        self.assertEqual([("get_sha1s", set([key_basis, key_missing]))],
 
1654
            basis.calls)
 
1655
 
 
1656
    def test_insert_record_stream(self):
 
1657
        # records are inserted as normal; insert_record_stream builds on
 
1658
        # add_lines, so a smoke test should be all that's needed:
 
1659
        key = ('foo',)
 
1660
        key_basis = ('bar',)
 
1661
        key_delta = ('zaphod',)
 
1662
        basis, test = self.get_basis_and_test_knit()
 
1663
        source = self.make_test_knit(name='source')
 
1664
        basis.add_lines(key_basis, (), ['foo\n'])
 
1665
        basis.calls = []
 
1666
        source.add_lines(key_basis, (), ['foo\n'])
 
1667
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
 
1668
        stream = source.get_record_stream([key_delta], 'unordered', False)
 
1669
        test.insert_record_stream(stream)
 
1670
        self.assertEqual([("get_parent_map", set([key_basis]))],
 
1671
            basis.calls)
 
1672
        self.assertEqual({key_delta:(key_basis,)},
 
1673
            test.get_parent_map([key_delta]))
 
1674
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
 
1675
            'unordered', True).next().get_bytes_as('fulltext'))
 
1676
 
 
1677
    def test_iter_lines_added_or_present_in_keys(self):
 
1678
        # Lines from the basis are returned, and lines for a given key are only
 
1679
        # returned once. 
 
1680
        key1 = ('foo1',)
 
1681
        key2 = ('foo2',)
 
1682
        # all sources are asked for keys:
 
1683
        basis, test = self.get_basis_and_test_knit()
 
1684
        basis.add_lines(key1, (), ["foo"])
 
1685
        basis.calls = []
 
1686
        lines = list(test.iter_lines_added_or_present_in_keys([key1]))
 
1687
        self.assertEqual([("foo\n", key1)], lines)
 
1688
        self.assertEqual([("iter_lines_added_or_present_in_keys", set([key1]))],
 
1689
            basis.calls)
 
1690
        # keys in both are not duplicated:
 
1691
        test.add_lines(key2, (), ["bar\n"])
 
1692
        basis.add_lines(key2, (), ["bar\n"])
 
1693
        basis.calls = []
 
1694
        lines = list(test.iter_lines_added_or_present_in_keys([key2]))
 
1695
        self.assertEqual([("bar\n", key2)], lines)
 
1696
        self.assertEqual([], basis.calls)
 
1697
 
 
1698
    def test_keys(self):
 
1699
        key1 = ('foo1',)
 
1700
        key2 = ('foo2',)
 
1701
        # all sources are asked for keys:
 
1702
        basis, test = self.get_basis_and_test_knit()
 
1703
        keys = test.keys()
 
1704
        self.assertEqual(set(), set(keys))
 
1705
        self.assertEqual([("keys",)], basis.calls)
 
1706
        # keys from a basis are returned:
 
1707
        basis.add_lines(key1, (), [])
 
1708
        basis.calls = []
 
1709
        keys = test.keys()
 
1710
        self.assertEqual(set([key1]), set(keys))
 
1711
        self.assertEqual([("keys",)], basis.calls)
 
1712
        # keys in both are not duplicated:
 
1713
        test.add_lines(key2, (), [])
 
1714
        basis.add_lines(key2, (), [])
 
1715
        basis.calls = []
 
1716
        keys = test.keys()
 
1717
        self.assertEqual(2, len(keys))
 
1718
        self.assertEqual(set([key1, key2]), set(keys))
 
1719
        self.assertEqual([("keys",)], basis.calls)
 
1720
 
 
1721
    def test_add_mpdiffs(self):
 
1722
        # records are inserted as normal; add_mpdiff builds on
 
1723
        # add_lines, so a smoke test should be all that's needed:
 
1724
        key = ('foo',)
 
1725
        key_basis = ('bar',)
 
1726
        key_delta = ('zaphod',)
 
1727
        basis, test = self.get_basis_and_test_knit()
 
1728
        source = self.make_test_knit(name='source')
 
1729
        basis.add_lines(key_basis, (), ['foo\n'])
 
1730
        basis.calls = []
 
1731
        source.add_lines(key_basis, (), ['foo\n'])
 
1732
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
 
1733
        diffs = source.make_mpdiffs([key_delta])
 
1734
        test.add_mpdiffs([(key_delta, (key_basis,),
 
1735
            source.get_sha1s([key_delta])[key_delta], diffs[0])])
 
1736
        self.assertEqual([("get_parent_map", set([key_basis])),
 
1737
            ('get_record_stream', [key_basis], 'unordered', True),
 
1738
            ('get_parent_map', set([key_basis]))],
 
1739
            basis.calls)
 
1740
        self.assertEqual({key_delta:(key_basis,)},
 
1741
            test.get_parent_map([key_delta]))
 
1742
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
 
1743
            'unordered', True).next().get_bytes_as('fulltext'))
 
1744
 
 
1745
    def test_make_mpdiffs(self):
 
1746
        # Generating an mpdiff across a stacking boundary should detect parent
 
1747
        # texts regions.
 
1748
        key = ('foo',)
 
1749
        key_left = ('bar',)
 
1750
        key_right = ('zaphod',)
 
1751
        basis, test = self.get_basis_and_test_knit()
 
1752
        basis.add_lines(key_left, (), ['bar\n'])
 
1753
        basis.add_lines(key_right, (), ['zaphod\n'])
 
1754
        basis.calls = []
 
1755
        test.add_lines(key, (key_left, key_right),
 
1756
            ['bar\n', 'foo\n', 'zaphod\n'])
 
1757
        diffs = test.make_mpdiffs([key])
 
1758
        self.assertEqual([
 
1759
            multiparent.MultiParent([multiparent.ParentText(0, 0, 0, 1),
 
1760
                multiparent.NewText(['foo\n']),
 
1761
                multiparent.ParentText(1, 0, 2, 1)])],
 
1762
            diffs)
 
1763
        self.assertEqual(4, len(basis.calls))
 
1764
        self.assertEqual([
 
1765
            ("get_parent_map", set([key_left, key_right])),
 
1766
            ("get_parent_map", set([key_left, key_right])),
 
1767
            ("get_parent_map", set([key_left, key_right])),
 
1768
            ],
 
1769
            basis.calls[:3])
 
1770
        last_call = basis.calls[3]
 
1771
        self.assertEqual('get_record_stream', last_call[0])
 
1772
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
 
1773
        self.assertEqual('unordered', last_call[2])
 
1774
        self.assertEqual(True, last_call[3])