~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__static_tuple.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009, 2010 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 StaticTuple type."""
 
18
 
 
19
import cPickle
 
20
import gc
 
21
import sys
 
22
 
 
23
from bzrlib import (
 
24
    _static_tuple_py,
 
25
    debug,
 
26
    errors,
 
27
    osutils,
 
28
    static_tuple,
 
29
    tests,
 
30
    )
 
31
 
 
32
 
 
33
def load_tests(standard_tests, module, loader):
 
34
    """Parameterize tests for all versions of groupcompress."""
 
35
    global compiled_static_tuple_feature
 
36
    suite, compiled_static_tuple_feature = tests.permute_tests_for_extension(
 
37
        standard_tests, loader, 'bzrlib._static_tuple_py',
 
38
        'bzrlib._static_tuple_c')
 
39
    return suite
 
40
 
 
41
 
 
42
class _Meliae(tests.Feature):
 
43
 
 
44
    def _probe(self):
 
45
        try:
 
46
            from meliae import scanner
 
47
        except ImportError:
 
48
            return False
 
49
        return True
 
50
 
 
51
    def feature_name(self):
 
52
        return "Meliae - python memory debugger"
 
53
 
 
54
Meliae = _Meliae()
 
55
 
 
56
 
 
57
class TestStaticTuple(tests.TestCase):
 
58
 
 
59
    def assertRefcount(self, count, obj):
 
60
        """Assert that the refcount for obj is what we expect.
 
61
 
 
62
        Note that this automatically adjusts for the fact that calling
 
63
        assertRefcount actually creates a new pointer, as does calling
 
64
        sys.getrefcount. So pass the expected value *before* the call.
 
