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")])
677
659
self.assertRaises(RevisionNotPresent,
678
660
index.get_ancestry_with_ghosts, ["e"])
680
def test_iter_parents(self):
681
transport = MockTransport()
682
index = self.get_knit_index(transport, "filename", "w", create=True)
684
index.add_version('r0', ['option'], (None, 0, 1), [])
686
index.add_version('r1', ['option'], (None, 0, 1), ['r0'])
688
index.add_version('r2', ['option'], (None, 0, 1), ['r1', 'r0'])
690
# cases: each sample data individually:
691
self.assertEqual(set([('r0', ())]),
692
set(index.iter_parents(['r0'])))
693
self.assertEqual(set([('r1', ('r0', ))]),
694
set(index.iter_parents(['r1'])))
695
self.assertEqual(set([('r2', ('r1', 'r0'))]),
696
set(index.iter_parents(['r2'])))
697
# no nodes returned for a missing node
698
self.assertEqual(set(),
699
set(index.iter_parents(['missing'])))
700
# 1 node returned with missing nodes skipped
701
self.assertEqual(set([('r1', ('r0', ))]),
702
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
704
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
705
set(index.iter_parents(['r0', 'r1'])))
706
# 2 nodes returned, missing skipped
707
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
708
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
710
662
def test_num_versions(self):
711
663
transport = MockTransport([
712
664
_KnitIndex.HEADER
1075
1027
self.addCleanup(reset)
1076
1028
from bzrlib._knit_load_data_c import _load_data_c
1077
1029
knit._load_data = _load_data_c
1078
return _KnitIndex(*args, **kwargs)
1030
return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
1082
1033
class KnitTests(TestCaseWithTransport):
1083
1034
"""Class containing knit test helper routines."""
1085
1036
def make_test_knit(self, annotate=False, delay_create=False, index=None,
1037
name='test', delta=True, access_mode='w'):
1087
1038
if not annotate:
1088
1039
factory = KnitPlainFactory()
1091
return KnitVersionedFile(name, get_transport('.'), access_mode='w',
1092
factory=factory, create=True,
1093
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)
1095
1053
def assertRecordContentEqual(self, knit, version_id, candidate_content):
1096
1054
"""Assert that some raw record content matches the raw record content
1162
1120
k = self.make_test_knit()
1163
1121
k.add_lines('text-1', [], split_lines(TEXT_1))
1165
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)
1166
1125
self.assertTrue(k2.has_version('text-1'))
1167
1126
self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1190
1149
def test_incomplete(self):
1191
1150
"""Test if texts without a ending line-end can be inserted and
1193
k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1152
k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1194
1153
k.add_lines('text-1', [], ['a\n', 'b' ])
1195
1154
k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1196
1155
# reopening ensures maximum room for confusion
1197
k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1156
k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1198
1157
self.assertEquals(k.get_lines('text-1'), ['a\n', 'b' ])
1199
1158
self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1223
1182
def test_add_delta(self):
1224
1183
"""Store in knit with parents"""
1225
k = KnitVersionedFile('test', get_transport('.'), factory=KnitPlainFactory(),
1226
delta=True, create=True)
1184
k = self.make_test_knit(annotate=False)
1227
1185
self.add_stock_one_and_one_a(k)
1229
1186
self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1231
1188
def test_add_delta_knit_graph_index(self):
1233
1190
index = InMemoryGraphIndex(2)
1234
1191
knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1236
k = KnitVersionedFile('test', get_transport('.'),
1237
delta=True, create=True, index=knit_index)
1193
k = self.make_test_knit(annotate=True, index=knit_index)
1238
1194
self.add_stock_one_and_one_a(k)
1240
1195
self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1241
1196
# check the index had the right data added.
1242
1197
self.assertEqual(set([
1264
1218
def test_annotate_fulltext(self):
1265
1219
"""Annotations"""
1266
k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1267
delta=False, create=True)
1220
k = self.make_test_knit(annotate=True, name='knit', delta=False)
1268
1221
self.insert_and_test_small_annotate(k)
1270
1223
def test_annotate_merge_1(self):
1341
1294
self.assertEquals(origins[2], ('text-1', 'c\n'))
1343
1296
def _test_join_with_factories(self, k1_factory, k2_factory):
1344
k1 = KnitVersionedFile('test1', get_transport('.'), factory=k1_factory, create=True)
1297
k1 = make_file_knit('test1', get_transport('.'), factory=k1_factory, create=True)
1345
1298
k1.add_lines('text-a', [], ['a1\n', 'a2\n', 'a3\n'])
1346
1299
k1.add_lines('text-b', ['text-a'], ['a1\n', 'b2\n', 'a3\n'])
1347
1300
k1.add_lines('text-c', [], ['c1\n', 'c2\n', 'c3\n'])
1348
1301
k1.add_lines('text-d', ['text-c'], ['c1\n', 'd2\n', 'd3\n'])
1349
1302
k1.add_lines('text-m', ['text-b', 'text-d'], ['a1\n', 'b2\n', 'd3\n'])
1350
k2 = KnitVersionedFile('test2', get_transport('.'), factory=k2_factory, create=True)
1303
k2 = make_file_knit('test2', get_transport('.'), factory=k2_factory, create=True)
1351
1304
count = k2.join(k1, version_ids=['text-m'])
1352
1305
self.assertEquals(count, 5)
1353
1306
self.assertTrue(k2.has_version('text-a'))
1374
1327
self._test_join_with_factories(KnitPlainFactory(), None)
1376
1329
def test_reannotate(self):
1377
k1 = KnitVersionedFile('knit1', get_transport('.'),
1330
k1 = make_file_knit('knit1', get_transport('.'),
1378
1331
factory=KnitAnnotateFactory(), create=True)
1380
1333
k1.add_lines('text-a', [], ['a\n', 'b\n'])
1382
1335
k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1384
k2 = KnitVersionedFile('test2', get_transport('.'),
1337
k2 = make_file_knit('test2', get_transport('.'),
1385
1338
factory=KnitAnnotateFactory(), create=True)
1386
1339
k2.join(k1, version_ids=['text-b'])
1405
1358
def test_get_line_delta_texts(self):
1406
1359
"""Make sure we can call get_texts on text with reused line deltas"""
1407
k1 = KnitVersionedFile('test1', get_transport('.'),
1408
factory=KnitPlainFactory(), create=True)
1360
k1 = make_file_knit('test1', get_transport('.'),
1361
factory=KnitPlainFactory(), create=True)
1409
1362
for t in range(3):
1417
1370
def test_iter_lines_reads_in_order(self):
1418
1371
instrumented_t = get_transport('trace+memory:///')
1419
k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
1372
k1 = make_file_knit('id', instrumented_t, create=True, delta=True)
1420
1373
self.assertEqual([('get', 'id.kndx',)], instrumented_t._activity)
1421
1374
# add texts with no required ordering
1422
1375
k1.add_lines('base', [], ['text\n'])
1423
1376
k1.add_lines('base2', [], ['text2\n'])
1425
1377
# clear the logged activity, but preserve the list instance in case of
1426
1378
# clones pointing at it.
1427
1379
del instrumented_t._activity[:]
1452
1404
"\nrevid2 line-delta 84 82 0 :",
1454
1406
# we should be able to load this file again
1455
knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
1407
knit = make_file_knit('test', get_transport('.'), access_mode='r')
1456
1408
self.assertEqual(['revid', 'revid2'], knit.versions())
1457
1409
# write a short write to the file and ensure that its ignored
1458
1410
indexfile = file('test.kndx', 'ab')
1459
1411
indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1460
1412
indexfile.close()
1461
1413
# we should be able to load this file again
1462
knit = KnitVersionedFile('test', get_transport('.'), access_mode='w')
1414
knit = make_file_knit('test', get_transport('.'), access_mode='w')
1463
1415
self.assertEqual(['revid', 'revid2'], knit.versions())
1464
1416
# and add a revision with the same id the failed write had
1465
1417
knit.add_lines('revid3', ['revid2'], ['a\n'])
1466
1418
# and when reading it revid3 should now appear.
1467
knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
1419
knit = make_file_knit('test', get_transport('.'), access_mode='r')
1468
1420
self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
1469
1421
self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
1485
1437
"""create_parent_dir can create knits in nonexistant dirs"""
1486
1438
# Has no effect if we don't set 'delay_create'
1487
1439
trans = get_transport('.')
1488
self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
1440
self.assertRaises(NoSuchFile, make_file_knit, 'dir/test',
1489
1441
trans, access_mode='w', factory=None,
1490
1442
create=True, create_parent_dir=True)
1491
1443
# Nothing should have changed yet
1492
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1444
knit = make_file_knit('dir/test', trans, access_mode='w',
1493
1445
factory=None, create=True,
1494
1446
create_parent_dir=True,
1495
1447
delay_create=True)
1510
1462
if not trans._can_roundtrip_unix_modebits():
1511
1463
# Can't roundtrip, so no need to run this test
1513
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1514
factory=None, create=True,
1515
create_parent_dir=True,
1465
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1466
create=True, create_parent_dir=True, delay_create=True,
1467
file_mode=0600, dir_mode=0700)
1519
1468
knit.add_lines('revid', [], ['a\n'])
1520
1469
self.assertTransportMode(trans, 'dir', 0700)
1521
1470
self.assertTransportMode(trans, 'dir/test.knit', 0600)
1526
1475
if not trans._can_roundtrip_unix_modebits():
1527
1476
# Can't roundtrip, so no need to run this test
1529
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1530
factory=None, create=True,
1531
create_parent_dir=True,
1478
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1479
create=True, create_parent_dir=True, delay_create=True,
1480
file_mode=0660, dir_mode=0770)
1535
1481
knit.add_lines('revid', [], ['a\n'])
1536
1482
self.assertTransportMode(trans, 'dir', 0770)
1537
1483
self.assertTransportMode(trans, 'dir/test.knit', 0660)
1542
1488
if not trans._can_roundtrip_unix_modebits():
1543
1489
# Can't roundtrip, so no need to run this test
1545
knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1546
factory=None, create=True,
1547
create_parent_dir=True,
1491
knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1492
create=True, create_parent_dir=True, delay_create=True,
1493
file_mode=0666, dir_mode=0777)
1551
1494
knit.add_lines('revid', [], ['a\n'])
1552
1495
self.assertTransportMode(trans, 'dir', 0777)
1553
1496
self.assertTransportMode(trans, 'dir/test.knit', 0666)
1924
1867
errors.KnitDataStreamUnknown,
1925
1868
target.insert_data_stream, data_stream)
1870
def test_insert_data_stream_bug_208418(self):
1871
"""You can insert a stream with an incompatible format, even when:
1872
* the stream has a line-delta record,
1873
* whose parent is in the target, also stored as a line-delta
1875
See <https://launchpad.net/bugs/208418>.
1877
base_lines = split_lines(TEXT_1)
1879
target = self.make_test_knit(name='target', annotate=True)
1880
target.add_lines('version-1', [], base_lines)
1881
target.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1882
# The second record should be a delta.
1883
self.assertEqual('line-delta', target._index.get_method('version-2'))
1885
# Make a source, with a different format, but the same data
1886
source = self.make_test_knit(name='source', annotate=False)
1887
source.add_lines('version-1', [], base_lines)
1888
source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1889
# Now add another record, which should be stored as a delta against
1891
source.add_lines('version-3', ['version-2'], base_lines + ['b\n'])
1892
self.assertEqual('line-delta', source._index.get_method('version-3'))
1894
# Make a stream of the new version
1895
data_stream = source.get_data_stream(['version-3'])
1896
# And insert into the target
1897
target.insert_data_stream(data_stream)
1898
# No errors should have been raised.
1927
1901
# * test that a stream of "already present version, then new version"
1928
1902
# inserts correctly.
2064
2038
self.failIf(WeaveToKnit.is_compatible(k, k))
2067
class TestKnitCaching(KnitTests):
2069
def create_knit(self):
2070
k = self.make_test_knit(True)
2071
k.add_lines('text-1', [], split_lines(TEXT_1))
2072
k.add_lines('text-2', [], split_lines(TEXT_2))
2075
def test_no_caching(self):
2076
k = self.create_knit()
2077
# Nothing should be cached without setting 'enable_cache'
2078
self.assertEqual({}, k._data._cache)
2080
def test_cache_data_read_raw(self):
2081
k = self.create_knit()
2083
# Now cache and read
2086
def read_one_raw(version):
2087
pos_map = k._get_components_positions([version])
2088
method, index_memo, next = pos_map[version]
2089
lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
2090
self.assertEqual(1, len(lst))
2093
val = read_one_raw('text-1')
2094
self.assertEqual({'text-1':val[1]}, k._data._cache)
2097
# After clear, new reads are not cached
2098
self.assertEqual({}, k._data._cache)
2100
val2 = read_one_raw('text-1')
2101
self.assertEqual(val, val2)
2102
self.assertEqual({}, k._data._cache)
2104
def test_cache_data_read(self):
2105
k = self.create_knit()
2107
def read_one(version):
2108
pos_map = k._get_components_positions([version])
2109
method, index_memo, next = pos_map[version]
2110
lst = list(k._data.read_records_iter([(version, index_memo)]))
2111
self.assertEqual(1, len(lst))
2114
# Now cache and read
2117
val = read_one('text-2')
2118
self.assertEqual(['text-2'], k._data._cache.keys())
2119
self.assertEqual('text-2', val[0])
2120
content, digest = k._data._parse_record('text-2',
2121
k._data._cache['text-2'])
2122
self.assertEqual(content, val[1])
2123
self.assertEqual(digest, val[2])
2126
self.assertEqual({}, k._data._cache)
2128
val2 = read_one('text-2')
2129
self.assertEqual(val, val2)
2130
self.assertEqual({}, k._data._cache)
2132
def test_cache_read(self):
2133
k = self.create_knit()
2136
text = k.get_text('text-1')
2137
self.assertEqual(TEXT_1, text)
2138
self.assertEqual(['text-1'], k._data._cache.keys())
2141
self.assertEqual({}, k._data._cache)
2143
text = k.get_text('text-1')
2144
self.assertEqual(TEXT_1, text)
2145
self.assertEqual({}, k._data._cache)
2148
2041
class TestKnitIndex(KnitTests):
2150
2043
def test_add_versions_dictionary_compresses(self):
2485
2378
('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2486
2379
self.assertEqual([], self.caught_entries)
2488
def test_iter_parents(self):
2489
index1 = self.make_g_index('1', 1, [
2491
(('r0', ), 'N0 100', ([], )),
2493
(('r1', ), '', ([('r0', )], ))])
2494
index2 = self.make_g_index('2', 1, [
2496
(('r2', ), 'N0 100', ([('r1', ), ('r0', )], )),
2498
combined_index = CombinedGraphIndex([index1, index2])
2499
index = KnitGraphIndex(combined_index)
2501
# cases: each sample data individually:
2502
self.assertEqual(set([('r0', ())]),
2503
set(index.iter_parents(['r0'])))
2504
self.assertEqual(set([('r1', ('r0', ))]),
2505
set(index.iter_parents(['r1'])))
2506
self.assertEqual(set([('r2', ('r1', 'r0'))]),
2507
set(index.iter_parents(['r2'])))
2508
# no nodes returned for a missing node
2509
self.assertEqual(set(),
2510
set(index.iter_parents(['missing'])))
2511
# 1 node returned with missing nodes skipped
2512
self.assertEqual(set([('r1', ('r0', ))]),
2513
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
2515
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2516
set(index.iter_parents(['r0', 'r1'])))
2517
# 2 nodes returned, missing skipped
2518
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2519
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
2522
2381
class TestNoParentsGraphIndexKnit(KnitTests):
2523
2382
"""Tests for knits using KnitGraphIndex with no parents."""
2736
2595
('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2737
2596
self.assertEqual([], self.caught_entries)
2739
def test_iter_parents(self):
2740
index = self.two_graph_index()
2741
self.assertEqual(set([
2742
('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2744
set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2745
self.assertEqual(set([('tip', ())]),
2746
set(index.iter_parents(['tip'])))
2747
self.assertEqual(set(),
2748
set(index.iter_parents([])))
2751
2598
class TestPackKnits(KnitTests):
2752
2599
"""Tests that use a _PackAccess and KnitGraphIndex."""
2803
2650
set(index.get_ancestry(ancestry_versions, False)))
2805
def assertIterParents(self, knit, versions, parent_versions, result):
2806
"""Check the result of an iter_parents call on knit."""
2807
index = self.get_index(knit, knit.get_data_stream(versions))
2808
self.assertEqual(result, index.iter_parents(parent_versions))
2810
2652
def assertGetMethod(self, knit, versions, version, result):
2811
2653
index = self.get_index(knit, knit.get_data_stream(versions))
2812
2654
self.assertEqual(result, index.get_method(version))
2877
2719
# we thunk across.
2878
2720
self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2880
def test_iter_parents(self):
2881
knit = self.make_knit_with_4_versions_2_dags()
2882
self.assertIterParents(knit, ['a'], ['a'], [('a', ())])
2883
self.assertIterParents(knit, ['a', 'b'], ['a', 'b'],
2884
[('a', ()), ('b', ())])
2885
self.assertIterParents(knit, ['a', 'b', 'c'], ['a', 'b', 'c'],
2886
[('a', ()), ('b', ()), ('c', ('b', 'a'))])
2887
self.assertIterParents(knit, ['a', 'b', 'c', 'd'],
2888
['a', 'b', 'c', 'd'],
2889
[('a', ()), ('b', ()), ('c', ('b', 'a')), ('d', ('e', 'f'))])
2890
self.assertIterParents(knit, ['c'], ['a', 'b', 'c'],
2891
[('c', ('b', 'a'))])
2893
2722
def test_get_options(self):
2894
2723
knit = self.make_knit_with_4_versions_2_dags()
2895
2724
self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
2981
2810
knit.get_data_stream([]))
2982
2811
self.assertRaises(errors.KnitCorrupt,
2983
2812
list, access.get_raw_records([(True, "A", None, None)]))
2815
class TestFormatSignatures(KnitTests):
2817
def test_knit_format_signatures(self):
2818
"""Different formats of knit have different signature strings."""
2819
knit = self.make_test_knit(name='a', annotate=True)
2820
self.assertEqual('knit-annotated', knit.get_format_signature())
2821
knit = self.make_test_knit(name='p', annotate=False)
2822
self.assertEqual('knit-plain', knit.get_format_signature())