~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_chk_map.py

  • Committer: John Arbash Meinel
  • Date: 2009-03-27 22:29:55 UTC
  • mto: (3735.39.2 clean)
  • mto: This revision was merged to the branch mainline in revision 4280.
  • Revision ID: john@arbash-meinel.com-20090327222955-utifmfm888zerixt
Implement apply_delta_to_source which doesn't have to malloc another string.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008 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
20
20
 
21
21
from bzrlib import (
22
22
    chk_map,
23
 
    errors,
24
 
    groupcompress,
25
23
    osutils,
26
24
    tests,
27
25
    )
31
29
    LeafNode,
32
30
    Node,
33
31
    )
34
 
from bzrlib.static_tuple import StaticTuple
35
32
 
36
33
 
37
34
class TestNode(tests.TestCase):
56
53
    def test_not_a_prefix(self):
57
54
        self.assertCommonPrefix('b', 'begin', 'b')
58
55
 
59
 
    def test_empty(self):
60
 
        self.assertCommonPrefix('', '', 'end')
61
 
        self.assertCommonPrefix('', 'begin', '')
62
 
        self.assertCommonPrefix('', '', '')
63
 
 
64
 
 
65
 
class TestCaseWithStore(tests.TestCaseWithMemoryTransport):
 
56
 
 
57
class TestCaseWithStore(tests.TestCaseWithTransport):
66
58
 
67
59
    def get_chk_bytes(self):
68
 
        # This creates a standalone CHK store.
69
 
        factory = groupcompress.make_pack_factory(False, False, 1)
70
 
        self.chk_bytes = factory(self.get_transport())
71
 
        return self.chk_bytes
 
60
        # The easiest way to get a CHK store is a development5 repository and
 
61
        # then work with the chk_bytes attribute directly.
 
62
        repo = self.make_repository(".", format="development5")
 
63
        repo.lock_write()
 
64
        self.addCleanup(repo.unlock)
 
65
        repo.start_write_group()
 
66
        self.addCleanup(repo.abort_write_group)
 
67
        return repo.chk_bytes
72
68
 
73
69
    def _get_map(self, a_dict, maximum_size=0, chk_bytes=None, key_width=1,
74
70
                 search_key_func=None):
77
73
        root_key = CHKMap.from_dict(chk_bytes, a_dict,
78
74
            maximum_size=maximum_size, key_width=key_width,
79
75
            search_key_func=search_key_func)
80
 
        root_key2 = CHKMap._create_via_map(chk_bytes, a_dict,
81
 
            maximum_size=maximum_size, key_width=key_width,
82
 
            search_key_func=search_key_func)
83
 
        self.assertEqual(root_key, root_key2, "CHKMap.from_dict() did not"
84
 
                         " match CHKMap._create_via_map")
85
76
        chkmap = CHKMap(chk_bytes, root_key, search_key_func=search_key_func)
86
77
        return chkmap
87
78
 
96
87
        return dict(node.iteritems(*args))
97
88
 
98
89
 
99
 
class TestCaseWithExampleMaps(TestCaseWithStore):
100
 
 
101
 
    def get_chk_bytes(self):
102
 
        if getattr(self, '_chk_bytes', None) is None:
103
 
            self._chk_bytes = super(TestCaseWithExampleMaps,
104
 
                                    self).get_chk_bytes()
105
 
        return self._chk_bytes
106
 
 
107
 
    def get_map(self, a_dict, maximum_size=100, search_key_func=None):
108
 
        c_map = self._get_map(a_dict, maximum_size=maximum_size,
109
 
                              chk_bytes=self.get_chk_bytes(),
110
 
                              search_key_func=search_key_func)
111
 
        return c_map
112
 
 
113
 
    def make_root_only_map(self, search_key_func=None):
114
 
        return self.get_map({
115
 
            ('aaa',): 'initial aaa content',
116
 
            ('abb',): 'initial abb content',
117
 
        }, search_key_func=search_key_func)
118
 
 
119
 
    def make_root_only_aaa_ddd_map(self, search_key_func=None):
120
 
        return self.get_map({
121
 
            ('aaa',): 'initial aaa content',
122
 
            ('ddd',): 'initial ddd content',
123
 
        }, search_key_func=search_key_func)
124
 
 
125
 
    def make_one_deep_map(self, search_key_func=None):
126
 
        # Same as root_only_map, except it forces an InternalNode at the root
127
 
        return self.get_map({
128
 
            ('aaa',): 'initial aaa content',
129
 
            ('abb',): 'initial abb content',
130
 
            ('ccc',): 'initial ccc content',
131
 
            ('ddd',): 'initial ddd content',
132
 
        }, search_key_func=search_key_func)
133
 
 
134
 
    def make_two_deep_map(self, search_key_func=None):
135
 
        # Carefully chosen so that it creates a 2-deep map for both
136
 
        # _search_key_plain and for _search_key_16
137
 
        # Also so that things line up with make_one_deep_two_prefix_map
138
 
        return self.get_map({
139
 
            ('aaa',): 'initial aaa content',
140
 
            ('abb',): 'initial abb content',
141
 
            ('acc',): 'initial acc content',
142
 
            ('ace',): 'initial ace content',
143
 
            ('add',): 'initial add content',
144
 
            ('adh',): 'initial adh content',
145
 
            ('adl',): 'initial adl content',
146
 
            ('ccc',): 'initial ccc content',
147
 
            ('ddd',): 'initial ddd content',
148
 
        }, search_key_func=search_key_func)
149
 
 
150
 
    def make_one_deep_two_prefix_map(self, search_key_func=None):
151
 
        """Create a map with one internal node, but references are extra long.
152
 
 
153
 
        Otherwise has similar content to make_two_deep_map.
154
 
        """
155
 
        return self.get_map({
156
 
            ('aaa',): 'initial aaa content',
157
 
            ('add',): 'initial add content',
158
 
            ('adh',): 'initial adh content',
159
 
            ('adl',): 'initial adl content',
160
 
        }, search_key_func=search_key_func)
161
 
 
162
 
    def make_one_deep_one_prefix_map(self, search_key_func=None):
163
 
        """Create a map with one internal node, but references are extra long.
164
 
 
165
 
        Similar to make_one_deep_two_prefix_map, except the split is at the
166
 
        first char, rather than the second.
167
 
        """
168
 
        return self.get_map({
169
 
            ('add',): 'initial add content',
170
 
            ('adh',): 'initial adh content',
171
 
            ('adl',): 'initial adl content',
172
 
            ('bbb',): 'initial bbb content',
173
 
        }, search_key_func=search_key_func)
174
 
 
175
 
 
176
 
class TestTestCaseWithExampleMaps(TestCaseWithExampleMaps):
177
 
    """Actual tests for the provided examples."""
178
 
 
179
 
    def test_root_only_map_plain(self):
180
 
        c_map = self.make_root_only_map()
181
 
        self.assertEqualDiff(
182
 
            "'' LeafNode\n"
183
 
            "      ('aaa',) 'initial aaa content'\n"
184
 
            "      ('abb',) 'initial abb content'\n",
185
 
            c_map._dump_tree())
186
 
 
187
 
    def test_root_only_map_16(self):
188
 
        c_map = self.make_root_only_map(search_key_func=chk_map._search_key_16)
189
 
        self.assertEqualDiff(
190
 
            "'' LeafNode\n"
191
 
            "      ('aaa',) 'initial aaa content'\n"
192
 
            "      ('abb',) 'initial abb content'\n",
193
 
            c_map._dump_tree())
194
 
 
195
 
    def test_one_deep_map_plain(self):
196
 
        c_map = self.make_one_deep_map()
197
 
        self.assertEqualDiff(
198
 
            "'' InternalNode\n"
199
 
            "  'a' LeafNode\n"
200
 
            "      ('aaa',) 'initial aaa content'\n"
201
 
            "      ('abb',) 'initial abb content'\n"
202
 
            "  'c' LeafNode\n"
203
 
            "      ('ccc',) 'initial ccc content'\n"
204
 
            "  'd' LeafNode\n"
205
 
            "      ('ddd',) 'initial ddd content'\n",
206
 
            c_map._dump_tree())
207
 
 
208
 
    def test_one_deep_map_16(self):
209
 
        c_map = self.make_one_deep_map(search_key_func=chk_map._search_key_16)
