~bzr-pqm/bzr/bzr.dev

4516.2.1 by John Arbash Meinel
Fix bug #396838, Update LRUCache to maintain invariant even
1
# Copyright (C) 2006, 2008, 2009 Canonical Ltd
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
16
17
"""Tests for the lru_cache module."""
18
19
from bzrlib import (
20
    lru_cache,
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
21
    symbol_versioning,
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
22
    tests,
23
    )
24
25
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
26
def walk_lru(lru):
27
    """Test helper to walk the LRU list and assert its consistency"""
28
    node = lru._most_recently_used
29
    if node is not None:
30
        if node.prev is not None:
31
            raise AssertionError('the _most_recently_used entry is not'
32
                                 ' supposed to have a previous entry'
33
                                 ' %s' % (node,))
34
    while node is not None:
35
        if node.next_key is lru_cache._null_key:
36
            if node is not lru._least_recently_used:
37
                raise AssertionError('only the last node should have'
38
                                     ' no next value: %s' % (node,))
39
            node_next = None
40
        else:
41
            node_next = lru._cache[node.next_key]
42
            if node_next.prev is not node:
43
                raise AssertionError('inconsistency found, node.next.prev'
44
                                     ' != node: %s' % (node,))
45
        if node.prev is None:
46
            if node is not lru._most_recently_used:
47
                raise AssertionError('only the _most_recently_used should'
48
                                     ' not have a previous node: %s'
49
                                     % (node,))
50
        else:
51
            if node.prev.next_key != node.key:
52
                raise AssertionError('inconsistency found, node.prev.next'
53
                                     ' != node: %s' % (node,))
54
        yield node
55
        node = node_next
56
57
 
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
58
class TestLRUCache(tests.TestCase):
59
    """Test that LRU cache properly keeps track of entries."""
60
4178.3.2 by John Arbash Meinel
Add tests for LRUCache.cache_size()
61
    def test_cache_size(self):
62
        cache = lru_cache.LRUCache(max_cache=10)
63
        self.assertEqual(10, cache.cache_size())
64
65
        cache = lru_cache.LRUCache(max_cache=256)
66
        self.assertEqual(256, cache.cache_size())
67
68
        cache.resize(512)
69
        self.assertEqual(512, cache.cache_size())
70
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
71
    def test_missing(self):
72
        cache = lru_cache.LRUCache(max_cache=10)
73
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
74
        self.assertFalse('foo' in cache)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
75
        self.assertRaises(KeyError, cache.__getitem__, 'foo')
76
77
        cache['foo'] = 'bar'
78
        self.assertEqual('bar', cache['foo'])
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
79
        self.assertTrue('foo' in cache)
80
        self.assertFalse('bar' in cache)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
81
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
82
    def test_map_None(self):
83
        # Make sure that we can properly map None as a key.
84
        cache = lru_cache.LRUCache(max_cache=10)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
85
        self.assertFalse(None in cache)
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
86
        cache[None] = 1
87
        self.assertEqual(1, cache[None])
88
        cache[None] = 2
89
        self.assertEqual(2, cache[None])
90
        # Test the various code paths of __getitem__, to make sure that we can
91
        # handle when None is the key for the LRU and the MRU
92
        cache[1] = 3
93
        cache[None] = 1
94
        cache[None]
95
        cache[1]
96
        cache[None]
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
97
        self.assertEqual([None, 1], [n.key for n in walk_lru(cache)])
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
98
99
    def test_add__null_key(self):
100
        cache = lru_cache.LRUCache(max_cache=10)
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
101
        self.assertRaises(ValueError,
102
            cache.__setitem__, lru_cache._null_key, 1)
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
103
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
104
    def test_overflow(self):
105
        """Adding extra entries will pop out old ones."""
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
106
        cache = lru_cache.LRUCache(max_cache=1, after_cleanup_count=1)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
107
108
        cache['foo'] = 'bar'
109
        # With a max cache of 1, adding 'baz' should pop out 'foo'
