diff options
| author | Russell Keith-Magee <russell@keith-magee.com> | 2009-03-11 13:27:03 +0000 |
|---|---|---|
| committer | Russell Keith-Magee <russell@keith-magee.com> | 2009-03-11 13:27:03 +0000 |
| commit | 638dbc3e8320652ba3bd759e0ac1d28590402096 (patch) | |
| tree | ff6ad143257001bd27fe80c11f8115a428faef56 /tests/regressiontests/cache | |
| parent | 1d8e6eae34974fd9c60e15884d155dd4bc4dfdc0 (diff) | |
Fixed #6464 -- Added incr() and decr() operations on cache backends. Atomic on Memcache; implemented as a 2 stage retrieve/update on other backends. Includes refactor of the cache tests to ensure all the backends are actually tested, and a fix to the DB cache backend that was discovered as a result. Thanks to Michael Malone for the original patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10031 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/regressiontests/cache')
| -rw-r--r-- | tests/regressiontests/cache/tests.py | 184 |
1 files changed, 162 insertions, 22 deletions
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 3541fa7311..4100a8cb1d 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -9,8 +9,10 @@ import tempfile import time import unittest -from django.core.cache import cache, get_cache -from django.core.cache.backends.filebased import CacheClass as FileCache +from django.conf import settings +from django.core import management +from django.core.cache import get_cache +from django.core.cache.backends.base import InvalidCacheBackendError from django.http import HttpResponse from django.utils.cache import patch_vary_headers from django.utils.hashcompat import md5_constructor @@ -22,39 +24,133 @@ class C: def m(n): return 24 -class Cache(unittest.TestCase): +class DummyCacheTests(unittest.TestCase): + # The Dummy cache backend doesn't really behave like a test backend, + # so it has different test requirements. def setUp(self): - # Special-case the file cache so we can clean up after ourselves. - if isinstance(cache, FileCache): - self.cache_dir = tempfile.mkdtemp() - self.cache = get_cache("file:///%s" % self.cache_dir) - else: - self.cache_dir = None - self.cache = cache - - def tearDown(self): - if self.cache_dir is not None: - shutil.rmtree(self.cache_dir) - + self.cache = get_cache('dummy://') + + def test_simple(self): + "Dummy cache backend ignores cache set calls" + self.cache.set("key", "value") + self.assertEqual(self.cache.get("key"), None) + + def test_add(self): + "Add doesn't do anything in dummy cache backend" + self.cache.add("addkey1", "value") + result = self.cache.add("addkey1", "newvalue") + self.assertEqual(result, True) + self.assertEqual(self.cache.get("addkey1"), None) + + def test_non_existent(self): + "Non-existent keys aren't found in the dummy cache backend" + self.assertEqual(self.cache.get("does_not_exist"), None) + self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!") + + def test_get_many(self): + "get_many returns nothing for the dummy cache backend" + self.cache.set('a', 'a') + self.cache.set('b', 'b') + self.cache.set('c', 'c') + self.cache.set('d', 'd') + self.assertEqual(self.cache.get_many(['a', 'c', 'd']), {}) + self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {}) + + def test_delete(self): + "Cache deletion is transparently ignored on the dummy cache backend" + self.cache.set("key1", "spam") + self.cache.set("key2", "eggs") + self.assertEqual(self.cache.get("key1"), None) + self.cache.delete("key1") + self.assertEqual(self.cache.get("key1"), None) + self.assertEqual(self.cache.get("key2"), None) + + def test_has_key(self): + "The has_key method doesn't ever return True for the dummy cache backend" + self.cache.set("hello1", "goodbye1") + self.assertEqual(self.cache.has_key("hello1"), False) + self.assertEqual(self.cache.has_key("goodbye1"), False) + + def test_in(self): + "The in operator doesn't ever return True for the dummy cache backend" + self.cache.set("hello2", "goodbye2") + self.assertEqual("hello2" in self.cache, False) + self.assertEqual("goodbye2" in self.cache, False) + + def test_incr(self): + "Dummy cache values can't be incremented" + self.cache.set('answer', 42) + self.assertRaises(ValueError, self.cache.incr, 'answer') + self.assertRaises(ValueError, self.cache.incr, 'does_not_exist') + + def test_decr(self): + "Dummy cache values can't be decremented" + self.cache.set('answer', 42) + self.assertRaises(ValueError, self.cache.decr, 'answer') + self.assertRaises(ValueError, self.cache.decr, 'does_not_exist') + + def test_data_types(self): + "All data types are ignored equally by the dummy cache" + stuff = { + 'string' : 'this is a string', + 'int' : 42, + 'list' : [1, 2, 3, 4], + 'tuple' : (1, 2, 3, 4), + 'dict' : {'A': 1, 'B' : 2}, + 'function' : f, + 'class' : C, + } + self.cache.set("stuff", stuff) + self.assertEqual(self.cache.get("stuff"), None) + + def test_expiration(self): + "Expiration has no effect on the dummy cache" + self.cache.set('expire1', 'very quickly', 1) + self.cache.set('expire2', 'very quickly', 1) + self.cache.set('expire3', 'very quickly', 1) + + time.sleep(2) + self.assertEqual(self.cache.get("expire1"), None) + + self.cache.add("expire2", "newvalue") + self.assertEqual(self.cache.get("expire2"), None) + self.assertEqual(self.cache.has_key("expire3"), False) + + def test_unicode(self): + "Unicode values are ignored by the dummy cache" + stuff = { + u'ascii': u'ascii_value', + u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1', + u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2', + u'ascii': {u'x' : 1 } + } + for (key, value) in stuff.items(): + self.cache.set(key, value) + self.assertEqual(self.cache.get(key), None) + + +class BaseCacheTests(object): + # A common set of tests to apply to all cache backends def test_simple(self): - # simple set/get + # Simple cache set/get works self.cache.set("key", "value") self.assertEqual(self.cache.get("key"), "value") def test_add(self): - # test add (only add if key isn't already in cache) + # A key can be added to a cache self.cache.add("addkey1", "value") result = self.cache.add("addkey1", "newvalue") self.assertEqual(result, False) self.assertEqual(self.cache.get("addkey1"), "value") def test_non_existent(self): + # Non-existent cache keys return as None/default # get with non-existent keys self.assertEqual(self.cache.get("does_not_exist"), None) self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!") def test_get_many(self): - # get_many + # Multiple cache keys can be returned using get_many self.cache.set('a', 'a') self.cache.set('b', 'b') self.cache.set('c', 'c') @@ -63,7 +159,7 @@ class Cache(unittest.TestCase): self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'}) def test_delete(self): - # delete + # Cache keys can be deleted self.cache.set("key1", "spam") self.cache.set("key2", "eggs") self.assertEqual(self.cache.get("key1"), "spam") @@ -72,17 +168,37 @@ class Cache(unittest.TestCase): self.assertEqual(self.cache.get("key2"), "eggs") def test_has_key(self): - # has_key + # The cache can be inspected for cache keys self.cache.set("hello1", "goodbye1") self.assertEqual(self.cache.has_key("hello1"), True) self.assertEqual(self.cache.has_key("goodbye1"), False) def test_in(self): + # The in operator can be used to inspet cache contents self.cache.set("hello2", "goodbye2") self.assertEqual("hello2" in self.cache, True) self.assertEqual("goodbye2" in self.cache, False) + def test_incr(self): + # Cache values can be incremented + self.cache.set('answer', 41) + self.assertEqual(self.cache.incr('answer'), 42) + self.assertEqual(self.cache.get('answer'), 42) + self.assertEqual(self.cache.incr('answer', 10), 52) + self.assertEqual(self.cache.get('answer'), 52) + self.assertRaises(ValueError, self.cache.incr, 'does_not_exist') + + def test_decr(self): + # Cache values can be decremented + self.cache.set('answer', 43) + self.assertEqual(self.cache.decr('answer'), 42) + self.assertEqual(self.cache.get('answer'), 42) + self.assertEqual(self.cache.decr('answer', 10), 32) + self.assertEqual(self.cache.get('answer'), 32) + self.assertRaises(ValueError, self.cache.decr, 'does_not_exist') + def test_data_types(self): + # Many different data types can be cached stuff = { 'string' : 'this is a string', 'int' : 42, @@ -96,6 +212,7 @@ class Cache(unittest.TestCase): self.assertEqual(self.cache.get("stuff"), stuff) def test_expiration(self): + # Cache values can be set to expire self.cache.set('expire1', 'very quickly', 1) self.cache.set('expire2', 'very quickly', 1) self.cache.set('expire3', 'very quickly', 1) @@ -108,6 +225,7 @@ class Cache(unittest.TestCase): self.assertEqual(self.cache.has_key("expire3"), False) def test_unicode(self): + # Unicode values can be cached stuff = { u'ascii': u'ascii_value', u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1', @@ -118,14 +236,36 @@ class Cache(unittest.TestCase): self.cache.set(key, value) self.assertEqual(self.cache.get(key), value) +class DBCacheTests(unittest.TestCase, BaseCacheTests): + def setUp(self): + management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False) + self.cache = get_cache('db://test_cache_table') + + def tearDown(self): + from django.db import connection + cursor = connection.cursor() + cursor.execute('DROP TABLE test_cache_table'); + +class LocMemCacheTests(unittest.TestCase, BaseCacheTests): + def setUp(self): + self.cache = get_cache('locmem://') + +# memcached backend isn't guaranteed to be available. +# To check the memcached backend, the test settings file will +# need to contain a CACHE_BACKEND setting that points at +# your memcache server. +if settings.CACHE_BACKEND.startswith('memcached://'): + class MemcachedCacheTests(unittest.TestCase, BaseCacheTests): + def setUp(self): + self.cache = get_cache(settings.CACHE_BACKEND) -class FileBasedCacheTests(unittest.TestCase): +class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): """ Specific test cases for the file-based cache. """ def setUp(self): self.dirname = tempfile.mkdtemp() - self.cache = FileCache(self.dirname, {}) + self.cache = get_cache('file:///%s' % self.dirname) def tearDown(self): shutil.rmtree(self.dirname) |
