158
162
self.assertEqual(content.annotate(),
159
163
[("bogus", "text1"), ("bogus", "text2")])
161
def test_annotate_iter(self):
162
content = self._make_content([])
163
it = content.annotate_iter()
164
self.assertRaises(StopIteration, it.next)
166
content = self._make_content([("bogus", "text1"), ("bogus", "text2")])
167
it = content.annotate_iter()
168
self.assertEqual(it.next(), ("bogus", "text1"))
169
self.assertEqual(it.next(), ("bogus", "text2"))
170
self.assertRaises(StopIteration, it.next)
172
165
def test_line_delta(self):
173
166
content1 = self._make_content([("", "a"), ("", "b")])
174
167
content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
196
189
self.assertEqual(content.annotate(),
197
190
[("origin1", "text1"), ("origin2", "text2")])
199
def test_annotate_iter(self):
200
content = self._make_content([])
201
it = content.annotate_iter()
202
self.assertRaises(StopIteration, it.next)
204
content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
205
it = content.annotate_iter()
206
self.assertEqual(it.next(), ("origin1", "text1"))
207
self.assertEqual(it.next(), ("origin2", "text2"))
208
self.assertRaises(StopIteration, it.next)
210
192
def test_line_delta(self):
211
193
content1 = self._make_content([("", "a"), ("", "b")])
212
194
content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
588
570
self.assertEqual("1", index._version_list_to_index(["version"]))
589
571
self.assertEqual((None, 3, 4), index.get_position("version"))
590
572
self.assertEqual(["options3"], index.get_options("version"))
591
self.assertEqual(["parent", "other"],
573
self.assertEqual(("parent", "other"),
592
574
index.get_parents_with_ghosts("version"))
594
576
def test_read_compressed_parents(self):
599
581
"c option 0 1 1 0 :",
601
583
index = self.get_knit_index(transport, "filename", "r")
602
self.assertEqual(["a"], index.get_parents("b"))
603
self.assertEqual(["b", "a"], index.get_parents("c"))
584
self.assertEqual({"b":("a",), "c":("b", "a")},
585
index.get_parent_map(["b", "c"]))
605
587
def test_write_utf8_version_id(self):
606
588
unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
629
611
transport.calls.pop(0))
631
def test_get_graph(self):
632
transport = MockTransport()
633
index = self.get_knit_index(transport, "filename", "w", create=True)
634
self.assertEqual([], index.get_graph())
636
index.add_version("a", ["option"], (None, 0, 1), ["b"])
637
self.assertEqual([("a", ["b"])], index.get_graph())
639
index.add_version("c", ["option"], (None, 0, 1), ["d"])
640
self.assertEqual([("a", ["b"]), ("c", ["d"])],
641
sorted(index.get_graph()))
643
613
def test_get_ancestry(self):
644
614
transport = MockTransport([
645
615
_KnitIndex.HEADER,
689
659
self.assertRaises(RevisionNotPresent,
690
660
index.get_ancestry_with_ghosts, ["e"])
692
def test_iter_parents(self):
693
transport = MockTransport()
694
index = self.get_knit_index(transport, "filename", "w", create=True)
696
index.add_version('r0', ['option'], (None, 0, 1), [])
698
index.add_version('r1', ['option'], (None, 0, 1), ['r0'])
700
index.add_version('r2', ['option'], (None, 0, 1), ['r1', 'r0'])
702
# cases: each sample data individually:
703
self.assertEqual(set([('r0', ())]),
704
set(index.iter_parents(['r0'])))
705
self.assertEqual(set([('r1', ('r0', ))]),
706
set(index.iter_parents(['r1'])))
707
self.assertEqual(set([('r2', ('r1', 'r0'))]),
708
set(index.iter_parents(['r2'])))
709
# no nodes returned for a missing node
710
self.assertEqual(set(),
711
set(index.iter_parents(['missing'])))
712
# 1 node returned with missing nodes skipped
713
self.assertEqual(set([('r1', ('r0', ))]),
714
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
716
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
717
set(index.iter_parents(['r0', 'r1'])))
718
# 2 nodes returned, missing skipped
719
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
720
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
722
662
def test_num_versions(self):
723
663
transport = MockTransport([
724
664
_KnitIndex.HEADER
771
711
self.assertEqual(1, index.num_versions())
772
712
self.assertEqual((None, 0, 1), index.get_position("a"))
773
713
self.assertEqual(["option"], index.get_options("a"))
774
self.assertEqual(["b"], index.get_parents_with_ghosts("a"))
714
self.assertEqual(("b",), index.get_parents_with_ghosts("a"))
776
716
index.add_version("a", ["opt"], (None, 1, 2), ["c"])
777
717
self.assertEqual(("append_bytes",
781
721
self.assertEqual(1, index.num_versions())
782
722
self.assertEqual((None, 1, 2), index.get_position("a"))
783
723
self.assertEqual(["opt"], index.get_options("a"))
784
self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
724
self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
786
726
index.add_version("b", ["option"], (None, 2, 3), ["a"])
787
727
self.assertEqual(("append_bytes",
816
756
self.assertEqual((None, 2, 3), index.get_position("b"))
817
757
self.assertEqual(["opt"], index.get_options("a"))
818
758
self.assertEqual(["option"], index.get_options("b"))
819
self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
820
self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
759
self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
760
self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
822
762
def test_add_versions_random_id_is_accepted(self):
823
763
transport = MockTransport([
912
852
index = self.get_knit_index(transport, "filename", "r")
914
self.assertEqual([], index.get_parents("a"))
915
self.assertEqual(["a", "c"], index.get_parents("b"))
916
self.assertEqual(["b", "a"], index.get_parents("c"))
858
}, index.get_parent_map(["a", "b", "c"]))
918
860
def test_get_parents_with_ghosts(self):
919
861
transport = MockTransport([
925
867
index = self.get_knit_index(transport, "filename", "r")
927
self.assertEqual([], index.get_parents_with_ghosts("a"))
928
self.assertEqual(["a", "c"], index.get_parents_with_ghosts("b"))
929
self.assertEqual(["b", "a", "e"],
869
self.assertEqual((), index.get_parents_with_ghosts("a"))
870
self.assertEqual(("a", "c"), index.get_parents_with_ghosts("b"))
871
self.assertEqual(("b", "a", "e"),
930
872
index.get_parents_with_ghosts("c"))
932
874
def test_check_versions_present(self):
1085
1027
self.addCleanup(reset)
1086
1028
from bzrlib._knit_load_data_c import _load_data_c
1087
1029
knit._load_data = _load_data_c
1088
return _KnitIndex(*args, **kwargs)
1030
return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
1092
1033
class KnitTests(TestCaseWithTransport):
1093
1034
"""Class containing knit test helper routines."""
1095
1036
def make_test_knit(self, annotate=False, delay_create=False, index=None,
1037
name='test', delta=True, access_mode='w'):
1097
1038
if not annotate:
1098
1039
factory = KnitPlainFactory()
1101
return KnitVersionedFile(name, get_transport('.'), access_mode='w',
1102
factory=factory, create=True,
1103
delay_create=delay_create, index=index)
1043
index = _KnitIndex(get_transport('.'), name + INDEX_SUFFIX,
1044
access_mode, create=True, file_mode=None,
1045
create_parent_dir=False, delay_create=delay_create,
1046
dir_mode=None, get_scope=lambda:None)
1047
access = _KnitAccess(get_transport('.'), name + DATA_SUFFIX, None,
1048
None, delay_create, False)
1049
return KnitVersionedFile(name, get_transport('.'), factory=factory,
1050
create=True, delay_create=delay_create, index=index,
1051
access_method=access, delta=delta)
1105
1053
def assertRecordContentEqual(self, knit, version_id, candidate_content):
1106
1054
"""Assert that some raw record content matches the raw record content
1172
1120
k = self.make_test_knit()
1173
1121
k.add_lines('text-1', [], split_lines(TEXT_1))
1175
k2 = KnitVersionedFile('test', get_transport('.'), access_mode='r', factory=KnitPlainFactory(), create=True)
1123
k2 = make_file_knit('test', get_transport('.'), access_mode='r',
1124
factory=KnitPlainFactory(), create=True)
1176
1125
self.assertTrue(k2.has_version('text-1'))
1177
1126
self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1200
1149
def test_incomplete(self):
1201
1150
"""Test if texts without a ending line-end can be inserted and
1203
k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1152
k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1204
1153
k.add_lines('text-1', [], ['a\n', 'b' ])
1205
1154
k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1206
1155
# reopening ensures maximum room for confusion
1207
k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1156
k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1208
1157
self.assertEquals(k.get_lines('text-1'), ['a\n', 'b' ])
1209
1158
self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1243
1190
index = InMemoryGraphIndex(2)
1244
1191
knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1246
k = KnitVersionedFile('test', get_transport('.'),
1247
delta=True, create=True, index=knit_index)
1193
k = self.make_test_knit(annotate=True, index=knit_index)
1248
1194
self.add_stock_one_and_one_a(k)
1250
1195
self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1251
1196
# check the index had the right data added.
1252
1197
self.assertEqual(set([
1350
1293
self.assertEquals(origins[1], ('text-1', 'b\n'))
1351
1294
self.assertEquals(origins[2], ('text-1', 'c\n'))
1353
def _test_join_with_factories(self, k1_factory, k2_factory):
1354
k1 = KnitVersionedFile('test1', get_transport('.'), factory=k1_factory, create=True)
1355
k1.add_lines('text-a', [], ['a1\n', 'a2\n', 'a3\n'])
1356
k1.add_lines('text-b', ['text-a'], ['a1\n', 'b2\n', 'a3\n'])
1357
k1.add_lines('text-c', [], ['c1\n', 'c2\n', 'c3\n'])
1358
k1.add_lines('text-d', ['text-c'], ['c1\n', 'd2\n', 'd3\n'])
1359
k1.add_lines('text-m', ['text-b', 'text-d'], ['a1\n', 'b2\n', 'd3\n'])
1360
k2 = KnitVersionedFile('test2', get_transport('.'), factory=k2_factory, create=True)
1361
count = k2.join(k1, version_ids=['text-m'])
1362
self.assertEquals(count, 5)
1363
self.assertTrue(k2.has_version('text-a'))
1364
self.assertTrue(k2.has_version('text-c'))
1365
origins = k2.annotate('text-m')
1366
self.assertEquals(origins[0], ('text-a', 'a1\n'))
1367
self.assertEquals(origins[1], ('text-b', 'b2\n'))
1368
self.assertEquals(origins[2], ('text-d', 'd3\n'))
1370
def test_knit_join_plain_to_plain(self):
1371
"""Test joining a plain knit with a plain knit."""
1372
self._test_join_with_factories(KnitPlainFactory(), KnitPlainFactory())
1374
def test_knit_join_anno_to_anno(self):
1375
"""Test joining an annotated knit with an annotated knit."""
1376
self._test_join_with_factories(None, None)
1378
def test_knit_join_anno_to_plain(self):
1379
"""Test joining an annotated knit with a plain knit."""
1380
self._test_join_with_factories(None, KnitPlainFactory())
1382
def test_knit_join_plain_to_anno(self):
1383
"""Test joining a plain knit with an annotated knit."""
1384
self._test_join_with_factories(KnitPlainFactory(), None)
1386
1296
def test_reannotate(self):
1387
k1 = KnitVersionedFile('knit1', get_transport('.'),
1297
k1 = make_file_knit('knit1', get_transport('.'),
1388
1298
factory=KnitAnnotateFactory(), create=True)
1390
1300
k1.add_lines('text-a', [], ['a\n', 'b\n'])
1392
1302
k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1394
k2 = KnitVersionedFile('test2', get_transport('.'),
1304
k2 = make_file_knit('test2', get_transport('.'),
1395
1305
factory=KnitAnnotateFactory(), create=True)
1396
k2.join(k1, version_ids=['text-b'])
1306
k2.insert_record_stream(k1.get_record_stream(k1.versions(),
1307
'unordered', False))
1399
1310
k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
1427
1339
def test_iter_lines_reads_in_order(self):
1428
1340
instrumented_t = get_transport('trace+memory:///')
1429
k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
1341
k1 = make_file_knit('id', instrumented_t, create=True, delta=True)
1430
1342
self.assertEqual([('get', 'id.kndx',)], instrumented_t._activity)
1431
1343
# add texts with no required ordering
1432
1344
k1.add_lines('base', [], ['text\n'])
1433
1345
k1.add_lines('base2', [], ['text2\n'])
1435
1346
# clear the logged activity, but preserve the list instance in case of
1436
1347
# clones pointing at it.
1437
1348
del instrumented_t._activity[:]
1443
1354
instrumented_t._activity)
1444
1355
self.assertEqual([('text\n', 'base'), ('text2\n', 'base2')], results)
1446
def test_create_empty_annotated(self):
1447
k1 = self.make_test_knit(True)
1449
k1.add_lines('text-a', [], ['a\n', 'b\n'])
1450
k2 = k1.create_empty('t', MemoryTransport())
1451
self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
1452
self.assertEqual(k1.delta, k2.delta)
1453
# the generic test checks for empty content and file class
1455
1357
def test_knit_format(self):
1456
1358
# this tests that a new knit index file has the expected content
1457
1359
# and that is writes the data we expect as records are added.
1471
1373
"\nrevid2 line-delta 84 82 0 :",
1473
1375
# we should be able to load this file again
1474
knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
1376
knit = make_file_knit('test', get_transport('.'), access_mode='r')
1475
1377
self.assertEqual(['revid', 'revid2'], knit.versions())
1476
1378
# write a short write to the file and ensure that its ignored
1477
1379
indexfile = file('test.kndx', 'ab')
1478
1380
indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1479
1381
indexfile.close()
1480
1382
# we should be able to load this file again
1481
knit = KnitVersionedFile('test', get_transport('.'), access_mode='w')
1383
knit = make_file_knit('test', get_transport('.'), access_mode='w')
1482
1384
self.assertEqual(['revid', 'revid2'], knit.versions())
1483
1385
# and add a revision with the same id the failed write had
1484
1386
knit.add_lines('revid3', ['revid2'], ['a\n'])
1485
1387
# and when reading it revid3 should now appear.
1486
knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
1388
knit = make_file_knit('test', get_transport('.'), access_mode='r')
1487
1389
self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
1488
self.assertEqual(['revid2'], knit.get_parents('revid3'))
1390
self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
1490
1392
def test_delay_create(self):
1491
1393
"""Test that passing delay_create=True creates files late"""
1504
1406
"""create_parent_dir can create knits in nonexistant dirs"""
1505
1407
# Has no effect if we don't set 'delay_create'
1506
1408
trans = get_transport('.')
1507
self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
1409
self.assertRaises(NoSuchFile, make_file_knit, 'dir/test',
1508
1410
trans, access_mode='w', factory=None,
1509
1411
create=True, create_parent_dir=True)
1510
1412
# Nothing should have changed yet
1511
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1413
knit = make_file_knit('dir/test', trans, access_mode='w',
1512
1414
factory=None, create=True,
1513
1415
create_parent_dir=True,
1514
1416
delay_create=True)
1529
1431
if not trans._can_roundtrip_unix_modebits():
1530
1432
# Can't roundtrip, so no need to run this test
1532
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1533
factory=None, create=True,
1534
create_parent_dir=True,
1434
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1435
create=True, create_parent_dir=True, delay_create=True,
1436
file_mode=0600, dir_mode=0700)
1538
1437
knit.add_lines('revid', [], ['a\n'])
1539
1438
self.assertTransportMode(trans, 'dir', 0700)
1540
1439
self.assertTransportMode(trans, 'dir/test.knit', 0600)
1545
1444
if not trans._can_roundtrip_unix_modebits():
1546
1445
# Can't roundtrip, so no need to run this test
1548
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1549
factory=None, create=True,
1550
create_parent_dir=True,
1447
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1448
create=True, create_parent_dir=True, delay_create=True,
1449
file_mode=0660, dir_mode=0770)
1554
1450
knit.add_lines('revid', [], ['a\n'])
1555
1451
self.assertTransportMode(trans, 'dir', 0770)
1556
1452
self.assertTransportMode(trans, 'dir/test.knit', 0660)
1561
1457
if not trans._can_roundtrip_unix_modebits():
1562
1458
# Can't roundtrip, so no need to run this test
1564
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1565
factory=None, create=True,
1566
create_parent_dir=True,
1460
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1461
create=True, create_parent_dir=True, delay_create=True,
1462
file_mode=0666, dir_mode=0777)
1570
1463
knit.add_lines('revid', [], ['a\n'])
1571
1464
self.assertTransportMode(trans, 'dir', 0777)
1572
1465
self.assertTransportMode(trans, 'dir/test.knit', 0666)
1602
1499
expected_data_list = [
1603
1500
# version, options, length, parents
1604
('text-a', ['fulltext'], 122, []),
1501
('text-a', ['fulltext'], 122, ()),
1606
1503
for version_id, parents, lines in test_data:
1607
1504
k1.add_lines(version_id, parents, split_lines(lines))
1631
1528
expected_data_list = [
1632
1529
# version, options, length, parents
1633
('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
1530
('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1635
1532
for version_id, parents, lines in test_data:
1636
1533
k1.add_lines(version_id, parents, split_lines(lines))
1660
1557
original_versions = k1.versions
1661
1558
k1.versions = lambda: reversed(original_versions())
1662
1559
expected_data_list = [
1663
('text-a', ['fulltext'], 122, []),
1664
('text-b', ['line-delta'], 84, ['text-a'])]
1560
('text-a', ['fulltext'], 122, ()),
1561
('text-b', ['line-delta'], 84, ('text-a',))]
1665
1562
# now check the fulltext is first and the delta second
1666
1563
format, data_list, _ = k1.get_data_stream(['text-a', 'text-b'])
1667
1564
self.assertEqual('knit-plain', format)
1673
1570
format, data_list, _ = k1.get_data_stream([
1674
1571
'text-m', 'text-b', 'text-a'])
1675
1572
self.assertEqual([
1676
('text-a', ['fulltext'], 122, []),
1677
('text-b', ['line-delta'], 84, ['text-a']),
1678
('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
1573
('text-a', ['fulltext'], 122, ()),
1574
('text-b', ['line-delta'], 84, ('text-a',)),
1575
('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1681
1578
def test_get_stream_ghost_parent(self):
1717
1614
expected_data_list = [
1718
1615
# version, options, length, parents
1719
('text-d', ['line-delta'], 84, ['text-c']),
1720
('text-b', ['line-delta'], 84, ['text-a']),
1616
('text-d', ['line-delta'], 84, ('text-c',)),
1617
('text-b', ['line-delta'], 84, ('text-a',)),
1722
1619
# Note that even though we request the revision IDs in a particular
1723
1620
# order, the data stream may return them in any order it likes. In this
1759
1656
expected_data_list = [
1760
1657
# version, options, length, parents
1761
('text-a', ['fulltext'], 122, []),
1762
('text-b', ['line-delta'], 84, ['text-a']),
1763
('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
1764
('text-c', ['fulltext'], 121, []),
1765
('text-d', ['line-delta'], 84, ['text-c']),
1658
('text-a', ['fulltext'], 122, ()),
1659
('text-b', ['line-delta'], 84, ('text-a',)),
1660
('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1661
('text-c', ['fulltext'], 121, ()),
1662
('text-d', ['line-delta'], 84, ('text-c',)),
1767
1664
format, data_list, reader_callable = k1.get_data_stream(
1768
1665
['text-a', 'text-b', 'text-c', 'text-d', 'text-m'])
1895
1796
# The target knit object is in a consistent state, i.e. the record we
1896
1797
# just added is immediately visible.
1897
1798
self.assertTrue(target.has_version('text-a'))
1898
self.assertTrue(target.has_ghost('text-ghost'))
1799
self.assertFalse(target.has_version('text-ghost'))
1800
self.assertEqual({'text-a':('text-ghost',)},
1801
target.get_parent_map(['text-a', 'text-ghost']))
1899
1802
self.assertEqual(split_lines(TEXT_1), target.get_lines('text-a'))
1901
def test_insert_data_stream_inconsistent_version_lines(self):
1804
def test_inconsistent_version_lines(self):
1902
1805
"""Inserting a data stream which has different content for a version_id
1903
1806
than already exists in the knit will raise KnitCorrupt.
1941
1844
errors.KnitDataStreamUnknown,
1942
1845
target.insert_data_stream, data_stream)
1944
# * test that a stream of "already present version, then new version"
1945
# inserts correctly.
1847
def test_bug_208418(self):
1848
"""You can insert a stream with an incompatible format, even when:
1849
* the stream has a line-delta record,
1850
* whose parent is in the target, also stored as a line-delta
1852
See <https://launchpad.net/bugs/208418>.
1854
base_lines = split_lines(TEXT_1)
1856
target = self.make_test_knit(name='target', annotate=True)
1857
target.add_lines('version-1', [], base_lines)
1858
target.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1859
# The second record should be a delta.
1860
self.assertEqual('line-delta', target._index.get_method('version-2'))
1862
# Make a source, with a different format, but the same data
1863
source = self.make_test_knit(name='source', annotate=False)
1864
source.add_lines('version-1', [], base_lines)
1865
source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1866
# Now add another record, which should be stored as a delta against
1868
source.add_lines('version-3', ['version-2'], base_lines + ['b\n'])
1869
self.assertEqual('line-delta', source._index.get_method('version-3'))
1871
# Make a stream of the new version
1872
data_stream = source.get_data_stream(['version-3'])
1873
# And insert into the target
1874
target.insert_data_stream(data_stream)
1875
# No errors should have been raised.
1877
def test_line_delta_record_into_non_delta_knit(self):
1878
# Make a data stream with a line-delta record
1879
source = self.make_test_knit(name='source', delta=True)
1880
base_lines = split_lines(TEXT_1)
1881
source.add_lines('version-1', [], base_lines)
1882
source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1883
# The second record should be a delta.
1884
self.assertEqual('line-delta', source._index.get_method('version-2'))
1885
data_stream = source.get_data_stream(['version-1', 'version-2'])
1887
# Insert the stream into a non-delta knit.
1888
target = self.make_test_knit(name='target', delta=False)
1889
target.insert_data_stream(data_stream)
1891
# Both versions are fulltexts in the target
1892
self.assertEqual('fulltext', target._index.get_method('version-1'))
1893
self.assertEqual('fulltext', target._index.get_method('version-2'))
1896
class DataStreamTests(KnitTests):
1948
1898
def assertMadeStreamKnit(self, source_knit, versions, target_knit):
1949
1899
"""Assert that a knit made from a stream is as expected."""
2081
2031
self.failIf(WeaveToKnit.is_compatible(k, k))
2084
class TestKnitCaching(KnitTests):
2086
def create_knit(self):
2087
k = self.make_test_knit(True)
2088
k.add_lines('text-1', [], split_lines(TEXT_1))
2089
k.add_lines('text-2', [], split_lines(TEXT_2))
2092
def test_no_caching(self):
2093
k = self.create_knit()
2094
# Nothing should be cached without setting 'enable_cache'
2095
self.assertEqual({}, k._data._cache)
2097
def test_cache_data_read_raw(self):
2098
k = self.create_knit()
2100
# Now cache and read
2103
def read_one_raw(version):
2104
pos_map = k._get_components_positions([version])
2105
method, index_memo, next = pos_map[version]
2106
lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
2107
self.assertEqual(1, len(lst))
2110
val = read_one_raw('text-1')
2111
self.assertEqual({'text-1':val[1]}, k._data._cache)
2114
# After clear, new reads are not cached
2115
self.assertEqual({}, k._data._cache)
2117
val2 = read_one_raw('text-1')
2118
self.assertEqual(val, val2)
2119
self.assertEqual({}, k._data._cache)
2121
def test_cache_data_read(self):
2122
k = self.create_knit()
2124
def read_one(version):
2125
pos_map = k._get_components_positions([version])
2126
method, index_memo, next = pos_map[version]
2127
lst = list(k._data.read_records_iter([(version, index_memo)]))
2128
self.assertEqual(1, len(lst))
2131
# Now cache and read
2134
val = read_one('text-2')
2135
self.assertEqual(['text-2'], k._data._cache.keys())
2136
self.assertEqual('text-2', val[0])
2137
content, digest = k._data._parse_record('text-2',
2138
k._data._cache['text-2'])
2139
self.assertEqual(content, val[1])
2140
self.assertEqual(digest, val[2])
2143
self.assertEqual({}, k._data._cache)
2145
val2 = read_one('text-2')
2146
self.assertEqual(val, val2)
2147
self.assertEqual({}, k._data._cache)
2149
def test_cache_read(self):
2150
k = self.create_knit()
2153
text = k.get_text('text-1')
2154
self.assertEqual(TEXT_1, text)
2155
self.assertEqual(['text-1'], k._data._cache.keys())
2158
self.assertEqual({}, k._data._cache)
2160
text = k.get_text('text-1')
2161
self.assertEqual(TEXT_1, text)
2162
self.assertEqual({}, k._data._cache)
2165
2034
class TestKnitIndex(KnitTests):
2167
2036
def test_add_versions_dictionary_compresses(self):
2185
2054
'a-3 fulltext 0 0 1 :'
2187
2056
self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
2188
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0),
2189
'a-2':('a-2', ['fulltext'], 0, 0, ['a-1'], 1),
2190
'a-3':('a-3', ['fulltext'], 0, 0, ['a-2'], 2),
2057
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0),
2058
'a-2':('a-2', ['fulltext'], 0, 0, ('a-1',), 1),
2059
'a-3':('a-3', ['fulltext'], 0, 0, ('a-2',), 2),
2193
2062
def test_add_versions_fails_clean(self):
2212
2081
def generate_failure():
2213
2082
"""Add some entries and then raise an exception"""
2214
yield ('a-2', ['fulltext'], (None, 0, 0), ['a-1'])
2215
yield ('a-3', ['fulltext'], (None, 0, 0), ['a-2'])
2083
yield ('a-2', ['fulltext'], (None, 0, 0), ('a-1',))
2084
yield ('a-3', ['fulltext'], (None, 0, 0), ('a-2',))
2216
2085
raise StopEarly()
2218
2087
# Assert the pre-condition
2219
2088
self.assertEqual(['a-1'], idx._history)
2220
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
2089
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2222
2091
self.assertRaises(StopEarly, idx.add_versions, generate_failure())
2224
2093
# And it shouldn't be modified
2225
2094
self.assertEqual(['a-1'], idx._history)
2226
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
2095
self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2228
2097
def test_knit_index_ignores_empty_files(self):
2229
2098
# There was a race condition in older bzr, where a ^C at the right time
2287
2156
return KnitGraphIndex(combined_index, deltas=deltas,
2288
2157
add_callback=add_callback)
2290
def test_get_graph(self):
2291
index = self.two_graph_index()
2292
self.assertEqual(set([
2293
('tip', ('parent', )),
2295
('parent', ('tail', 'ghost')),
2297
]), set(index.get_graph()))
2299
2159
def test_get_ancestry(self):
2300
2160
# get_ancestry is defined as eliding ghosts, not erroring.
2301
2161
index = self.two_graph_index()
2385
2245
self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2386
2246
self.assertEqual(['fulltext'], index.get_options('parent'))
2388
def test_get_parents(self):
2389
# get_parents ignores ghosts
2390
index = self.two_graph_index()
2391
self.assertEqual(('tail', ), index.get_parents('parent'))
2392
# and errors on ghosts.
2393
self.assertRaises(errors.RevisionNotPresent,
2394
index.get_parents, 'ghost')
2396
2248
def test_get_parents_with_ghosts(self):
2397
2249
index = self.two_graph_index()
2398
2250
self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
2519
2371
('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2520
2372
self.assertEqual([], self.caught_entries)
2522
def test_iter_parents(self):
2523
index1 = self.make_g_index('1', 1, [
2525
(('r0', ), 'N0 100', ([], )),
2527
(('r1', ), '', ([('r0', )], ))])
2528
index2 = self.make_g_index('2', 1, [
2530
(('r2', ), 'N0 100', ([('r1', ), ('r0', )], )),
2532
combined_index = CombinedGraphIndex([index1, index2])
2533
index = KnitGraphIndex(combined_index)
2535
# cases: each sample data individually:
2536
self.assertEqual(set([('r0', ())]),
2537
set(index.iter_parents(['r0'])))
2538
self.assertEqual(set([('r1', ('r0', ))]),
2539
set(index.iter_parents(['r1'])))
2540
self.assertEqual(set([('r2', ('r1', 'r0'))]),
2541
set(index.iter_parents(['r2'])))
2542
# no nodes returned for a missing node
2543
self.assertEqual(set(),
2544
set(index.iter_parents(['missing'])))
2545
# 1 node returned with missing nodes skipped
2546
self.assertEqual(set([('r1', ('r0', ))]),
2547
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
2549
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2550
set(index.iter_parents(['r0', 'r1'])))
2551
# 2 nodes returned, missing skipped
2552
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2553
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
2556
2374
class TestNoParentsGraphIndexKnit(KnitTests):
2557
2375
"""Tests for knits using KnitGraphIndex with no parents."""
2660
2469
self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2661
2470
self.assertEqual(['fulltext'], index.get_options('parent'))
2663
def test_get_parents(self):
2664
index = self.two_graph_index()
2665
self.assertEqual((), index.get_parents('parent'))
2666
# and errors on ghosts.
2667
self.assertRaises(errors.RevisionNotPresent,
2668
index.get_parents, 'ghost')
2670
2472
def test_get_parents_with_ghosts(self):
2671
2473
index = self.two_graph_index()
2672
2474
self.assertEqual((), index.get_parents_with_ghosts('parent'))
2786
2588
('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2787
2589
self.assertEqual([], self.caught_entries)
2789
def test_iter_parents(self):
2790
index = self.two_graph_index()
2791
self.assertEqual(set([
2792
('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2794
set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2795
self.assertEqual(set([('tip', ())]),
2796
set(index.iter_parents(['tip'])))
2797
self.assertEqual(set(),
2798
set(index.iter_parents([])))
2801
2591
class TestPackKnits(KnitTests):
2802
2592
"""Tests that use a _PackAccess and KnitGraphIndex."""
2853
2643
set(index.get_ancestry(ancestry_versions, False)))
2855
def assertIterParents(self, knit, versions, parent_versions, result):
2856
"""Check the result of an iter_parents call on knit."""
2857
index = self.get_index(knit, knit.get_data_stream(versions))
2858
self.assertEqual(result, index.iter_parents(parent_versions))
2860
2645
def assertGetMethod(self, knit, versions, version, result):
2861
2646
index = self.get_index(knit, knit.get_data_stream(versions))
2862
2647
self.assertEqual(result, index.get_method(version))
2903
2688
"""Constructing a StreamIndex generates index data."""
2904
2689
data_list = [('text-a', ['fulltext'], 127, []),
2905
2690
('text-b', ['option'], 128, ['text-c'])]
2906
index = _StreamIndex(data_list)
2691
index = _StreamIndex(data_list, None)
2907
2692
self.assertEqual({'text-a':(['fulltext'], (0, 127), []),
2908
2693
'text-b':(['option'], (127, 127 + 128), ['text-c'])},
2909
2694
index._by_version)
2927
2712
# we thunk across.
2928
2713
self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2930
def test_iter_parents(self):
2931
knit = self.make_knit_with_4_versions_2_dags()
2932
self.assertIterParents(knit, ['a'], ['a'], [('a', [])])
2933
self.assertIterParents(knit, ['a', 'b'], ['a', 'b'],
2934
[('a', []), ('b', [])])
2935
self.assertIterParents(knit, ['a', 'b', 'c'], ['a', 'b', 'c'],
2936
[('a', []), ('b', []), ('c', ['b', 'a'])])
2937
self.assertIterParents(knit, ['a', 'b', 'c', 'd'],
2938
['a', 'b', 'c', 'd'],
2939
[('a', []), ('b', []), ('c', ['b', 'a']), ('d', ['e', 'f'])])
2940
self.assertIterParents(knit, ['c'], ['a', 'b', 'c'],
2941
[('c', ['b', 'a'])])
2943
2715
def test_get_options(self):
2944
2716
knit = self.make_knit_with_4_versions_2_dags()
2945
2717
self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
2948
2720
def test_get_parents_with_ghosts(self):
2949
2721
knit = self.make_knit_with_4_versions_2_dags()
2950
self.assertGetParentsWithGhosts(knit, ['a'], 'a', [])
2951
self.assertGetParentsWithGhosts(knit, ['c'], 'c', ['b', 'a'])
2952
self.assertGetParentsWithGhosts(knit, ['d'], 'd', ['e', 'f'])
2722
self.assertGetParentsWithGhosts(knit, ['a'], 'a', ())
2723
self.assertGetParentsWithGhosts(knit, ['c'], 'c', ('b', 'a'))
2724
self.assertGetParentsWithGhosts(knit, ['d'], 'd', ('e', 'f'))
2954
2726
def test_get_position(self):
2955
2727
knit = self.make_knit_with_4_versions_2_dags()
3014
2786
target_knit = self.make_test_knit(name='annotated', annotate=True)
3015
2787
source_knit.add_lines("A", [], ["Foo\n"])
3016
2788
# Give the target A, so we can try to thunk across to it.
3017
target_knit.join(source_knit)
2789
target_knit.insert_record_stream(source_knit.get_record_stream(['A'],
2790
'unordered', False))
3018
2791
index, access = self.get_index_access(target_knit,
3019
2792
source_knit.get_data_stream([]))
3020
2793
raw_data = list(access.get_raw_records([(True, "A", None, None)]))[0]
3031
2804
knit.get_data_stream([]))
3032
2805
self.assertRaises(errors.KnitCorrupt,
3033
2806
list, access.get_raw_records([(True, "A", None, None)]))
2809
class TestFormatSignatures(KnitTests):
2811
def test_knit_format_signatures(self):
2812
"""Different formats of knit have different signature strings."""
2813
knit = self.make_test_knit(name='a', annotate=True)
2814
self.assertEqual('knit-annotated', knit.get_format_signature())
2815
knit = self.make_test_knit(name='p', annotate=False)
2816
self.assertEqual('knit-plain', knit.get_format_signature())