~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_chk_map.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-01 10:53:08 UTC
  • mto: This revision was merged to the branch mainline in revision 4504.
  • Revision ID: andrew.bennetts@canonical.com-20090701105308-8892qpe3lhikhe3g
RemoveĀ unusedĀ import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Tests for maps built on a CHK versionedfiles facility."""
18
18
 
 
19
from itertools import izip
 
20
 
19
21
from bzrlib import (
20
22
    chk_map,
21
 
    errors,
22
 
    groupcompress,
23
23
    osutils,
24
24
    tests,
25
25
    )
29
29
    LeafNode,
30
30
    Node,
31
31
    )
32
 
from bzrlib.static_tuple import StaticTuple
33
32
 
34
33
 
35
34
class TestNode(tests.TestCase):
60
59
        self.assertCommonPrefix('', '', '')
61
60
 
62
61
 
63
 
class TestCaseWithStore(tests.TestCaseWithMemoryTransport):
 
62
class TestCaseWithStore(tests.TestCaseWithTransport):
64
63
 
65
64
    def get_chk_bytes(self):
66
 
        # This creates a standalone CHK store.
67
 
        factory = groupcompress.make_pack_factory(False, False, 1)
68
 
        self.chk_bytes = factory(self.get_transport())
69
 
        return self.chk_bytes
 
65
        # The easiest way to get a CHK store is a development6 repository and
 
66
        # then work with the chk_bytes attribute directly.
 
67
        repo = self.make_repository(".", format="development6-rich-root")
 
68
        repo.lock_write()
 
69
        self.addCleanup(repo.unlock)
 
70
        repo.start_write_group()
 
71
        self.addCleanup(repo.abort_write_group)
 
72
        return repo.chk_bytes
70
73
 
71
74
    def _get_map(self, a_dict, maximum_size=0, chk_bytes=None, key_width=1,
72
75
                 search_key_func=None):
94
97
        return dict(node.iteritems(*args))
95
98
 
96
99
 
97
 
class TestCaseWithExampleMaps(TestCaseWithStore):
98
 
 
99
 
    def get_chk_bytes(self):
100
 
        if getattr(self, '_chk_bytes', None) is None:
101
 
            self._chk_bytes = super(TestCaseWithExampleMaps,
102
 
                                    self).get_chk_bytes()
103
 
        return self._chk_bytes
104
 
 
105
 
    def get_map(self, a_dict, maximum_size=100, search_key_func=None):
106
 
        c_map = self._get_map(a_dict, maximum_size=maximum_size,
107
 
                              chk_bytes=self.get_chk_bytes(),
108
 
                              search_key_func=search_key_func)
109
 
        return c_map
110
 
 
111
 
    def make_root_only_map(self, search_key_func=None):
112
 
        return self.get_map({
113
 
            ('aaa',): 'initial aaa content',
114
 
            ('abb',): 'initial abb content',
115
 
        }, search_key_func=search_key_func)
116
 
 
117
 
    def make_root_only_aaa_ddd_map(self, search_key_func=None):
118
 
        return self.get_map({
119
 
            ('aaa',): 'initial aaa content',
120
 
            ('ddd',): 'initial ddd content',
121
 
        }, search_key_func=search_key_func)
122
 
 
123
 
    def make_one_deep_map(self, search_key_func=None):
124
 
        # Same as root_only_map, except it forces an InternalNode at the root
125
 
        return self.get_map({
126
 
            ('aaa',): 'initial aaa content',
127
 
            ('abb',): 'initial abb content',
128
 
            ('ccc',): 'initial ccc content',
129
 
            ('ddd',): 'initial ddd content',
130
 
        }, search_key_func=search_key_func)
131
 
 
132
 
    def make_two_deep_map(self, search_key_func=None):
133
 
        # Carefully chosen so that it creates a 2-deep map for both
134
 
        # _search_key_plain and for _search_key_16
135
 
        # Also so that things line up with make_one_deep_two_prefix_map
136
 
        return self.get_map({
137
 
            ('aaa',): 'initial aaa content',
138
 
            ('abb',): 'initial abb content',
139
 
            ('acc',): 'initial acc content',
140
 
            ('ace',): 'initial ace content',
141
 
            ('add',): 'initial add content',
142
 
            ('adh',): 'initial adh content',
143
 
            ('adl',): 'initial adl content',
144
 
            ('ccc',): 'initial ccc content',
145
 
            ('ddd',): 'initial ddd content',
146
 
        }, search_key_func=search_key_func)
147
 
 
148
 
    def make_one_deep_two_prefix_map(self, search_key_func=None):
149
 
        """Create a map with one internal node, but references are extra long.
150
 
 
151
 
        Otherwise has similar content to make_two_deep_map.
152
 
        """
153
 
        return self.get_map({
154
 
            ('aaa',): 'initial aaa content',
155
 
            ('add',): 'initial add content',
156
 
            ('adh',): 'initial adh content',
157
 
            ('adl',): 'initial adl content',
158
 
        }, search_key_func=search_key_func)
159
 
 
160
 
    def make_one_deep_one_prefix_map(self, search_key_func=None):
161
 
        """Create a map with one internal node, but references are extra long.
162
 
 
163
 
        Similar to make_one_deep_two_prefix_map, except the split is at the
164
 
        first char, rather than the second.
165
 
        """
166
 
        return self.get_map({
167
 
            ('add',): 'initial add content',
168
 
            ('adh',): 'initial adh content',
169
 
            ('adl',): 'initial adl content',
170
 
            ('bbb',): 'initial bbb content',
171
 
        }, search_key_func=search_key_func)
172
 
 
173
 
 
174
 
class TestTestCaseWithExampleMaps(TestCaseWithExampleMaps):
175
 
    """Actual tests for the provided examples."""
176
 
 
177
 
    def test_root_only_map_plain(self):
178
 
        c_map = self.make_root_only_map()
179
 
        self.assertEqualDiff(
180
 
            "'' LeafNode\n"
181
 
            "      ('aaa',) 'initial aaa content'\n"
182
 
            "      ('abb',) 'initial abb content'\n",
183
 
            c_map._dump_tree())
184
 
 
185
 
    def test_root_only_map_16(self):
186
 
        c_map = self.make_root_only_map(search_key_func=chk_map._search_key_16)
187
 
        self.assertEqualDiff(
188
 
            "'' LeafNode\n"
189
 
            "      ('aaa',) 'initial aaa content'\n"
190
 
            "      ('abb',) 'initial abb content'\n",
191
 
            c_map._dump_tree())
192
 
 
193
 
    def test_one_deep_map_plain(self):
194
 
        c_map = self.make_one_deep_map()
195
 
        self.assertEqualDiff(
196
 
            "'' InternalNode\n"
197
 
            "  'a' LeafNode\n"
198
 
            "      ('aaa',) 'initial aaa content'\n"
199
 
            "      ('abb',) 'initial abb content'\n"
200
 
            "  'c' LeafNode\n"
201
 
            "      ('ccc',) 'initial ccc content'\n"
202
 
            "  'd' LeafNode\n"
203
 
            "      ('ddd',) 'initial ddd content'\n",
204
 
            c_map._dump_tree())
205
 
 
206
 
    def test_one_deep_map_16(self):
207
 
        c_map = self.make_one_deep_map(search_key_func=chk_map._search_key_16)
208
 
        self.assertEqualDiff(
209
 
            "'' InternalNode\n"
210
 
            "  '2' LeafNode\n"
211
 
            "      ('ccc',) 'initial ccc content'\n"
212
 
            "  '4' LeafNode\n"
213
 
            "      ('abb',) 'initial abb content'\n"
214
 
            "  'F' LeafNode\n"
215
 
            "      ('aaa',) 'initial aaa content'\n"
216
 
            "      ('ddd',) 'initial ddd content'\n",
217
 
            c_map._dump_tree())
218
 
 
219
 
    def test_root_only_aaa_ddd_plain(self):
220
 
        c_map = self.make_root_only_aaa_ddd_map()
221
 
        self.assertEqualDiff(
222
 
            "'' LeafNode\n"
223
 
            "      ('aaa',) 'initial aaa content'\n"
224
 
            "      ('ddd',) 'initial ddd content'\n",
225
 
            c_map._dump_tree())
226
 
 
227
 
    def test_root_only_aaa_ddd_16(self):
228
 
        c_map = self.make_root_only_aaa_ddd_map(
229
 
                search_key_func=chk_map._search_key_16)
230
 
        # We use 'aaa' and 'ddd' because they happen to map to 'F' when using
231
 
        # _search_key_16
232
 
        self.assertEqualDiff(
233
 
            "'' LeafNode\n"
234
 
            "      ('aaa',) 'initial aaa content'\n"
235
 
            "      ('ddd',) 'initial ddd content'\n",
236
 
            c_map._dump_tree())
237
 
 
238
 
    def test_two_deep_map_plain(self):
239
 
        c_map = self.make_two_deep_map()
240
 
        self.assertEqualDiff(
241
 
            "'' InternalNode\n"
242
 
            "  'a' InternalNode\n"
243
 
            "    'aa' LeafNode\n"
244
 
            "      ('aaa',) 'initial aaa content'\n"
245
 
            "    'ab' LeafNode\n"
246
 
            "      ('abb',) 'initial abb content'\n"
247
 
            "    'ac' LeafNode\n"
248
 
            "      ('acc',) 'initial acc content'\n"
249
 
            "      ('ace',) 'initial ace content'\n"
250
 
            "    'ad' LeafNode\n"
251
 
            "      ('add',) 'initial add content'\n"
252
 
            "      ('adh',) 'initial adh content'\n"
253
 
            "      ('adl',) 'initial adl content'\n"
254
 
            "  'c' LeafNode\n"
255
 
            "      ('ccc',) 'initial ccc content'\n"
256
 
            "  'd' LeafNode\n"
257
 
            "      ('ddd',) 'initial ddd content'\n",
258
 
            c_map._dump_tree())
259
 
 
260
 
    def test_two_deep_map_16(self):
261
 
        c_map = self.make_two_deep_map(search_key_func=chk_map._search_key_16)
262
 
        self.assertEqualDiff(
263
 
            "'' InternalNode\n"
264
 
            "  '2' LeafNode\n"
265
 
            "      ('acc',) 'initial acc content'\n"
266
 
            "      ('ccc',) 'initial ccc content'\n"
267
 
            "  '4' LeafNode\n"
268
 
            "      ('abb',) 'initial abb content'\n"
269
 
            "  'C' LeafNode\n"
270
 
            "      ('ace',) 'initial ace content'\n"
271
 
            "  'F' InternalNode\n"
272
 
            "    'F0' LeafNode\n"
273
 
            "      ('aaa',) 'initial aaa content'\n"
274
 
            "    'F3' LeafNode\n"
275
 
            "      ('adl',) 'initial adl content'\n"
276
 
            "    'F4' LeafNode\n"
277
 
            "      ('adh',) 'initial adh content'\n"
278
 
            "    'FB' LeafNode\n"
279
 
            "      ('ddd',) 'initial ddd content'\n"
280
 
            "    'FD' LeafNode\n"
281
 
            "      ('add',) 'initial add content'\n",
282
 
            c_map._dump_tree())
283
 
 
284
 
    def test_one_deep_two_prefix_map_plain(self):
285
 
        c_map = self.make_one_deep_two_prefix_map()
286
 
        self.assertEqualDiff(
287
 
            "'' InternalNode\n"
288
 
            "  'aa' LeafNode\n"
289
 
            "      ('aaa',) 'initial aaa content'\n"
290
 
            "  'ad' LeafNode\n"
291
 
            "      ('add',) 'initial add content'\n"
292
 
            "      ('adh',) 'initial adh content'\n"
293
 
            "      ('adl',) 'initial adl content'\n",
294
 
            c_map._dump_tree())
295
 
 
296
 
    def test_one_deep_two_prefix_map_16(self):
297
 
        c_map = self.make_one_deep_two_prefix_map(
298
 
            search_key_func=chk_map._search_key_16)
299
 
        self.assertEqualDiff(
300
 
            "'' InternalNode\n"
301
 
            "  'F0' LeafNode\n"
302
 
            "      ('aaa',) 'initial aaa content'\n"
303
 
            "  'F3' LeafNode\n"
304
 
            "      ('adl',) 'initial adl content'\n"
305
 
            "  'F4' LeafNode\n"
306
 
            "      ('adh',) 'initial adh content'\n"
307
 
            "  'FD' LeafNode\n"
308
 
            "      ('add',) 'initial add content'\n",
309
 
            c_map._dump_tree())
310
 
 
311
 
    def test_one_deep_one_prefix_map_plain(self):
312
 
        c_map = self.make_one_deep_one_prefix_map()
313
 
        self.assertEqualDiff(
314
 
            "'' InternalNode\n"
315
 
            "  'a' LeafNode\n"
316
 
            "      ('add',) 'initial add content'\n"
317
 
            "      ('adh',) 'initial adh content'\n"
318
 
            "      ('adl',) 'initial adl content'\n"
319
 
            "  'b' LeafNode\n"
320
 
            "      ('bbb',) 'initial bbb content'\n",
321
 
            c_map._dump_tree())
322
 
 
323
 
    def test_one_deep_one_prefix_map_16(self):
324
 
        c_map = self.make_one_deep_one_prefix_map(
325
 
            search_key_func=chk_map._search_key_16)
326
 
        self.assertEqualDiff(
327
 
            "'' InternalNode\n"
328
 
            "  '4' LeafNode\n"
329
 
            "      ('bbb',) 'initial bbb content'\n"
330
 
            "  'F' LeafNode\n"
331
 
            "      ('add',) 'initial add content'\n"
332
 
            "      ('adh',) 'initial adh content'\n"
333
 
            "      ('adl',) 'initial adl content'\n",
334
 
            c_map._dump_tree())
335
 
 
336
 
 
337
100
class TestMap(TestCaseWithStore):
338
101
 
339
102
    def assertHasABMap(self, chk_bytes):
465
228
        # updated key.
466
229
        self.assertEqual(new_root, chkmap._root_node._key)
467
230
 
468
 
    def test_apply_delete_to_internal_node(self):
469
 
        # applying a delta should be convert an internal root node to a leaf
470
 
        # node if the delta shrinks the map enough.
471
 
        store = self.get_chk_bytes()
472
 
        chkmap = CHKMap(store, None)
473
 
        # Add three items: 2 small enough to fit in one node, and one huge to
474
 
        # force multiple nodes.
475
 
        chkmap._root_node.set_maximum_size(100)
476
 
        chkmap.map(('small',), 'value')
477
 
        chkmap.map(('little',), 'value')
478
 
        chkmap.map(('very-big',), 'x' * 100)
479
 
        # (Check that we have constructed the scenario we want to test)
480
 
        self.assertIsInstance(chkmap._root_node, InternalNode)
481
 
        # Delete the huge item so that the map fits in one node again.
482
 
        delta = [(('very-big',), None, None)]
483
 
        chkmap.apply_delta(delta)
484
 
        self.assertCanonicalForm(chkmap)
485
 
        self.assertIsInstance(chkmap._root_node, LeafNode)
486
 
 
487
 
    def test_apply_new_keys_must_be_new(self):
488
 
        # applying a delta (None, "a", "b") to a map with 'a' in it generates
489
 
        # an error.
490
 
        chk_bytes = self.get_chk_bytes()
491
 
        root_key = CHKMap.from_dict(chk_bytes, {("a",):"b"})
492
 
        chkmap = CHKMap(chk_bytes, root_key)
493
 
        self.assertRaises(errors.InconsistentDelta, chkmap.apply_delta,
494
 
            [(None, ("a",), "b")])
495
 
        # As an error occured, the update should have left us without changing
496
 
        # anything (the root should be unchanged).
497
 
        self.assertEqual(root_key, chkmap._root_node._key)
498
 
 
499
231
    def test_apply_delta_is_deterministic(self):
500
232
        chk_bytes = self.get_chk_bytes()
501
233
        chkmap1 = CHKMap(chk_bytes, None)
849
581
        # 'ab' and 'ac' nodes
850
582
        chkmap.map(('aad',), 'v')
851
583
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
852
 
        self.assertIsInstance(chkmap._root_node._items['ab'], StaticTuple)
853
 
        self.assertIsInstance(chkmap._root_node._items['ac'], StaticTuple)
 
584
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
 
585
        self.assertIsInstance(chkmap._root_node._items['ac'], tuple)
854
586
        # Unmapping 'acd' can notice that 'aa' is an InternalNode and not have
855
587
        # to map in 'ab'
856
588
        chkmap.unmap(('acd',))
857
589
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
858
 
        self.assertIsInstance(chkmap._root_node._items['ab'], StaticTuple)
 
590
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
859
591
 
860
592
    def test_unmap_without_fitting_doesnt_page_in(self):
861
593
        store = self.get_chk_bytes()
878
610
        chkmap.map(('aaf',), 'v')
879
611
        # At this point, the previous nodes should not be paged in, but the
880
612
        # newly added nodes would be
881
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
882
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
 
613
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
614
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
883
615
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
884
616
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
885
617
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
887
619
        # Now unmapping one of the new nodes will use only the already-paged-in
888
620
        # nodes to determine that we don't need to do more.
889
621
        chkmap.unmap(('aaf',))
890
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
891
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
 
622
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
623
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
892
624
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
893
625
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
894
626
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
915
647
        chkmap.map(('aad',), 'v')
916
648
        # At this point, the previous nodes should not be paged in, but the
917
649
        # newly added node would be
918
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
919
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
920
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
650
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
651
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
652
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
921
653
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
922
654
        # Unmapping the new node will check the existing nodes to see if they
923
655
        # would fit.
924
656
        # Clear the page cache so we ensure we have to read all the children
925
 
        chk_map.clear_cache()
 
657
        chk_map._page_cache.clear()
926
658
        chkmap.unmap(('aad',))
927
659
        self.assertIsInstance(chkmap._root_node._items['aaa'], LeafNode)
928
660
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
955
687
        chkmap.map(('aad',), 'v')
956
688
        # At this point, the previous nodes should not be paged in, but the
957
689
        # newly added node would be
958
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
959
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
960
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
690
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
691
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
692
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
961
693
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
962
694
        # Now clear the page cache, and only include 2 of the children in the
963
695
        # cache
964
696
        aab_key = chkmap._root_node._items['aab']
965
 
        aab_bytes = chk_map._get_cache()[aab_key]
 
697
        aab_bytes = chk_map._page_cache[aab_key]
966
698
        aac_key = chkmap._root_node._items['aac']
967
 
        aac_bytes = chk_map._get_cache()[aac_key]
968
 
        chk_map.clear_cache()
969
 
        chk_map._get_cache()[aab_key] = aab_bytes
970
 
        chk_map._get_cache()[aac_key] = aac_bytes
 
699
        aac_bytes = chk_map._page_cache[aac_key]
 
700
        chk_map._page_cache.clear()
 
701
        chk_map._page_cache[aab_key] = aab_bytes
 
702
        chk_map._page_cache[aac_key] = aac_bytes
971
703
 
972
704
        # Unmapping the new node will check the nodes from the page cache
973
705
        # first, and not have to read in 'aaa'
974
706
        chkmap.unmap(('aad',))
975
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
 
707
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
976
708
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
977
709
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
978
710
 
992
724
        chkmap.map(('aaf',), 'val')
993
725
        # At this point, the previous nodes should not be paged in, but the
994
726
        # newly added node would be
995
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
996
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
997
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
727
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
728
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
729
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
998
730
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
999
731
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
1000
732
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
1002
734
        # Unmapping a new node will see the other nodes that are already in
1003
735
        # memory, and not need to page in anything else
1004
736
        chkmap.unmap(('aad',))
1005
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
1006
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
1007
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
737
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
738
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
739
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
1008
740
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
1009
741
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
1010
742
 
1049
781
            {('a',): 'content here', ('b',): 'more content'},
1050
782
            chk_bytes=basis._store, maximum_size=10)
1051
783
        list(target.iter_changes(basis))
1052
 
        self.assertIsInstance(target._root_node, StaticTuple)
1053
 
        self.assertIsInstance(basis._root_node, StaticTuple)
 
784
        self.assertIsInstance(target._root_node, tuple)
 
785
        self.assertIsInstance(basis._root_node, tuple)
1054
786
 
1055
787
    def test_iter_changes_ab_ab_changed_values_shown(self):
1056
788
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
1108
840
        basis_get = basis._store.get_record_stream
1109
841
        def get_record_stream(keys, order, fulltext):
1110
842
            if ('sha1:1adf7c0d1b9140ab5f33bb64c6275fa78b1580b7',) in keys:
1111
 
                raise AssertionError("'aaa' pointer was followed %r" % keys)
 
843
                self.fail("'aaa' pointer was followed %r" % keys)
1112
844
            return basis_get(keys, order, fulltext)
1113
845
        basis._store.get_record_stream = get_record_stream
1114
846
        result = sorted(list(target.iter_changes(basis)))
1162
894
 
1163
895
    def test_iteritems_keys_prefixed_by_2_width_nodes_hashed(self):
1164
896
        search_key_func = chk_map.search_key_registry.get('hash-16-way')
1165
 
        self.assertEqual('E8B7BE43\x00E8B7BE43',
1166
 
                         search_key_func(StaticTuple('a', 'a')))
1167
 
        self.assertEqual('E8B7BE43\x0071BEEFF9',
1168
 
                         search_key_func(StaticTuple('a', 'b')))
1169
 
        self.assertEqual('71BEEFF9\x0000000000',
1170
 
                         search_key_func(StaticTuple('b', '')))
 
897
        self.assertEqual('E8B7BE43\x00E8B7BE43', search_key_func(('a', 'a')))
 
898
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
 
899
        self.assertEqual('71BEEFF9\x0000000000', search_key_func(('b', '')))
1171
900
        chkmap = self._get_map(
1172
901
            {("a","a"):"content here", ("a", "b",):"more content",
1173
902
             ("b", ""): 'boring content'},
1470
1199
                             , chkmap._dump_tree())
1471
1200
 
1472
1201
 
 
1202
class TestSearchKeyFuncs(tests.TestCase):
 
1203
 
 
1204
    def assertSearchKey16(self, expected, key):
 
1205
        self.assertEqual(expected, chk_map._search_key_16(key))
 
1206
 
 
1207
    def assertSearchKey255(self, expected, key):
 
1208
        actual = chk_map._search_key_255(key)
 
1209
        self.assertEqual(expected, actual, 'actual: %r' % (actual,))
 
1210
 
 
1211
    def test_simple_16(self):
 
1212
        self.assertSearchKey16('8C736521', ('foo',))
 
1213
        self.assertSearchKey16('8C736521\x008C736521', ('foo', 'foo'))
 
1214
        self.assertSearchKey16('8C736521\x0076FF8CAA', ('foo', 'bar'))
 
1215
        self.assertSearchKey16('ED82CD11', ('abcd',))
 
1216
 
 
1217
    def test_simple_255(self):
 
1218
        self.assertSearchKey255('\x8cse!', ('foo',))
 
1219
        self.assertSearchKey255('\x8cse!\x00\x8cse!', ('foo', 'foo'))
 
1220
        self.assertSearchKey255('\x8cse!\x00v\xff\x8c\xaa', ('foo', 'bar'))
 
1221
        # The standard mapping for these would include '\n', so it should be
 
1222
        # mapped to '_'
 
1223
        self.assertSearchKey255('\xfdm\x93_\x00P_\x1bL', ('<', 'V'))
 
1224
 
 
1225
    def test_255_does_not_include_newline(self):
 
1226
        # When mapping via _search_key_255, we should never have the '\n'
 
1227
        # character, but all other 255 values should be present
 
1228
        chars_used = set()
 
1229
        for char_in in range(256):
 
1230
            search_key = chk_map._search_key_255((chr(char_in),))
 
1231
            chars_used.update(search_key)
 
1232
        all_chars = set([chr(x) for x in range(256)])
 
1233
        unused_chars = all_chars.symmetric_difference(chars_used)
 
1234
        self.assertEqual(set('\n'), unused_chars)
 
1235
 
 
1236
 
1473
1237
class TestLeafNode(TestCaseWithStore):
1474
1238
 
1475
1239
    def test_current_size_empty(self):
1894
1658
        search_key_func = chk_map.search_key_registry.get('hash-255-way')
1895
1659
        node = InternalNode(search_key_func=search_key_func)
1896
1660
        leaf1 = LeafNode(search_key_func=search_key_func)
1897
 
        leaf1.map(None, StaticTuple('foo bar',), 'quux')
 
1661
        leaf1.map(None, ('foo bar',), 'quux')
1898
1662
        leaf2 = LeafNode(search_key_func=search_key_func)
1899
 
        leaf2.map(None, StaticTuple('strange',), 'beast')
1900
 
        self.assertEqual('\xbeF\x014', search_key_func(StaticTuple('foo bar',)))
1901
 
        self.assertEqual('\x85\xfa\xf7K', search_key_func(StaticTuple('strange',)))
 
1663
        leaf2.map(None, ('strange',), 'beast')
 
1664
        self.assertEqual('\xbeF\x014', search_key_func(('foo bar',)))
 
1665
        self.assertEqual('\x85\xfa\xf7K', search_key_func(('strange',)))
1902
1666
        node.add_node("\xbe", leaf1)
1903
1667
        # This sets up a path that should not be followed - it will error if
1904
1668
        # the code tries to.
1905
1669
        node._items['\xbe'] = None
1906
1670
        node.add_node("\x85", leaf2)
1907
1671
        self.assertEqual([(('strange',), 'beast')],
1908
 
            sorted(node.iteritems(None, [StaticTuple('strange',),
1909
 
                                         StaticTuple('weird',)])))
 
1672
            sorted(node.iteritems(None, [('strange',), ('weird',)])))
1910
1673
 
1911
1674
    def test_iteritems_partial_empty(self):
1912
1675
        node = InternalNode()
1919
1682
        # Ensure test validity: nothing paged in below the root.
1920
1683
        self.assertEqual(2,
1921
1684
            len([value for value in node._items.values()
1922
 
                if type(value) is StaticTuple]))
 
1685
                if type(value) == tuple]))
1923
1686
        # now, mapping to k3 should add a k3 leaf
1924
1687
        prefix, nodes = node.map(None, ('k3',), 'quux')
1925
1688
        self.assertEqual("k", prefix)
1958
1721
        # Ensure test validity: nothing paged in below the root.
1959
1722
        self.assertEqual(2,
1960
1723
            len([value for value in node._items.values()
1961
 
                if type(value) is StaticTuple]))
 
1724
                if type(value) == tuple]))
1962
1725
        # now, mapping to k23 causes k22 ('k2' in node) to split into k22 and
1963
1726
        # k23, which for simplicity in the current implementation generates
1964
1727
        # a new internal node between node, and k22/k23.
2003
1766
        node = InternalNode(search_key_func=search_key_func)
2004
1767
        node._key_width = 2
2005
1768
        node._node_width = 4
2006
 
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(
2007
 
            StaticTuple('a', 'b')))
2008
 
        self.assertEqual('E8B7', node._search_prefix_filter(
2009
 
            StaticTuple('a', 'b')))
2010
 
        self.assertEqual('E8B7', node._search_prefix_filter(
2011
 
            StaticTuple('a',)))
 
1769
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
 
1770
        self.assertEqual('E8B7', node._search_prefix_filter(('a', 'b')))
 
1771
        self.assertEqual('E8B7', node._search_prefix_filter(('a',)))
2012
1772
 
2013
1773
    def test_unmap_k23_from_k1_k22_k23_gives_k1_k22_tree_new(self):
2014
1774
        chkmap = self._get_map(
2126
1886
# 1-4K get0
2127
1887
 
2128
1888
 
2129
 
class TestCHKMapDifference(TestCaseWithExampleMaps):
2130
 
 
2131
 
    def get_difference(self, new_roots, old_roots,
2132
 
                       search_key_func=None):
2133
 
        if search_key_func is None:
2134
 
            search_key_func = chk_map._search_key_plain
2135
 
        return chk_map.CHKMapDifference(self.get_chk_bytes(),
2136
 
            new_roots, old_roots, search_key_func)
2137
 
 
2138
 
    def test__init__(self):
2139
 
        c_map = self.make_root_only_map()
2140
 
        key1 = c_map.key()
2141
 
        c_map.map(('aaa',), 'new aaa content')
2142
 
        key2 = c_map._save()
2143
 
        diff = self.get_difference([key2], [key1])
2144
 
        self.assertEqual(set([key1]), diff._all_old_chks)
2145
 
        self.assertEqual([], diff._old_queue)
2146
 
        self.assertEqual([], diff._new_queue)
2147
 
 
2148
 
    def help__read_all_roots(self, search_key_func):
2149
 
        c_map = self.make_root_only_map(search_key_func=search_key_func)
2150
 
        key1 = c_map.key()
2151
 
        c_map.map(('aaa',), 'new aaa content')
2152
 
        key2 = c_map._save()
2153
 
        diff = self.get_difference([key2], [key1], search_key_func)
2154
 
        root_results = [record.key for record in diff._read_all_roots()]
2155
 
        self.assertEqual([key2], root_results)
2156
 
        # We should have queued up only items that aren't in the old
2157
 
        # set
2158
 
        self.assertEqual([(('aaa',), 'new aaa content')],
2159
 
                         diff._new_item_queue)
2160
 
        self.assertEqual([], diff._new_queue)
2161
 
        # And there are no old references, so that queue should be
2162
 
        # empty
2163
 
        self.assertEqual([], diff._old_queue)
2164
 
 
2165
 
    def test__read_all_roots_plain(self):
2166
 
        self.help__read_all_roots(search_key_func=chk_map._search_key_plain)
2167
 
 
2168
 
    def test__read_all_roots_16(self):
2169
 
        self.help__read_all_roots(search_key_func=chk_map._search_key_16)
2170
 
 
2171
 
    def test__read_all_roots_skips_known_old(self):
2172
 
        c_map = self.make_one_deep_map(chk_map._search_key_plain)
2173
 
        key1 = c_map.key()
2174
 
        c_map2 = self.make_root_only_map(chk_map._search_key_plain)
2175
 
        key2 = c_map2.key()
2176
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2177
 
        root_results = [record.key for record in diff._read_all_roots()]
2178
 
        # We should have no results. key2 is completely contained within key1,
2179
 
        # and we should have seen that in the first pass
2180
 
        self.assertEqual([], root_results)
2181
 
 
2182
 
    def test__read_all_roots_prepares_queues(self):
2183
 
        c_map = self.make_one_deep_map(chk_map._search_key_plain)
2184
 
        key1 = c_map.key()
2185
 
        c_map._dump_tree() # load everything
2186
 
        key1_a = c_map._root_node._items['a'].key()
2187
 
        c_map.map(('abb',), 'new abb content')
2188
 
        key2 = c_map._save()
2189
 
        key2_a = c_map._root_node._items['a'].key()
2190
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2191
 
        root_results = [record.key for record in diff._read_all_roots()]
2192
 
        self.assertEqual([key2], root_results)
2193
 
        # At this point, we should have queued up only the 'a' Leaf on both
2194
 
        # sides, both 'c' and 'd' are known to not have changed on both sides
2195
 
        self.assertEqual([key2_a], diff._new_queue)
2196
 
        self.assertEqual([], diff._new_item_queue)
2197
 
        self.assertEqual([key1_a], diff._old_queue)
2198
 
 
2199
 
    def test__read_all_roots_multi_new_prepares_queues(self):
2200
 
        c_map = self.make_one_deep_map(chk_map._search_key_plain)
2201
 
        key1 = c_map.key()
2202
 
        c_map._dump_tree() # load everything
2203
 
        key1_a = c_map._root_node._items['a'].key()
2204
 
        key1_c = c_map._root_node._items['c'].key()
2205
 
        c_map.map(('abb',), 'new abb content')
2206
 
        key2 = c_map._save()
2207
 
        key2_a = c_map._root_node._items['a'].key()
2208
 
        key2_c = c_map._root_node._items['c'].key()
2209
 
        c_map = chk_map.CHKMap(self.get_chk_bytes(), key1,
2210
 
                               chk_map._search_key_plain)
2211
 
        c_map.map(('ccc',), 'new ccc content')
2212
 
        key3 = c_map._save()
2213
 
        key3_a = c_map._root_node._items['a'].key()
2214
 
        key3_c = c_map._root_node._items['c'].key()
2215
 
        diff = self.get_difference([key2, key3], [key1],
2216
 
                                   chk_map._search_key_plain)
2217
 
        root_results = [record.key for record in diff._read_all_roots()]
2218
 
        self.assertEqual(sorted([key2, key3]), sorted(root_results))
2219
 
        # We should have queued up key2_a, and key3_c, but not key2_c or key3_c
2220
 
        self.assertEqual([key2_a, key3_c], diff._new_queue)
2221
 
        self.assertEqual([], diff._new_item_queue)
2222
 
        # And we should have queued up both a and c for the old set
2223
 
        self.assertEqual([key1_a, key1_c], diff._old_queue)
2224
 
 
2225
 
    def test__read_all_roots_different_depths(self):
2226
 
        c_map = self.make_two_deep_map(chk_map._search_key_plain)
2227
 
        c_map._dump_tree() # load everything
2228
 
        key1 = c_map.key()
2229
 
        key1_a = c_map._root_node._items['a'].key()
2230
 
        key1_c = c_map._root_node._items['c'].key()
2231
 
        key1_d = c_map._root_node._items['d'].key()
2232
 
 
2233
 
        c_map2 = self.make_one_deep_two_prefix_map(chk_map._search_key_plain)
2234
 
        c_map2._dump_tree()
2235
 
        key2 = c_map2.key()
2236
 
        key2_aa = c_map2._root_node._items['aa'].key()
2237
 
        key2_ad = c_map2._root_node._items['ad'].key()
2238
 
 
2239
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2240
 
        root_results = [record.key for record in diff._read_all_roots()]
2241
 
        self.assertEqual([key2], root_results)
2242
 
        # Only the 'a' subset should be queued up, since 'c' and 'd' cannot be
2243
 
        # present
2244
 
        self.assertEqual([key1_a], diff._old_queue)
2245
 
        self.assertEqual([key2_aa, key2_ad], diff._new_queue)
2246
 
        self.assertEqual([], diff._new_item_queue)
2247
 
 
2248
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_plain)
2249
 
        root_results = [record.key for record in diff._read_all_roots()]
2250
 
        self.assertEqual([key1], root_results)
2251
 
 
2252
 
        self.assertEqual([key2_aa, key2_ad], diff._old_queue)
2253
 
        self.assertEqual([key1_a, key1_c, key1_d], diff._new_queue)
2254
 
        self.assertEqual([], diff._new_item_queue)
2255
 
 
2256
 
    def test__read_all_roots_different_depths_16(self):
2257
 
        c_map = self.make_two_deep_map(chk_map._search_key_16)
2258
 
        c_map._dump_tree() # load everything
2259
 
        key1 = c_map.key()
2260
 
        key1_2 = c_map._root_node._items['2'].key()
2261
 
        key1_4 = c_map._root_node._items['4'].key()
2262
 
        key1_C = c_map._root_node._items['C'].key()
2263
 
        key1_F = c_map._root_node._items['F'].key()
2264
 
 
2265
 
        c_map2 = self.make_one_deep_two_prefix_map(chk_map._search_key_16)
2266
 
        c_map2._dump_tree()
2267
 
        key2 = c_map2.key()
2268
 
        key2_F0 = c_map2._root_node._items['F0'].key()
2269
 
        key2_F3 = c_map2._root_node._items['F3'].key()
2270
 
        key2_F4 = c_map2._root_node._items['F4'].key()
2271
 
        key2_FD = c_map2._root_node._items['FD'].key()
2272
 
 
2273
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_16)
2274
 
        root_results = [record.key for record in diff._read_all_roots()]
2275
 
        self.assertEqual([key2], root_results)
2276
 
        # Only the subset of keys that may be present should be queued up.
2277
 
        self.assertEqual([key1_F], diff._old_queue)
2278
 
        self.assertEqual(sorted([key2_F0, key2_F3, key2_F4, key2_FD]),
2279
 
                         sorted(diff._new_queue))
2280
 
        self.assertEqual([], diff._new_item_queue)
2281
 
 
2282
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_16)
2283
 
        root_results = [record.key for record in diff._read_all_roots()]
2284
 
        self.assertEqual([key1], root_results)
2285
 
 
2286
 
        self.assertEqual(sorted([key2_F0, key2_F3, key2_F4, key2_FD]),
2287
 
                         sorted(diff._old_queue))
2288
 
        self.assertEqual(sorted([key1_2, key1_4, key1_C, key1_F]),
2289
 
                         sorted(diff._new_queue))
2290
 
        self.assertEqual([], diff._new_item_queue)
2291
 
 
2292
 
    def test__read_all_roots_mixed_depth(self):
2293
 
        c_map = self.make_one_deep_two_prefix_map(chk_map._search_key_plain)
2294
 
        c_map._dump_tree() # load everything
2295
 
        key1 = c_map.key()
2296
 
        key1_aa = c_map._root_node._items['aa'].key()
2297
 
        key1_ad = c_map._root_node._items['ad'].key()
2298
 
 
2299
 
        c_map2 = self.make_one_deep_one_prefix_map(chk_map._search_key_plain)
2300
 
        c_map2._dump_tree()
2301
 
        key2 = c_map2.key()
2302
 
        key2_a = c_map2._root_node._items['a'].key()
2303
 
        key2_b = c_map2._root_node._items['b'].key()
2304
 
 
2305
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2306
 
        root_results = [record.key for record in diff._read_all_roots()]
2307
 
        self.assertEqual([key2], root_results)
2308
 
        # 'ad' matches exactly 'a' on the other side, so it should be removed,
2309
 
        # and neither side should have it queued for walking
2310
 
        self.assertEqual([], diff._old_queue)
2311
 
        self.assertEqual([key2_b], diff._new_queue)
2312
 
        self.assertEqual([], diff._new_item_queue)
2313
 
 
2314
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_plain)
2315
 
        root_results = [record.key for record in diff._read_all_roots()]
2316
 
        self.assertEqual([key1], root_results)
2317
 
        # Note: This is technically not the 'true minimal' set that we could
2318
 
        #       use The reason is that 'a' was matched exactly to 'ad' (by sha
2319
 
        #       sum).  However, the code gets complicated in the case of more
2320
 
        #       than one interesting key, so for now, we live with this
2321
 
        #       Consider revising, though benchmarking showing it to be a
2322
 
        #       real-world issue should be done
2323
 
        self.assertEqual([key2_a], diff._old_queue)
2324
 
        # self.assertEqual([], diff._old_queue)
2325
 
        self.assertEqual([key1_aa], diff._new_queue)
2326
 
        self.assertEqual([], diff._new_item_queue)
2327
 
 
2328
 
    def test__read_all_roots_yields_extra_deep_records(self):
2329
 
        # This is slightly controversial, as we will yield a chk page that we
2330
 
        # might later on find out could be filtered out. (If a root node is
2331
 
        # referenced deeper in the old set.)
2332
 
        # However, even with stacking, we always have all chk pages that we
2333
 
        # will need. So as long as we filter out the referenced keys, we'll
2334
 
        # never run into problems.
2335
 
        # This allows us to yield a root node record immediately, without any
2336
 
        # buffering.
2337
 
        c_map = self.make_two_deep_map(chk_map._search_key_plain)
2338
 
        c_map._dump_tree() # load all keys
2339
 
        key1 = c_map.key()
2340
 
        key1_a = c_map._root_node._items['a'].key()
2341
 
        c_map2 = self.get_map({
2342
 
            ('acc',): 'initial acc content',
2343
 
            ('ace',): 'initial ace content',
2344
 
        }, maximum_size=100)
2345
 
        self.assertEqualDiff(
2346
 
            "'' LeafNode\n"
2347
 
            "      ('acc',) 'initial acc content'\n"
2348
 
            "      ('ace',) 'initial ace content'\n",
2349
 
            c_map2._dump_tree())
2350
 
        key2 = c_map2.key()
2351
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2352
 
        root_results = [record.key for record in diff._read_all_roots()]
2353
 
        self.assertEqual([key2], root_results)
2354
 
        # However, even though we have yielded the root node to be fetched,
2355
 
        # we should have enqued all of the chk pages to be walked, so that we
2356
 
        # can find the keys if they are present
2357
 
        self.assertEqual([key1_a], diff._old_queue)
2358
 
        self.assertEqual([(('acc',), 'initial acc content'),
2359
 
                          (('ace',), 'initial ace content'),
2360
 
                         ], diff._new_item_queue)
2361
 
 
2362
 
    def test__read_all_roots_multiple_targets(self):
2363
 
        c_map = self.make_root_only_map()
2364
 
        key1 = c_map.key()
2365
 
        c_map = self.make_one_deep_map()
2366
 
        key2 = c_map.key()
2367
 
        c_map._dump_tree()
2368
 
        key2_c = c_map._root_node._items['c'].key()
2369
 
        key2_d = c_map._root_node._items['d'].key()
2370
 
        c_map.map(('ccc',), 'new ccc value')
2371
 
        key3 = c_map._save()
2372
 
        key3_c = c_map._root_node._items['c'].key()
2373
 
        diff = self.get_difference([key2, key3], [key1],
2374
 
                                   chk_map._search_key_plain)
2375
 
        root_results = [record.key for record in diff._read_all_roots()]
2376
 
        self.assertEqual(sorted([key2, key3]), sorted(root_results))
2377
 
        self.assertEqual([], diff._old_queue)
2378
 
        # the key 'd' is interesting from key2 and key3, but should only be
2379
 
        # entered into the queue 1 time
2380
 
        self.assertEqual(sorted([key2_c, key3_c, key2_d]),
2381
 
                         sorted(diff._new_queue))
2382
 
        self.assertEqual([], diff._new_item_queue)
2383
 
 
2384
 
    def test__read_all_roots_no_old(self):
2385
 
        # This is the 'initial branch' case. With nothing in the old
2386
 
        # set, we can just queue up all root nodes into interesting queue, and
2387
 
        # then have them fast-path flushed via _flush_new_queue
2388
 
        c_map = self.make_two_deep_map()
2389
 
        key1 = c_map.key()
2390
 
        diff = self.get_difference([key1], [], chk_map._search_key_plain)
2391
 
        root_results = [record.key for record in diff._read_all_roots()]
2392
 
        self.assertEqual([], root_results)
2393
 
        self.assertEqual([], diff._old_queue)
2394
 
        self.assertEqual([key1], diff._new_queue)
2395
 
        self.assertEqual([], diff._new_item_queue)
2396
 
 
2397
 
        c_map2 = self.make_one_deep_map()
2398
 
        key2 = c_map2.key()
2399
 
        diff = self.get_difference([key1, key2], [], chk_map._search_key_plain)
2400
 
        root_results = [record.key for record in diff._read_all_roots()]
2401
 
        self.assertEqual([], root_results)
2402
 
        self.assertEqual([], diff._old_queue)
2403
 
        self.assertEqual(sorted([key1, key2]), sorted(diff._new_queue))
2404
 
        self.assertEqual([], diff._new_item_queue)
2405
 
 
2406
 
    def test__read_all_roots_no_old_16(self):
2407
 
        c_map = self.make_two_deep_map(chk_map._search_key_16)
2408
 
        key1 = c_map.key()
2409
 
        diff = self.get_difference([key1], [], chk_map._search_key_16)
2410
 
        root_results = [record.key for record in diff._read_all_roots()]
2411
 
        self.assertEqual([], root_results)
2412
 
        self.assertEqual([], diff._old_queue)
2413
 
        self.assertEqual([key1], diff._new_queue)
2414
 
        self.assertEqual([], diff._new_item_queue)
2415
 
 
2416
 
        c_map2 = self.make_one_deep_map(chk_map._search_key_16)
2417
 
        key2 = c_map2.key()
2418
 
        diff = self.get_difference([key1, key2], [],
2419
 
                                   chk_map._search_key_16)
2420
 
        root_results = [record.key for record in diff._read_all_roots()]
2421
 
        self.assertEqual([], root_results)
2422
 
        self.assertEqual([], diff._old_queue)
2423
 
        self.assertEqual(sorted([key1, key2]),
2424
 
                         sorted(diff._new_queue))
2425
 
        self.assertEqual([], diff._new_item_queue)
2426
 
 
2427
 
    def test__read_all_roots_multiple_old(self):
2428
 
        c_map = self.make_two_deep_map()
2429
 
        key1 = c_map.key()
2430
 
        c_map._dump_tree() # load everything
2431
 
        key1_a = c_map._root_node._items['a'].key()
2432
 
        c_map.map(('ccc',), 'new ccc value')
2433
 
        key2 = c_map._save()
2434
 
        key2_a = c_map._root_node._items['a'].key()
2435
 
        c_map.map(('add',), 'new add value')
2436
 
        key3 = c_map._save()
2437
 
        key3_a = c_map._root_node._items['a'].key()
2438
 
        diff = self.get_difference([key3], [key1, key2],
2439
 
                                   chk_map._search_key_plain)
2440
 
        root_results = [record.key for record in diff._read_all_roots()]
2441
 
        self.assertEqual([key3], root_results)
2442
 
        # the 'a' keys should not be queued up 2 times, since they are
2443
 
        # identical
2444
 
        self.assertEqual([key1_a], diff._old_queue)
2445
 
        self.assertEqual([key3_a], diff._new_queue)
2446
 
        self.assertEqual([], diff._new_item_queue)
2447
 
 
2448
 
    def test__process_next_old_batched_no_dupes(self):
2449
 
        c_map = self.make_two_deep_map()
2450
 
        key1 = c_map.key()
2451
 
        c_map._dump_tree() # load everything
2452
 
        key1_a = c_map._root_node._items['a'].key()
2453
 
        key1_aa = c_map._root_node._items['a']._items['aa'].key()
2454
 
        key1_ab = c_map._root_node._items['a']._items['ab'].key()
2455
 
        key1_ac = c_map._root_node._items['a']._items['ac'].key()
2456
 
        key1_ad = c_map._root_node._items['a']._items['ad'].key()
2457
 
        c_map.map(('aaa',), 'new aaa value')
2458
 
        key2 = c_map._save()
2459
 
        key2_a = c_map._root_node._items['a'].key()
2460
 
        key2_aa = c_map._root_node._items['a']._items['aa'].key()
2461
 
        c_map.map(('acc',), 'new acc content')
2462
 
        key3 = c_map._save()
2463
 
        key3_a = c_map._root_node._items['a'].key()
2464
 
        key3_ac = c_map._root_node._items['a']._items['ac'].key()
2465
 
        diff = self.get_difference([key3], [key1, key2],
2466
 
                                   chk_map._search_key_plain)
2467
 
        root_results = [record.key for record in diff._read_all_roots()]
2468
 
        self.assertEqual([key3], root_results)
2469
 
        self.assertEqual(sorted([key1_a, key2_a]),
2470
 
                         sorted(diff._old_queue))
2471
 
        self.assertEqual([key3_a], diff._new_queue)
2472
 
        self.assertEqual([], diff._new_item_queue)
2473
 
        diff._process_next_old()
2474
 
        # All of the old records should be brought in and queued up,
2475
 
        # but we should not have any duplicates
2476
 
        self.assertEqual(sorted([key1_aa, key1_ab, key1_ac, key1_ad, key2_aa]),
2477
 
                         sorted(diff._old_queue))
2478
 
 
2479
 
 
2480
 
class TestIterInterestingNodes(TestCaseWithExampleMaps):
 
1889
class TestIterInterestingNodes(TestCaseWithStore):
 
1890
 
 
1891
    def get_chk_bytes(self):
 
1892
        if getattr(self, '_chk_bytes', None) is None:
 
1893
            self._chk_bytes = super(TestIterInterestingNodes,
 
1894
                                    self).get_chk_bytes()
 
1895
        return self._chk_bytes
2481
1896
 
2482
1897
    def get_map_key(self, a_dict, maximum_size=10):
2483
 
        c_map = self.get_map(a_dict, maximum_size=maximum_size)
 
1898
        c_map = self._get_map(a_dict, maximum_size=maximum_size,
 
1899
                              chk_bytes=self.get_chk_bytes())
2484
1900
        return c_map.key()
2485
1901
 
2486
 
    def assertIterInteresting(self, records, items, interesting_keys,
2487
 
                              old_keys):
 
1902
    def assertIterInteresting(self, expected, interesting_keys,
 
1903
                              uninteresting_keys):
2488
1904
        """Check the result of iter_interesting_nodes.