210
 
        self.assertEqualDiff(
211
 
            "'' InternalNode\n"
212
 
            "  '2' LeafNode\n"
213
 
            "      ('ccc',) 'initial ccc content'\n"
214
 
            "  '4' LeafNode\n"
215
 
            "      ('abb',) 'initial abb content'\n"
216
 
            "  'F' LeafNode\n"
217
 
            "      ('aaa',) 'initial aaa content'\n"
218
 
            "      ('ddd',) 'initial ddd content'\n",
219
 
            c_map._dump_tree())
220
 
 
221
 
    def test_root_only_aaa_ddd_plain(self):
222
 
        c_map = self.make_root_only_aaa_ddd_map()
223
 
        self.assertEqualDiff(
224
 
            "'' LeafNode\n"
225
 
            "      ('aaa',) 'initial aaa content'\n"
226
 
            "      ('ddd',) 'initial ddd content'\n",
227
 
            c_map._dump_tree())
228
 
 
229
 
    def test_one_deep_map_16(self):
230
 
        c_map = self.make_root_only_aaa_ddd_map(
231
 
                search_key_func=chk_map._search_key_16)
232
 
        # We use 'aaa' and 'ddd' because they happen to map to 'F' when using
233
 
        # _search_key_16
234
 
        self.assertEqualDiff(
235
 
            "'' LeafNode\n"
236
 
            "      ('aaa',) 'initial aaa content'\n"
237
 
            "      ('ddd',) 'initial ddd content'\n",
238
 
            c_map._dump_tree())
239
 
 
240
 
    def test_two_deep_map_plain(self):
241
 
        c_map = self.make_two_deep_map()
242
 
        self.assertEqualDiff(
243
 
            "'' InternalNode\n"
244
 
            "  'a' InternalNode\n"
245
 
            "    'aa' LeafNode\n"
246
 
            "      ('aaa',) 'initial aaa content'\n"
247
 
            "    'ab' LeafNode\n"
248
 
            "      ('abb',) 'initial abb content'\n"
249
 
            "    'ac' LeafNode\n"
250
 
            "      ('acc',) 'initial acc content'\n"
251
 
            "      ('ace',) 'initial ace content'\n"
252
 
            "    'ad' LeafNode\n"
253
 
            "      ('add',) 'initial add content'\n"
254
 
            "      ('adh',) 'initial adh content'\n"
255
 
            "      ('adl',) 'initial adl content'\n"
256
 
            "  'c' LeafNode\n"
257
 
            "      ('ccc',) 'initial ccc content'\n"
258
 
            "  'd' LeafNode\n"
259
 
            "      ('ddd',) 'initial ddd content'\n",
260
 
            c_map._dump_tree())
261
 
 
262
 
    def test_two_deep_map_16(self):
263
 
        c_map = self.make_two_deep_map(search_key_func=chk_map._search_key_16)
264
 
        self.assertEqualDiff(
265
 
            "'' InternalNode\n"
266
 
            "  '2' LeafNode\n"
267
 
            "      ('acc',) 'initial acc content'\n"
268
 
            "      ('ccc',) 'initial ccc content'\n"
269
 
            "  '4' LeafNode\n"
270
 
            "      ('abb',) 'initial abb content'\n"
271
 
            "  'C' LeafNode\n"
272
 
            "      ('ace',) 'initial ace content'\n"
273
 
            "  'F' InternalNode\n"
274
 
            "    'F0' LeafNode\n"
275
 
            "      ('aaa',) 'initial aaa content'\n"
276
 
            "    'F3' LeafNode\n"
277
 
            "      ('adl',) 'initial adl content'\n"
278
 
            "    'F4' LeafNode\n"
279
 
            "      ('adh',) 'initial adh content'\n"
280
 
            "    'FB' LeafNode\n"
281
 
            "      ('ddd',) 'initial ddd content'\n"
282
 
            "    'FD' LeafNode\n"
283
 
            "      ('add',) 'initial add content'\n",
284
 
            c_map._dump_tree())
285
 
 
286
 
    def test_one_deep_two_prefix_map_plain(self):
287
 
        c_map = self.make_one_deep_two_prefix_map()
288
 
        self.assertEqualDiff(
289
 
            "'' InternalNode\n"
290
 
            "  'aa' LeafNode\n"
291
 
            "      ('aaa',) 'initial aaa content'\n"
292
 
            "  'ad' LeafNode\n"
293
 
            "      ('add',) 'initial add content'\n"
294
 
            "      ('adh',) 'initial adh content'\n"
295
 
            "      ('adl',) 'initial adl content'\n",
296
 
            c_map._dump_tree())
297
 
 
298
 
    def test_one_deep_two_prefix_map_16(self):
299
 
        c_map = self.make_one_deep_two_prefix_map(
300
 
            search_key_func=chk_map._search_key_16)
301
 
        self.assertEqualDiff(
302
 
            "'' InternalNode\n"
303
 
            "  'F0' LeafNode\n"
304
 
            "      ('aaa',) 'initial aaa content'\n"
305
 
            "  'F3' LeafNode\n"
306
 
            "      ('adl',) 'initial adl content'\n"
307
 
            "  'F4' LeafNode\n"
308
 
            "      ('adh',) 'initial adh content'\n"
309
 
            "  'FD' LeafNode\n"
310
 
            "      ('add',) 'initial add content'\n",
311
 
            c_map._dump_tree())
312
 
 
313
 
    def test_one_deep_one_prefix_map_plain(self):
314
 
        c_map = self.make_one_deep_one_prefix_map()
315
 
        self.assertEqualDiff(
316
 
            "'' InternalNode\n"
317
 
            "  'a' LeafNode\n"
318
 
            "      ('add',) 'initial add content'\n"
319
 
            "      ('adh',) 'initial adh content'\n"
320
 
            "      ('adl',) 'initial adl content'\n"
321
 
            "  'b' LeafNode\n"
322
 
            "      ('bbb',) 'initial bbb content'\n",
323
 
            c_map._dump_tree())
324
 
 
325
 
    def test_one_deep_one_prefix_map_16(self):
326
 
        c_map = self.make_one_deep_one_prefix_map(
327
 
            search_key_func=chk_map._search_key_16)
328
 
        self.assertEqualDiff(
329
 
            "'' InternalNode\n"
330
 
            "  '4' LeafNode\n"
331
 
            "      ('bbb',) 'initial bbb content'\n"
332
 
            "  'F' LeafNode\n"
333
 
            "      ('add',) 'initial add content'\n"
334
 
            "      ('adh',) 'initial adh content'\n"
335
 
            "      ('adl',) 'initial adl content'\n",
336
 
            c_map._dump_tree())
337
 
 
338
 
 
339
90
class TestMap(TestCaseWithStore):
340
91
 
341
92
    def assertHasABMap(self, chk_bytes):
467
218
        # updated key.
468
219
        self.assertEqual(new_root, chkmap._root_node._key)
469
220
 
470
 
    def test_apply_new_keys_must_be_new(self):
471
 
        # applying a delta (None, "a", "b") to a map with 'a' in it generates
472
 
        # an error.
473
 
        chk_bytes = self.get_chk_bytes()
474
 
        root_key = CHKMap.from_dict(chk_bytes, {("a",):"b"})
475
 
        chkmap = CHKMap(chk_bytes, root_key)
476
 
        self.assertRaises(errors.InconsistentDelta, chkmap.apply_delta,
477
 
            [(None, ("a",), "b")])
478
 
        # As an error occured, the update should have left us without changing
479
 
        # anything (the root should be unchanged).
480
 
        self.assertEqual(root_key, chkmap._root_node._key)
481
 
 
482
221
    def test_apply_delta_is_deterministic(self):
483
222
        chk_bytes = self.get_chk_bytes()
484
223
        chkmap1 = CHKMap(chk_bytes, None)
832
571
        # 'ab' and 'ac' nodes
833
572
        chkmap.map(('aad',), 'v')
834
573
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
835
 
        self.assertIsInstance(chkmap._root_node._items['ab'], StaticTuple)
836
 
        self.assertIsInstance(chkmap._root_node._items['ac'], StaticTuple)
 
574
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
 
575
        self.assertIsInstance(chkmap._root_node._items['ac'], tuple)
837
576
        # Unmapping 'acd' can notice that 'aa' is an InternalNode and not have
838
577
        # to map in 'ab'
839
578
        chkmap.unmap(('acd',))
840
579
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
841
 
        self.assertIsInstance(chkmap._root_node._items['ab'], StaticTuple)
 
580
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
842
581
 
843
582
    def test_unmap_without_fitting_doesnt_page_in(self):
844
583
        store = self.get_chk_bytes()
861
600
        chkmap.map(('aaf',), 'v')
