Ticket #11505: restore_cache_during_tests.v2.diff
File restore_cache_during_tests.v2.diff, 20.6 KB (added by , 13 years ago) |
---|
-
django/test/client.py
diff --git a/django/test/client.py b/django/test/client.py index a5ef075..97ef589 100644
a b from django.core.handlers.base import BaseHandler 17 17 from django.core.handlers.wsgi import WSGIRequest 18 18 from django.core.signals import got_request_exception 19 19 from django.http import SimpleCookie, HttpRequest, QueryDict 20 from django.middleware.cache import (UpdateCacheMiddleware, 21 FetchFromCacheMiddleware, CacheMiddleware) 20 22 from django.template import TemplateDoesNotExist 21 23 from django.test import signals 24 from django.test.utils import modified_get_cache 22 25 from django.utils.functional import curry 23 26 from django.utils.encoding import smart_str 24 27 from django.utils.http import urlencode … … class ClientHandler(BaseHandler): 62 65 """ 63 66 def __init__(self, enforce_csrf_checks=True, *args, **kwargs): 64 67 self.enforce_csrf_checks = enforce_csrf_checks 68 self._caches_replaced = False 65 69 super(ClientHandler, self).__init__(*args, **kwargs) 66 70 67 71 def __call__(self, environ): … … class ClientHandler(BaseHandler): 81 85 # required for backwards compatibility with external tests against 82 86 # admin views. 83 87 request._dont_enforce_csrf_checks = not self.enforce_csrf_checks 88 89 # Ensure cache middleware uses resetable cache for testing 90 if not self._caches_replaced: 91 self.replace_cache_in_cache_middleware() 84 92 response = self.get_response(request) 85 93 finally: 86 94 signals.request_finished.disconnect(close_connection) … … class ClientHandler(BaseHandler): 89 97 90 98 return response 91 99 100 def replace_cache_in_cache_middleware(self): 101 """ 102 Iterate over loaded cache middleware and replace cache with modified 103 cache (so it can be reset between tests). 104 """ 105 cache_middleware_classes = [ 106 UpdateCacheMiddleware, FetchFromCacheMiddleware, CacheMiddleware 107 ] 108 mw_methods = set(self._request_middleware + self._response_middleware) 109 for mw_method in mw_methods: 110 mw_instance = mw_method.im_self 111 if any([issubclass(type(mw_instance), cls) for cls in cache_middleware_classes]): 112 mw_instance._original_cache = mw_instance.cache 113 mw_instance.cache = modified_get_cache(mw_instance.cache_alias) 114 self._caches_replaced = True 115 92 116 def store_rendered_templates(store, signal, sender, template, context, **kwargs): 93 117 """ 94 118 Stores templates and contexts that are rendered. -
django/test/testcases.py
diff --git a/django/test/testcases.py b/django/test/testcases.py index 010850d..ed05926 100644
a b from django.http import QueryDict 20 20 from django.test import _doctest as doctest 21 21 from django.test.client import Client 22 22 from django.test.utils import (get_warnings_state, restore_warnings_state, 23 override_settings )23 override_settings, _caches_used_during_test) 24 24 from django.utils import simplejson, unittest as ut2 25 25 from django.utils.encoding import smart_str 26 26 … … class TransactionTestCase(SimpleTestCase): 350 350 ROOT_URLCONF with it. 351 351 * Clearing the mail test outbox. 352 352 """ 353 self._cache_setup() 353 354 self._fixture_setup() 354 355 self._urlconf_setup() 355 356 mail.outbox = [] … … class TransactionTestCase(SimpleTestCase): 376 377 settings.ROOT_URLCONF = self.urls 377 378 clear_url_caches() 378 379 380 def _cache_setup(self): 381 """ 382 Resets the list of caches used in preparation for test 383 """ 384 global _caches_used_during_test 385 _caches_used_during_test.clear() 386 379 387 def __call__(self, result=None): 380 388 """ 381 389 Wrapper around default __call__ method to perform common Django test … … class TransactionTestCase(SimpleTestCase): 414 422 """ 415 423 self._fixture_teardown() 416 424 self._urlconf_teardown() 425 self._cache_teardown() 417 426 # Some DB cursors include SQL statements as part of cursor 418 427 # creation. If you have a test that does rollback, the effect 419 428 # of these statements is lost, which can effect the operation … … class TransactionTestCase(SimpleTestCase): 431 440 if hasattr(self, '_old_root_urlconf'): 432 441 settings.ROOT_URLCONF = self._old_root_urlconf 433 442 clear_url_caches() 443 444 def _cache_teardown(self): 445 """ 446 Clears values set in cache during test 447 """ 448 for cache in _caches_used_during_test: 449 cache.delete_many(list(cache._keys_set_during_test)) 434 450 435 451 def assertRedirects(self, response, expected_url, status_code=302, 436 452 target_status_code=200, host=None, msg_prefix=''): -
django/test/utils.py
diff --git a/django/test/utils.py b/django/test/utils.py index 87f2311..eb000fc 100644
a b 1 1 from __future__ import with_statement 2 2 3 import types 3 4 import warnings 4 5 from django.conf import settings, UserSettingsHolder 5 6 from django.core import mail 7 from django.core import cache 6 8 from django.test.signals import template_rendered, setting_changed 7 9 from django.template import Template, loader, TemplateDoesNotExist 8 10 from django.template.loaders import cached … … def instrumented_test_render(self, context): 62 64 return self.nodelist.render(context) 63 65 64 66 67 _caches_used_during_test = set() 68 69 def modified_get_cache(backend, **kwargs): 70 """ 71 Modifies the original get_cache so that we can track any keys set during a cache run and reset 72 them at the end. 73 """ 74 requested_cache = cache.original_get_cache(backend, **kwargs) 75 76 # Add '_test_' to key_prefix to ensure pre-existing cache values don't get touched 77 requested_cache.original_key_prefix = requested_cache.key_prefix 78 requested_cache.key_prefix = '_test_%s' % requested_cache.original_key_prefix 79 80 # Keep track of which caches we use during a test 81 global _caches_used_during_test 82 _caches_used_during_test.add(requested_cache) 83 84 requested_cache._keys_set_during_test = set() 85 86 # Modify cache.set() to collect keys in _keys_set_during_test 87 requested_cache.original_set = requested_cache.set 88 def modified_set(self, key, value, timeout=None, version=None): 89 requested_cache._keys_set_during_test.add(key) 90 requested_cache.original_set(key, value, timeout, version) 91 requested_cache.set = types.MethodType(modified_set, requested_cache) 92 93 # Modify cache.add() to collect keys in _keys_set_during_test 94 requested_cache.original_add = requested_cache.add 95 def modified_add(self, key, value, timeout=None, version=None): 96 requested_cache._keys_set_during_test.add(key) 97 return requested_cache.original_add(key, value, timeout, version) 98 requested_cache.add = types.MethodType(modified_add, requested_cache) 99 100 # Modify cache.incr() to collect keys in _keys_set_during_test 101 requested_cache.original_incr = requested_cache.incr 102 def modified_incr(self, key, delta=1, version=None): 103 requested_cache._keys_set_during_test.add(key) 104 return requested_cache.original_incr(key, delta, version) 105 requested_cache.incr = types.MethodType(modified_incr, requested_cache) 106 107 # Modify cache.decr() to collect keys in _keys_set_during_test 108 requested_cache.original_decr = requested_cache.decr 109 def modified_decr(self, key, delta=1, version=None): 110 requested_cache._keys_set_during_test.add(key) 111 return requested_cache.original_decr(key, delta, version) 112 requested_cache.decr = types.MethodType(modified_decr, requested_cache) 113 114 # Modify cache.set_many() to collect keys in _keys_set_during_test 115 requested_cache.original_set_many = requested_cache.set_many 116 def modified_set_many(self, data, timeout=None, version=None): 117 requested_cache._keys_set_during_test.update(data.keys()) 118 requested_cache.original_set_many(data, timeout, version) 119 requested_cache.set_many = types.MethodType(modified_set_many, requested_cache) 120 121 return requested_cache 122 123 65 124 def setup_test_environment(): 66 125 """Perform any global pre-test setup. This involves: 67 126 … … def setup_test_environment(): 74 133 75 134 mail.original_email_backend = settings.EMAIL_BACKEND 76 135 settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' 77 78 136 mail.outbox = [] 137 138 cache.original_get_cache = cache.get_cache 139 cache.get_cache = modified_get_cache 140 141 # Make sure django.core.cache.cache also uses the modified cache 142 cache.original_cache = cache.cache 143 cache.cache = cache.get_cache(cache.DEFAULT_CACHE_ALIAS) 79 144 80 145 deactivate() 81 146 … … def teardown_test_environment(): 92 157 93 158 settings.EMAIL_BACKEND = mail.original_email_backend 94 159 del mail.original_email_backend 95 96 160 del mail.outbox 161 162 cache.cache = cache.original_cache 163 del cache.original_cache 164 cache.get_cache = cache.original_get_cache 165 del cache.original_get_cache 97 166 98 167 99 168 def get_warnings_state(): -
tests/regressiontests/cache/tests.py
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 07bcab7..971d494 100644
a b class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): 894 894 self.assertTrue(not os.path.exists(os.path.dirname(os.path.dirname(keypath)))) 895 895 896 896 def test_cull(self): 897 self.perform_cull_test(50, 2 9)897 self.perform_cull_test(50, 28) 898 898 899 899 def test_old_initialization(self): 900 900 self.cache = get_cache('file://%s?max_entries=30' % self.dirname) 901 self.perform_cull_test(50, 2 9)901 self.perform_cull_test(50, 28) 902 902 903 903 class CustomCacheKeyValidationTests(unittest.TestCase): 904 904 """ -
new file tests/regressiontests/test_utils/templates/test_utils/simple_view.html
diff --git a/tests/regressiontests/test_utils/templates/test_utils/simple_view.html b/tests/regressiontests/test_utils/templates/test_utils/simple_view.html new file mode 100644 index 0000000..479d8d7
- + 1 <html><p>Howdy!</p></html> -
tests/regressiontests/test_utils/tests.py
diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index 942aa85..31fead5 100644
a b 1 1 from __future__ import with_statement 2 2 3 import tempfile 3 4 from django.forms import EmailField 4 5 from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature 5 from django.utils.unittest import skip 6 from django.test.utils import override_settings 7 from django.utils.unittest import skip, skipUnless 8 from django.conf import settings 9 from django.core import management 10 from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS 6 11 7 12 from models import Person 8 13 … … class SkippingExtraTests(TestCase): 131 136 pass 132 137 133 138 139 # We must set this via a function to confirm that cache set test has run 140 # before cache get test 141 _cache_set_test_has_run = False 142 143 def cache_set_test_run(): 144 global _cache_set_test_has_run 145 _cache_set_test_has_run = True 146 147 def cache_get_test_finished(): 148 global _cache_set_test_has_run 149 _cache_set_test_has_run = False 150 151 152 class BaseCacheReset(TestCase): 153 @classmethod 154 def setUpClass(cls): 155 # Setup everything here, because setupClass is guaranteed to run first 156 cache = cls.original_cache() 157 158 # Add a "pre-existing" value to cache to ensure it gets reset properly 159 cache.set('salt', 'pepper') # We won't touch this one 160 cache.set('sweet', 'sour') # We will touch a key with this name and check if it's restored at the end 161 162 # Sanity checks 163 assert cache.get('salt') == 'pepper' 164 assert cache.get('sweet') == 'sour' 165 166 # We manually prefix the test_ here because we are not trying to pretend these are part 167 # of the original cache. We're just making sure the values exist so that we can call incr/decr 168 # without using cache.set() first 169 cache.orig_key_prefix = cache.key_prefix 170 cache.key_prefix = '_test_%s' % cache.orig_key_prefix 171 try: 172 cache.set('going_up', 1) 173 cache.set('going_down', 2) 174 finally: 175 cache.key_prefix = cache.orig_key_prefix 176 177 def setUp(self): 178 # Set up cache again at the instance level. This one will get reset 179 self.cache = self.modified_cache() 180 181 182 class CacheResetTestsMixin(object): 183 # Note test names start with a/b. This ensures test order is correct (which we're normally 184 # not supposed to worry about) 185 def test_a_set_cache_in_various_ways_in_one_test_method(self): 186 """ 187 Set some cache stuff in this method, and then we'll sure it doesn't carry over to the next 188 """ 189 sweet_result = self.cache.set('sweet', 'bitter') 190 left_result = self.cache.set('left', 'right') 191 loud_result = self.cache.add('loud', 'quiet') 192 193 assert self.cache.get('going_up') == 1 194 going_up_result = self.cache.incr('going_up') 195 196 assert self.cache.get('going_down') == 2 197 going_down_result = self.cache.decr('going_down') 198 199 over_above_result = self.cache.set_many({'over': 'under', 'above': 'below'}) 200 201 # Sanity checks 202 self.assertEqual(self.cache.get('sweet'), 'bitter') 203 self.assertEqual(sweet_result, None) 204 205 self.assertEqual(self.cache.get('left'), 'right') 206 self.assertEqual(left_result, None) 207 208 self.assertEqual(self.cache.get('loud'), 'quiet') 209 self.assertEqual(loud_result, True) 210 211 self.assertEqual(self.cache.get('going_up'), 2) 212 self.assertEqual(going_up_result, 2) 213 214 self.assertEqual(self.cache.get('going_down'), 1) 215 self.assertEqual(going_down_result, 1) 216 217 self.assertEqual(self.cache.get('over'), 'under') 218 self.assertEqual(self.cache.get('above'), 'below') 219 self.assertEqual(over_above_result, None) 220 221 # Mark that set_cache tests have run 222 cache_set_test_run() 223 224 def test_b_get_cache_in_another_test_method(self): 225 # Confirm that set tests has already run 226 if not _cache_set_test_has_run: 227 self.skipTest("set_cache test did not run first!") 228 229 try: 230 self.assertEqual(self.cache.get('left'), None) 231 self.assertEqual(self.cache.get('loud'), None) 232 self.assertEqual(self.cache.get('going_up'), None) 233 self.assertEqual(self.cache.get('going_down'), None) 234 self.assertEqual(self.cache.get('over'), None) 235 self.assertEqual(self.cache.get('above'), None) 236 237 # Make sure pre-existing values are still correct 238 original_cache = self.__class__.original_cache() 239 self.assertEqual(original_cache.get('sweet'), 'sour') 240 self.assertEqual(original_cache.get('salt'), 'pepper') 241 finally: 242 # Mark get_cache tests finished 243 cache_get_test_finished() 244 245 246 class LocMemCacheResetTests(BaseCacheReset, CacheResetTestsMixin): 247 backend_name = 'django.core.cache.backends.locmem.LocMemCache' 248 249 @classmethod 250 def original_cache(cls): 251 if not hasattr(cls, '_original_cache'): 252 from django.core.cache import original_get_cache 253 cls._original_cache = original_get_cache(cls.backend_name, LOCATION='test') 254 return cls._original_cache 255 256 def modified_cache(self): 257 return get_cache(self.backend_name, LOCATION='test') 258 259 260 class FileBasedCacheResetTests(BaseCacheReset, CacheResetTestsMixin): 261 backend_name = 'django.core.cache.backends.filebased.FileBasedCache' 262 263 @classmethod 264 def original_cache(cls): 265 if not hasattr(cls, '_original_cache'): 266 cls.cache_dirname = tempfile.mkdtemp() 267 from django.core.cache import original_get_cache 268 cls._original_cache = original_get_cache(cls.backend_name, LOCATION=cls.cache_dirname) 269 return cls._original_cache 270 271 def modified_cache(self): 272 return get_cache(self.backend_name, LOCATION=self.cache_dirname) 273 274 275 class DBCacheResetTests(BaseCacheReset, CacheResetTestsMixin): 276 backend_name = 'django.core.cache.backends.db.DatabaseCache' 277 278 @classmethod 279 def original_cache(cls): 280 if not hasattr(cls, '_original_cache'): 281 cls.cache_table_name = 'test_cache_table' 282 management.call_command('createcachetable', cls.cache_table_name, verbosity=0, interactive=False) 283 from django.core.cache import original_get_cache 284 cls._original_cache = original_get_cache(cls.backend_name, LOCATION=cls.cache_table_name) 285 return cls._original_cache 286 287 def modified_cache(self): 288 return get_cache(self.backend_name, LOCATION=self.cache_table_name) 289 290 @classmethod 291 def tearDownClass(cls): 292 from django.db import connection 293 cursor = connection.cursor() 294 cursor.execute('DROP TABLE %s' % connection.ops.quote_name(cls.cache_table_name)) 295 296 297 @skipUnless(settings.CACHES[DEFAULT_CACHE_ALIAS]['BACKEND'].startswith('django.core.cache.backends.memcached.'), "memcached not available") 298 class MemcachedCacheResetTests(BaseCacheReset, CacheResetTestsMixin): 299 backend_name = 'django.core.cache.backends.memcached.MemcachedCache' 300 301 @classmethod 302 def original_cache(cls): 303 if not hasattr(cls, '_original_cache'): 304 cls.memcached_location = settings.CACHES[DEFAULT_CACHE_ALIAS]['LOCATION'] 305 from django.core.cache import original_get_cache 306 cls._original_cache = original_get_cache(cls.backend_name, LOCATION=cls.memcached_location) 307 return cls._original_cache 308 309 def modified_cache(self): 310 return get_cache(self.backend_name, LOCATION=self.memcached_location) 311 312 313 class CacheMiddlewareTestingTests(TestCase): 314 """ 315 CacheMiddleware caches should be reset between tests to avoid broken test 316 client when views are cached. 317 """ 318 urls = 'regressiontests.test_utils.urls' 319 320 def test_a_request_cached_in_view(self): 321 """A simple view, which gets cached by cache middleware""" 322 response = self.client.get("/test_utils/simple_view/") 323 self.assertEqual(response.status_code, 200) 324 self.assertEqual(response.context["foo"], "bar") 325 326 def test_b_request_cached_in_view(self): 327 """ 328 This test runs after test_a above, and assures that the view is not 329 being cached between tests. 330 """ 331 response = self.client.get("/test_utils/simple_view/") 332 self.assertEqual(response.status_code, 200) 333 334 # These assertions fail if the cache from test_a is carried over, since 335 # the cached response doesn't have a context 336 self.assertTrue(response.context) 337 self.assertEqual(response.context["foo"], "bar") 338 339 CacheMiddlewareTestingTests = override_settings( 340 CACHES = { 341 'default': { 342 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' 343 }, 344 }, 345 CACHE_MIDDLEWARE_SECONDS=30, 346 MIDDLEWARE_CLASSES = ( 347 'django.middleware.cache.UpdateCacheMiddleware', 348 'django.middleware.common.CommonMiddleware', 349 'django.middleware.cache.FetchFromCacheMiddleware', 350 ) 351 )(CacheMiddlewareTestingTests) 352 353 134 354 class AssertRaisesMsgTest(SimpleTestCase): 135 355 136 356 def test_special_re_chars(self): -
tests/regressiontests/test_utils/urls.py
diff --git a/tests/regressiontests/test_utils/urls.py b/tests/regressiontests/test_utils/urls.py index 1bf0a0c..16c19ca 100644
a b import views 5 5 6 6 urlpatterns = patterns('', 7 7 (r'^test_utils/get_person/(\d+)/$', views.get_person), 8 (r'^test_utils/simple_view/$', views.simple_view), 8 9 ) -
tests/regressiontests/test_utils/views.py
diff --git a/tests/regressiontests/test_utils/views.py b/tests/regressiontests/test_utils/views.py index 62af0d9..4d68563 100644
a b 1 1 from django.http import HttpResponse 2 from django.shortcuts import get_object_or_404 2 from django.template import RequestContext 3 from django.shortcuts import get_object_or_404, render_to_response 3 4 from models import Person 4 5 5 6 def get_person(request, pk): 6 7 person = get_object_or_404(Person, pk=pk) 7 return HttpResponse(person.name) 8 No newline at end of file 8 return HttpResponse(person.name) 9 10 def simple_view(request): 11 return render_to_response('test_utils/simple_view.html', { 12 "foo": "bar" 13 }, context_instance=RequestContext(request))