Commit 5f6f3836 authored by Sean Bleier's avatar Sean Bleier

Added more tests to increase coverage and fixed a couple bugs.

We can't inherit a class from a basestring and instantiate it, so I changed
the base class to 'object' so we can still to the type checking.

I also discovered a bug with the Django 1.3 compatibility work.  In the
incr_version method, we were using the Redis's rename function to move the key,
but in Redis 2.0 you cannot rename a volitile key.

Added some moar authors
parent aaff2679
......@@ -2,5 +2,7 @@ Sean Bleier <http://github.com/sebleier>
Matt Dennewitz <http://github.com/blackbrrr>
Jannis Leidel <http://github.com/jezdez>
S. Angel / Twidi <http://github.com/twidi>
Noah Kantrowitz / coderanger <http://github.com/coderanger>
Martin Mahner / bartTC <http://github.com/bartTC>
Forked from http://github.com/sebleier/django-redis-cache
......@@ -14,11 +14,22 @@ except ImportError:
"Redis cache backend requires the 'redis-py' library")
class CacheKey(basestring):
class CacheKey(object):
"""
A stub string class that we can use to check if a key was created already.
"""
pass
def __init__(self, key):
self._key = key
def __eq__(self, other):
return self._key == other
def __str__(self):
return self.__unicode__()
def __unicode__(self):
return smart_str(self._key)
class CacheClass(BaseCache):
def __init__(self, server, params):
......@@ -49,8 +60,8 @@ class CacheClass(BaseCache):
Returns the utf-8 encoded bytestring of the given key as a CacheKey
instance to be able to check if it was "made" before.
"""
if isinstance(key, CacheKey):
key = CacheKey(smart_str(key))
if not isinstance(key, CacheKey):
key = CacheKey(key)
return key
def add(self, key, value, timeout=None, version=None):
......@@ -93,12 +104,13 @@ class CacheClass(BaseCache):
Set content expiration, if necessary
"""
key = self.make_key(key, version=version)
if timeout == 0:
if timeout is None:
timeout = self.default_timeout
if timeout <= 0:
# force the key to be non-volatile
result = self._cache.get(key)
self._cache.set(key, result)
else:
timeout = timeout or self.default_timeout
# If the expiration command returns false, we need to reset the key
# with the new expiration
if not self._cache.expire(key, timeout):
......@@ -130,9 +142,7 @@ class CacheClass(BaseCache):
"""
Unpickles the given value.
"""
# pickle doesn't want a unicode!
value = smart_str(value)
# hydrate that pickle
return pickle.loads(value)
def get_many(self, keys, version=None):
......@@ -178,22 +188,30 @@ class RedisCache(CacheClass):
"""
A subclass that is supposed to be used on Django >= 1.3.
"""
def make_key(self, key, version=None):
if not isinstance(key, CacheKey):
key = CacheKey(smart_str(super(CacheClass, self).make_key(key, version)))
key = CacheKey(super(CacheClass, self).make_key(key, version))
return key
def incr_version(self, key, delta=1, version=None):
"""
Adds delta to the cache version for the supplied key. Returns the
new version.
Note: In Redis 2.0 you cannot rename a volitle key, so we have to move
the value from the old key to the new key and maintain the ttl.
"""
if version is None:
version = self.version
key = self.make_key(key, version)
value = self.get(key, version=version)
old_key = self.make_key(key, version)
value = self.get(old_key, version=version)
ttl = self._cache.ttl(old_key)
if value is None:
raise ValueError("Key '%s' not found" % key)
incr_key = self.make_key(key, version=version+delta)
self._cache.rename(key, incr_key)
new_key = self.make_key(key, version=version+delta)
# TODO: See if we can check the version of Redis, since 2.2 will be able
# to rename volitile keys.
self.set(new_key, value, timeout=ttl)
self.delete(old_key)
return version + delta
......@@ -12,7 +12,17 @@ if not settings.configured:
},
INSTALLED_APPS = [
'tests.testapp',
]
],
CACHES = {
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': '127.0.0.1:6379',
'OPTIONS': {
'DB': 15,
'PASSWORD': 'yadayada',
},
},
}
)
from django.test.simple import DjangoTestSuiteRunner
......@@ -27,4 +37,4 @@ def runtests(*test_args):
sys.exit(failures)
if __name__ == '__main__':
runtests(*sys.argv[1:])
\ No newline at end of file
runtests(*sys.argv[1:])
......@@ -9,3 +9,14 @@ DATABASES = {
INSTALLED_APPS = [
'tests.testapp',
]
CACHES = {
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': '127.0.0.1:6379',
'OPTIONS': { # optional
'DB': 15,
'PASSWORD': 'yadayada',
},
},
}
......@@ -7,9 +7,10 @@ try:
import cPickle as pickle
except ImportError:
import pickle
from django import VERSION
from django.core.cache import get_cache
from models import Poll, expensive_calculation
from redis_cache.cache import RedisCache
# functions/classes for complex data type tests
def f():
......@@ -25,10 +26,32 @@ class RedisCacheTests(unittest.TestCase):
"""
def setUp(self):
# use DB 16 for testing and hope there isn't any important data :->
self.cache = get_cache('redis_cache.cache://127.0.0.1:6379?db=15')
self.cache = self.get_cache()
def tearDown(self):
self.cache.clear()
self.cache.close()
def get_cache(self, backend=None):
if VERSION[0] == 1 and VERSION[1] < 3:
cache = get_cache(backend or 'redis_cache.cache://127.0.0.1:6379?db=15')
elif VERSION[0] == 1 and VERSION[1] >= 3:
cache = get_cache(backend or 'default')
return cache
def test_bad_db_initialization(self):
self.cache = self.get_cache('redis_cache.cache://127.0.0.1:6379?db=not_a_number')
self.assertEqual(self.cache._cache.db, 1)
def test_bad_port_initialization(self):
self.cache = self.get_cache('redis_cache.cache://127.0.0.1:not_a_number?db=15')
self.assertEqual(self.cache._cache.port, 6379)
def test_default_initialization(self):
self.cache = self.get_cache('redis_cache.cache://127.0.0.1')
self.assertEqual(self.cache._cache.host, '127.0.0.1')
self.assertEqual(self.cache._cache.db, 1)
self.assertEqual(self.cache._cache.port, 6379)
def test_simple(self):
# Simple cache set/get works
......@@ -166,12 +189,12 @@ class RedisCacheTests(unittest.TestCase):
self.assertEqual(self.cache.has_key("expire3"), False)
def test_set_expiration_timeout_None(self):
key, value = 'key', 'value'
key, value = self.cache.make_key('key'), 'value'
self.cache.set(key, value);
self.assertTrue(self.cache._cache.ttl(key) > 0)
def test_set_expiration_timeout_0(self):
key, value = 'key', 'value'
key, value = self.cache.make_key('key'), 'value'
self.cache.set(key, value)
self.assertTrue(self.cache._cache.ttl(key) > 0)
self.cache.expire(key, 0)
......@@ -268,5 +291,18 @@ class RedisCacheTests(unittest.TestCase):
self.assertEqual(self.cache.get('key3'), 'sausage')
self.assertEqual(self.cache.get('key4'), 'lobster bisque')
def test_incr_version(self):
if isinstance(self.cache, RedisCache):
old_key = "key1"
self.cache.set(old_key, "spam", version=1)
self.assertEqual(self.cache.make_key(old_key), ':1:key1')
new_version = self.cache.incr_version(old_key, 1)
self.assertEqual(new_version, 2)
new_key = self.cache.make_key(old_key, version=new_version)
self.assertEqual(new_key, ':2:key1')
self.assertEqual(self.cache.get(old_key), None)
self.assertEqual(self.cache.get(new_key), 'spam')
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment