1
# -*- coding: utf-8 -*-
3
# Unit tests for cache framework
4
# Uses whatever cache backend is set in the test settings file.
12
from django.conf import settings
13
from django.core import management
14
from django.core.cache import get_cache
15
from django.core.cache.backends.base import InvalidCacheBackendError
16
from django.http import HttpResponse, HttpRequest
17
from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
18
from django.utils.hashcompat import md5_constructor
20
# functions/classes for complex data type tests
27
class DummyCacheTests(unittest.TestCase):
28
# The Dummy cache backend doesn't really behave like a test backend,
29
# so it has different test requirements.
31
self.cache = get_cache('dummy://')
33
def test_simple(self):
34
"Dummy cache backend ignores cache set calls"
35
self.cache.set("key", "value")
36
self.assertEqual(self.cache.get("key"), None)
39
"Add doesn't do anything in dummy cache backend"
40
self.cache.add("addkey1", "value")
41
result = self.cache.add("addkey1", "newvalue")
42
self.assertEqual(result, True)
43
self.assertEqual(self.cache.get("addkey1"), None)
45
def test_non_existent(self):
46
"Non-existent keys aren't found in the dummy cache backend"
47
self.assertEqual(self.cache.get("does_not_exist"), None)
48
self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
50
def test_get_many(self):
51
"get_many returns nothing for the dummy cache backend"
52
self.cache.set('a', 'a')
53
self.cache.set('b', 'b')
54
self.cache.set('c', 'c')
55
self.cache.set('d', 'd')
56
self.assertEqual(self.cache.get_many(['a', 'c', 'd']), {})
57
self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {})
59
def test_delete(self):
60
"Cache deletion is transparently ignored on the dummy cache backend"
61
self.cache.set("key1", "spam")
62
self.cache.set("key2", "eggs")
63
self.assertEqual(self.cache.get("key1"), None)
64
self.cache.delete("key1")
65
self.assertEqual(self.cache.get("key1"), None)
66
self.assertEqual(self.cache.get("key2"), None)
68
def test_has_key(self):
69
"The has_key method doesn't ever return True for the dummy cache backend"
70
self.cache.set("hello1", "goodbye1")
71
self.assertEqual(self.cache.has_key("hello1"), False)
72
self.assertEqual(self.cache.has_key("goodbye1"), False)
75
"The in operator doesn't ever return True for the dummy cache backend"
76
self.cache.set("hello2", "goodbye2")
77
self.assertEqual("hello2" in self.cache, False)
78
self.assertEqual("goodbye2" in self.cache, False)
81
"Dummy cache values can't be incremented"
82
self.cache.set('answer', 42)
83
self.assertRaises(ValueError, self.cache.incr, 'answer')
84
self.assertRaises(ValueError, self.cache.incr, 'does_not_exist')
87
"Dummy cache values can't be decremented"
88
self.cache.set('answer', 42)
89
self.assertRaises(ValueError, self.cache.decr, 'answer')
90
self.assertRaises(ValueError, self.cache.decr, 'does_not_exist')
92
def test_data_types(self):
93
"All data types are ignored equally by the dummy cache"
95
'string' : 'this is a string',
97
'list' : [1, 2, 3, 4],
98
'tuple' : (1, 2, 3, 4),
99
'dict' : {'A': 1, 'B' : 2},
103
self.cache.set("stuff", stuff)
104
self.assertEqual(self.cache.get("stuff"), None)
106
def test_expiration(self):
107
"Expiration has no effect on the dummy cache"
108
self.cache.set('expire1', 'very quickly', 1)
109
self.cache.set('expire2', 'very quickly', 1)
110
self.cache.set('expire3', 'very quickly', 1)
113
self.assertEqual(self.cache.get("expire1"), None)
115
self.cache.add("expire2", "newvalue")
116
self.assertEqual(self.cache.get("expire2"), None)
117
self.assertEqual(self.cache.has_key("expire3"), False)
119
def test_unicode(self):
120
"Unicode values are ignored by the dummy cache"
122
u'ascii': u'ascii_value',
123
u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
124
u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
125
u'ascii': {u'x' : 1 }
127
for (key, value) in stuff.items():
128
self.cache.set(key, value)
129
self.assertEqual(self.cache.get(key), None)
132
class BaseCacheTests(object):
133
# A common set of tests to apply to all cache backends
134
def test_simple(self):
135
# Simple cache set/get works
136
self.cache.set("key", "value")
137
self.assertEqual(self.cache.get("key"), "value")
140
# A key can be added to a cache
141
self.cache.add("addkey1", "value")
142
result = self.cache.add("addkey1", "newvalue")
143
self.assertEqual(result, False)
144
self.assertEqual(self.cache.get("addkey1"), "value")
146
def test_non_existent(self):
147
# Non-existent cache keys return as None/default
148
# get with non-existent keys
149
self.assertEqual(self.cache.get("does_not_exist"), None)
150
self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
152
def test_get_many(self):
153
# Multiple cache keys can be returned using get_many
154
self.cache.set('a', 'a')
155
self.cache.set('b', 'b')
156
self.cache.set('c', 'c')
157
self.cache.set('d', 'd')
158
self.assertEqual(self.cache.get_many(['a', 'c', 'd']), {'a' : 'a', 'c' : 'c', 'd' : 'd'})
159
self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'})
161
def test_delete(self):
162
# Cache keys can be deleted
163
self.cache.set("key1", "spam")
164
self.cache.set("key2", "eggs")
165
self.assertEqual(self.cache.get("key1"), "spam")
166
self.cache.delete("key1")
167
self.assertEqual(self.cache.get("key1"), None)
168
self.assertEqual(self.cache.get("key2"), "eggs")
170
def test_has_key(self):
171
# The cache can be inspected for cache keys
172
self.cache.set("hello1", "goodbye1")
173
self.assertEqual(self.cache.has_key("hello1"), True)
174
self.assertEqual(self.cache.has_key("goodbye1"), False)
177
# The in operator can be used to inspet cache contents
178
self.cache.set("hello2", "goodbye2")
179
self.assertEqual("hello2" in self.cache, True)
180
self.assertEqual("goodbye2" in self.cache, False)
183
# Cache values can be incremented
184
self.cache.set('answer', 41)
185
self.assertEqual(self.cache.incr('answer'), 42)
186
self.assertEqual(self.cache.get('answer'), 42)
187
self.assertEqual(self.cache.incr('answer', 10), 52)
188
self.assertEqual(self.cache.get('answer'), 52)
189
self.assertRaises(ValueError, self.cache.incr, 'does_not_exist')
192
# Cache values can be decremented
193
self.cache.set('answer', 43)
194
self.assertEqual(self.cache.decr('answer'), 42)
195
self.assertEqual(self.cache.get('answer'), 42)
196
self.assertEqual(self.cache.decr('answer', 10), 32)
197
self.assertEqual(self.cache.get('answer'), 32)
198
self.assertRaises(ValueError, self.cache.decr, 'does_not_exist')
200
def test_data_types(self):
201
# Many different data types can be cached
203
'string' : 'this is a string',
205
'list' : [1, 2, 3, 4],
206
'tuple' : (1, 2, 3, 4),
207
'dict' : {'A': 1, 'B' : 2},
211
self.cache.set("stuff", stuff)
212
self.assertEqual(self.cache.get("stuff"), stuff)
214
def test_expiration(self):
215
# Cache values can be set to expire
216
self.cache.set('expire1', 'very quickly', 1)
217
self.cache.set('expire2', 'very quickly', 1)
218
self.cache.set('expire3', 'very quickly', 1)
221
self.assertEqual(self.cache.get("expire1"), None)
223
self.cache.add("expire2", "newvalue")
224
self.assertEqual(self.cache.get("expire2"), "newvalue")
225
self.assertEqual(self.cache.has_key("expire3"), False)
227
def test_unicode(self):
228
# Unicode values can be cached
230
u'ascii': u'ascii_value',
231
u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
232
u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
233
u'ascii': {u'x' : 1 }
235
for (key, value) in stuff.items():
236
self.cache.set(key, value)
237
self.assertEqual(self.cache.get(key), value)
239
class DBCacheTests(unittest.TestCase, BaseCacheTests):
241
management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False)
242
self.cache = get_cache('db://test_cache_table')
245
from django.db import connection
246
cursor = connection.cursor()
247
cursor.execute('DROP TABLE test_cache_table');
249
class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
251
self.cache = get_cache('locmem://')
253
# memcached backend isn't guaranteed to be available.
254
# To check the memcached backend, the test settings file will
255
# need to contain a CACHE_BACKEND setting that points at
256
# your memcache server.
257
if settings.CACHE_BACKEND.startswith('memcached://'):
258
class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
260
self.cache = get_cache(settings.CACHE_BACKEND)
262
class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
264
Specific test cases for the file-based cache.
267
self.dirname = tempfile.mkdtemp()
268
self.cache = get_cache('file://%s' % self.dirname)
271
shutil.rmtree(self.dirname)
273
def test_hashing(self):
274
"""Test that keys are hashed into subdirectories correctly"""
275
self.cache.set("foo", "bar")
276
keyhash = md5_constructor("foo").hexdigest()
277
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
278
self.assert_(os.path.exists(keypath))
280
def test_subdirectory_removal(self):
282
Make sure that the created subdirectories are correctly removed when empty.
284
self.cache.set("foo", "bar")
285
keyhash = md5_constructor("foo").hexdigest()
286
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
287
self.assert_(os.path.exists(keypath))
289
self.cache.delete("foo")
290
self.assert_(not os.path.exists(keypath))
291
self.assert_(not os.path.exists(os.path.dirname(keypath)))
292
self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
294
class CacheUtils(unittest.TestCase):
295
"""TestCase for django.utils.cache functions."""
298
self.path = '/cache/test/'
299
self.old_settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
300
self.old_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
301
settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
302
settings.CACHE_MIDDLEWARE_SECONDS = 1
305
settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_settings_key_prefix
306
settings.CACHE_MIDDLEWARE_SECONDS = self.old_middleware_seconds
308
def _get_request(self, path):
309
request = HttpRequest()
311
'SERVER_NAME': 'testserver',
314
request.path = request.path_info = "/cache/%s" % path
317
def test_patch_vary_headers(self):
319
# Initial vary, new headers, resulting vary.
320
(None, ('Accept-Encoding',), 'Accept-Encoding'),
321
('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'),
322
('Accept-Encoding', ('ACCEPT-ENCODING',), 'Accept-Encoding'),
323
('Cookie', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
324
('Cookie, Accept-Encoding', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
325
('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
326
(None, ('Accept-Encoding', 'COOKIE'), 'Accept-Encoding, COOKIE'),
327
('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
328
('Cookie , Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
330
for initial_vary, newheaders, resulting_vary in headers:
331
response = HttpResponse()
332
if initial_vary is not None:
333
response['Vary'] = initial_vary
334
patch_vary_headers(response, newheaders)
335
self.assertEqual(response['Vary'], resulting_vary)
337
def test_get_cache_key(self):
338
request = self._get_request(self.path)
339
response = HttpResponse()
340
key_prefix = 'localprefix'
341
# Expect None if no headers have been set yet.
342
self.assertEqual(get_cache_key(request), None)
343
# Set headers to an empty list.
344
learn_cache_key(request, response)
345
self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
346
# Verify that a specified key_prefix is taken in to account.
347
learn_cache_key(request, response, key_prefix=key_prefix)
348
self.assertEqual(get_cache_key(request, key_prefix=key_prefix), 'views.decorators.cache.cache_page.localprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
350
def test_learn_cache_key(self):
351
request = self._get_request(self.path)
352
response = HttpResponse()
353
response['Vary'] = 'Pony'
354
# Make sure that the Vary header is added to the key hash
355
learn_cache_key(request, response)
356
self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
358
if __name__ == '__main__':