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)
2095
2038
self.failIf(WeaveToKnit.is_compatible(k, k))
2098
class TestKnitCaching(KnitTests):
2100
def create_knit(self):
2101
k = self.make_test_knit(True)
2102
k.add_lines('text-1', [], split_lines(TEXT_1))
2103
k.add_lines('text-2', [], split_lines(TEXT_2))
2106
def test_no_caching(self):
2107
k = self.create_knit()
2108
# Nothing should be cached without setting 'enable_cache'
2109
self.assertEqual({}, k._data._cache)
2111
def test_cache_data_read_raw(self):
2112
k = self.create_knit()
2114
# Now cache and read
2117
def read_one_raw(version):
2118
pos_map = k._get_components_positions([version])
2119
method, index_memo, next = pos_map[version]
2120
lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
2121
self.assertEqual(1, len(lst))
2124
val = read_one_raw('text-1')
2125
self.assertEqual({'text-1':val[1]}, k._data._cache)
2128
# After clear, new reads are not cached
2129
self.assertEqual({}, k._data._cache)
2131
val2 = read_one_raw('text-1')
2132
self.assertEqual(val, val2)
2133
self.assertEqual({}, k._data._cache)
2135
def test_cache_data_read(self):
2136
k = self.create_knit()
2138
def read_one(version):
2139
pos_map = k._get_components_positions([version])
2140
method, index_memo, next = pos_map[version]
2141
lst = list(k._data.read_records_iter([(version, index_memo)]))
2142
self.assertEqual(1, len(lst))
2145
# Now cache and read
2148
val = read_one('text-2')
2149
self.assertEqual(['text-2'], k._data._cache.keys())
2150
self.assertEqual('text-2', val[0])
2151
content, digest = k._data._parse_record('text-2',
2152
k._data._cache['text-2'])
2153
self.assertEqual(content, val[1])
2154
self.assertEqual(digest, val[2])
2157
self.assertEqual({}, k._data._cache)
2159
val2 = read_one('text-2')
2160
self.assertEqual(val, val2)
2161
self.assertEqual({}, k._data._cache)
2163
def test_cache_read(self):
2164
k = self.create_knit()
2167
text = k.get_text('text-1')
2168
self.assertEqual(TEXT_1, text)
2169
self.assertEqual(['text-1'], k._data._cache.keys())
2172
self.assertEqual({}, k._data._cache)
2174
text = k.get_text('text-1')
2175
self.assertEqual(TEXT_1, text)
2176
self.assertEqual({}, k._data._cache)
2179
2041
class TestKnitIndex(KnitTests):
2181
2043
def test_add_versions_dictionary_compresses(self):
2516
2378
('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2517
2379
self.assertEqual([], self.caught_entries)
2519
def test_iter_parents(self):
2520
index1 = self.make_g_index('1', 1, [
2522
(('r0', ), 'N0 100', ([], )),
2524
(('r1', ), '', ([('r0', )], ))])
2525
index2 = self.make_g_index('2', 1, [
2527
(('r2', ), 'N0 100', ([('r1', ), ('r0', )], )),
2529
combined_index = CombinedGraphIndex([index1, index2])
2530
index = KnitGraphIndex(combined_index)
2532
# cases: each sample data individually:
2533
self.assertEqual(set([('r0', ())]),
2534
set(index.iter_parents(['r0'])))
2535
self.assertEqual(set([('r1', ('r0', ))]),
2536
set(index.iter_parents(['r1'])))
2537
self.assertEqual(set([('r2', ('r1', 'r0'))]),
2538
set(index.iter_parents(['r2'])))
2539
# no nodes returned for a missing node
2540
self.assertEqual(set(),
2541
set(index.iter_parents(['missing'])))
2542
# 1 node returned with missing nodes skipped
2543
self.assertEqual(set([('r1', ('r0', ))]),
2544
set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
2546
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2547
set(index.iter_parents(['r0', 'r1'])))
2548
# 2 nodes returned, missing skipped
2549
self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2550
set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
2553
2381
class TestNoParentsGraphIndexKnit(KnitTests):
2554
2382
"""Tests for knits using KnitGraphIndex with no parents."""
2767
2595
('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2768
2596
self.assertEqual([], self.caught_entries)
2770
def test_iter_parents(self):
2771
index = self.two_graph_index()
2772
self.assertEqual(set([
2773
('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2775
set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2776
self.assertEqual(set([('tip', ())]),
2777
set(index.iter_parents(['tip'])))
2778
self.assertEqual(set(),
2779
set(index.iter_parents([])))
2782
2598
class TestPackKnits(KnitTests):
2783
2599
"""Tests that use a _PackAccess and KnitGraphIndex."""
2834
2650
set(index.get_ancestry(ancestry_versions, False)))
2836
def assertIterParents(self, knit, versions, parent_versions, result):
2837
"""Check the result of an iter_parents call on knit."""
2838
index = self.get_index(knit, knit.get_data_stream(versions))
2839
self.assertEqual(result, index.iter_parents(parent_versions))
2841
2652
def assertGetMethod(self, knit, versions, version, result):
2842
2653
index = self.get_index(knit, knit.get_data_stream(versions))
2843
2654
self.assertEqual(result, index.get_method(version))
2908
2719
# we thunk across.
2909
2720
self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2911
def test_iter_parents(self):
2912
knit = self.make_knit_with_4_versions_2_dags()
2913
self.assertIterParents(knit, ['a'], ['a'], [('a', ())])
2914
self.assertIterParents(knit, ['a', 'b'], ['a', 'b'],
2915
[('a', ()), ('b', ())])
2916
self.assertIterParents(knit, ['a', 'b', 'c'], ['a', 'b', 'c'],
2917
[('a', ()), ('b', ()), ('c', ('b', 'a'))])
2918
self.assertIterParents(knit, ['a', 'b', 'c', 'd'],
2919
['a', 'b', 'c', 'd'],
2920
[('a', ()), ('b', ()), ('c', ('b', 'a')), ('d', ('e', 'f'))])
2921
self.assertIterParents(knit, ['c'], ['a', 'b', 'c'],
2922
[('c', ('b', 'a'))])
2924
2722
def test_get_options(self):
2925
2723
knit = self.make_knit_with_4_versions_2_dags()
2926
2724
self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
3012
2810
knit.get_data_stream([]))
3013
2811
self.assertRaises(errors.KnitCorrupt,
3014
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())