~bzr-pqm/bzr/bzr.dev

4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
1
# Copyright (C) 2009 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Tests for the StaticTupleInterned type."""
18
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
19
import sys
20
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
21
from bzrlib import (
22
    errors,
23
    osutils,
24
    tests,
25
    )
26
27
from bzrlib.tests import (
28
    test__static_tuple,
29
    )
30
try:
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
31
    from bzrlib import _simple_set_pyx as _module
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
32
except ImportError:
33
    _module = None
34
try:
35
    from bzrlib._static_tuple_c import StaticTuple
36
except ImportError:
4679.3.75 by John Arbash Meinel
Minor tweak.
37
    from bzrlib._static_tuple_py import StaticTuple
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
38
39
40
# Even though this is an extension, we don't permute the tests for a python
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
41
# version. As the plain python version is just a dict or set
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
42
43
class _CompiledStaticTupleInterned(tests.Feature):
44
45
    def _probe(self):
46
        if _module is None:
47
            return False
48
        return True
49
50
    def feature_name(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
51
        return 'bzrlib._simple_set_pyx'
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
52
53
CompiledStaticTupleInterned = _CompiledStaticTupleInterned()
54
55
56
class TestStaticTupleInterned(tests.TestCase):
57
58
    _test_needs_features = [CompiledStaticTupleInterned, 
59
                            test__static_tuple.CompiledStaticTuple]
60
61
    def assertIn(self, obj, container):
62
        self.assertTrue(obj in container,
63
            '%s not found in %s' % (obj, container))
64
65
    def assertNotIn(self, obj, container):
66
        self.assertTrue(obj not in container,
67
            'We found %s in %s' % (obj, container))
68
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
69
    def assertFillState(self, used, fill, mask, obj):
70
        self.assertEqual((used, fill, mask), (obj.used, obj.fill, obj.mask))
71
72
    def assertRefcount(self, count, obj):
73
        """Assert that the refcount for obj is what we expect.
74
75
        Note that this automatically adjusts for the fact that calling
76
        assertRefcount actually creates a new pointer, as does calling
77
        sys.getrefcount. So pass the expected value *before* the call.
78
        """
79
        # I don't understand why it is count+3 here, but it seems to be
80
        # correct. If I check in the calling function, with:
81
        # self.assertEqual(count+1, sys.getrefcount(obj))
82
        # Then it works fine. Something about passing it to assertRefcount is
83
        # actually double-incrementing (and decrementing) the refcount
84
        self.assertEqual(count+3, sys.getrefcount(obj))
85
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
86
    def test_initial(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
87
        obj = _module.SimpleSet()
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
88
        self.assertEqual(0, len(obj))
89
        st = StaticTuple('foo', 'bar')
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
90
        self.assertFillState(0, 0, 0x3ff, obj)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
91
92
    def test__lookup(self):
93
        # The tuple hash function is rather good at entropy. For all integers
94
        # 0=>1023, hash((i,)) & 1023 maps to a unique output, and hash((i,j))
95
        # maps to all 1024 fields evenly.
96
        # However, hash((c,d))& 1023 for characters has an uneven distribution
97
        # of collisions, for example:
98
        #  ('a', 'a'), ('f', '4'), ('p', 'r'), ('q', '1'), ('F', 'T'),
99
        #  ('Q', 'Q'), ('V', 'd'), ('7', 'C')
100
        # all collide @ 643
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
101
        obj = _module.SimpleSet()
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
102
        offset, val = obj._test_lookup(StaticTuple('a', 'a'))
103
        self.assertEqual(643, offset)
104
        self.assertEqual('<null>', val)
105
        offset, val = obj._test_lookup(StaticTuple('f', '4'))
106
        self.assertEqual(643, offset)
107
        self.assertEqual('<null>', val)
108
        offset, val = obj._test_lookup(StaticTuple('p', 'r'))
109
        self.assertEqual(643, offset)
110
        self.assertEqual('<null>', val)
111
112
    def test_get_set_del_with_collisions(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
113
        obj = _module.SimpleSet()
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
114
        k1 = StaticTuple('a', 'a')
115
        k2 = StaticTuple('f', '4') # collides
116
        k3 = StaticTuple('p', 'r')
117
        k4 = StaticTuple('q', '1')
118
        self.assertEqual((643, '<null>'), obj._test_lookup(k1))
119
        self.assertEqual((643, '<null>'), obj._test_lookup(k2))
120
        self.assertEqual((643, '<null>'), obj._test_lookup(k3))
121
        self.assertEqual((643, '<null>'), obj._test_lookup(k4))
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
122
        obj.add(k1)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
123
        self.assertIn(k1, obj)
124
        self.assertNotIn(k2, obj)
125
        self.assertNotIn(k3, obj)
126
        self.assertNotIn(k4, obj)
127
        self.assertEqual((643, k1), obj._test_lookup(k1))
128
        self.assertEqual((787, '<null>'), obj._test_lookup(k2))
129
        self.assertEqual((787, '<null>'), obj._test_lookup(k3))
130
        self.assertEqual((787, '<null>'), obj._test_lookup(k4))
131
        self.assertIs(k1, obj[k1])
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
132
        obj.add(k2)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
133
        self.assertIs(k2, obj[k2])
134
        self.assertEqual((643, k1), obj._test_lookup(k1))
135
        self.assertEqual((787, k2), obj._test_lookup(k2))
136
        self.assertEqual((660, '<null>'), obj._test_lookup(k3))
137
        # Even though k4 collides for the first couple of iterations, the hash
138
        # perturbation uses the full width hash (not just the masked value), so
139
        # it now diverges
140
        self.assertEqual((180, '<null>'), obj._test_lookup(k4))
141
        self.assertEqual((643, k1), obj._test_lookup(('a', 'a')))
142
        self.assertEqual((787, k2), obj._test_lookup(('f', '4')))
143
        self.assertEqual((660, '<null>'), obj._test_lookup(('p', 'r')))
144
        self.assertEqual((180, '<null>'), obj._test_lookup(('q', '1')))
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
145
        obj.add(k3)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
146
        self.assertIs(k3, obj[k3])
147
        self.assertIn(k1, obj)
148
        self.assertIn(k2, obj)
149
        self.assertIn(k3, obj)
150
        self.assertNotIn(k4, obj)
151
152
        del obj[k1]
153
        self.assertEqual((643, '<dummy>'), obj._test_lookup(k1))
154
        self.assertEqual((787, k2), obj._test_lookup(k2))
155
        self.assertEqual((660, k3), obj._test_lookup(k3))
156
        self.assertEqual((643, '<dummy>'), obj._test_lookup(k4))
157
        self.assertNotIn(k1, obj)
158
        self.assertIn(k2, obj)
159
        self.assertIn(k3, obj)
160
        self.assertNotIn(k4, obj)
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
161
162
    def test_add(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
163
        obj = _module.SimpleSet()
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
164
        self.assertFillState(0, 0, 0x3ff, obj)
165
        k1 = StaticTuple('foo')
166
        self.assertRefcount(1, k1)
167
        self.assertIs(k1, obj.add(k1))
168
        self.assertFillState(1, 1, 0x3ff, obj)
169
        self.assertRefcount(2, k1)
170
        ktest = obj[k1]
171
        self.assertRefcount(3, k1)
172
        self.assertIs(k1, ktest)
173
        del ktest
174
        self.assertRefcount(2, k1)
175
        k2 = StaticTuple('foo')
176
        self.assertRefcount(1, k2)
177
        self.assertIsNot(k1, k2)
178
        # doesn't add anything, so the counters shouldn't be adjusted
179
        self.assertIs(k1, obj.add(k2))
180
        self.assertFillState(1, 1, 0x3ff, obj)
181
        self.assertRefcount(2, k1) # not changed
182
        self.assertRefcount(1, k2) # not incremented
183
        self.assertIs(k1, obj[k1])
184
        self.assertIs(k1, obj[k2])
185
        self.assertRefcount(2, k1)
186
        self.assertRefcount(1, k2)
187
        # Deleting an entry should remove the fill, but not the used
188
        del obj[k1]
189
        self.assertFillState(0, 1, 0x3ff, obj)
190
        self.assertRefcount(1, k1)
191
        k3 = StaticTuple('bar')
192
        self.assertRefcount(1, k3)
193
        self.assertIs(k3, obj.add(k3))
194
        self.assertFillState(1, 2, 0x3ff, obj)
195
        self.assertRefcount(2, k3)
196
        self.assertIs(k2, obj.add(k2))
197
        self.assertFillState(2, 2, 0x3ff, obj)
198
        self.assertRefcount(1, k1)
199
        self.assertRefcount(2, k2)
200
        self.assertRefcount(2, k3)
201
202
    def test_discard(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
203
        obj = _module.SimpleSet()
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
204
        k1 = StaticTuple('foo')
205
        k2 = StaticTuple('foo')
206
        k3 = StaticTuple('bar')
207
        self.assertRefcount(1, k1)
208
        self.assertRefcount(1, k2)
209
        self.assertRefcount(1, k3)
210
        obj.add(k1)
211
        self.assertRefcount(2, k1)
212
        self.assertEqual(0, obj.discard(k3))
213
        self.assertRefcount(1, k3)
214
        obj.add(k3)
215
        self.assertRefcount(2, k3)
216
        self.assertEqual(1, obj.discard(k3))
217
        self.assertRefcount(1, k3)
218
219
    def test__delitem__(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
220
        obj = _module.SimpleSet()
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
221
        k1 = StaticTuple('foo')
222
        k2 = StaticTuple('foo')
223
        k3 = StaticTuple('bar')
224
        self.assertRefcount(1, k1)
225
        self.assertRefcount(1, k2)
226
        self.assertRefcount(1, k3)
227
        obj.add(k1)
228
        self.assertRefcount(2, k1)
229
        self.assertRaises(KeyError, obj.__delitem__, k3)
230
        self.assertRefcount(1, k3)
231
        obj.add(k3)
232
        self.assertRefcount(2, k3)
233
        del obj[k3]
234
        self.assertRefcount(1, k3)
4679.3.63 by John Arbash Meinel
Implement resizing.
235
236
    def test__resize(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
237
        obj = _module.SimpleSet()
4679.3.63 by John Arbash Meinel
Implement resizing.
238
        k1 = StaticTuple('foo')
239
        k2 = StaticTuple('bar')
240
        k3 = StaticTuple('baz')
241
        obj.add(k1)
242
        obj.add(k2)
243
        obj.add(k3)
244
        del obj[k2]
245
        self.assertFillState(2, 3, 0x3ff, obj)
246
        self.assertEqual(1024, obj._resize(500))
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
247
        # Doesn't change the size, but does change the content
248
        self.assertFillState(2, 2, 0x3ff, obj)
249
        obj.add(k2)
250
        del obj[k3]
4679.3.63 by John Arbash Meinel
Implement resizing.
251
        self.assertFillState(2, 3, 0x3ff, obj)
252
        self.assertEqual(4096, obj._resize(4095))
253
        self.assertFillState(2, 2, 0xfff, obj)
254
        self.assertIn(k1, obj)
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
255
        self.assertIn(k2, obj)
256
        self.assertNotIn(k3, obj)
4679.3.63 by John Arbash Meinel
Implement resizing.
257
        obj.add(k2)
258
        self.assertIn(k2, obj)
259
        del obj[k2]
260
        self.assertEqual((591, '<dummy>'), obj._test_lookup(k2))
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
261
        self.assertFillState(1, 2, 0xfff, obj)
4679.3.63 by John Arbash Meinel
Implement resizing.
262
        self.assertEqual(2048, obj._resize(1024))
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
263
        self.assertFillState(1, 1, 0x7ff, obj)
4679.3.63 by John Arbash Meinel
Implement resizing.
264
        self.assertEqual((591, '<null>'), obj._test_lookup(k2))
265
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
266
    def test_add_and_remove_lots_of_items(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
267
        obj = _module.SimpleSet()
4679.3.63 by John Arbash Meinel
Implement resizing.
268
        chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
269
        for i in chars:
270
            for j in chars:
271
                k = StaticTuple(i, j)
272
                obj.add(k)
273
        num = len(chars)*len(chars)
274
        self.assertFillState(num, num, 0x1fff, obj)
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
275
        # Now delete all of the entries and it should shrink again
276
        for i in chars:
277
            for j in chars:
278
                k = StaticTuple(i, j)
279
                obj.discard(k)
280
        # It should be back to 1024 wide mask, though there may still be some
281
        # dummy values in there
282
        self.assertFillState(0, obj.fill, 0x3ff, obj)
283
        # but there should be fewer than 1/5th dummy entries
284
        self.assertTrue(obj.fill < 1024 / 5)
4679.3.65 by John Arbash Meinel
Add __iter__ support.
285
286
    def test__iter__(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
287
        obj = _module.SimpleSet()
4679.3.65 by John Arbash Meinel
Add __iter__ support.
288
        k1 = StaticTuple('1')
289
        k2 = StaticTuple('1', '2')
290
        k3 = StaticTuple('3', '4')
291
        obj.add(k1)
292
        obj.add(k2)
293
        obj.add(k3)
294
        all = set()
295
        for key in obj:
296
            all.add(key)
297
        self.assertEqual(sorted([k1, k2, k3]), sorted(all))
298
        iterator = iter(obj)
299
        iterator.next()
300
        obj.add(StaticTuple('foo'))
301
        # Set changed size
302
        self.assertRaises(RuntimeError, iterator.next)
303
        # And even removing an item still causes it to fail
304
        del obj[k2]
305
        self.assertRaises(RuntimeError, iterator.next)