644
657
# becuase without a 3-level index, we don't have any internal
646
659
self.assertEqual(internal_node_pre_clear,
647
index._internal_node_cache.keys())
660
set(index._internal_node_cache))
648
661
self.assertEqual(0, len(index._leaf_node_cache))
650
663
def test_trivial_constructor(self):
651
transport = get_transport('trace+' + self.get_url(''))
652
index = btree_index.BTreeGraphIndex(transport, 'index', None)
664
t = transport.get_transport_from_url('trace+' + self.get_url(''))
665
index = btree_index.BTreeGraphIndex(t, 'index', None)
653
666
# Checks the page size at load, but that isn't logged yet.
654
self.assertEqual([], transport._activity)
667
self.assertEqual([], t._activity)
656
669
def test_with_size_constructor(self):
657
transport = get_transport('trace+' + self.get_url(''))
658
index = btree_index.BTreeGraphIndex(transport, 'index', 1)
670
t = transport.get_transport_from_url('trace+' + self.get_url(''))
671
index = btree_index.BTreeGraphIndex(t, 'index', 1)
659
672
# Checks the page size at load, but that isn't logged yet.
660
self.assertEqual([], transport._activity)
673
self.assertEqual([], t._activity)
662
675
def test_empty_key_count_no_size(self):
663
676
builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
664
transport = get_transport('trace+' + self.get_url(''))
665
transport.put_file('index', builder.finish())
666
index = btree_index.BTreeGraphIndex(transport, 'index', None)
667
del transport._activity[:]
668
self.assertEqual([], transport._activity)
677
t = transport.get_transport_from_url('trace+' + self.get_url(''))
678
t.put_file('index', builder.finish())
679
index = btree_index.BTreeGraphIndex(t, 'index', None)
681
self.assertEqual([], t._activity)
669
682
self.assertEqual(0, index.key_count())
670
683
# The entire index should have been requested (as we generally have the
671
684
# size available, and doing many small readvs is inappropriate).
672
685
# We can't tell how much was actually read here, but - check the code.
673
self.assertEqual([('get', 'index')], transport._activity)
686
self.assertEqual([('get', 'index')], t._activity)
675
688
def test_empty_key_count(self):
676
689
builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
677
transport = get_transport('trace+' + self.get_url(''))
678
size = transport.put_file('index', builder.finish())
690
t = transport.get_transport_from_url('trace+' + self.get_url(''))
691
size = t.put_file('index', builder.finish())
679
692
self.assertEqual(72, size)
680
index = btree_index.BTreeGraphIndex(transport, 'index', size)
681
del transport._activity[:]
682
self.assertEqual([], transport._activity)
693
index = btree_index.BTreeGraphIndex(t, 'index', size)
695
self.assertEqual([], t._activity)
683
696
self.assertEqual(0, index.key_count())
684
697
# The entire index should have been read, as 4K > size
685
698
self.assertEqual([('readv', 'index', [(0, 72)], False, None)],
688
701
def test_non_empty_key_count_2_2(self):
689
702
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
690
703
nodes = self.make_nodes(35, 2, 2)
691
704
for node in nodes:
692
705
builder.add_node(*node)
693
transport = get_transport('trace+' + self.get_url(''))
694
size = transport.put_file('index', builder.finish())
695
index = btree_index.BTreeGraphIndex(transport, 'index', size)
696
del transport._activity[:]
697
self.assertEqual([], transport._activity)
706
t = transport.get_transport_from_url('trace+' + self.get_url(''))
707
size = t.put_file('index', builder.finish())
708
index = btree_index.BTreeGraphIndex(t, 'index', size)
710
self.assertEqual([], t._activity)
698
711
self.assertEqual(70, index.key_count())
699
712
# The entire index should have been read, as it is one page long.
700
713
self.assertEqual([('readv', 'index', [(0, size)], False, None)],
702
self.assertEqual(1173, size)
715
self.assertEqualApproxCompressed(1173, size)
704
717
def test_with_offset_no_size(self):
705
718
index = self.make_index_with_offset(key_elements=1, ref_lists=1,
736
749
index.key_count()
737
750
num_pages = index._row_offsets[-1]
738
751
# Reopen with a traced transport and no size
739
trans = get_transport('trace+' + self.get_url())
752
trans = transport.get_transport_from_url('trace+' + self.get_url())
740
753
index = btree_index.BTreeGraphIndex(trans, 'index', None)
741
754
del trans._activity[:]
742
755
nodes = dict(index._read_nodes([0]))
743
self.assertEqual(range(num_pages), nodes.keys())
756
self.assertEqual(list(range(num_pages)), sorted(nodes))
745
758
def test_2_levels_key_count_2_2(self):
746
759
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
747
760
nodes = self.make_nodes(160, 2, 2)
748
761
for node in nodes:
749
762
builder.add_node(*node)
750
transport = get_transport('trace+' + self.get_url(''))
751
size = transport.put_file('index', builder.finish())
752
self.assertEqual(17692, size)
753
index = btree_index.BTreeGraphIndex(transport, 'index', size)
754
del transport._activity[:]
755
self.assertEqual([], transport._activity)
763
t = transport.get_transport_from_url('trace+' + self.get_url(''))
764
size = t.put_file('index', builder.finish())
765
self.assertEqualApproxCompressed(17692, size)
766
index = btree_index.BTreeGraphIndex(t, 'index', size)
768
self.assertEqual([], t._activity)
756
769
self.assertEqual(320, index.key_count())
757
770
# The entire index should not have been read.
758
771
self.assertEqual([('readv', 'index', [(0, 4096)], False, None)],
761
774
def test_validate_one_page(self):
762
775
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
763
776
nodes = self.make_nodes(45, 2, 2)
764
777
for node in nodes:
765
778
builder.add_node(*node)
766
transport = get_transport('trace+' + self.get_url(''))
767
size = transport.put_file('index', builder.finish())
768
index = btree_index.BTreeGraphIndex(transport, 'index', size)
769
del transport._activity[:]
770
self.assertEqual([], transport._activity)
779
t = transport.get_transport_from_url('trace+' + self.get_url(''))
780
size = t.put_file('index', builder.finish())
781
index = btree_index.BTreeGraphIndex(t, 'index', size)
783
self.assertEqual([], t._activity)
772
785
# The entire index should have been read linearly.
773
786
self.assertEqual([('readv', 'index', [(0, size)], False, None)],
775
self.assertEqual(1488, size)
788
self.assertEqualApproxCompressed(1488, size)
777
790
def test_validate_two_pages(self):
778
791
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
779
792
nodes = self.make_nodes(80, 2, 2)
780
793
for node in nodes:
781
794
builder.add_node(*node)
782
transport = get_transport('trace+' + self.get_url(''))
783
size = transport.put_file('index', builder.finish())
795
t = transport.get_transport_from_url('trace+' + self.get_url(''))
796
size = t.put_file('index', builder.finish())
784
797
# Root page, 2 leaf pages
785
self.assertEqual(9339, size)
786
index = btree_index.BTreeGraphIndex(transport, 'index', size)
787
del transport._activity[:]
788
self.assertEqual([], transport._activity)
798
self.assertEqualApproxCompressed(9339, size)
799
index = btree_index.BTreeGraphIndex(t, 'index', size)
801
self.assertEqual([], t._activity)
803
rem = size - 8192 # Number of remaining bytes after second block
790
804
# The entire index should have been read linearly.
791
self.assertEqual([('readv', 'index', [(0, 4096)], False, None),
792
('readv', 'index', [(4096, 4096), (8192, 1147)], False, None)],
806
[('readv', 'index', [(0, 4096)], False, None),
807
('readv', 'index', [(4096, 4096), (8192, rem)], False, None)],
794
809
# XXX: TODO: write some badly-ordered nodes, and some pointers-to-wrong
795
810
# node and make validate find them.
797
812
def test_eq_ne(self):
798
813
# two indices are equal when constructed with the same parameters:
799
transport1 = get_transport('trace+' + self.get_url(''))
800
transport2 = get_transport(self.get_url(''))
802
btree_index.BTreeGraphIndex(transport1, 'index', None) ==
803
btree_index.BTreeGraphIndex(transport1, 'index', None))
805
btree_index.BTreeGraphIndex(transport1, 'index', 20) ==
806
btree_index.BTreeGraphIndex(transport1, 'index', 20))
808
btree_index.BTreeGraphIndex(transport1, 'index', 20) ==
809
btree_index.BTreeGraphIndex(transport2, 'index', 20))
811
btree_index.BTreeGraphIndex(transport1, 'inde1', 20) ==
812
btree_index.BTreeGraphIndex(transport1, 'inde2', 20))
814
btree_index.BTreeGraphIndex(transport1, 'index', 10) ==
815
btree_index.BTreeGraphIndex(transport1, 'index', 20))
817
btree_index.BTreeGraphIndex(transport1, 'index', None) !=
818
btree_index.BTreeGraphIndex(transport1, 'index', None))
820
btree_index.BTreeGraphIndex(transport1, 'index', 20) !=
821
btree_index.BTreeGraphIndex(transport1, 'index', 20))
823
btree_index.BTreeGraphIndex(transport1, 'index', 20) !=
824
btree_index.BTreeGraphIndex(transport2, 'index', 20))
826
btree_index.BTreeGraphIndex(transport1, 'inde1', 20) !=
827
btree_index.BTreeGraphIndex(transport1, 'inde2', 20))
829
btree_index.BTreeGraphIndex(transport1, 'index', 10) !=
830
btree_index.BTreeGraphIndex(transport1, 'index', 20))
814
t1 = transport.get_transport_from_url('trace+' + self.get_url(''))
815
t2 = self.get_transport()
817
btree_index.BTreeGraphIndex(t1, 'index', None) ==
818
btree_index.BTreeGraphIndex(t1, 'index', None))
820
btree_index.BTreeGraphIndex(t1, 'index', 20) ==
821
btree_index.BTreeGraphIndex(t1, 'index', 20))
823
btree_index.BTreeGraphIndex(t1, 'index', 20) ==
824
btree_index.BTreeGraphIndex(t2, 'index', 20))
826
btree_index.BTreeGraphIndex(t1, 'inde1', 20) ==
827
btree_index.BTreeGraphIndex(t1, 'inde2', 20))
829
btree_index.BTreeGraphIndex(t1, 'index', 10) ==
830
btree_index.BTreeGraphIndex(t1, 'index', 20))
832
btree_index.BTreeGraphIndex(t1, 'index', None) !=
833
btree_index.BTreeGraphIndex(t1, 'index', None))
835
btree_index.BTreeGraphIndex(t1, 'index', 20) !=
836
btree_index.BTreeGraphIndex(t1, 'index', 20))
838
btree_index.BTreeGraphIndex(t1, 'index', 20) !=
839
btree_index.BTreeGraphIndex(t2, 'index', 20))
841
btree_index.BTreeGraphIndex(t1, 'inde1', 20) !=
842
btree_index.BTreeGraphIndex(t1, 'inde2', 20))
844
btree_index.BTreeGraphIndex(t1, 'index', 10) !=
845
btree_index.BTreeGraphIndex(t1, 'index', 20))
847
def test_key_too_big(self):
848
# the size that matters here is the _compressed_ size of the key, so we can't
849
# do a simple character repeat.
850
bigKey = ''.join(map(repr, range(btree_index._PAGE_SIZE)))
851
self.assertRaises(errors.BadIndexKey,
853
nodes=[((bigKey,), 'value', ())])
832
855
def test_iter_all_only_root_no_size(self):
833
856
self.make_index(nodes=[(('key',), 'value', ())])
834
trans = get_transport('trace+' + self.get_url(''))
835
index = btree_index.BTreeGraphIndex(trans, 'index', None)
836
del trans._activity[:]
857
t = transport.get_transport_from_url('trace+' + self.get_url(''))
858
index = btree_index.BTreeGraphIndex(t, 'index', None)
837
860
self.assertEqual([(('key',), 'value')],
838
861
[x[1:] for x in index.iter_all_entries()])
839
self.assertEqual([('get', 'index')], trans._activity)
862
self.assertEqual([('get', 'index')], t._activity)
841
864
def test_iter_all_entries_reads(self):
842
865
# iterating all entries reads the header, then does a linear
873
894
# The entire index should have been read
874
895
total_pages = sum(index._row_lengths)
875
896
self.assertEqual(total_pages, index._row_offsets[-1])
876
self.assertEqual(1303220, size)
897
self.assertEqualApproxCompressed(1303220, size)
877
898
# The start of the leaves
878
899
first_byte = index._row_offsets[-2] * page_size
879
900
readv_request = []
880
901
for offset in range(first_byte, size, page_size):
881
902
readv_request.append((offset, page_size))
882
903
# The last page is truncated
883
readv_request[-1] = (readv_request[-1][0], 1303220 % page_size)
904
readv_request[-1] = (readv_request[-1][0], size % page_size)
884
905
expected = [('readv', 'index', [(0, page_size)], False, None),
885
906
('readv', 'index', readv_request, False, None)]
886
if expected != transport._activity:
907
if expected != t._activity:
887
908
self.assertEqualDiff(pprint.pformat(expected),
888
pprint.pformat(transport._activity))
909
pprint.pformat(t._activity))
890
911
def _test_iter_entries_references_resolved(self):
891
912
index = self.make_index(1, nodes=[
892
913
(('name', ), 'data', ([('ref', ), ('ref', )], )),
893
914
(('ref', ), 'refdata', ([], ))])
894
self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),('ref',)),)),
895
(index, ('ref', ), 'refdata', ((), ))]),
915
self.assertEqual({(index, ('name', ), 'data', ((('ref',),('ref',)),)),
916
(index, ('ref', ), 'refdata', ((), ))},
896
917
set(index.iter_entries([('name',), ('ref',)])))
898
919
def test_iter_entries_references_2_refs_resolved(self):
943
964
index = self.make_index( nodes=[
944
965
(('name', ), 'data', ()),
945
966
(('ref', ), 'refdata', ())])
946
self.assertEqual(set([(index, ('name', ), 'data'),
947
(index, ('ref', ), 'refdata')]),
967
self.assertEqual({(index, ('name', ), 'data'),
968
(index, ('ref', ), 'refdata')},
948
969
set(index.iter_entries_prefix([('name', ), ('ref', )])))
950
971
def test_iter_key_prefix_1_key_element_refs(self):
951
972
index = self.make_index(1, nodes=[
952
973
(('name', ), 'data', ([('ref', )], )),
953
974
(('ref', ), 'refdata', ([], ))])
954
self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
955
(index, ('ref', ), 'refdata', ((), ))]),
975
self.assertEqual({(index, ('name', ), 'data', ((('ref',),),)),
976
(index, ('ref', ), 'refdata', ((), ))},
956
977
set(index.iter_entries_prefix([('name', ), ('ref', )])))
958
979
def test_iter_key_prefix_2_key_element_no_refs(self):
972
993
(('name', 'fin1'), 'data', ([('ref', 'erence')], )),
973
994
(('name', 'fin2'), 'beta', ([], )),
974
995
(('ref', 'erence'), 'refdata', ([], ))])
975
self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
976
(index, ('ref', 'erence'), 'refdata', ((), ))]),
996
self.assertEqual({(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
997
(index, ('ref', 'erence'), 'refdata', ((), ))},
977
998
set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
978
self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
979
(index, ('name', 'fin2'), 'beta', ((), ))]),
999
self.assertEqual({(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1000
(index, ('name', 'fin2'), 'beta', ((), ))},
980
1001
set(index.iter_entries_prefix([('name', None)])))
982
1003
# XXX: external_references tests are duplicated in test_index. We