964
915
index = self.make_index(nodes=[(('key', ), 'value', ())])
967
# XXX: external_references tests are duplicated in test_btree_index. We
968
# probably should have per_graph_index tests...
969
def test_external_references_no_refs(self):
970
index = self.make_index(ref_lists=0, nodes=[])
971
self.assertRaises(ValueError, index.external_references, 0)
973
def test_external_references_no_results(self):
974
index = self.make_index(ref_lists=1, nodes=[
975
(('key',), 'value', ([],))])
976
self.assertEqual(set(), index.external_references(0))
978
def test_external_references_missing_ref(self):
979
missing_key = ('missing',)
980
index = self.make_index(ref_lists=1, nodes=[
981
(('key',), 'value', ([missing_key],))])
982
self.assertEqual(set([missing_key]), index.external_references(0))
984
def test_external_references_multiple_ref_lists(self):
985
missing_key = ('missing',)
986
index = self.make_index(ref_lists=2, nodes=[
987
(('key',), 'value', ([], [missing_key]))])
988
self.assertEqual(set([]), index.external_references(0))
989
self.assertEqual(set([missing_key]), index.external_references(1))
991
def test_external_references_two_records(self):
992
index = self.make_index(ref_lists=1, nodes=[
993
(('key-1',), 'value', ([('key-2',)],)),
994
(('key-2',), 'value', ([],)),
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)
1061
919
class TestCombinedGraphIndex(TestCaseWithMemoryTransport):
1269
1070
index = CombinedGraphIndex([])
1270
1071
index.validate()
1272
def test_key_count_reloads(self):
1273
index, reload_counter = self.make_combined_index_with_missing()
1274
self.assertEqual(2, index.key_count())
1275
self.assertEqual([1, 1, 0], reload_counter)
1277
def test_key_count_no_reload(self):
1278
index, reload_counter = self.make_combined_index_with_missing()
1279
index._reload_func = None
1280
# Without a _reload_func we just raise the exception
1281
self.assertRaises(errors.NoSuchFile, index.key_count)
1283
def test_key_count_reloads_and_fails(self):
1284
# We have deleted all underlying indexes, so we will try to reload, but
1285
# still fail. This is mostly to test we don't get stuck in an infinite
1286
# loop trying to reload
1287
index, reload_counter = self.make_combined_index_with_missing(
1289
self.assertRaises(errors.NoSuchFile, index.key_count)
1290
self.assertEqual([2, 1, 1], reload_counter)
1292
def test_iter_entries_reloads(self):
1293
index, reload_counter = self.make_combined_index_with_missing()
1294
result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1295
index3 = index._indices[0]
1296
self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1298
self.assertEqual([1, 1, 0], reload_counter)
1300
def test_iter_entries_reloads_midway(self):
1301
# The first index still looks present, so we get interrupted mid-way
1303
index, reload_counter = self.make_combined_index_with_missing(['2'])
1304
index1, index2 = index._indices
1305
result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1306
index3 = index._indices[0]
1307
# We had already yielded '1', so we just go on to the next, we should
1308
# not yield '1' twice.
1309
self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1311
self.assertEqual([1, 1, 0], reload_counter)
1313
def test_iter_entries_no_reload(self):
1314
index, reload_counter = self.make_combined_index_with_missing()
1315
index._reload_func = None
1316
# Without a _reload_func we just raise the exception
1317
self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1319
def test_iter_entries_reloads_and_fails(self):
1320
index, reload_counter = self.make_combined_index_with_missing(
1322
self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1323
self.assertEqual([2, 1, 1], reload_counter)
1325
def test_iter_all_entries_reloads(self):
1326
index, reload_counter = self.make_combined_index_with_missing()
1327
result = list(index.iter_all_entries())
1328
index3 = index._indices[0]
1329
self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1331
self.assertEqual([1, 1, 0], reload_counter)
1333
def test_iter_all_entries_reloads_midway(self):
1334
index, reload_counter = self.make_combined_index_with_missing(['2'])
1335
index1, index2 = index._indices
1336
result = list(index.iter_all_entries())
1337
index3 = index._indices[0]
1338
# We had already yielded '1', so we just go on to the next, we should
1339
# not yield '1' twice.
1340
self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1342
self.assertEqual([1, 1, 0], reload_counter)
1344
def test_iter_all_entries_no_reload(self):
1345
index, reload_counter = self.make_combined_index_with_missing()
1346
index._reload_func = None
1347
self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
1349
def test_iter_all_entries_reloads_and_fails(self):
1350
index, reload_counter = self.make_combined_index_with_missing(
1352
self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
1354
def test_iter_entries_prefix_reloads(self):
1355
index, reload_counter = self.make_combined_index_with_missing()
1356
result = list(index.iter_entries_prefix([('1',)]))
1357
index3 = index._indices[0]
1358
self.assertEqual([(index3, ('1',), '')], result)
1359
self.assertEqual([1, 1, 0], reload_counter)
1361
def test_iter_entries_prefix_reloads_midway(self):
1362
index, reload_counter = self.make_combined_index_with_missing(['2'])
1363
index1, index2 = index._indices
1364
result = list(index.iter_entries_prefix([('1',)]))
1365
index3 = index._indices[0]
1366
# We had already yielded '1', so we just go on to the next, we should
1367
# not yield '1' twice.
1368
self.assertEqual([(index1, ('1',), '')], result)
1369
self.assertEqual([1, 1, 0], reload_counter)
1371
def test_iter_entries_prefix_no_reload(self):
1372
index, reload_counter = self.make_combined_index_with_missing()
1373
index._reload_func = None
1374
self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1377
def test_iter_entries_prefix_reloads_and_fails(self):
1378
index, reload_counter = self.make_combined_index_with_missing(
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)
1427
def test_validate_reloads(self):
1428
index, reload_counter = self.make_combined_index_with_missing()
1430
self.assertEqual([1, 1, 0], reload_counter)
1432
def test_validate_reloads_midway(self):
1433
index, reload_counter = self.make_combined_index_with_missing(['2'])
1436
def test_validate_no_reload(self):
1437
index, reload_counter = self.make_combined_index_with_missing()
1438
index._reload_func = None
1439
self.assertRaises(errors.NoSuchFile, index.validate)
1441
def test_validate_reloads_and_fails(self):
1442
index, reload_counter = self.make_combined_index_with_missing(
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)
1527
1074
class TestInMemoryGraphIndex(TestCaseWithMemoryTransport):