65
        """
 
66
        # I don't understand why it is getrefcount()-3 here, but it seems to be
 
67
        # correct. If I check in the calling function, with:
 
68
        # self.assertEqual(count, sys.getrefcount(obj)-1)
 
69
        # Then it works fine. Something about passing it to assertRefcount is
 
70
        # actually double-incrementing (and decrementing) the refcount
 
71
        self.assertEqual(count, sys.getrefcount(obj)-3)
 
72
 
 
73
    def test_create(self):
 
74
        k = self.module.StaticTuple('foo')
 
75
        k = self.module.StaticTuple('foo', 'bar')
 
76
 
 
77
    def test_create_bad_args(self):
 
78
        args_256 = ['a']*256
 
79
        # too many args
 
80
        self.assertRaises(TypeError, self.module.StaticTuple, *args_256)
 
81
        args_300 = ['a']*300
 
82
        self.assertRaises(TypeError, self.module.StaticTuple, *args_300)
 
83
        # not a string
 
84
        self.assertRaises(TypeError, self.module.StaticTuple, object())
 
85
 
 
86
    def test_concat(self):
 
87
        st1 = self.module.StaticTuple('foo')
 
88
        st2 = self.module.StaticTuple('bar')
 
89
        st3 = self.module.StaticTuple('foo', 'bar')
 
90
        st4 = st1 + st2
 
91
        self.assertEqual(st3, st4)
 
92
        self.assertIsInstance(st4, self.module.StaticTuple)
 
93
 
 
94
    def test_concat_with_tuple(self):
 
95
        st1 = self.module.StaticTuple('foo')
 
96
        t2 = ('bar',)
 
97
        st3 = self.module.StaticTuple('foo', 'bar')
 
98
        st4 = self.module.StaticTuple('bar', 'foo')
 
99
        st5 = st1 + t2
 
100
        st6 = t2 + st1
 
101
        self.assertEqual(st3, st5)
 
102
        self.assertIsInstance(st5, self.module.StaticTuple)
 
103
        self.assertEqual(st4, st6)
 
104
        if self.module is _static_tuple_py:
 
105
            # _static_tuple_py has StaticTuple(tuple), so tuple thinks it
 
106
            # already knows how to concatenate, as such we can't "inject" our
 
107
            # own concatenation...
 
108
            self.assertIsInstance(st6, tuple)
 
109
        else:
 
110
            self.assertIsInstance(st6, self.module.StaticTuple)
 
111
 
 
112
    def test_concat_with_bad_tuple(self):
 
113
        st1 = self.module.StaticTuple('foo')
 
114
        t2 = (object(),)
 
115
        # Using st1.__add__ doesn't give the same results as doing the '+' form
 
116
        self.assertRaises(TypeError, lambda: st1 + t2)
 
117
 
 
118
    def test_concat_with_non_tuple(self):
 
119
        st1 = self.module.StaticTuple('foo')
 
120
        self.assertRaises(TypeError, lambda: st1 + 10)
 
121
        
 
122
    def test_as_tuple(self):
 
123
        k = self.module.StaticTuple('foo')
 
124
        t = k.as_tuple()
 
125
        self.assertEqual(('foo',), t)
 
126
        self.assertIsInstance(t, tuple)
 
127
        self.assertFalse(isinstance(t, self.module.StaticTuple))
 
128
        k = self.module.StaticTuple('foo', 'bar')
 
129
        t = k.as_tuple()
 
130
        self.assertEqual(('foo', 'bar'), t)
 
131
        k2 = self.module.StaticTuple(1, k)
 
132
        t = k2.as_tuple()
 
133
        self.assertIsInstance(t, tuple)
 
134
        # For pickling to work, we need to keep the sub-items as StaticTuple so
 
135
        # that it knows that they also need to be converted.
 
136
        self.assertIsInstance(t[1], self.module.StaticTuple)
 
137
        self.assertEqual((1, ('foo', 'bar')), t)
 
138
 
 
139
    def test_as_tuples(self):
 
140
        k1 = self.module.StaticTuple('foo', 'bar')
 
141
        t = static_tuple.as_tuples(k1)
 
142
        self.assertIsInstance(t, tuple)
 
143
        self.assertEqual(('foo', 'bar'), t)
 
144
        k2 = self.module.StaticTuple(1, k1)
 
145
        t = static_tuple.as_tuples(k2)
 
146
        self.assertIsInstance(t, tuple)
 
147
        self.assertIsInstance(t[1], tuple)
 
148
        self.assertEqual((1, ('foo', 'bar')), t)
 
149
        mixed = (1, k1)
 
150
        t = static_tuple.as_tuples(mixed)
 
151
        self.assertIsInstance(t, tuple)
 
152
        self.assertIsInstance(t[1], tuple)
 
153
        self.assertEqual((1, ('foo', 'bar')), t)
 
154
 
 
155
    def test_len(self):
 
156
        k = self.module.StaticTuple()
 
157
        self.assertEqual(0, len(k))
 
158
        k = self.module.StaticTuple('foo')
 
159
        self.assertEqual(1, len(k))
 
160
        k = self.module.StaticTuple('foo', 'bar')
 
161
        self.assertEqual(2, len(k))
 
162
        k = self.module.StaticTuple('foo', 'bar', 'b', 'b', 'b', 'b', 'b')
 
163
        self.assertEqual(7, len(k))
 
164
        args = ['foo']*255
 
165
        k = self.module.StaticTuple(*args)
 
166
        self.assertEqual(255, len(k))
 
167
 
 
168
    def test_hold_other_static_tuples(self):
 
169
        k = self.module.StaticTuple('foo', 'bar')
 
170
        k2 = self.module.StaticTuple(k, k)
 
171
        self.assertEqual(2, len(k2))
 
172
        self.assertIs(k, k2[0])
 
173
        self.assertIs(k, k2[1])
 
174
 
 
175
    def test_getitem(self):
 
176
        k = self.module.StaticTuple('foo', 'bar', 'b', 'b', 'b', 'b', 'z')
 
177
        self.assertEqual('foo', k[0])
 
178
        self.assertEqual('foo', k[0])
 
179
        self.assertEqual('foo', k[0])
 
180
        self.assertEqual('z', k[6])
 
181
        self.assertEqual('z', k[-1])
 
182
        self.assertRaises(IndexError, k.__getitem__, 7)
 
183
        self.assertRaises(IndexError, k.__getitem__, 256+7)
 
184
        self.assertRaises(IndexError, k.__getitem__, 12024)
 
185
        # Python's [] resolver handles the negative arguments, so we can't
 
186
        # really test StaticTuple_item() with negative values.
 
187
        self.assertRaises(TypeError, k.__getitem__, 'not-an-int')
 
188
        self.assertRaises(TypeError, k.__getitem__, '5')
 
189
 
 
190
    def test_refcount(self):
 
191
        f = 'fo' + 'oo'
 
192
        num_refs = sys.getrefcount(f) - 1 #sys.getrefcount() adds one
 
193
        k = self.module.StaticTuple(f)
 
194
        self.assertRefcount(num_refs + 1, f)
 
195
        b = k[0]
 
196
        self.assertRefcount(num_refs + 2, f)
 
197
        b = k[0]
 
198
        self.assertRefcount(num_refs + 2, f)
 
199
        c = k[0]
 
200
        self.assertRefcount(num_refs + 3, f)
 
201
        del b, c
 
202
        self.assertRefcount(num_refs + 1, f)
 
203
        del k
 
204
        self.assertRefcount(num_refs, f)
 
205
 
 
206
    def test__repr__(self):
 
207
        k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')
 
208
        self.assertEqual("StaticTuple('foo', 'bar', 'baz', 'bing')", repr(k))
 
209
 
 
210
    def assertCompareEqual(self, k1, k2):
 
211
        self.assertTrue(k1 == k2)
 
212
        self.assertTrue(k1 <= k2)
 
213
        self.assertTrue(k1 >= k2)
 
214
        self.assertFalse(k1 != k2)
 
215
        self.assertFalse(k1 < k2)
 
216
        self.assertFalse(k1 > k2)
 
217
 
 
218
    def test_holds_None(self):
 
219
        k1 = self.module.StaticTuple(None)
 
220
        # You cannot subclass None anyway
 
221
 
 
222
    def test_holds_int(self):
 
223
        k1 = self.module.StaticTuple(1)
 
224
        class subint(int):
 
225
            pass
 
226
        # But not a subclass, because subint could introduce refcycles
 
227
        self.assertRaises(TypeError, self.module.StaticTuple, subint(2))
 
228
 
 
229
    def test_holds_long(self):
 
230
        k1 = self.module.StaticTuple(2L**65)
 
231
        class sublong(long):
 
232
            pass
 
233
        # But not a subclass
 
234
        self.assertRaises(TypeError, self.module.StaticTuple, sublong(1))
 
235
 
 
236
    def test_holds_float(self):
 
237
        k1 = self.module.StaticTuple(1.2)
 
238
        class subfloat(float):
 
239
            pass
 
240
        self.assertRaises(TypeError, self.module.StaticTuple, subfloat(1.5))
 
241
 
 
242
    def test_holds_str(self):
 
243
        k1 = self.module.StaticTuple('astring')
 
244
        class substr(str):
 
245
            pass
 
246
        self.assertRaises(TypeError, self.module.StaticTuple, substr('a'))
 
247
 
 
248
    def test_holds_unicode(self):
 
249
        k1 = self.module.StaticTuple(u'\xb5')
 
250
        class subunicode(unicode):
 
251
            pass
 
252
        self.assertRaises(TypeError, self.module.StaticTuple,
 
253
                          subunicode(u'\xb5'))
 
254
 
 
255
    def test_hold_bool(self):
 
256
        k1 = self.module.StaticTuple(True)
 
257
        k2 = self.module.StaticTuple(False)
 
258
        # Cannot subclass bool
 
259
 
 
260
    def test_compare_same_obj(self):
 
261
        k1 = self.module.StaticTuple('foo', 'bar')
 
262
        self.assertCompareEqual(k1, k1)
 
263
        k2 = self.module.StaticTuple(k1, k1)
 
264
        self.assertCompareEqual(k2, k2)
 
265
        k3 = self.module.StaticTuple('foo', 1, None, u'\xb5', 1.2, 2**65, True,
 
266
                                     k1)
 
267
        self.assertCompareEqual(k3, k3)
 
268
 
 
269
    def test_compare_equivalent_obj(self):
 
270
        k1 = self.module.StaticTuple('foo', 'bar')
 
271
        k2 = self.module.StaticTuple('foo', 'bar')
 
272
        self.assertCompareEqual(k1, k2)
 
273
        k3 = self.module.StaticTuple(k1, k2)
 
274
        k4 = self.module.StaticTuple(k2, k1)
 
275
        self.assertCompareEqual(k1, k2)
 
276
        k5 = self.module.StaticTuple('foo', 1, None, u'\xb5', 1.2, 2**65, True,
 
277
                                     k1)
 
278
        k6 = self.module.StaticTuple('foo', 1, None, u'\xb5', 1.2, 2**65, True,
 
279
                                     k1)
 
280
        self.assertCompareEqual(k5, k6)
 
281
        k7 = self.module.StaticTuple(None)
 
282
        k8 = self.module.StaticTuple(None)
 
283
        self.assertCompareEqual(k7, k8)
 
284
 
 
285
    def test_compare_similar_obj(self):
 
286
        k1 = self.module.StaticTuple('foo' + ' bar', 'bar' + ' baz')
 
287
        k2 = self.module.StaticTuple('fo' + 'o bar', 'ba' + 'r baz')
 
288
        self.assertCompareEqual(k1, k2)
 
289
        k3 = self.module.StaticTuple('foo ' + 'bar', 'bar ' + 'baz')
 
290
        k4 = self.module.StaticTuple('f' + 'oo bar', 'b' + 'ar baz')
 
291
        k5 = self.module.StaticTuple(k1, k2)
 
292
        k6 = self.module.StaticTuple(k3, k4)
 
293
        self.assertCompareEqual(k5, k6)
 
294
 
 
295
    def assertCompareDifferent(self, k_small, k_big):
 
296
        self.assertFalse(k_small == k_big)
 
297
        self.assertFalse(k_small >= k_big)
 
298
        self.assertFalse(k_small > k_big)
 
299
        self.assertTrue(k_small != k_big)
 
300
        self.assertTrue(k_small <= k_big)
 
301
        self.assertTrue(k_small < k_big)
 
302
 
 
303
    def assertCompareNoRelation(self, k1, k2):
 
304
        """Run the comparison operators, make sure they do something.
 
