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)
1382
class TestStacking(KnitTests):
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)
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)
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()
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
1409
basis.add_lines(key_basis, (), ['foo\n'])
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.
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)
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()
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
1432
basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
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)
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()
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()
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, (), [])
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]))],
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()
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'])
1478
records = list(test.get_record_stream([key_basis, key_missing],
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)
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.
1499
("get_parent_map", set([key_basis, key_missing])),
1500
("get_record_stream", [key_basis], 'unordered', True)],
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()
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'])
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))
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)
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:
1536
record = source.get_record_stream([result[0]], 'unordered',
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.
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)],
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()
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'])
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)
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.
1583
("get_parent_map", set([key_basis, key_missing])),
1584
("get_record_stream", [key_basis], 'unordered', False)],
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()
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'])
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))
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)
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:
1620
record = source.get_record_stream([result[0]], 'unordered',
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.
1629
("get_parent_map", set([key_basis, key_basis_2, key_missing])),
1630
("get_record_stream", [key_basis_2, key_basis], 'topological', False)],
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()
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()
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]))],
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:
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'])
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]))],
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'))
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
1682
# all sources are asked for keys:
1683
basis, test = self.get_basis_and_test_knit()
1684
basis.add_lines(key1, (), ["foo"])
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]))],
1690
# keys in both are not duplicated:
1691
test.add_lines(key2, (), ["bar\n"])
1692
basis.add_lines(key2, (), ["bar\n"])
1694
lines = list(test.iter_lines_added_or_present_in_keys([key2]))
1695
self.assertEqual([("bar\n", key2)], lines)
1696
self.assertEqual([], basis.calls)
1698
def test_keys(self):
1701
# all sources are asked for keys:
1702
basis, test = self.get_basis_and_test_knit()
1704
self.assertEqual(set(), set(keys))
1705
self.assertEqual([("keys",)], basis.calls)
1706
# keys from a basis are returned:
1707
basis.add_lines(key1, (), [])
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, (), [])
1717
self.assertEqual(2, len(keys))
1718
self.assertEqual(set([key1, key2]), set(keys))
1719
self.assertEqual([("keys",)], basis.calls)
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:
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'])
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]))],
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'))
1745
def test_make_mpdiffs(self):
1746
# Generating an mpdiff across a stacking boundary should detect parent
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'])
1755
test.add_lines(key, (key_left, key_right),
1756
['bar\n', 'foo\n', 'zaphod\n'])
1757
diffs = test.make_mpdiffs([key])
1759
multiparent.MultiParent([multiparent.ParentText(0, 0, 0, 1),
1760
multiparent.NewText(['foo\n']),
1761
multiparent.ParentText(1, 0, 2, 1)])],
1763
self.assertEqual(4, len(basis.calls))
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])),
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])