110
        cache['baz'] = 'biz'
111
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
112
        self.assertFalse('foo' in cache)
113
        self.assertTrue('baz' in cache)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
114
115
        self.assertEqual('biz', cache['baz'])
116
117
    def test_by_usage(self):
118
        """Accessing entries bumps them up in priority."""
119
        cache = lru_cache.LRUCache(max_cache=2)
120
121
        cache['baz'] = 'biz'
122
        cache['foo'] = 'bar'
123
124
        self.assertEqual('biz', cache['baz'])
125
126
        # This must kick out 'foo' because it was the last accessed
127
        cache['nub'] = 'in'
128
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
129
        self.assertFalse('foo' in cache)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
130
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
131
    def test_cleanup_function_deprecated(self):
132
        """Test that per-node cleanup functions are no longer allowed"""
133
        cache = lru_cache.LRUCache()
134
        self.assertRaises(ValueError, self.applyDeprecated,
135
            symbol_versioning.deprecated_in((2, 5, 0)),
136
            cache.add, "key", 1, cleanup=lambda: None)
4516.2.1 by John Arbash Meinel
Fix bug #396838, Update LRUCache to maintain invariant even
137
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
138
    def test_len(self):
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
139
        cache = lru_cache.LRUCache(max_cache=10, after_cleanup_count=10)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
140
141
        cache[1] = 10
142
        cache[2] = 20
143
        cache[3] = 30
144
        cache[4] = 40
145
146
        self.assertEqual(4, len(cache))
147
148
        cache[5] = 50
149
        cache[6] = 60
150
        cache[7] = 70
151
        cache[8] = 80
152
153
        self.assertEqual(8, len(cache))
154
155
        cache[1] = 15 # replacement
156
157
        self.assertEqual(8, len(cache))
158
159
        cache[9] = 90
160
        cache[10] = 100
161
        cache[11] = 110
162
163
        # We hit the max
164
        self.assertEqual(10, len(cache))
4287.1.2 by John Arbash Meinel
Properly remove the nodes from the internal linked list in _remove_node.
165
        self.assertEqual([11, 10, 9, 1, 8, 7, 6, 5, 4, 3],
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
166
                         [n.key for n in walk_lru(cache)])
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
167
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
168
    def test_cleanup_shrinks_to_after_clean_count(self):
169
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=3)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
170
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
171
        cache[1] = 10
172
        cache[2] = 20
173
        cache[3] = 25
174
        cache[4] = 30
175
        cache[5] = 35
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
176
177
        self.assertEqual(5, len(cache))
178
        # This will bump us over the max, which causes us to shrink down to
179
        # after_cleanup_cache size
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
180
        cache[6] = 40
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
181
        self.assertEqual(3, len(cache))
182
183
    def test_after_cleanup_larger_than_max(self):
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
184
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=10)
185
        self.assertEqual(5, cache._after_cleanup_count)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
186
187
    def test_after_cleanup_none(self):
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
188
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=None)
189
        # By default _after_cleanup_size is 80% of the normal size
190
        self.assertEqual(4, cache._after_cleanup_count)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
191
192
    def test_cleanup(self):
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
193
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=2)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
194
195
        # Add these in order
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
196
        cache[1] = 10
197
        cache[2] = 20
198
        cache[3] = 25
199
        cache[4] = 30
200
        cache[5] = 35
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
201
202
        self.assertEqual(5, len(cache))
203
        # Force a compaction
204
        cache.cleanup()
205
        self.assertEqual(2, len(cache))
206
4178.3.7 by John Arbash Meinel
Review tweaks from Ian.
207
    def test_preserve_last_access_order(self):
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
208
        cache = lru_cache.LRUCache(max_cache=5)
209
210
        # Add these in order
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
211
        cache[1] = 10
212
        cache[2] = 20
213
        cache[3] = 25
214
        cache[4] = 30
215
        cache[5] = 35
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
216
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
217
        self.assertEqual([5, 4, 3, 2, 1], [n.key for n in walk_lru(cache)])
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
218
219
        # Now access some randomly
220
        cache[2]
221
        cache[5]
222
        cache[3]
223
        cache[2]
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
224
        self.assertEqual([2, 3, 5, 4, 1], [n.key for n in walk_lru(cache)])
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
225
2998.2.1 by John Arbash Meinel
Implement LRUCache.get() which acts like dict.get()
226
    def test_get(self):
227
        cache = lru_cache.LRUCache(max_cache=5)
228
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
229
        cache[1] = 10
230
        cache[2] = 20
2998.2.1 by John Arbash Meinel
Implement LRUCache.get() which acts like dict.get()
231
        self.assertEqual(20, cache.get(2))
232
        self.assertIs(None, cache.get(3))
233
        obj = object()
234
        self.assertIs(obj, cache.get(3, obj))
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
235
        self.assertEqual([2, 1], [n.key for n in walk_lru(cache)])
4178.3.5 by John Arbash Meinel
Add tests that LRUCache.get() properly tracks accesses.
236
        self.assertEqual(10, cache.get(1))
6215.1.2 by Martin Packman
Move private test helper LRUCache._walk_lru to test module
237
        self.assertEqual([1, 2], [n.key for n in walk_lru(cache)])
2998.2.1 by John Arbash Meinel
Implement LRUCache.get() which acts like dict.get()
238
3763.8.10 by John Arbash Meinel
Add a .keys() member to LRUCache and LRUSizeCache.
239
    def test_keys(self):
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
240
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=5)
3763.8.10 by John Arbash Meinel
Add a .keys() member to LRUCache and LRUSizeCache.
241
242
        cache[1] = 2
243
        cache[2] = 3
244
        cache[3] = 4
245
        self.assertEqual([1, 2, 3], sorted(cache.keys()))
246
        cache[4] = 5
247
        cache[5] = 6
248
        cache[6] = 7
249
        self.assertEqual([2, 3, 4, 5, 6], sorted(cache.keys()))
250
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
251
    def test_resize_smaller(self):
252
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=4)
253
        cache[1] = 2
254
        cache[2] = 3
255
        cache[3] = 4
256
        cache[4] = 5
257
        cache[5] = 6
258
        self.assertEqual([1, 2, 3, 4, 5], sorted(cache.keys()))
259
        cache[6] = 7
260
        self.assertEqual([3, 4, 5, 6], sorted(cache.keys()))
261
        # Now resize to something smaller, which triggers a cleanup
262
        cache.resize(max_cache=3, after_cleanup_count=2)
263
        self.assertEqual([5, 6], sorted(cache.keys()))
264
        # Adding something will use the new size
265
        cache[7] = 8
266
        self.assertEqual([5, 6, 7], sorted(cache.keys()))
267
        cache[8] = 9
268
        self.assertEqual([7, 8], sorted(cache.keys()))
269
270
    def test_resize_larger(self):
271
        cache = lru_cache.LRUCache(max_cache=5, after_cleanup_count=4)
272
        cache[1] = 2
273
        cache[2] = 3
274
        cache[3] = 4
275
        cache[4] = 5
276
        cache[5] = 6
277
        self.assertEqual([1, 2, 3, 4, 5], sorted(cache.keys()))
278
        cache[6] = 7
279
        self.assertEqual([3, 4, 5, 6], sorted(cache.keys()))
280
        cache.resize(max_cache=8, after_cleanup_count=6)
281
        self.assertEqual([3, 4, 5, 6], sorted(cache.keys()))
282
        cache[7] = 8
283
        cache[8] = 9
284
        cache[9] = 10
285
        cache[10] = 11
286
        self.assertEqual([3, 4, 5, 6, 7, 8, 9, 10], sorted(cache.keys()))
287
        cache[11] = 12 # triggers cleanup back to new after_cleanup_count
288
        self.assertEqual([6, 7, 8, 9, 10, 11], sorted(cache.keys()))
