Commit f7812058 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

[1.0.X] Corrected a problem with the database cache backend, and refactored...

[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
parent 0abd798b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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)])
        except DatabaseError:
            # To be threadsafe, updates/inserts are allowed to fail silently
            transaction.rollback()
            return False
        else:
            transaction.commit_unless_managed()
+25 −18
Original line number Diff line number Diff line
@@ -782,6 +782,10 @@ dependencies:
    *  Textile_
    *  Docutils_
    *  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
associated tests will be skipped.
@@ -791,6 +795,9 @@ associated tests will be skipped.
.. _Textile: http://pypi.python.org/pypi/textile
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
.. _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
``runtests.py`` command line. See the list of directories in
+132 −22
Original line number Diff line number Diff line
@@ -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,121 @@ 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
        self.cache = get_cache('dummy://')

    def tearDown(self):
        if self.cache_dir is not None:
            shutil.rmtree(self.cache_dir)
    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_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 +147,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 +156,19 @@ 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_data_types(self):
        # Many different data types can be cached
        stuff = {
            'string'    : 'this is a string',
            'int'       : 42,
@@ -96,6 +182,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 +195,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 +206,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)