383
388
size = trans.put_file('index', stream)
384
389
return GraphIndex(trans, 'index', size)
391
def make_index_with_offset(self, ref_lists=0, key_elements=1, nodes=[],
393
builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
394
for key, value, references in nodes:
395
builder.add_node(key, value, references)
396
content = builder.finish().read()
398
trans = self.get_transport()
399
trans.put_bytes('index', (' '*offset) + content)
400
return GraphIndex(trans, 'index', size, offset=offset)
402
def test_clear_cache(self):
403
index = self.make_index()
404
# For now, we just want to make sure the api is available. As this is
405
# old code, we don't really worry if it *does* anything.
386
408
def test_open_bad_index_no_error(self):
387
409
trans = self.get_transport()
388
410
trans.put_bytes('name', "not an index\n")
389
411
index = GraphIndex(trans, 'name', 13)
413
def test_with_offset(self):
414
nodes = self.make_nodes(200)
415
index = self.make_index_with_offset(offset=1234567, nodes=nodes)
416
self.assertEqual(200, index.key_count())
418
def test_buffer_all_with_offset(self):
419
nodes = self.make_nodes(200)
420
index = self.make_index_with_offset(offset=1234567, nodes=nodes)
422
self.assertEqual(200, index.key_count())
424
def test_side_effect_buffering_with_offset(self):
425
nodes = self.make_nodes(20)
426
index = self.make_index_with_offset(offset=1234567, nodes=nodes)
427
index._transport.recommended_page_size = lambda:64*1024
428
subset_nodes = [nodes[0][0], nodes[10][0], nodes[19][0]]
429
entries = [n[1] for n in index.iter_entries(subset_nodes)]
430
self.assertEqual(sorted(subset_nodes), sorted(entries))
431
self.assertEqual(20, index.key_count())
391
433
def test_open_sets_parsed_map_empty(self):
392
434
index = self.make_index()
393
435
self.assertEqual([], index._parsed_byte_map)
954
996
self.assertEqual(set([]), index.external_references(0))
998
def test__find_ancestors(self):
1001
index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1002
(key1, 'value', ([key2],)),
1003
(key2, 'value', ([],)),
1006
missing_keys = set()
1007
search_keys = index._find_ancestors([key1], 0, parent_map, missing_keys)
1008
self.assertEqual({key1: (key2,)}, parent_map)
1009
self.assertEqual(set(), missing_keys)
1010
self.assertEqual(set([key2]), search_keys)
1011
search_keys = index._find_ancestors(search_keys, 0, parent_map,
1013
self.assertEqual({key1: (key2,), key2: ()}, parent_map)
1014
self.assertEqual(set(), missing_keys)
1015
self.assertEqual(set(), search_keys)
1017
def test__find_ancestors_w_missing(self):
1021
index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1022
(key1, 'value', ([key2],)),
1023
(key2, 'value', ([],)),
1026
missing_keys = set()
1027
search_keys = index._find_ancestors([key2, key3], 0, parent_map,
1029
self.assertEqual({key2: ()}, parent_map)
1030
self.assertEqual(set([key3]), missing_keys)
1031
self.assertEqual(set(), search_keys)
1033
def test__find_ancestors_dont_search_known(self):
1037
index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1038
(key1, 'value', ([key2],)),
1039
(key2, 'value', ([key3],)),
1040
(key3, 'value', ([],)),
1042
# We already know about key2, so we won't try to search for key3
1043
parent_map = {key2: (key3,)}
1044
missing_keys = set()
1045
search_keys = index._find_ancestors([key1], 0, parent_map,
1047
self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
1048
self.assertEqual(set(), missing_keys)
1049
self.assertEqual(set(), search_keys)
1051
def test_supports_unlimited_cache(self):
1052
builder = GraphIndexBuilder(0, key_elements=1)
1053
stream = builder.finish()
1054
trans = get_transport(self.get_url())
1055
size = trans.put_file('index', stream)
1056
# It doesn't matter what unlimited_cache does here, just that it can be
1058
index = GraphIndex(trans, 'index', size, unlimited_cache=True)
957
1061
class TestCombinedGraphIndex(TestCaseWithMemoryTransport):
1009
1113
index.insert_index(0, index1)
1010
1114
self.assertEqual([(index1, ('key', ), '')], list(index.iter_all_entries()))
1116
def test_clear_cache(self):
1119
class ClearCacheProxy(object):
1121
def __init__(self, index):
1124
def __getattr__(self, name):
1125
return getattr(self._index)
1127
def clear_cache(self):
1128
log.append(self._index)
1129
return self._index.clear_cache()
1131
index = CombinedGraphIndex([])
1132
index1 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
1133
index.insert_index(0, ClearCacheProxy(index1))
1134
index2 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
1135
index.insert_index(1, ClearCacheProxy(index2))
1136
# CombinedGraphIndex should call 'clear_cache()' on all children
1138
self.assertEqual(sorted([index1, index2]), sorted(log))
1012
1140
def test_iter_all_entries_empty(self):
1013
1141
index = CombinedGraphIndex([])
1014
1142
self.assertEqual([], list(index.iter_all_entries()))
1252
1380
self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1384
def make_index_with_simple_nodes(self, name, num_nodes=1):
1385
"""Make an index named after 'name', with keys named after 'name' too.
1387
Nodes will have a value of '' and no references.
1390
(('index-%s-key-%s' % (name, n),), '', ())
1391
for n in range(1, num_nodes+1)]
1392
return self.make_index('index-%s' % name, 0, nodes=nodes)
1394
def test_reorder_after_iter_entries(self):
1395
# Four indices: [key1] in index1, [key2,key3] in index2, [] in index3,
1397
index = CombinedGraphIndex([])
1398
index.insert_index(0, self.make_index_with_simple_nodes('1'), '1')
1399
index.insert_index(1, self.make_index_with_simple_nodes('2'), '2')
1400
index.insert_index(2, self.make_index_with_simple_nodes('3'), '3')
1401
index.insert_index(3, self.make_index_with_simple_nodes('4'), '4')
1402
index1, index2, index3, index4 = index._indices
1403
# Query a key from index4 and index2.
1404
self.assertLength(2, list(index.iter_entries(
1405
[('index-4-key-1',), ('index-2-key-1',)])))
1406
# Now index2 and index4 should be moved to the front (and index1 should
1407
# still be before index3).
1408
self.assertEqual([index2, index4, index1, index3], index._indices)
1409
self.assertEqual(['2', '4', '1', '3'], index._index_names)
1411
def test_reorder_propagates_to_siblings(self):
1412
# Two CombinedGraphIndex objects, with the same number of indicies with
1414
cgi1 = CombinedGraphIndex([])
1415
cgi2 = CombinedGraphIndex([])
1416
cgi1.insert_index(0, self.make_index_with_simple_nodes('1-1'), 'one')
1417
cgi1.insert_index(1, self.make_index_with_simple_nodes('1-2'), 'two')
1418
cgi2.insert_index(0, self.make_index_with_simple_nodes('2-1'), 'one')
1419
cgi2.insert_index(1, self.make_index_with_simple_nodes('2-2'), 'two')
1420
index2_1, index2_2 = cgi2._indices
1421
cgi1.set_sibling_indices([cgi2])
1422
# Trigger a reordering in cgi1. cgi2 will be reordered as well.
1423
list(cgi1.iter_entries([('index-1-2-key-1',)]))
1424
self.assertEqual([index2_2, index2_1], cgi2._indices)
1425
self.assertEqual(['two', 'one'], cgi2._index_names)
1255
1427
def test_validate_reloads(self):
1256
1428
index, reload_counter = self.make_combined_index_with_missing()
1257
1429
index.validate()
1271
1443
['1', '2', '3'])
1272
1444
self.assertRaises(errors.NoSuchFile, index.validate)
1446
def test_find_ancestors_across_indexes(self):
1451
index1 = self.make_index('12', ref_lists=1, nodes=[
1452
(key1, 'value', ([],)),
1453
(key2, 'value', ([key1],)),
1455
index2 = self.make_index('34', ref_lists=1, nodes=[
1456
(key3, 'value', ([key2],)),
1457
(key4, 'value', ([key3],)),
1459
c_index = CombinedGraphIndex([index1, index2])
1460
parent_map, missing_keys = c_index.find_ancestry([key1], 0)
1461
self.assertEqual({key1: ()}, parent_map)
1462
self.assertEqual(set(), missing_keys)
1463
# Now look for a key from index2 which requires us to find the key in
1464
# the second index, and then continue searching for parents in the
1466
parent_map, missing_keys = c_index.find_ancestry([key3], 0)
1467
self.assertEqual({key1: (), key2: (key1,), key3: (key2,)}, parent_map)
1468
self.assertEqual(set(), missing_keys)
1470
def test_find_ancestors_missing_keys(self):
1475
index1 = self.make_index('12', ref_lists=1, nodes=[
1476
(key1, 'value', ([],)),
1477
(key2, 'value', ([key1],)),
1479
index2 = self.make_index('34', ref_lists=1, nodes=[
1480
(key3, 'value', ([key2],)),
1482
c_index = CombinedGraphIndex([index1, index2])
1483
# Searching for a key which is actually not present at all should
1484
# eventually converge
1485
parent_map, missing_keys = c_index.find_ancestry([key4], 0)
1486
self.assertEqual({}, parent_map)
1487
self.assertEqual(set([key4]), missing_keys)
1489
def test_find_ancestors_no_indexes(self):
1490
c_index = CombinedGraphIndex([])
1492
parent_map, missing_keys = c_index.find_ancestry([key1], 0)
1493
self.assertEqual({}, parent_map)
1494
self.assertEqual(set([key1]), missing_keys)
1496
def test_find_ancestors_ghost_parent(self):
1501
index1 = self.make_index('12', ref_lists=1, nodes=[
1502
(key1, 'value', ([],)),
1503
(key2, 'value', ([key1],)),
1505
index2 = self.make_index('34', ref_lists=1, nodes=[
1506
(key4, 'value', ([key2, key3],)),
1508
c_index = CombinedGraphIndex([index1, index2])
1509
# Searching for a key which is actually not present at all should
1510
# eventually converge
1511
parent_map, missing_keys = c_index.find_ancestry([key4], 0)
1512
self.assertEqual({key4: (key2, key3), key2: (key1,), key1: ()},
1514
self.assertEqual(set([key3]), missing_keys)
1516
def test__find_ancestors_empty_index(self):
1517
index = self.make_index('test', ref_lists=1, key_elements=1, nodes=[])
1519
missing_keys = set()
1520
search_keys = index._find_ancestors([('one',), ('two',)], 0, parent_map,
1522
self.assertEqual(set(), search_keys)
1523
self.assertEqual({}, parent_map)
1524
self.assertEqual(set([('one',), ('two',)]), missing_keys)
1275
1527
class TestInMemoryGraphIndex(TestCaseWithMemoryTransport):