289
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
290
291
class TestLRUSizeCache(tests.TestCase):
292
293
    def test_basic_init(self):
294
        cache = lru_cache.LRUSizeCache()
295
        self.assertEqual(2048, cache._max_cache)
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
296
        self.assertEqual(int(cache._max_size*0.8), cache._after_cleanup_size)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
297
        self.assertEqual(0, cache._value_size)
298
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
299
    def test_add__null_key(self):
300
        cache = lru_cache.LRUSizeCache()
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
301
        self.assertRaises(ValueError,
302
            cache.__setitem__, lru_cache._null_key, 1)
4287.1.10 by John Arbash Meinel
Restore the ability to handle None as a key.
303
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
304
    def test_add_tracks_size(self):
305
        cache = lru_cache.LRUSizeCache()
306
        self.assertEqual(0, cache._value_size)
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
307
        cache['my key'] = 'my value text'
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
308
        self.assertEqual(13, cache._value_size)
309
310
    def test_remove_tracks_size(self):
311
        cache = lru_cache.LRUSizeCache()
312
        self.assertEqual(0, cache._value_size)
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
313
        cache['my key'] = 'my value text'
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
314
        self.assertEqual(13, cache._value_size)
4178.3.3 by John Arbash Meinel
LRUCache is now implemented with a dict to a linked list,
315
        node = cache._cache['my key']
316
        cache._remove_node(node)
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
317
        self.assertEqual(0, cache._value_size)
318
319
    def test_no_add_over_size(self):
320
        """Adding a large value may not be cached at all."""
321
        cache = lru_cache.LRUSizeCache(max_size=10, after_cleanup_size=5)
322
        self.assertEqual(0, cache._value_size)
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
323
        self.assertEqual({}, cache.as_dict())
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
324
        cache['test'] = 'key'
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
325
        self.assertEqual(3, cache._value_size)
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
326
        self.assertEqual({'test': 'key'}, cache.as_dict())
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
327
        cache['test2'] = 'key that is too big'
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
328
        self.assertEqual(3, cache._value_size)
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
329
        self.assertEqual({'test':'key'}, cache.as_dict())
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
330
        # If we would add a key, only to cleanup and remove all cached entries,
331
        # then obviously that value should not be stored
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
332
        cache['test3'] = 'bigkey'
333
        self.assertEqual(3, cache._value_size)
334
        self.assertEqual({'test':'key'}, cache.as_dict())
335
336
        cache['test4'] = 'bikey'
337
        self.assertEqual(3, cache._value_size)
338
        self.assertEqual({'test':'key'}, cache.as_dict())
4178.3.7 by John Arbash Meinel
Review tweaks from Ian.
339
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
340
    def test_adding_clears_cache_based_on_size(self):
341
        """The cache is cleared in LRU order until small enough"""
342
        cache = lru_cache.LRUSizeCache(max_size=20)
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
343
        cache['key1'] = 'value' # 5 chars
344
        cache['key2'] = 'value2' # 6 chars
345
        cache['key3'] = 'value23' # 7 chars
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
346
        self.assertEqual(5+6+7, cache._value_size)
347
        cache['key2'] # reference key2 so it gets a newer reference time
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
348
        cache['key4'] = 'value234' # 8 chars, over limit
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
349
        # We have to remove 2 keys to get back under limit
350
        self.assertEqual(6+8, cache._value_size)
351
        self.assertEqual({'key2':'value2', 'key4':'value234'},
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
352
                         cache.as_dict())
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
353
354
    def test_adding_clears_to_after_cleanup_size(self):
355
        cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10)
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
356
        cache['key1'] = 'value' # 5 chars
357
        cache['key2'] = 'value2' # 6 chars
358
        cache['key3'] = 'value23' # 7 chars
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
359
        self.assertEqual(5+6+7, cache._value_size)
360
        cache['key2'] # reference key2 so it gets a newer reference time
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
361
        cache['key4'] = 'value234' # 8 chars, over limit
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
362
        # We have to remove 3 keys to get back under limit
363
        self.assertEqual(8, cache._value_size)
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
364
        self.assertEqual({'key4':'value234'}, cache.as_dict())
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
365
366
    def test_custom_sizes(self):
367
        def size_of_list(lst):
368
            return sum(len(x) for x in lst)
369
        cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10,
370
                                       compute_size=size_of_list)
371
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
372
        cache['key1'] = ['val', 'ue'] # 5 chars
373
        cache['key2'] = ['val', 'ue2'] # 6 chars
374
        cache['key3'] = ['val', 'ue23'] # 7 chars
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
375
        self.assertEqual(5+6+7, cache._value_size)
376
        cache['key2'] # reference key2 so it gets a newer reference time
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
377
        cache['key4'] = ['value', '234'] # 8 chars, over limit
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
378
        # We have to remove 3 keys to get back under limit
379
        self.assertEqual(8, cache._value_size)
6215.1.1 by Martin Packman
Rename confusing LRUCache.items method that doesn't act like dict.items to as_dict
380
        self.assertEqual({'key4':['value', '234']}, cache.as_dict())
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
381
382
    def test_cleanup(self):
383
        cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10)
384
385
        # Add these in order
6215.1.4 by Martin Packman
Remove unneeded _LRUNode.cleanup callback ability and deprecate LRUCache.add
386
        cache['key1'] = 'value' # 5 chars
387
        cache['key2'] = 'value2' # 6 chars
388
        cache['key3'] = 'value23' # 7 chars
2993.1.1 by Robert Collins
* New module ``lru_cache`` providing a cache for use by tasks that need
389
        self.assertEqual(5+6+7, cache._value_size)
390
391
        cache.cleanup()
392
        # Only the most recent fits after cleaning up
393
        self.assertEqual(7, cache._value_size)
3763.8.10 by John Arbash Meinel
Add a .keys() member to LRUCache and LRUSizeCache.
394
395
    def test_keys(self):
396
        cache = lru_cache.LRUSizeCache(max_size=10)
397
398
        cache[1] = 'a'
399
        cache[2] = 'b'
400
        cache[3] = 'cdef'
401
        self.assertEqual([1, 2, 3], sorted(cache.keys()))
3882.3.1 by John Arbash Meinel
Add LRUCache.resize(), and change the init arguments for LRUCache.
402
403
    def test_resize_smaller(self):
404
        cache = lru_cache.LRUSizeCache(max_size=10, after_cleanup_size=9)
405
        cache[1] = 'abc'
406
        cache[2] = 'def'
407
        cache[3] = 'ghi'
408
        cache[4] = 'jkl'
409
        # Triggers a cleanup
410
        self.assertEqual([2, 3, 4], sorted(cache.keys()))
411
        # Resize should also cleanup again
412
        cache.resize(max_size=6, after_cleanup_size=4)
413
        self.assertEqual([4], sorted(cache.keys()))
414
        # Adding should use the new max size
415
        cache[5] = 'mno'
416
        self.assertEqual([4, 5], sorted(cache.keys()))
417
        cache[6] = 'pqr'
418
        self.assertEqual([6], sorted(cache.keys()))
419
420
    def test_resize_larger(self):
421
        cache = lru_cache.LRUSizeCache(max_size=10, after_cleanup_size=9)
422
        cache[1] = 'abc'
423
        cache[2] = 'def'
424
        cache[3] = 'ghi'
425
        cache[4] = 'jkl'
426
        # Triggers a cleanup
427
        self.assertEqual([2, 3, 4], sorted(cache.keys()))
428
        cache.resize(max_size=15, after_cleanup_size=12)
429
        self.assertEqual([2, 3, 4], sorted(cache.keys()))
430
        cache[5] = 'mno'
431
        cache[6] = 'pqr'
432
        self.assertEqual([2, 3, 4, 5, 6], sorted(cache.keys()))
433
        cache[7] = 'stu'
434
        self.assertEqual([4, 5, 6, 7], sorted(cache.keys()))
435