2489
1905
 
2490
 
        Note that we no longer care how many steps are taken, etc, just that
2491
 
        the right contents are returned.
2492
 
 
2493
 
        :param records: A list of record keys that should be yielded
2494
 
        :param items: A list of items (key,value) that should be yielded.
 
1906
        :param expected: A list of (record_keys, interesting_chk_pages,
 
1907
                                    interesting key value pairs)
2495
1908
        """
2496
1909
        store = self.get_chk_bytes()
2497
 
        store._search_key_func = chk_map._search_key_plain
2498
1910
        iter_nodes = chk_map.iter_interesting_nodes(store, interesting_keys,
2499
 
                                                    old_keys)
2500
 
        record_keys = []
2501
 
        all_items = []
2502
 
        for record, new_items in iter_nodes:
2503
 
            if record is not None:
2504
 
                record_keys.append(record.key)
2505
 
            if new_items:
2506
 
                all_items.extend(new_items)
2507
 
        self.assertEqual(sorted(records), sorted(record_keys))
2508
 
        self.assertEqual(sorted(items), sorted(all_items))
 
1911
                                                    uninteresting_keys)
 
1912
        nodes = list(iter_nodes)
 
1913
        for count, (exp, act) in enumerate(izip(expected, nodes)):
 
1914
            exp_record, exp_items = exp
 
1915
            record, items = act
 
1916
            exp_tuple = (exp_record, sorted(exp_items))
 
1917
            if record is None:
 
1918
                act_tuple = (None, sorted(items))
 
1919
            else:
 
1920
                act_tuple = (record.key, sorted(items))
 
1921
            self.assertEqual(exp_tuple, act_tuple,
 
1922
                             'entry %d did not match expected' % count)
 
1923
        self.assertEqual(len(expected), len(nodes))
2509
1924
 
2510
1925
    def test_empty_to_one_keys(self):
2511
1926
        target = self.get_map_key({('a',): 'content'})
2512
 
        self.assertIterInteresting([target],
2513
 
                                   [(('a',), 'content')],
2514
 
                                   [target], [])
 
1927
        self.assertIterInteresting(
 
1928
            [(target, [(('a',), 'content')]),
 
1929
            ], [target], [])
2515
1930
 
2516
1931
    def test_none_to_one_key(self):
2517
1932
        basis = self.get_map_key({})
2518
1933
        target = self.get_map_key({('a',): 'content'})
2519
 
        self.assertIterInteresting([target],
2520
 
                                   [(('a',), 'content')],
2521
 
                                   [target], [basis])
 
1934
        self.assertIterInteresting(
 
1935
            [(None, [(('a',), 'content')]),
 
1936
             (target, []),
 
1937
            ], [target], [basis])
2522
1938
 
2523
1939
    def test_one_to_none_key(self):
2524
1940
        basis = self.get_map_key({('a',): 'content'})
2525
1941
        target = self.get_map_key({})
2526
 
        self.assertIterInteresting([target],
2527
 
                                   [],
2528
 
                                   [target], [basis])
 
1942
        self.assertIterInteresting(
 
1943
            [(target, [])],
 
1944
            [target], [basis])
2529
1945
 
2530
1946
    def test_common_pages(self):
2531
1947
        basis = self.get_map_key({('a',): 'content',
2548
1964
            target_map._dump_tree())
2549
1965
        b_key = target_map._root_node._items['b'].key()
2550
1966
        # This should return the root node, and the node for the 'b' key
2551
 
        self.assertIterInteresting([target, b_key],
2552
 
                                   [(('b',), 'other content')],
2553
 
                                   [target], [basis])
 
1967
        self.assertIterInteresting(
 
1968
            [(target, []),
 
1969
             (b_key, [(('b',), 'other content')])],
 
1970
            [target], [basis])
2554
1971
 
2555
1972
    def test_common_sub_page(self):
2556
1973
        basis = self.get_map_key({('aaa',): 'common',
2574
1991
        # The key for the internal aa node
2575
1992
        a_key = target_map._root_node._items['a'].key()
2576
1993
        # The key for the leaf aab node
2577
 
        # aaa_key = target_map._root_node._items['a']._items['aaa'].key()
2578
1994
        aab_key = target_map._root_node._items['a']._items['aab'].key()
2579
 
        self.assertIterInteresting([target, a_key, aab_key],
2580
 
                                   [(('aab',), 'new')],
2581
 
                                   [target], [basis])
 
1995
        self.assertIterInteresting(
 
1996
            [(target, []),
 
1997
             (a_key, []),
 
1998
             (aab_key, [(('aab',), 'new')])],
 
1999
            [target], [basis])
2582
2000
 
2583
2001
    def test_common_leaf(self):
2584
2002
        basis = self.get_map_key({})
2622
2040
        a_key = target3_map._root_node._items['a'].key()
2623
2041
        aac_key = target3_map._root_node._items['a']._items['aac'].key()
2624
2042
        self.assertIterInteresting(
2625
 
            [target1, target2, target3, a_key, aac_key, b_key],
2626
 
            [(('aaa',), 'common'), (('bbb',), 'new'), (('aac',), 'other')],
2627
 
            [target1, target2, target3], [basis])
2628
 
 
2629
 
        self.assertIterInteresting(
2630
 
            [target2, target3, a_key, aac_key, b_key],
2631
 
            [(('bbb',), 'new'), (('aac',), 'other')],
2632
 
            [target2, target3], [target1])
2633
 
 
2634
 
        # Technically, target1 could be filtered out, but since it is a root
2635
 
        # node, we yield it immediately, rather than waiting to find out much
2636
 
        # later on.
2637
 
        self.assertIterInteresting(
2638
 
            [target1],
2639
 
            [],
2640
 
            [target1], [target3])
 
2043
            [(None, [(('aaa',), 'common')]),
 
2044
             (target1, []),
 
2045
             (target2, []),
 
2046
             (target3, []),
 
2047
             (b_key, [(('bbb',), 'new')]),
 
2048
             (a_key, []),
 
2049
             (aac_key, [(('aac',), 'other')]),
 
2050
            ], [target1, target2, target3], [basis])
 
2051
 
 
2052
        self.assertIterInteresting(
 
2053
            [(target2, []),
 
2054
             (target3, []),
 
2055
             (b_key, [(('bbb',), 'new')]),
 
2056
             (a_key, []),
 
2057
             (aac_key, [(('aac',), 'other')]),
 
2058
            ], [target2, target3], [target1])
 
2059
 
 
2060
        # This may be a case that we relax. A root node is a deep child of the
 
2061
        # excluded set. The cost is buffering root nodes until we have
 
2062
        # determined all possible exclusions. (Because a prefix of '', cannot
 
2063
        # be excluded.)
 
2064
        self.assertIterInteresting(
 
2065
            [], [target1], [target3])
2641
2066
 
2642
2067
    def test_multiple_maps(self):
2643
2068
        basis1 = self.get_map_key({('aaa',): 'common',
2686
2111
        # The key for the leaf bba node
2687
2112
        bba_key = target2_map._root_node._items['b']._items['bba'].key()
2688
2113
        self.assertIterInteresting(
2689
 
            [target1, target2, a_key, aac_key, b_key, bba_key],
2690
 
            [(('aac',), 'target1'), (('bba',), 'target2')],
2691
 
            [target1, target2], [basis1, basis2])
 
2114
            [(target1, []),
 
2115
             (target2, []),
 
2116
             (a_key, []),
 
2117
             (b_key, []),
 
2118
             (aac_key, [(('aac',), 'target1')]),
 
2119
             (bba_key, [(('bba',), 'target2')]),
 
2120
            ], [target1, target2], [basis1, basis2])
2692
2121
 
2693
2122
    def test_multiple_maps_overlapping_common_new(self):
2694
2123
        # Test that when a node found through the interesting_keys iteration
2695
 
        # for *some roots* and also via the old keys iteration, that
2696
 
        # it is still scanned for old refs and items, because its
 
2124
        # for *some roots* and also via the uninteresting keys iteration, that
 
2125
        # it is still scanned for uninteresting refs and items, because its
2697
2126
        # not truely new. This requires 2 levels of InternalNodes to expose,
2698
2127
        # because of the way the bootstrap in _find_children_info works.
2699
2128
        # This suggests that the code is probably amenable to/benefit from
2759
2188
            right_map._dump_tree())
2760
2189
        # Keys from the right side target - none, the root is enough.
2761
2190
        # Test behaviour
 
2191
        self.expectFailure("we don't properly filter different depths",
 
2192
            self.assertIterInteresting,
 
2193
            [(left, []),
 
2194
             (right, []),
 
2195
             (l_d_key, [(('ddd',), 'change')]),
 
2196
            ], [left, right], [basis])
2762
2197
        self.assertIterInteresting(
2763
 
            [right, left, l_d_key],
2764
 
            [(('ddd',), 'change')],
2765
 
            [left, right], [basis])
 
2198
            [(left, []),
 
2199
             (right, []),
 
2200
             (l_d_key, [(('ddd',), 'change')]),
 
2201
            ], [left, right], [basis])
2766
2202
 
2767
2203
    def test_multiple_maps_similar(self):
2768
2204
        # We want to have a depth=2 tree, with multiple entries in each leaf
2823
2259
        r_a_key = right_map._root_node._items['a'].key()
2824
2260
        r_c_key = right_map._root_node._items['c'].key()
2825
2261
        self.assertIterInteresting(
2826
 
            [right, left, l_a_key, r_c_key],
2827
 
            [(('abb',), 'changed left'), (('cbb',), 'changed right')],
2828
 
            [left, right], [basis])
 
2262
            [(left, []),
 
2263
             (right, []),
 
2264
             (l_a_key, [(('abb',), 'changed left')]),
 
2265
             (r_c_key, [(('cbb',), 'changed right')]),
 
2266
            ], [left, right], [basis])