862
601
        # At this point, the previous nodes should not be paged in, but the
863
602
        # newly added nodes would be
864
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
865
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
 
603
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
604
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
866
605
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
867
606
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
868
607
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
870
609
        # Now unmapping one of the new nodes will use only the already-paged-in
871
610
        # nodes to determine that we don't need to do more.
872
611
        chkmap.unmap(('aaf',))
873
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
874
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
 
612
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
613
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
875
614
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
876
615
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
877
616
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
898
637
        chkmap.map(('aad',), 'v')
899
638
        # At this point, the previous nodes should not be paged in, but the
900
639
        # newly added node would be
901
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
902
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
903
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
640
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
641
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
642
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
904
643
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
905
644
        # Unmapping the new node will check the existing nodes to see if they
906
645
        # would fit.
907
646
        # Clear the page cache so we ensure we have to read all the children
908
 
        chk_map.clear_cache()
 
647
        chk_map._page_cache.clear()
909
648
        chkmap.unmap(('aad',))
910
649
        self.assertIsInstance(chkmap._root_node._items['aaa'], LeafNode)
911
650
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
938
677
        chkmap.map(('aad',), 'v')
939
678
        # At this point, the previous nodes should not be paged in, but the
940
679
        # newly added node would be
941
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
942
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
943
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
680
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
681
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
682
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
944
683
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
945
684
        # Now clear the page cache, and only include 2 of the children in the
946
685
        # cache
947
686
        aab_key = chkmap._root_node._items['aab']
948
 
        aab_bytes = chk_map._get_cache()[aab_key]
 
687
        aab_bytes = chk_map._page_cache[aab_key]
949
688
        aac_key = chkmap._root_node._items['aac']
950
 
        aac_bytes = chk_map._get_cache()[aac_key]
951
 
        chk_map.clear_cache()
952
 
        chk_map._get_cache()[aab_key] = aab_bytes
953
 
        chk_map._get_cache()[aac_key] = aac_bytes
 
689
        aac_bytes = chk_map._page_cache[aac_key]
 
690
        chk_map._page_cache.clear()
 
691
        chk_map._page_cache[aab_key] = aab_bytes
 
692
        chk_map._page_cache[aac_key] = aac_bytes
954
693
 
955
694
        # Unmapping the new node will check the nodes from the page cache
956
695
        # first, and not have to read in 'aaa'
957
696
        chkmap.unmap(('aad',))
958
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
 
697
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
959
698
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
960
699
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
961
700
 
975
714
        chkmap.map(('aaf',), 'val')
976
715
        # At this point, the previous nodes should not be paged in, but the
977
716
        # newly added node would be
978
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
979
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
980
 
        self.assertIsInstance(chkmap._root_node._items['aac'], StaticTuple)
 
717
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
 
718
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
 
719
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
981
720
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
982
721
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
983
722
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
985
724
        # Unmapping a new node will see the other nodes that are already in
986
725
        # memory, and not need to page in anything else
987
726
        chkmap.unmap(('aad',))
988
 
        self.assertIsInstance(chkmap._root_node._items['aaa'], StaticTuple)
989
 
        self.assertIsInstance(chkmap._root_node._items['aab'], StaticTuple)
990
 
        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)
991
730
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
992
731
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
993
732
 
1032
771
            {('a',): 'content here', ('b',): 'more content'},
1033
772
            chk_bytes=basis._store, maximum_size=10)
1034
773
        list(target.iter_changes(basis))
1035
 
        self.assertIsInstance(target._root_node, StaticTuple)
1036
 
        self.assertIsInstance(basis._root_node, StaticTuple)
 
774
        self.assertIsInstance(target._root_node, tuple)
 
775
        self.assertIsInstance(basis._root_node, tuple)
1037
776
 
1038
777
    def test_iter_changes_ab_ab_changed_values_shown(self):
1039
778
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
1145
884
 
1146
885
    def test_iteritems_keys_prefixed_by_2_width_nodes_hashed(self):
1147
886
        search_key_func = chk_map.search_key_registry.get('hash-16-way')
1148
 
        self.assertEqual('E8B7BE43\x00E8B7BE43',
1149
 
                         search_key_func(StaticTuple('a', 'a')))
1150
 
        self.assertEqual('E8B7BE43\x0071BEEFF9',
1151
 
                         search_key_func(StaticTuple('a', 'b')))
1152
 
        self.assertEqual('71BEEFF9\x0000000000',
1153
 
                         search_key_func(StaticTuple('b', '')))
 
887
        self.assertEqual('E8B7BE43\x00E8B7BE43', search_key_func(('a', 'a')))
 
888
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
 
889
        self.assertEqual('71BEEFF9\x0000000000', search_key_func(('b', '')))
1154
890
        chkmap = self._get_map(
1155
891
            {("a","a"):"content here", ("a", "b",):"more content",
1156
892
             ("b", ""): 'boring content'},
1453
1189
                             , chkmap._dump_tree())
1454
1190
 
1455
1191
 
 
1192
class TestSearchKeyFuncs(tests.TestCase):
 
1193
 
 
1194
    def assertSearchKey16(self, expected, key):
 
1195
        self.assertEqual(expected, chk_map._search_key_16(key))
 
1196
 
 
1197
    def assertSearchKey255(self, expected, key):
 
1198
        actual = chk_map._search_key_255(key)
 
1199
        self.assertEqual(expected, actual, 'actual: %r' % (actual,))
 
1200
 
 
1201
    def test_simple_16(self):
 
1202
        self.assertSearchKey16('8C736521', ('foo',))
 
1203
        self.assertSearchKey16('8C736521\x008C736521', ('foo', 'foo'))
 
1204
        self.assertSearchKey16('8C736521\x0076FF8CAA', ('foo', 'bar'))
 
1205
        self.assertSearchKey16('ED82CD11', ('abcd',))
 
1206
 
 
1207
    def test_simple_255(self):
 
1208
        self.assertSearchKey255('\x8cse!', ('foo',))
 
1209
        self.assertSearchKey255('\x8cse!\x00\x8cse!', ('foo', 'foo'))
 
1210
        self.assertSearchKey255('\x8cse!\x00v\xff\x8c\xaa', ('foo', 'bar'))
 
1211
        # The standard mapping for these would include '\n', so it should be
 
1212
        # mapped to '_'
 
1213
        self.assertSearchKey255('\xfdm\x93_\x00P_\x1bL', ('<', 'V'))
 
1214
 
 
1215
    def test_255_does_not_include_newline(self):
 
1216
        # When mapping via _search_key_255, we should never have the '\n'
 
1217
        # character, but all other 255 values should be present
 
1218
        chars_used = set()
 
1219
        for char_in in range(256):
 
1220
            search_key = chk_map._search_key_255((chr(char_in),))
 
1221
            chars_used.update(search_key)
 
1222
        all_chars = set([chr(x) for x in range(256)])
 
1223
        unused_chars = all_chars.symmetric_difference(chars_used)
 
1224
        self.assertEqual(set('\n'), unused_chars)
 
1225
 
 
1226
 
1456
1227
class TestLeafNode(TestCaseWithStore):
1457
1228
 
1458
1229
    def test_current_size_empty(self):
1784
1555
        child.map(None, ("baz",), "val")
1785
1556
        node.add_node("b", child)
1786
1557
 
1787
 
        # Note that 'ram' doesn't match anything, so it should be freely
1788
 
        # ignored
1789
 
        key_filter = (('foo',), ('fob',), ('bar',), ('baz',), ('ram',))
 
1558
        key_filter = (('foo',), ('fob',), ('bar',), ('baz',))
1790
1559
        for child, node_key_filter in node._iter_nodes(None,
1791
1560
                                                       key_filter=key_filter):
1792
 
            # each child could match two key filters, so make sure they were
 
1561
            # each child could matches two key filters, so make sure they were
1793
1562
            # both included.
1794
1563
            self.assertEqual(2, len(node_key_filter))
1795
1564
 
1796
 
    def make_fo_fa_node(self):
1797
 
        node = InternalNode('f')
1798
 
        child = LeafNode()
1799
 
        child.set_maximum_size(100)
1800
 
        child.map(None, ("foo",), "val")
1801
 
        child.map(None, ("fob",), "val")
1802
 
        node.add_node('fo', child)
1803
 
        child = LeafNode()
1804
 
        child.set_maximum_size(100)
1805
 
        child.map(None, ("far",), "val")
1806
 
        child.map(None, ("faz",), "val")
1807
 
        node.add_node("fa", child)
1808
 
        return node
1809
 
 
1810
 
    def test__iter_nodes_single_entry(self):
1811
 
        node = self.make_fo_fa_node()
1812
 
        key_filter = [('foo',)]
1813
 
        nodes = list(node._iter_nodes(None, key_filter=key_filter))
1814
 
        self.assertEqual(1, len(nodes))
1815
 
        self.assertEqual(key_filter, nodes[0][1])
1816
 
 
1817
 
    def test__iter_nodes_single_entry_misses(self):
1818
 
        node = self.make_fo_fa_node()
1819
 
        key_filter = [('bar',)]
1820
 
        nodes = list(node._iter_nodes(None, key_filter=key_filter))
1821
 
        self.assertEqual(0, len(nodes))
1822
 
 
1823
 
    def test__iter_nodes_mixed_key_width(self):
1824
 
        node = self.make_fo_fa_node()
1825
 
        key_filter = [('foo', 'bar'), ('foo',), ('fo',), ('b',)]
1826
 
        nodes = list(node._iter_nodes(None, key_filter=key_filter))
1827
 
        self.assertEqual(1, len(nodes))
1828
 
        matches = key_filter[:]
1829
 
        matches.remove(('b',))
1830
 
        self.assertEqual(sorted(matches), sorted(nodes[0][1]))
1831
 
 
1832
 
    def test__iter_nodes_match_all(self):
1833
 
        node = self.make_fo_fa_node()
1834
 
        key_filter = [('foo', 'bar'), ('foo',), ('fo',), ('f',)]
1835
 
        nodes = list(node._iter_nodes(None, key_filter=key_filter))
1836
 
        self.assertEqual(2, len(nodes))
1837
 
 
1838
 
    def test__iter_nodes_fixed_widths_and_misses(self):
1839
 
        node = self.make_fo_fa_node()
1840
 
        # foo and faa should both match one child, baz should miss
1841
 
        key_filter = [('foo',), ('faa',), ('baz',)]
1842
 
        nodes = list(node._iter_nodes(None, key_filter=key_filter))
1843
 
        self.assertEqual(2, len(nodes))
1844
 
        for node, matches in nodes:
1845
 
            self.assertEqual(1, len(matches))
1846
 
 
1847
1565
    def test_iteritems_empty_new(self):
1848
1566
        node = InternalNode()
1849
1567
        self.assertEqual([], sorted(node.iteritems(None)))
1877
1595
        search_key_func = chk_map.search_key_registry.get('hash-255-way')
1878
1596
        node = InternalNode(search_key_func=search_key_func)
1879
1597
        leaf1 = LeafNode(search_key_func=search_key_func)
1880
 
        leaf1.map(None, StaticTuple('foo bar',), 'quux')
 
1598
        leaf1.map(None, ('foo bar',), 'quux')
1881
1599
        leaf2 = LeafNode(search_key_func=search_key_func)
1882
 
        leaf2.map(None, StaticTuple('strange',), 'beast')
1883
 
        self.assertEqual('\xbeF\x014', search_key_func(StaticTuple('foo bar',)))
1884
 
        self.assertEqual('\x85\xfa\xf7K', search_key_func(StaticTuple('strange',)))
 
1600
        leaf2.map(None, ('strange',), 'beast')
 
1601
        self.assertEqual('\xbeF\x014', search_key_func(('foo bar',)))
 
1602
        self.assertEqual('\x85\xfa\xf7K', search_key_func(('strange',)))
1885
1603
        node.add_node("\xbe", leaf1)
1886
1604
        # This sets up a path that should not be followed - it will error if
1887
1605
        # the code tries to.
1888
1606
        node._items['\xbe'] = None
1889
1607
        node.add_node("\x85", leaf2)
1890
1608
        self.assertEqual([(('strange',), 'beast')],
1891
 
            sorted(node.iteritems(None, [StaticTuple('strange',),
1892
 
                                         StaticTuple('weird',)])))
 
1609
            sorted(node.iteritems(None, [('strange',), ('weird',)])))
1893
1610
 
1894
1611
    def test_iteritems_partial_empty(self):
1895
1612
        node = InternalNode()
1902
1619
        # Ensure test validity: nothing paged in below the root.
1903
1620
        self.assertEqual(2,
1904
1621
            len([value for value in node._items.values()
1905
 
                if type(value) is StaticTuple]))
 
1622
                if type(value) == tuple]))
1906
1623
        # now, mapping to k3 should add a k3 leaf
1907
1624
        prefix, nodes = node.map(None, ('k3',), 'quux')
1908
1625
        self.assertEqual("k", prefix)
1941
1658
        # Ensure test validity: nothing paged in below the root.
1942
1659
        self.assertEqual(2,
1943
1660
            len([value for value in node._items.values()
1944
 
                if type(value) is StaticTuple]))
 
1661
                if type(value) == tuple]))
1945
1662
        # now, mapping to k23 causes k22 ('k2' in node) to split into k22 and
1946
1663
        # k23, which for simplicity in the current implementation generates
1947
1664
        # a new internal node between node, and k22/k23.
1986
1703
        node = InternalNode(search_key_func=search_key_func)
1987
1704
        node._key_width = 2
1988
1705
        node._node_width = 4
1989
 
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(
1990
 
            StaticTuple('a', 'b')))
1991
 
        self.assertEqual('E8B7', node._search_prefix_filter(
1992
 
            StaticTuple('a', 'b')))
1993
 
        self.assertEqual('E8B7', node._search_prefix_filter(
1994
 
            StaticTuple('a',)))
 
1706
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
 
1707
        self.assertEqual('E8B7', node._search_prefix_filter(('a', 'b')))
 
1708
        self.assertEqual('E8B7', node._search_prefix_filter(('a',)))
1995
1709
 
1996
1710
    def test_unmap_k23_from_k1_k22_k23_gives_k1_k22_tree_new(self):