305
 
 
306
        However, we don't actually care what comes first or second. This is
 
307
        stuff like cross-class comparisons. We don't want to segfault/raise an
 
308
        exception, but we don't care about the sort order.
 
309
        """
 
310
        self.assertFalse(k1 == k2)
 
311
        self.assertTrue(k1 != k2)
 
312
        # Do the comparison, but we don't care about the result
 
313
        k1 >= k2
 
314
        k1 > k2
 
315
        k1 <= k2
 
316
        k1 < k2
 
317
 
 
318
    def test_compare_vs_none(self):
 
319
        k1 = self.module.StaticTuple('baz', 'bing')
 
320
        self.assertCompareDifferent(None, k1)
 
321
    
 
322
    def test_compare_cross_class(self):
 
323
        k1 = self.module.StaticTuple('baz', 'bing')
 
324
        self.assertCompareNoRelation(10, k1)
 
325
        self.assertCompareNoRelation('baz', k1)
 
326
 
 
327
    def test_compare_all_different_same_width(self):
 
328
        k1 = self.module.StaticTuple('baz', 'bing')
 
329
        k2 = self.module.StaticTuple('foo', 'bar')
 
330
        self.assertCompareDifferent(k1, k2)
 
331
        k3 = self.module.StaticTuple(k1, k2)
 
332
        k4 = self.module.StaticTuple(k2, k1)
 
333
        self.assertCompareDifferent(k3, k4)
 
334
        k5 = self.module.StaticTuple(1)
 
335
        k6 = self.module.StaticTuple(2)
 
336
        self.assertCompareDifferent(k5, k6)
 
337
        k7 = self.module.StaticTuple(1.2)
 
338
        k8 = self.module.StaticTuple(2.4)
 
339
        self.assertCompareDifferent(k7, k8)
 
340
        k9 = self.module.StaticTuple(u's\xb5')
 
341
        k10 = self.module.StaticTuple(u's\xe5')
 
342
        self.assertCompareDifferent(k9, k10)
 
343
 
 
344
    def test_compare_some_different(self):
 
345
        k1 = self.module.StaticTuple('foo', 'bar')
 
346
        k2 = self.module.StaticTuple('foo', 'zzz')
 
347
        self.assertCompareDifferent(k1, k2)
 
348
        k3 = self.module.StaticTuple(k1, k1)
 
349
        k4 = self.module.StaticTuple(k1, k2)
 
350
        self.assertCompareDifferent(k3, k4)
 
351
        k5 = self.module.StaticTuple('foo', None)
 
352
        self.assertCompareDifferent(k5, k1)
 
353
        self.assertCompareDifferent(k5, k2)
 
354
 
 
355
    def test_compare_diff_width(self):
 
356
        k1 = self.module.StaticTuple('foo')
 
357
        k2 = self.module.StaticTuple('foo', 'bar')
 
358
        self.assertCompareDifferent(k1, k2)
 
359
        k3 = self.module.StaticTuple(k1)
 
360
        k4 = self.module.StaticTuple(k1, k2)
 
361
        self.assertCompareDifferent(k3, k4)
 
362
 
 
363
    def test_compare_different_types(self):
 
364
        k1 = self.module.StaticTuple('foo', 'bar')
 
365
        k2 = self.module.StaticTuple('foo', 1, None, u'\xb5', 1.2, 2**65, True,
 
366
                                     k1)
 
367
        self.assertCompareNoRelation(k1, k2)
 
368
        k3 = self.module.StaticTuple('foo')
 
369
        self.assertCompareDifferent(k3, k1)
 
370
        k4 = self.module.StaticTuple(None)
 
371
        self.assertCompareDifferent(k4, k1)
 
372
        k5 = self.module.StaticTuple(1)
 
373
        self.assertCompareNoRelation(k1, k5)
 
374
 
 
375
    def test_compare_to_tuples(self):
 
376
        k1 = self.module.StaticTuple('foo')
 
377
        self.assertCompareEqual(k1, ('foo',))
 
378
        self.assertCompareEqual(('foo',), k1)
 
379
        self.assertCompareDifferent(k1, ('foo', 'bar'))
 
380
        self.assertCompareDifferent(k1, ('foo', 10))
 
381
 
 
382
        k2 = self.module.StaticTuple('foo', 'bar')
 
383
        self.assertCompareEqual(k2, ('foo', 'bar'))
 
384
        self.assertCompareEqual(('foo', 'bar'), k2)
 
385
        self.assertCompareDifferent(k2, ('foo', 'zzz'))
 
386
        self.assertCompareDifferent(('foo',), k2)
 
387
        self.assertCompareDifferent(('foo', 'aaa'), k2)
 
388
        self.assertCompareDifferent(('baz', 'bing'), k2)
 
389
        self.assertCompareDifferent(('foo', 10), k2)
 
390
 
 
391
        k3 = self.module.StaticTuple(k1, k2)
 
392
        self.assertCompareEqual(k3, (('foo',), ('foo', 'bar')))
 
393
        self.assertCompareEqual((('foo',), ('foo', 'bar')), k3)
 
394
        self.assertCompareEqual(k3, (k1, ('foo', 'bar')))
 
395
        self.assertCompareEqual((k1, ('foo', 'bar')), k3)
 
396
 
 
397
    def test_compare_mixed_depths(self):
 
398
        stuple = self.module.StaticTuple
 
399
        k1 = stuple(stuple('a',), stuple('b',))
 
400
        k2 = stuple(stuple(stuple('c',), stuple('d',)),
 
401
                    stuple('b',))
 
402
        # This requires comparing a StaticTuple to a 'string', and then
 
403
        # interpreting that value in the next higher StaticTuple. This used to
 
404
        # generate a PyErr_BadIternalCall. We now fall back to *something*.
 
405
        self.assertCompareNoRelation(k1, k2)
 
406
 
 
407
    def test_hash(self):
 
408
        k = self.module.StaticTuple('foo')
 
409
        self.assertEqual(hash(k), hash(('foo',)))
 
410
        k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')
 
411
        as_tuple = ('foo', 'bar', 'baz', 'bing')
 
412
        self.assertEqual(hash(k), hash(as_tuple))
 
413
        x = {k: 'foo'}
 
414
        # Because k == , it replaces the slot, rather than having both
 
415
        # present in the dict.
 
416
        self.assertEqual('foo', x[as_tuple])
 
417
        x[as_tuple] = 'bar'
 
418
        self.assertEqual({as_tuple: 'bar'}, x)
 
419
 
 
420
        k2 = self.module.StaticTuple(k)
 
421
        as_tuple2 = (('foo', 'bar', 'baz', 'bing'),)
 
422
        self.assertEqual(hash(k2), hash(as_tuple2))
 
423
 
 
424
        k3 = self.module.StaticTuple('foo', 1, None, u'\xb5', 1.2, 2**65, True,
 
425
                                     k)
 
426
        as_tuple3 = ('foo', 1, None, u'\xb5', 1.2, 2**65, True, k)
 
427
        self.assertEqual(hash(as_tuple3), hash(k3))
 
428
 
 
429
    def test_slice(self):
 
430
        k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')
 
431
        self.assertEqual(('foo', 'bar'), k[:2])
 
432
        self.assertEqual(('baz',), k[2:-1])
 
433
        try:
 
434
            val = k[::2]
 
435
        except TypeError:
 
436
            # C implementation raises a TypeError, we don't need the
 
437
            # implementation yet, so allow this to pass
 
438
            pass
 
439
        else:
 
440
            # Python implementation uses a regular Tuple, so make sure it gives
 
441
            # the right result
 
442
            self.assertEqual(('foo', 'baz'), val)
 
443
 
 
444
    def test_referents(self):
 
445
        # We implement tp_traverse so that things like 'meliae' can measure the
 
446
        # amount of referenced memory. Unfortunately gc.get_referents() first
 
447
        # checks the IS_GC flag before it traverses anything. We could write a
 
448
        # helper func, but that won't work for the generic implementation...
 
449
        self.requireFeature(Meliae)
 
450
        from meliae import scanner
 
451
        strs = ['foo', 'bar', 'baz', 'bing']
 
452
        k = self.module.StaticTuple(*strs)
 
453
        if self.module is _static_tuple_py:
 
454
            refs = strs + [self.module.StaticTuple]
 
455
        else:
 
456
            refs = strs
 
457
        self.assertEqual(sorted(refs), sorted(scanner.get_referents(k)))
 
458
 
 
459
    def test_nested_referents(self):
 
460
        self.requireFeature(Meliae)
 
461
        from meliae import scanner
 
462
        strs = ['foo', 'bar', 'baz', 'bing']
 
463
        k1 = self.module.StaticTuple(*strs[:2])
 
464
        k2 = self.module.StaticTuple(*strs[2:])
 
465
        k3 = self.module.StaticTuple(k1, k2)
 
466
        refs = [k1, k2]
 
467
        if self.module is _static_tuple_py:
 
468
            refs.append(self.module.StaticTuple)
 
469
        self.assertEqual(sorted(refs),
 
470
                         sorted(scanner.get_referents(k3)))
 
471
 
 
472
    def test_empty_is_singleton(self):
 
473
        key = self.module.StaticTuple()
 
474
        self.assertIs(key, self.module._empty_tuple)
 
475
 
 
476
    def test_intern(self):
 
477
        unique_str1 = 'unique str ' + osutils.rand_chars(20)
 
478
        unique_str2 = 'unique str ' + osutils.rand_chars(20)
 
479
        key = self.module.StaticTuple(unique_str1, unique_str2)
 
480
        self.assertFalse(key in self.module._interned_tuples)
 
481
        key2 = self.module.StaticTuple(unique_str1, unique_str2)
 
482
        self.assertEqual(key, key2)
 
483
        self.assertIsNot(key, key2)
 
484
        key3 = key.intern()
 
485
        self.assertIs(key, key3)
 
486
        self.assertTrue(key in self.module._interned_tuples)
 
487
        self.assertEqual(key, self.module._interned_tuples[key])
 
488
        key2 = key2.intern()
 
489
        self.assertIs(key, key2)
 
490
 
 
491
    def test__c_intern_handles_refcount(self):
 
492
        if self.module is _static_tuple_py:
 
493
            return # Not applicable
 
494
        unique_str1 = 'unique str ' + osutils.rand_chars(20)
 
495
        unique_str2 = 'unique str ' + osutils.rand_chars(20)
 
496
        key = self.module.StaticTuple(unique_str1, unique_str2)
 
497
        self.assertRefcount(1, key)
 
498
        self.assertFalse(key in self.module._interned_tuples)
 
499
        self.assertFalse(key._is_interned())
 
500
        key2 = self.module.StaticTuple(unique_str1, unique_str2)
 
501
        self.assertRefcount(1, key)
 
502
        self.assertRefcount(1, key2)
 
503
        self.assertEqual(key, key2)
 
504
        self.assertIsNot(key, key2)
 
505
 
 
506
        key3 = key.intern()
 
507
        self.assertIs(key, key3)
 
508
        self.assertTrue(key in self.module._interned_tuples)
 
509
        self.assertEqual(key, self.module._interned_tuples[key])
 
510
        # key and key3, but we 'hide' the one in _interned_tuples
 
511
        self.assertRefcount(2, key)
 
512
        del key3
 
513
        self.assertRefcount(1, key)
 
514
        self.assertTrue(key._is_interned())
 
515
        self.assertRefcount(1, key2)
 
516
        key3 = key2.intern()
 
517
        # key3 now points to key as well, and *not* to key2
 
518
        self.assertRefcount(2, key)
 
519
        self.assertRefcount(1, key2)
 
520
        self.assertIs(key, key3)
 
521
        self.assertIsNot(key3, key2)
 
522
        del key2
 
523
        del key3
 
524
        self.assertRefcount(1, key)
 
525
 
 
526
    def test__c_keys_are_not_immortal(self):
 
527
        if self.module is _static_tuple_py:
 
528
            return # Not applicable
 
529
        unique_str1 = 'unique str ' + osutils.rand_chars(20)
 
530
        unique_str2 = 'unique str ' + osutils.rand_chars(20)
 
531
        key = self.module.StaticTuple(unique_str1, unique_str2)
 
532
        self.assertFalse(key in self.module._interned_tuples)
 
533
        self.assertRefcount(1, key)
 
534
        key = key.intern()
 
535
        self.assertRefcount(1, key)
 
536
        self.assertTrue(key in self.module._interned_tuples)
 
537
        self.assertTrue(key._is_interned())
 
538
        del key
 
539
        # Create a new entry, which would point to the same location
 
540
        key = self.module.StaticTuple(unique_str1, unique_str2)
 
541
        self.assertRefcount(1, key)
 
542
        # This old entry in _interned_tuples should be gone
 
543
        self.assertFalse(key in self.module._interned_tuples)
 
544
        self.assertFalse(key._is_interned())
 
545
 
 
546
    def test__c_has_C_API(self):
 
547
        if self.module is _static_tuple_py:
 
548
            return
 
549
        self.assertIsNot(None, self.module._C_API)
 
550
 
 
551
    def test_from_sequence_tuple(self):
 
552
        st = self.module.StaticTuple.from_sequence(('foo', 'bar'))
 
553
        self.assertIsInstance(st, self.module.StaticTuple)
 
554
        self.assertEqual(('foo', 'bar'), st)
 
555
 
 
556
    def test_from_sequence_str(self):
 
557
        st = self.module.StaticTuple.from_sequence('foo')
 
558
        self.assertIsInstance(st, self.module.StaticTuple)
 
559
        self.assertEqual(('f', 'o', 'o'), st)
 
560
 
 
561
    def test_from_sequence_list(self):
 
562
        st = self.module.StaticTuple.from_sequence(['foo', 'bar'])
 
563
        self.assertIsInstance(st, self.module.StaticTuple)
 
564
        self.assertEqual(('foo', 'bar'), st)
 
565
 
 
566
    def test_from_sequence_static_tuple(self):
 
567
        st = self.module.StaticTuple('foo', 'bar')
 
568
        st2 = self.module.StaticTuple.from_sequence(st)
 
569
        # If the source is a StaticTuple already, we return the exact object
 
570
        self.assertIs(st, st2)
 
571
 
 
572
    def test_from_sequence_not_sequence(self):
 
573
        self.assertRaises(TypeError,
 
574
                          self.module.StaticTuple.from_sequence, object())
 
575
        self.assertRaises(TypeError,
 
576
                          self.module.StaticTuple.from_sequence, 10)
 
577
 
 
578
    def test_from_sequence_incorrect_args(self):
 
579
        self.assertRaises(TypeError,
 
580
                          self.module.StaticTuple.from_sequence, object(), 'a')
 
581
        self.assertRaises(TypeError,
 
582
                          self.module.StaticTuple.from_sequence, foo='a')
 
583
 
 
584
    def test_from_sequence_iterable(self):
 
585
        st = self.module.StaticTuple.from_sequence(iter(['foo', 'bar']))
 
586
        self.assertIsInstance(st, self.module.StaticTuple)
 
587
        self.assertEqual(('foo', 'bar'), st)
 
588
 
 
589
    def test_from_sequence_generator(self):
 
590
        def generate_tuple():
 
591
            yield 'foo'
 
592
            yield 'bar'
 
593
        st = self.module.StaticTuple.from_sequence(generate_tuple())
 
594
        self.assertIsInstance(st, self.module.StaticTuple)
 
595
        self.assertEqual(('foo', 'bar'), st)
 
596
 
 
597
    def test_pickle(self):
 
598
        st = self.module.StaticTuple('foo', 'bar')
 
599
        pickled = cPickle.dumps(st)
 
600
        unpickled = cPickle.loads(pickled)
 
601
        self.assertEqual(unpickled, st)
 
602
 
 
603
    def test_pickle_empty(self):
 
604
        st = self.module.StaticTuple()
 
605
        pickled = cPickle.dumps(st)
 
606
        unpickled = cPickle.loads(pickled)
 
607
        self.assertIs(st, unpickled)
 
608
 
 
609
    def test_pickle_nested(self):
 
610
        st = self.module.StaticTuple('foo', self.module.StaticTuple('bar'))
 
611
        pickled = cPickle.dumps(st)
 
612
        unpickled = cPickle.loads(pickled)
 
613
        self.assertEqual(unpickled, st)
 
614
 
 
615
    def test_static_tuple_thunk(self):
 
616
        # Make sure the right implementation is available from
 
617
        # bzrlib.static_tuple.StaticTuple.
 
618
        if self.module is _static_tuple_py:
 
619
            if compiled_static_tuple_feature.available():
 
620
                # We will be using the C version
 
621
                return
 
622
        self.assertIs(static_tuple.StaticTuple,
 
623
                      self.module.StaticTuple)
 
624
 
 
625
 
 
626
class TestEnsureStaticTuple(tests.TestCase):
 
627
 
 
628
    def test_is_static_tuple(self):
 
629
        st = static_tuple.StaticTuple('foo')
 
630
        st2 = static_tuple.expect_static_tuple(st)
 
631
        self.assertIs(st, st2)
 
632
 
 
633
    def test_is_tuple(self):
 
634
        t = ('foo',)
 
635
        st = static_tuple.expect_static_tuple(t)
 
636
        self.assertIsInstance(st, static_tuple.StaticTuple)
 
637
        self.assertEqual(t, st)
 
638
 
 
639
    def test_flagged_is_static_tuple(self):
 
640
        debug.debug_flags.add('static_tuple')
 
641
        st = static_tuple.StaticTuple('foo')
 
642
        st2 = static_tuple.expect_static_tuple(st)
 
643
        self.assertIs(st, st2)
 
644
 
 
645
    def test_flagged_is_tuple(self):
 
646
        debug.debug_flags.add('static_tuple')
 
647
        t = ('foo',)
 
648
        self.assertRaises(TypeError, static_tuple.expect_static_tuple, t)