mirror of
https://github.com/django/django.git
synced 2025-07-19 00:59:17 +00:00
[1.0.X] Corrected a problem with the database cache backend, and refactored the cache test suite to ensure that all the backends are actually tested.
Partial merge of r10031 and r10071 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10086 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0abd798b9b
commit
f781205859
1
django/core/cache/backends/db.py
vendored
1
django/core/cache/backends/db.py
vendored
@ -65,6 +65,7 @@ class CacheClass(BaseCache):
|
|||||||
cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
|
cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
|
||||||
except DatabaseError:
|
except DatabaseError:
|
||||||
# To be threadsafe, updates/inserts are allowed to fail silently
|
# To be threadsafe, updates/inserts are allowed to fail silently
|
||||||
|
transaction.rollback()
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
@ -782,6 +782,10 @@ dependencies:
|
|||||||
* Textile_
|
* Textile_
|
||||||
* Docutils_
|
* Docutils_
|
||||||
* setuptools_
|
* setuptools_
|
||||||
|
* memcached_, plus the either the python-memcached_ or cmemcached_ Python binding
|
||||||
|
|
||||||
|
If you want to test the memcached cache backend, you will also need to define
|
||||||
|
a :setting:`CACHE_BACKEND` setting that points at your memcached instance.
|
||||||
|
|
||||||
Each of these dependencies is optional. If you're missing any of them, the
|
Each of these dependencies is optional. If you're missing any of them, the
|
||||||
associated tests will be skipped.
|
associated tests will be skipped.
|
||||||
@ -791,6 +795,9 @@ associated tests will be skipped.
|
|||||||
.. _Textile: http://pypi.python.org/pypi/textile
|
.. _Textile: http://pypi.python.org/pypi/textile
|
||||||
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
|
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
|
||||||
.. _setuptools: http://pypi.python.org/pypi/setuptools/
|
.. _setuptools: http://pypi.python.org/pypi/setuptools/
|
||||||
|
.. _memcached: http://www.danga.com/memcached/
|
||||||
|
.. _python-memcached: http://pypi.python.org/pypi/python-memcached/
|
||||||
|
.. _cmemcached: http://pypi.python.org/pypi/cmemcache
|
||||||
|
|
||||||
To run a subset of the unit tests, append the names of the test modules to the
|
To run a subset of the unit tests, append the names of the test modules to the
|
||||||
``runtests.py`` command line. See the list of directories in
|
``runtests.py`` command line. See the list of directories in
|
||||||
|
152
tests/regressiontests/cache/tests.py
vendored
152
tests/regressiontests/cache/tests.py
vendored
@ -9,8 +9,10 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.core.cache import cache, get_cache
|
from django.conf import settings
|
||||||
from django.core.cache.backends.filebased import CacheClass as FileCache
|
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.http import HttpResponse
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils.hashcompat import md5_constructor
|
from django.utils.hashcompat import md5_constructor
|
||||||
@ -22,39 +24,121 @@ class C:
|
|||||||
def m(n):
|
def m(n):
|
||||||
return 24
|
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):
|
def setUp(self):
|
||||||
# Special-case the file cache so we can clean up after ourselves.
|
self.cache = get_cache('dummy://')
|
||||||
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)
|
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
# simple set/get
|
"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_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 cache set/get works
|
||||||
self.cache.set("key", "value")
|
self.cache.set("key", "value")
|
||||||
self.assertEqual(self.cache.get("key"), "value")
|
self.assertEqual(self.cache.get("key"), "value")
|
||||||
|
|
||||||
def test_add(self):
|
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")
|
self.cache.add("addkey1", "value")
|
||||||
result = self.cache.add("addkey1", "newvalue")
|
result = self.cache.add("addkey1", "newvalue")
|
||||||
self.assertEqual(result, False)
|
self.assertEqual(result, False)
|
||||||
self.assertEqual(self.cache.get("addkey1"), "value")
|
self.assertEqual(self.cache.get("addkey1"), "value")
|
||||||
|
|
||||||
def test_non_existent(self):
|
def test_non_existent(self):
|
||||||
|
# Non-existent cache keys return as None/default
|
||||||
# get with non-existent keys
|
# get with non-existent keys
|
||||||
self.assertEqual(self.cache.get("does_not_exist"), None)
|
self.assertEqual(self.cache.get("does_not_exist"), None)
|
||||||
self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
|
self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
|
||||||
|
|
||||||
def test_get_many(self):
|
def test_get_many(self):
|
||||||
# get_many
|
# Multiple cache keys can be returned using get_many
|
||||||
self.cache.set('a', 'a')
|
self.cache.set('a', 'a')
|
||||||
self.cache.set('b', 'b')
|
self.cache.set('b', 'b')
|
||||||
self.cache.set('c', 'c')
|
self.cache.set('c', 'c')
|
||||||
@ -63,7 +147,7 @@ class Cache(unittest.TestCase):
|
|||||||
self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'})
|
self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'})
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
# delete
|
# Cache keys can be deleted
|
||||||
self.cache.set("key1", "spam")
|
self.cache.set("key1", "spam")
|
||||||
self.cache.set("key2", "eggs")
|
self.cache.set("key2", "eggs")
|
||||||
self.assertEqual(self.cache.get("key1"), "spam")
|
self.assertEqual(self.cache.get("key1"), "spam")
|
||||||
@ -72,17 +156,19 @@ class Cache(unittest.TestCase):
|
|||||||
self.assertEqual(self.cache.get("key2"), "eggs")
|
self.assertEqual(self.cache.get("key2"), "eggs")
|
||||||
|
|
||||||
def test_has_key(self):
|
def test_has_key(self):
|
||||||
# has_key
|
# The cache can be inspected for cache keys
|
||||||
self.cache.set("hello1", "goodbye1")
|
self.cache.set("hello1", "goodbye1")
|
||||||
self.assertEqual(self.cache.has_key("hello1"), True)
|
self.assertEqual(self.cache.has_key("hello1"), True)
|
||||||
self.assertEqual(self.cache.has_key("goodbye1"), False)
|
self.assertEqual(self.cache.has_key("goodbye1"), False)
|
||||||
|
|
||||||
def test_in(self):
|
def test_in(self):
|
||||||
|
# The in operator can be used to inspet cache contents
|
||||||
self.cache.set("hello2", "goodbye2")
|
self.cache.set("hello2", "goodbye2")
|
||||||
self.assertEqual("hello2" in self.cache, True)
|
self.assertEqual("hello2" in self.cache, True)
|
||||||
self.assertEqual("goodbye2" in self.cache, False)
|
self.assertEqual("goodbye2" in self.cache, False)
|
||||||
|
|
||||||
def test_data_types(self):
|
def test_data_types(self):
|
||||||
|
# Many different data types can be cached
|
||||||
stuff = {
|
stuff = {
|
||||||
'string' : 'this is a string',
|
'string' : 'this is a string',
|
||||||
'int' : 42,
|
'int' : 42,
|
||||||
@ -96,6 +182,7 @@ class Cache(unittest.TestCase):
|
|||||||
self.assertEqual(self.cache.get("stuff"), stuff)
|
self.assertEqual(self.cache.get("stuff"), stuff)
|
||||||
|
|
||||||
def test_expiration(self):
|
def test_expiration(self):
|
||||||
|
# Cache values can be set to expire
|
||||||
self.cache.set('expire1', 'very quickly', 1)
|
self.cache.set('expire1', 'very quickly', 1)
|
||||||
self.cache.set('expire2', 'very quickly', 1)
|
self.cache.set('expire2', 'very quickly', 1)
|
||||||
self.cache.set('expire3', 'very quickly', 1)
|
self.cache.set('expire3', 'very quickly', 1)
|
||||||
@ -108,6 +195,7 @@ class Cache(unittest.TestCase):
|
|||||||
self.assertEqual(self.cache.has_key("expire3"), False)
|
self.assertEqual(self.cache.has_key("expire3"), False)
|
||||||
|
|
||||||
def test_unicode(self):
|
def test_unicode(self):
|
||||||
|
# Unicode values can be cached
|
||||||
stuff = {
|
stuff = {
|
||||||
u'ascii': u'ascii_value',
|
u'ascii': u'ascii_value',
|
||||||
u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
|
u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
|
||||||
@ -118,14 +206,36 @@ class Cache(unittest.TestCase):
|
|||||||
self.cache.set(key, value)
|
self.cache.set(key, value)
|
||||||
self.assertEqual(self.cache.get(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')
|
||||||
|
|
||||||
class FileBasedCacheTests(unittest.TestCase):
|
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, BaseCacheTests):
|
||||||
"""
|
"""
|
||||||
Specific test cases for the file-based cache.
|
Specific test cases for the file-based cache.
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.dirname = tempfile.mkdtemp()
|
self.dirname = tempfile.mkdtemp()
|
||||||
self.cache = FileCache(self.dirname, {})
|
self.cache = get_cache('file://%s' % self.dirname)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
shutil.rmtree(self.dirname)
|
shutil.rmtree(self.dirname)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user