1997
1711
        chkmap = self._get_map(
2109
1823
# 1-4K get0
2110
1824
 
2111
1825
 
2112
 
class TestCHKMapDifference(TestCaseWithExampleMaps):
2113
 
 
2114
 
    def get_difference(self, new_roots, old_roots,
2115
 
                       search_key_func=None):
2116
 
        if search_key_func is None:
2117
 
            search_key_func = chk_map._search_key_plain
2118
 
        return chk_map.CHKMapDifference(self.get_chk_bytes(),
2119
 
            new_roots, old_roots, search_key_func)
2120
 
 
2121
 
    def test__init__(self):
2122
 
        c_map = self.make_root_only_map()
2123
 
        key1 = c_map.key()
2124
 
        c_map.map(('aaa',), 'new aaa content')
2125
 
        key2 = c_map._save()
2126
 
        diff = self.get_difference([key2], [key1])
2127
 
        self.assertEqual(set([key1]), diff._all_old_chks)
2128
 
        self.assertEqual([], diff._old_queue)
2129
 
        self.assertEqual([], diff._new_queue)
2130
 
 
2131
 
    def help__read_all_roots(self, search_key_func):
2132
 
        c_map = self.make_root_only_map(search_key_func=search_key_func)
2133
 
        key1 = c_map.key()
2134
 
        c_map.map(('aaa',), 'new aaa content')
2135
 
        key2 = c_map._save()
2136
 
        diff = self.get_difference([key2], [key1], search_key_func)
2137
 
        root_results = [record.key for record in diff._read_all_roots()]
2138
 
        self.assertEqual([key2], root_results)
2139
 
        # We should have queued up only items that aren't in the old
2140
 
        # set
2141
 
        self.assertEqual([(('aaa',), 'new aaa content')],
2142
 
                         diff._new_item_queue)
2143
 
        self.assertEqual([], diff._new_queue)
2144
 
        # And there are no old references, so that queue should be
2145
 
        # empty
2146
 
        self.assertEqual([], diff._old_queue)
2147
 
 
2148
 
    def test__read_all_roots_plain(self):
2149
 
        self.help__read_all_roots(search_key_func=chk_map._search_key_plain)
2150
 
 
2151
 
    def test__read_all_roots_16(self):
2152
 
        self.help__read_all_roots(search_key_func=chk_map._search_key_16)
2153
 
 
2154
 
    def test__read_all_roots_skips_known_old(self):
2155
 
        c_map = self.make_one_deep_map(chk_map._search_key_plain)
2156
 
        key1 = c_map.key()
2157
 
        c_map2 = self.make_root_only_map(chk_map._search_key_plain)
2158
 
        key2 = c_map2.key()
2159
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2160
 
        root_results = [record.key for record in diff._read_all_roots()]
2161
 
        # We should have no results. key2 is completely contained within key1,
2162
 
        # and we should have seen that in the first pass
2163
 
        self.assertEqual([], root_results)
2164
 
 
2165
 
    def test__read_all_roots_prepares_queues(self):
2166
 
        c_map = self.make_one_deep_map(chk_map._search_key_plain)
2167
 
        key1 = c_map.key()
2168
 
        c_map._dump_tree() # load everything
2169
 
        key1_a = c_map._root_node._items['a'].key()
2170
 
        c_map.map(('abb',), 'new abb content')
2171
 
        key2 = c_map._save()
2172
 
        key2_a = c_map._root_node._items['a'].key()
2173
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2174
 
        root_results = [record.key for record in diff._read_all_roots()]
2175
 
        self.assertEqual([key2], root_results)
2176
 
        # At this point, we should have queued up only the 'a' Leaf on both
2177
 
        # sides, both 'c' and 'd' are known to not have changed on both sides
2178
 
        self.assertEqual([key2_a], diff._new_queue)
2179
 
        self.assertEqual([], diff._new_item_queue)
2180
 
        self.assertEqual([key1_a], diff._old_queue)
2181
 
 
2182
 
    def test__read_all_roots_multi_new_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
 
        key1_c = c_map._root_node._items['c'].key()
2188
 
        c_map.map(('abb',), 'new abb content')
2189
 
        key2 = c_map._save()
2190
 
        key2_a = c_map._root_node._items['a'].key()
2191
 
        key2_c = c_map._root_node._items['c'].key()
2192
 
        c_map = chk_map.CHKMap(self.get_chk_bytes(), key1,
2193
 
                               chk_map._search_key_plain)
2194
 
        c_map.map(('ccc',), 'new ccc content')
2195
 
        key3 = c_map._save()
2196
 
        key3_a = c_map._root_node._items['a'].key()
2197
 
        key3_c = c_map._root_node._items['c'].key()
2198
 
        diff = self.get_difference([key2, key3], [key1],
2199
 
                                   chk_map._search_key_plain)
2200
 
        root_results = [record.key for record in diff._read_all_roots()]
2201
 
        self.assertEqual(sorted([key2, key3]), sorted(root_results))
2202
 
        # We should have queued up key2_a, and key3_c, but not key2_c or key3_c
2203
 
        self.assertEqual([key2_a, key3_c], diff._new_queue)
2204
 
        self.assertEqual([], diff._new_item_queue)
2205
 
        # And we should have queued up both a and c for the old set
2206
 
        self.assertEqual([key1_a, key1_c], diff._old_queue)
2207
 
 
2208
 
    def test__read_all_roots_different_depths(self):
2209
 
        c_map = self.make_two_deep_map(chk_map._search_key_plain)
2210
 
        c_map._dump_tree() # load everything
2211
 
        key1 = c_map.key()
2212
 
        key1_a = c_map._root_node._items['a'].key()
2213
 
        key1_c = c_map._root_node._items['c'].key()
2214
 
        key1_d = c_map._root_node._items['d'].key()
2215
 
 
2216
 
        c_map2 = self.make_one_deep_two_prefix_map(chk_map._search_key_plain)
2217
 
        c_map2._dump_tree()
2218
 
        key2 = c_map2.key()
2219
 
        key2_aa = c_map2._root_node._items['aa'].key()
2220
 
        key2_ad = c_map2._root_node._items['ad'].key()
2221
 
 
2222
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2223
 
        root_results = [record.key for record in diff._read_all_roots()]
2224
 
        self.assertEqual([key2], root_results)
2225
 
        # Only the 'a' subset should be queued up, since 'c' and 'd' cannot be
2226
 
        # present
2227
 
        self.assertEqual([key1_a], diff._old_queue)
2228
 
        self.assertEqual([key2_aa, key2_ad], diff._new_queue)
2229
 
        self.assertEqual([], diff._new_item_queue)
2230
 
 
2231
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_plain)
2232
 
        root_results = [record.key for record in diff._read_all_roots()]
2233
 
        self.assertEqual([key1], root_results)
2234
 
 
2235
 
        self.assertEqual([key2_aa, key2_ad], diff._old_queue)
2236
 
        self.assertEqual([key1_a, key1_c, key1_d], diff._new_queue)
2237
 
        self.assertEqual([], diff._new_item_queue)
2238
 
 
2239
 
    def test__read_all_roots_different_depths_16(self):
2240
 
        c_map = self.make_two_deep_map(chk_map._search_key_16)
2241
 
        c_map._dump_tree() # load everything
2242
 
        key1 = c_map.key()
2243
 
        key1_2 = c_map._root_node._items['2'].key()
2244
 
        key1_4 = c_map._root_node._items['4'].key()
2245
 
        key1_C = c_map._root_node._items['C'].key()
2246
 
        key1_F = c_map._root_node._items['F'].key()
2247
 
 
2248
 
        c_map2 = self.make_one_deep_two_prefix_map(chk_map._search_key_16)
2249
 
        c_map2._dump_tree()
2250
 
        key2 = c_map2.key()
2251
 
        key2_F0 = c_map2._root_node._items['F0'].key()
2252
 
        key2_F3 = c_map2._root_node._items['F3'].key()
2253
 
        key2_F4 = c_map2._root_node._items['F4'].key()
2254
 
        key2_FD = c_map2._root_node._items['FD'].key()
2255
 
 
2256
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_16)
2257
 
        root_results = [record.key for record in diff._read_all_roots()]
2258
 
        self.assertEqual([key2], root_results)
2259
 
        # Only the subset of keys that may be present should be queued up.
2260
 
        self.assertEqual([key1_F], diff._old_queue)
2261
 
        self.assertEqual(sorted([key2_F0, key2_F3, key2_F4, key2_FD]),
2262
 
                         sorted(diff._new_queue))
2263
 
        self.assertEqual([], diff._new_item_queue)
2264
 
 
2265
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_16)
2266
 
        root_results = [record.key for record in diff._read_all_roots()]
2267
 
        self.assertEqual([key1], root_results)
2268
 
 
2269
 
        self.assertEqual(sorted([key2_F0, key2_F3, key2_F4, key2_FD]),
2270
 
                         sorted(diff._old_queue))
2271
 
        self.assertEqual(sorted([key1_2, key1_4, key1_C, key1_F]),
2272
 
                         sorted(diff._new_queue))
2273
 
        self.assertEqual([], diff._new_item_queue)
2274
 
 
2275
 
    def test__read_all_roots_mixed_depth(self):
2276
 
        c_map = self.make_one_deep_two_prefix_map(chk_map._search_key_plain)
2277
 
        c_map._dump_tree() # load everything
2278
 
        key1 = c_map.key()
2279
 
        key1_aa = c_map._root_node._items['aa'].key()
2280
 
        key1_ad = c_map._root_node._items['ad'].key()
2281
 
 
2282
 
        c_map2 = self.make_one_deep_one_prefix_map(chk_map._search_key_plain)
2283
 
        c_map2._dump_tree()
2284
 
        key2 = c_map2.key()
2285
 
        key2_a = c_map2._root_node._items['a'].key()
2286
 
        key2_b = c_map2._root_node._items['b'].key()
2287
 
 
2288
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2289
 
        root_results = [record.key for record in diff._read_all_roots()]
2290
 
        self.assertEqual([key2], root_results)
2291
 
        # 'ad' matches exactly 'a' on the other side, so it should be removed,
2292
 
        # and neither side should have it queued for walking
2293
 
        self.assertEqual([], diff._old_queue)
2294
 
        self.assertEqual([key2_b], diff._new_queue)
2295
 
        self.assertEqual([], diff._new_item_queue)
2296
 
 
2297
 
        diff = self.get_difference([key1], [key2], chk_map._search_key_plain)
2298
 
        root_results = [record.key for record in diff._read_all_roots()]
2299
 
        self.assertEqual([key1], root_results)
2300
 
        # Note: This is technically not the 'true minimal' set that we could
2301
 
        #       use The reason is that 'a' was matched exactly to 'ad' (by sha
2302
 
        #       sum).  However, the code gets complicated in the case of more
2303
 
        #       than one interesting key, so for now, we live with this
2304
 
        #       Consider revising, though benchmarking showing it to be a
2305
 
        #       real-world issue should be done
2306
 
        self.assertEqual([key2_a], diff._old_queue)
2307
 
        # self.assertEqual([], diff._old_queue)
2308
 
        self.assertEqual([key1_aa], diff._new_queue)
2309
 
        self.assertEqual([], diff._new_item_queue)
2310
 
 
2311
 
    def test__read_all_roots_yields_extra_deep_records(self):
2312
 
        # This is slightly controversial, as we will yield a chk page that we
2313
 
        # might later on find out could be filtered out. (If a root node is
2314
 
        # referenced deeper in the old set.)
2315
 
        # However, even with stacking, we always have all chk pages that we
2316
 
        # will need. So as long as we filter out the referenced keys, we'll
2317
 
        # never run into problems.
2318
 
        # This allows us to yield a root node record immediately, without any
2319
 
        # buffering.
2320
 
        c_map = self.make_two_deep_map(chk_map._search_key_plain)
2321
 
        c_map._dump_tree() # load all keys
2322
 
        key1 = c_map.key()
2323
 
        key1_a = c_map._root_node._items['a'].key()
2324
 
        c_map2 = self.get_map({
2325
 
            ('acc',): 'initial acc content',
2326
 
            ('ace',): 'initial ace content',
2327
 
        }, maximum_size=100)
2328
 
        self.assertEqualDiff(
2329
 
            "'' LeafNode\n"
2330
 
            "      ('acc',) 'initial acc content'\n"
2331
 
            "      ('ace',) 'initial ace content'\n",
2332
 
            c_map2._dump_tree())
2333
 
        key2 = c_map2.key()
2334
 
        diff = self.get_difference([key2], [key1], chk_map._search_key_plain)
2335
 
        root_results = [record.key for record in diff._read_all_roots()]
2336
 
        self.assertEqual([key2], root_results)
2337
 
        # However, even though we have yielded the root node to be fetched,
2338
 
        # we should have enqued all of the chk pages to be walked, so that we
2339
 
        # can find the keys if they are present
2340
 
        self.assertEqual([key1_a], diff._old_queue)
2341
 
        self.assertEqual([(('acc',), 'initial acc content'),
2342
 
                          (('ace',), 'initial ace content'),
2343
 
                         ], diff._new_item_queue)
2344
 
 
2345
 
    def test__read_all_roots_multiple_targets(self):
2346
 
        c_map = self.make_root_only_map()
2347
 
        key1 = c_map.key()
2348
 
        c_map = self.make_one_deep_map()
2349
 
        key2 = c_map.key()
2350
 
        c_map._dump_tree()
2351
 
        key2_c = c_map._root_node._items['c'].key()
2352
 
        key2_d = c_map._root_node._items['d'].key()
2353
 
        c_map.map(('ccc',), 'new ccc value')
2354
 
        key3 = c_map._save()
2355
 
        key3_c = c_map._root_node._items['c'].key()
2356
 
        diff = self.get_difference([key2, key3], [key1],
2357
 
                                   chk_map._search_key_plain)
2358
 
        root_results = [record.key for record in diff._read_all_roots()]
2359
 
        self.assertEqual(sorted([key2, key3]), sorted(root_results))
2360
 
        self.assertEqual([], diff._old_queue)
2361
 
        # the key 'd' is interesting from key2 and key3, but should only be
2362
 
        # entered into the queue 1 time
2363
 
        self.assertEqual(sorted([key2_c, key3_c, key2_d]),
2364
 
                         sorted(diff._new_queue))
2365
 
        self.assertEqual([], diff._new_item_queue)
2366
 
 
2367
 
    def test__read_all_roots_no_old(self):
2368
 
        # This is the 'initial branch' case. With nothing in the old
2369
 
        # set, we can just queue up all root nodes into interesting queue, and
2370
 
        # then have them fast-path flushed via _flush_new_queue
2371
 
        c_map = self.make_two_deep_map()
2372
 
        key1 = c_map.key()
2373
 
        diff = self.get_difference([key1], [], chk_map._search_key_plain)
2374
 
        root_results = [record.key for record in diff._read_all_roots()]
2375
 
        self.assertEqual([], root_results)
2376
 
        self.assertEqual([], diff._old_queue)
2377
 
        self.assertEqual([key1], diff._new_queue)
2378
 
        self.assertEqual([], diff._new_item_queue)
2379
 
 
2380
 
        c_map2 = self.make_one_deep_map()
2381
 
        key2 = c_map2.key()
2382
 
        diff = self.get_difference([key1, key2], [], chk_map._search_key_plain)
2383
 
        root_results = [record.key for record in diff._read_all_roots()]
2384
 
        self.assertEqual([], root_results)
2385
 
        self.assertEqual([], diff._old_queue)
2386
 
        self.assertEqual(sorted([key1, key2]), sorted(diff._new_queue))
2387
 
        self.assertEqual([], diff._new_item_queue)
2388
 
 
2389
 
    def test__read_all_roots_no_old_16(self):
2390
 
        c_map = self.make_two_deep_map(chk_map._search_key_16)
2391
 
        key1 = c_map.key()
2392
 
        diff = self.get_difference([key1], [], chk_map._search_key_16)
2393
 
        root_results = [record.key for record in diff._read_all_roots()]
2394
 
        self.assertEqual([], root_results)
2395
 
        self.assertEqual([], diff._old_queue)
2396
 
        self.assertEqual([key1], diff._new_queue)
2397
 
        self.assertEqual([], diff._new_item_queue)
2398
 
 
2399
 
        c_map2 = self.make_one_deep_map(chk_map._search_key_16)
2400
 
        key2 = c_map2.key()
2401
 
        diff = self.get_difference([key1, key2], [],
2402
 
                                   chk_map._search_key_16)
2403
 
        root_results = [record.key for record in diff._read_all_roots()]
2404
 
        self.assertEqual([], root_results)
2405
 
        self.assertEqual([], diff._old_queue)
2406
 
        self.assertEqual(sorted([key1, key2]),
2407
 
                         sorted(diff._new_queue))
2408
 
        self.assertEqual([], diff._new_item_queue)
2409
 
 
2410
 
    def test__read_all_roots_multiple_old(self):
2411
 
        c_map = self.make_two_deep_map()
2412
 
        key1 = c_map.key()
2413
 
        c_map._dump_tree() # load everything
2414
 
        key1_a = c_map._root_node._items['a'].key()
2415
 
        c_map.map(('ccc',), 'new ccc value')
2416
 
        key2 = c_map._save()
2417
 
        key2_a = c_map._root_node._items['a'].key()
2418
 
        c_map.map(('add',), 'new add value')
2419
 
        key3 = c_map._save()
2420
 
        key3_a = c_map._root_node._items['a'].key()
2421
 
        diff = self.get_difference([key3], [key1, key2],
2422
 
                                   chk_map._search_key_plain)
2423
 
        root_results = [record.key for record in diff._read_all_roots()]
2424
 
        self.assertEqual([key3], root_results)
2425
 
        # the 'a' keys should not be queued up 2 times, since they are
2426
 
        # identical
2427
 
        self.assertEqual([key1_a], diff._old_queue)
2428
 
        self.assertEqual([key3_a], diff._new_queue)
2429
 
        self.assertEqual([], diff._new_item_queue)
2430
 
 
2431
 
    def test__process_next_old_batched_no_dupes(self):
2432
 
        c_map = self.make_two_deep_map()
2433
 
        key1 = c_map.key()
2434
 
        c_map._dump_tree() # load everything
2435
 
        key1_a = c_map._root_node._items['a'].key()
2436
 
        key1_aa = c_map._root_node._items['a']._items['aa'].key()
2437
 
        key1_ab = c_map._root_node._items['a']._items['ab'].key()
2438
 
        key1_ac = c_map._root_node._items['a']._items['ac'].key()
2439
 
        key1_ad = c_map._root_node._items['a']._items['ad'].key()
2440
 
        c_map.map(('aaa',), 'new aaa value')
2441
 
        key2 = c_map._save()
2442
 
        key2_a = c_map._root_node._items['a'].key()
2443
 
        key2_aa = c_map._root_node._items['a']._items['aa'].key()
2444
 
        c_map.map(('acc',), 'new acc content')
2445
 
        key3 = c_map._save()
2446
 
        key3_a = c_map._root_node._items['a'].key()
2447
 
        key3_ac = c_map._root_node._items['a']._items['ac'].key()
2448
 
        diff = self.get_difference([key3], [key1, key2],
2449
 
                                   chk_map._search_key_plain)
2450
 
        root_results = [record.key for record in diff._read_all_roots()]
2451
 
        self.assertEqual([key3], root_results)
2452
 
        self.assertEqual(sorted([key1_a, key2_a]),
2453
 
                         sorted(diff._old_queue))
2454
 
        self.assertEqual([key3_a], diff._new_queue)
2455
 
        self.assertEqual([], diff._new_item_queue)
2456
 
        diff._process_next_old()
2457
 
        # All of the old records should be brought in and queued up,
2458
 
        # but we should not have any duplicates
2459
 
        self.assertEqual(sorted([key1_aa, key1_ab, key1_ac, key1_ad, key2_aa]),
2460
 
                         sorted(diff._old_queue))
2461
 
 
2462
 
 
2463
 
class TestIterInterestingNodes(TestCaseWithExampleMaps):
2464
 
 
2465
 
    def get_map_key(self, a_dict, maximum_size=10):
2466
 
        c_map = self.get_map(a_dict, maximum_size=maximum_size)
 
1826
class TestIterInterestingNodes(TestCaseWithStore):
 
1827
 
 
1828
    def get_chk_bytes(self):
 
1829
        if getattr(self, '_chk_bytes', None) is None:
 
1830
            self._chk_bytes = super(TestIterInterestingNodes,
 
1831
                                    self).get_chk_bytes()
 
1832
        return self._chk_bytes
 
1833
 
 
1834
    def get_map_key(self, a_dict):
 
1835
        c_map = self._get_map(a_dict, maximum_size=10,
 
1836
                              chk_bytes=self.get_chk_bytes())
2467
1837
        return c_map.key()
2468
1838
 
2469
 
    def assertIterInteresting(self, records, items, interesting_keys,
2470
 
                              old_keys):
 
1839
    def assertIterInteresting(self, expected, interesting_keys,
 
1840
                              uninteresting_keys):
2471
1841
        """Check the result of iter_interesting_nodes.
2472
1842
 
2473
 
        Note that we no longer care how many steps are taken, etc, just that
2474
 
        the right contents are returned.
2475
 
 
2476
 
        :param records: A list of record keys that should be yielded
2477
 
        :param items: A list of items (key,value) that should be yielded.
 
1843
        :param expected: A list of (record_keys, interesting_chk_pages,
 
1844
                                    interesting key value pairs)
2478
1845
        """
2479
1846
        store = self.get_chk_bytes()
2480
 
        store._search_key_func = chk_map._search_key_plain
2481
1847
        iter_nodes = chk_map.iter_interesting_nodes(store, interesting_keys,
2482
 
                                                    old_keys)
2483
 
        record_keys = []
2484
 
        all_items = []
2485
 
        for record, new_items in iter_nodes:
2486
 
            if record is not None:
2487
 
                record_keys.append(record.key)
2488
 
            if new_items:
2489
 
                all_items.extend(new_items)
2490
 
        self.assertEqual(sorted(records), sorted(record_keys))
2491
 
        self.assertEqual(sorted(items), sorted(all_items))
 
1848
                                                    uninteresting_keys)
 
1849
        for count, (exp, act) in enumerate(izip(expected, iter_nodes)):
 
1850
            exp_record_keys, exp_items = exp
 
1851
            records, items = act
 
1852
            exp_tuple = (sorted(exp_record_keys), sorted(exp_items))
 
1853
            act_tuple = (sorted(records.keys()), sorted(items))
 
1854
            self.assertEqual(exp_tuple, act_tuple)
 
1855
        self.assertEqual(len(expected), count + 1)
2492
1856
 
2493
1857
    def test_empty_to_one_keys(self):
2494
1858
        target = self.get_map_key({('a',): 'content'})
2495
 
        self.assertIterInteresting([target],
2496
 
                                   [(('a',), 'content')],
2497
 
                                   [target], [])
 
1859
        self.assertIterInteresting(
 
1860
            [([target], [(('a',), 'content')])],
 
1861
            [target], [])
2498
1862
 
2499
1863
    def test_none_to_one_key(self):
2500
1864
        basis = self.get_map_key({})
2501
1865
        target = self.get_map_key({('a',): 'content'})
2502
 
        self.assertIterInteresting([target],
2503
 
                                   [(('a',), 'content')],
2504
 
                                   [target], [basis])
 
1866
        self.assertIterInteresting(
 
1867
            [([target], [(('a',), 'content')])],
 
1868
            [target], [basis])
2505
1869
 
2506
1870
    def test_one_to_none_key(self):
2507
1871
        basis = self.get_map_key({('a',): 'content'})
2508
1872
        target = self.get_map_key({})
2509
 
        self.assertIterInteresting([target],
2510
 
                                   [],
2511
 
                                   [target], [basis])
 
1873
        self.assertIterInteresting(
 
1874
            [([target], [])],
 
1875
            [target], [basis])
2512
1876
 
2513
1877
    def test_common_pages(self):
2514
1878
        basis = self.get_map_key({('a',): 'content',
2531
1895
            target_map._dump_tree())
2532
1896
        b_key = target_map._root_node._items['b'].key()
2533
1897
        # This should return the root node, and the node for the 'b' key
2534
 
        self.assertIterInteresting([target, b_key],
2535
 
                                   [(('b',), 'other content')],
2536
 
                                   [target], [basis])
 
1898
        self.assertIterInteresting(
 
1899
            [([target], []),
 
1900
             ([b_key], [(('b',), 'other content')])],
 
1901
            [target], [basis])
2537
1902
 
2538
1903
    def test_common_sub_page(self):
2539
1904
        basis = self.get_map_key({('aaa',): 'common',
2557
1922
        # The key for the internal aa node
2558
1923
        a_key = target_map._root_node._items['a'].key()
2559
1924
        # The key for the leaf aab node
2560
 
        # aaa_key = target_map._root_node._items['a']._items['aaa'].key()
2561
1925
        aab_key = target_map._root_node._items['a']._items['aab'].key()
2562
 
        self.assertIterInteresting([target, a_key, aab_key],
2563
 
                                   [(('aab',), 'new')],
2564
 
                                   [target], [basis])
2565
 
 
2566
 
    def test_common_leaf(self):
2567
 
        basis = self.get_map_key({})
2568
 
        target1 = self.get_map_key({('aaa',): 'common'})
2569
 
        target2 = self.get_map_key({('aaa',): 'common',
2570
 
                                    ('bbb',): 'new',
2571
 
                                   })
2572
 
        target3 = self.get_map_key({('aaa',): 'common',
2573
 
                                    ('aac',): 'other',
2574
 
                                    ('bbb',): 'new',
2575
 
                                   })
2576
 
        # The LeafNode containing 'aaa': 'common' occurs at 3 different levels.
2577
 
        # Once as a root node, once as a second layer, and once as a third
2578
 
        # layer. It should only be returned one time regardless
2579
 
        target1_map = CHKMap(self.get_chk_bytes(), target1)
2580
 
        self.assertEqualDiff(
2581
 
            "'' LeafNode\n"
2582
 
            "      ('aaa',) 'common'\n",
2583
 
            target1_map._dump_tree())
2584
 
        target2_map = CHKMap(self.get_chk_bytes(), target2)
2585
 
        self.assertEqualDiff(
2586
 
            "'' InternalNode\n"
2587
 
            "  'a' LeafNode\n"
2588
 
            "      ('aaa',) 'common'\n"
2589
 
            "  'b' LeafNode\n"
2590
 
            "      ('bbb',) 'new'\n",
2591
 
            target2_map._dump_tree())
2592
 
        target3_map = CHKMap(self.get_chk_bytes(), target3)
2593
 
        self.assertEqualDiff(
2594
 
            "'' InternalNode\n"
2595
 
            "  'a' InternalNode\n"
2596
 
            "    'aaa' LeafNode\n"
2597
 
            "      ('aaa',) 'common'\n"
2598
 
            "    'aac' LeafNode\n"
2599
 
            "      ('aac',) 'other'\n"
2600
 
            "  'b' LeafNode\n"
2601
 
            "      ('bbb',) 'new'\n",
2602
 
            target3_map._dump_tree())
2603
 
        aaa_key = target1_map._root_node.key()
2604
 
        b_key = target2_map._root_node._items['b'].key()
2605
 
        a_key = target3_map._root_node._items['a'].key()
2606
 
        aac_key = target3_map._root_node._items['a']._items['aac'].key()
2607
 
        self.assertIterInteresting(
2608
 
            [target1, target2, target3, a_key, aac_key, b_key],
2609
 
            [(('aaa',), 'common'), (('bbb',), 'new'), (('aac',), 'other')],
2610
 
            [target1, target2, target3], [basis])
2611
 
 
2612
 
        self.assertIterInteresting(
2613
 
            [target2, target3, a_key, aac_key, b_key],
2614
 
            [(('bbb',), 'new'), (('aac',), 'other')],
2615
 
            [target2, target3], [target1])
2616
 
 
2617
 
        # Technically, target1 could be filtered out, but since it is a root
2618
 
        # node, we yield it immediately, rather than waiting to find out much
2619
 
        # later on.
2620
 
        self.assertIterInteresting(
2621
 
            [target1],
2622
 
            [],
2623
 
            [target1], [target3])
 
1926
        self.assertIterInteresting(
 
1927
            [([target], []),
 
1928
             ([a_key], []),
 
1929
             ([aab_key], [(('aab',), 'new')])],
 
1930
            [target], [basis])
2624
1931
 
2625
1932
    def test_multiple_maps(self):
2626
1933
        basis1 = self.get_map_key({('aaa',): 'common',
2669
1976
        # The key for the leaf bba node
2670
1977
        bba_key = target2_map._root_node._items['b']._items['bba'].key()
2671
1978
        self.assertIterInteresting(
2672
 
            [target1, target2, a_key, aac_key, b_key, bba_key],
2673
 
            [(('aac',), 'target1'), (('bba',), 'target2')],
2674
 
            [target1, target2], [basis1, basis2])
2675
 
 
2676
 
    def test_multiple_maps_overlapping_common_new(self):
2677
 
        # Test that when a node found through the interesting_keys iteration
2678
 
        # for *some roots* and also via the old keys iteration, that
2679
 
        # it is still scanned for old refs and items, because its
2680
 
        # not truely new. This requires 2 levels of InternalNodes to expose,
2681
 
        # because of the way the bootstrap in _find_children_info works.
2682
 
        # This suggests that the code is probably amenable to/benefit from
2683
 
        # consolidation.
2684
 
        # How does this test work?
2685
 
        # 1) We need a second level InternalNode present in a basis tree.
2686
 
        # 2) We need a left side new tree that uses that InternalNode
2687
 
        # 3) We need a right side new tree that does not use that InternalNode
2688
 
        #    at all but that has an unchanged *value* that was reachable inside
2689
 
        #    that InternalNode
2690
 
        basis = self.get_map_key({
2691
 
            # InternalNode, unchanged in left:
2692
 
            ('aaa',): 'left',
2693
 
            ('abb',): 'right',
2694
 
            # Forces an internalNode at 'a'
2695
 
            ('ccc',): 'common',
2696
 
            })
2697
 
        left = self.get_map_key({
2698
 
            # All of basis unchanged
2699
 
            ('aaa',): 'left',
2700
 
            ('abb',): 'right',
2701
 
            ('ccc',): 'common',
2702
 
            # And a new top level node so the root key is different
2703
 
            ('ddd',): 'change',
2704
 
            })
2705
 
        right = self.get_map_key({
2706
 
            # A value that is unchanged from basis and thus should be filtered
2707
 
            # out.
2708
 
            ('abb',): 'right'
2709
 
            })
2710
 
        basis_map = CHKMap(self.get_chk_bytes(), basis)
2711
 
        self.assertEqualDiff(
2712
 
            "'' InternalNode\n"
2713
 
            "  'a' InternalNode\n"
2714
 
            "    'aa' LeafNode\n"
2715
 
            "      ('aaa',) 'left'\n"
2716
 
            "    'ab' LeafNode\n"
2717
 
            "      ('abb',) 'right'\n"
2718
 
            "  'c' LeafNode\n"
2719
 
            "      ('ccc',) 'common'\n",
2720
 
            basis_map._dump_tree())
2721
 
        # Get left expected data
2722
 
        left_map = CHKMap(self.get_chk_bytes(), left)
2723
 
        self.assertEqualDiff(
2724
 
            "'' InternalNode\n"
2725
 
            "  'a' InternalNode\n"
2726
 
            "    'aa' LeafNode\n"
2727
 
            "      ('aaa',) 'left'\n"
2728
 
            "    'ab' LeafNode\n"
2729
 
            "      ('abb',) 'right'\n"
2730
 
            "  'c' LeafNode\n"
2731
 
            "      ('ccc',) 'common'\n"
2732
 
            "  'd' LeafNode\n"
2733
 
            "      ('ddd',) 'change'\n",
2734
 
            left_map._dump_tree())
2735
 
        # Keys from left side target
2736
 
        l_d_key = left_map._root_node._items['d'].key()
2737
 
        # Get right expected data
2738
 
        right_map = CHKMap(self.get_chk_bytes(), right)
2739
 
        self.assertEqualDiff(
2740
 
            "'' LeafNode\n"
2741
 
            "      ('abb',) 'right'\n",
2742
 
            right_map._dump_tree())
2743
 
        # Keys from the right side target - none, the root is enough.
2744
 
        # Test behaviour
2745
 
        self.assertIterInteresting(
2746
 
            [right, left, l_d_key],
2747
 
            [(('ddd',), 'change')],
2748
 
            [left, right], [basis])
2749
 
 
2750
 
    def test_multiple_maps_similar(self):
2751
 
        # We want to have a depth=2 tree, with multiple entries in each leaf
2752
 
        # node
2753
 
        basis = self.get_map_key({
2754
 
            ('aaa',): 'unchanged',
2755
 
            ('abb',): 'will change left',
2756
 
            ('caa',): 'unchanged',
2757
 
            ('cbb',): 'will change right',
2758
 
            }, maximum_size=60)
2759
 
        left = self.get_map_key({
2760
 
            ('aaa',): 'unchanged',
2761
 
            ('abb',): 'changed left',
2762
 
            ('caa',): 'unchanged',
2763
 
            ('cbb',): 'will change right',
2764
 
            }, maximum_size=60)
2765
 
        right = self.get_map_key({
2766
 
            ('aaa',): 'unchanged',
2767
 
            ('abb',): 'will change left',
2768
 
            ('caa',): 'unchanged',
2769
 
            ('cbb',): 'changed right',
2770
 
            }, maximum_size=60)
2771
 
        basis_map = CHKMap(self.get_chk_bytes(), basis)
2772
 
        self.assertEqualDiff(
2773
 
            "'' InternalNode\n"
2774
 
            "  'a' LeafNode\n"
2775
 
            "      ('aaa',) 'unchanged'\n"
2776
 
            "      ('abb',) 'will change left'\n"
2777
 
            "  'c' LeafNode\n"
2778
 
            "      ('caa',) 'unchanged'\n"
2779
 
            "      ('cbb',) 'will change right'\n",
2780
 
            basis_map._dump_tree())
2781
 
        # Get left expected data
2782
 
        left_map = CHKMap(self.get_chk_bytes(), left)
2783
 
        self.assertEqualDiff(
2784
 
            "'' InternalNode\n"
2785
 
            "  'a' LeafNode\n"
2786
 
            "      ('aaa',) 'unchanged'\n"
2787
 
            "      ('abb',) 'changed left'\n"
2788
 
            "  'c' LeafNode\n"
2789
 
            "      ('caa',) 'unchanged'\n"
2790
 
            "      ('cbb',) 'will change right'\n",
2791
 
            left_map._dump_tree())
2792
 
        # Keys from left side target
2793
 
        l_a_key = left_map._root_node._items['a'].key()
2794
 
        l_c_key = left_map._root_node._items['c'].key()
2795
 
        # Get right expected data
2796
 
        right_map = CHKMap(self.get_chk_bytes(), right)
2797
 
        self.assertEqualDiff(
2798
 
            "'' InternalNode\n"
2799
 
            "  'a' LeafNode\n"
2800
 
            "      ('aaa',) 'unchanged'\n"
2801
 
            "      ('abb',) 'will change left'\n"
2802
 
            "  'c' LeafNode\n"
2803
 
            "      ('caa',) 'unchanged'\n"
2804
 
            "      ('cbb',) 'changed right'\n",
2805
 
            right_map._dump_tree())
2806
 
        r_a_key = right_map._root_node._items['a'].key()
2807
 
        r_c_key = right_map._root_node._items['c'].key()
2808
 
        self.assertIterInteresting(
2809
 
            [right, left, l_a_key, r_c_key],
2810
 
            [(('abb',), 'changed left'), (('cbb',), 'changed right')],
2811
 
            [left, right], [basis])
 
1979
            [([target1, target2], []),
 
1980
             ([a_key], []),
 
1981
             ([b_key], []),
 
1982
             ([aac_key], [(('aac',), 'target1')]),
 
1983
             ([bba_key], [(('bba',), 'target2')]),
 
1984
            ], [target1, target2], [basis1, basis2])