From 02fc6276d7577dcec26be30f3805b242ff0ce8a0 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 11 Nov 2010 15:06:20 +0000 Subject: [PATCH] Fixed #14508 - test suite silences warnings. Utility functions get_warnings_state and save_warnings_state have been added to django.test.utils, and methods to django.test.TestCase for convenience. The implementation is based on the catch_warnings context manager from Python 2.6. git-svn-id: http://code.djangoproject.com/svn/django/trunk@14526 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/auth/tests/auth_backends.py | 4 +-- django/contrib/messages/tests/base.py | 4 +-- django/test/testcases.py | 14 +++++++++ django/test/utils.py | 22 ++++++++++++++ tests/regressiontests/cache/tests.py | 22 ++++++-------- .../context_processors/tests.py | 4 +-- tests/regressiontests/csrf_tests/tests.py | 4 +-- tests/regressiontests/syndication/tests.py | 4 +-- tests/regressiontests/templates/loaders.py | 5 ++-- tests/regressiontests/test_utils/tests.py | 29 +++++++++++++++++++ 10 files changed, 87 insertions(+), 25 deletions(-) diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py index 2ba8a73639..5dbfab12c1 100644 --- a/django/contrib/auth/tests/auth_backends.py +++ b/django/contrib/auth/tests/auth_backends.py @@ -154,13 +154,13 @@ class RowlevelBackendTest(TestCase): self.user1 = User.objects.create_user('test', 'test@example.com', 'test') self.user2 = User.objects.create_user('test2', 'test2@example.com', 'test') self.user3 = User.objects.create_user('test3', 'test3@example.com', 'test') + self.save_warnings_state() warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.auth') def tearDown(self): settings.AUTHENTICATION_BACKENDS = self.curr_auth - warnings.resetwarnings() - warnings.simplefilter('ignore', PendingDeprecationWarning) + self.restore_warnings_state() def test_has_perm(self): self.assertEqual(self.user1.has_perm('perm', TestObj()), False) diff --git a/django/contrib/messages/tests/base.py b/django/contrib/messages/tests/base.py index 1c42c1d0a3..9c6132bf3b 100644 --- a/django/contrib/messages/tests/base.py +++ b/django/contrib/messages/tests/base.py @@ -51,6 +51,7 @@ class BaseTest(TestCase): self._message_storage = settings.MESSAGE_STORAGE settings.MESSAGE_STORAGE = '%s.%s' % (self.storage_class.__module__, self.storage_class.__name__) + self.save_warnings_state() warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.auth.models') @@ -63,8 +64,7 @@ class BaseTest(TestCase): self._template_context_processors settings.INSTALLED_APPS = self._installed_apps settings.MESSAGE_STORAGE = self._message_storage - warnings.resetwarnings() - warnings.simplefilter('ignore', PendingDeprecationWarning) + self.restore_warnings_state() def restore_setting(self, setting): if setting in self._remembered_settings: diff --git a/django/test/testcases.py b/django/test/testcases.py index 57472f7631..398ad774d8 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -11,6 +11,7 @@ from django.db import transaction, connection, connections, DEFAULT_DB_ALIAS from django.http import QueryDict from django.test import _doctest as doctest from django.test.client import Client +from django.test.utils import get_warnings_state, restore_warnings_state from django.utils import simplejson, unittest as ut2 from django.utils.encoding import smart_str from django.utils.functional import wraps @@ -328,6 +329,19 @@ class TransactionTestCase(ut2.TestCase): settings.ROOT_URLCONF = self._old_root_urlconf clear_url_caches() + def save_warnings_state(self): + """ + Saves the state of the warnings module + """ + self._warnings_state = get_warnings_state() + + def restore_warnings_state(self): + """ + Restores the sate of the warnings module to the state + saved by save_warnings_state() + """ + restore_warnings_state(self._warnings_state) + def assertRedirects(self, response, expected_url, status_code=302, target_status_code=200, host=None, msg_prefix=''): """Asserts that a response redirected to a specific URL, and that the diff --git a/django/test/utils.py b/django/test/utils.py index 063e30297f..90a8b3e88f 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -1,6 +1,7 @@ import sys import time import os +import warnings from django.conf import settings from django.core import mail from django.core.mail.backends import locmem @@ -46,6 +47,7 @@ class ContextList(list): return False return True + def instrumented_test_render(self, context): """ An instrumented Template render method, providing a signal @@ -75,6 +77,7 @@ def setup_test_environment(): deactivate() + def teardown_test_environment(): """Perform any global post-test teardown. This involves: @@ -93,6 +96,25 @@ def teardown_test_environment(): del mail.outbox + +def get_warnings_state(): + """ + Returns an object containing the state of the warnings module + """ + # There is no public interface for doing this, but this implementation of + # get_warnings_state and restore_warnings_state appears to work on Python + # 2.4 to 2.7. + return warnings.filters[:] + + +def restore_warnings_state(state): + """ + Restores the state of the warnings module when passed an object that was + returned by get_warnings_state() + """ + warnings.filters = state[:] + + def get_runner(settings): test_path = settings.TEST_RUNNER.split('.') # Allow for Python 2.5 relative paths diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index da4c9a8e7a..575021dc2b 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -15,6 +15,7 @@ from django.core.cache import get_cache from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning from django.http import HttpResponse, HttpRequest from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware +from django.test.utils import get_warnings_state, restore_warnings_state from django.utils import translation from django.utils import unittest from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key @@ -379,21 +380,16 @@ class BaseCacheTests(object): # manager to test this warning nicely. Since we can't do that # yet, the cleanest option is to temporarily ask for # CacheKeyWarning to be raised as an exception. + _warnings_state = get_warnings_state() warnings.simplefilter("error", CacheKeyWarning) - # memcached does not allow whitespace or control characters in keys - self.assertRaises(CacheKeyWarning, self.cache.set, 'key with spaces', 'value') - # memcached limits key length to 250 - self.assertRaises(CacheKeyWarning, self.cache.set, 'a' * 251, 'value') - - # The warnings module has no public API for getting the - # current list of warning filters, so we can't save that off - # and reset to the previous value, we have to globally reset - # it. The effect will be the same, as long as the Django test - # runner doesn't add any global warning filters (it currently - # does not). - warnings.resetwarnings() - warnings.simplefilter("ignore", PendingDeprecationWarning) + try: + # memcached does not allow whitespace or control characters in keys + self.assertRaises(CacheKeyWarning, self.cache.set, 'key with spaces', 'value') + # memcached limits key length to 250 + self.assertRaises(CacheKeyWarning, self.cache.set, 'a' * 251, 'value') + finally: + restore_warnings_state(_warnings_state) class DBCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self): diff --git a/tests/regressiontests/context_processors/tests.py b/tests/regressiontests/context_processors/tests.py index 2b5ddbe94c..5a89561bb6 100644 --- a/tests/regressiontests/context_processors/tests.py +++ b/tests/regressiontests/context_processors/tests.py @@ -48,14 +48,14 @@ class AuthContextProcessorTests(TestCase): fixtures = ['context-processors-users.xml'] def setUp(self): + self.save_warnings_state() warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.auth.models') warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.core.context_processors') def tearDown(self): - warnings.resetwarnings() - warnings.simplefilter('ignore', PendingDeprecationWarning) + self.restore_warnings_state() def test_session_not_accessed(self): """ diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 7265b5b788..ea18d7d010 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -71,12 +71,12 @@ class CsrfMiddlewareTest(TestCase): _secret_key_for_session_test= "test" def setUp(self): + self.save_warnings_state() warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.middleware.csrf') def tearDown(self): - warnings.resetwarnings() - warnings.simplefilter('ignore', PendingDeprecationWarning) + self.restore_warnings_state() def _get_GET_no_csrf_cookie_request(self): return TestingHttpRequest() diff --git a/tests/regressiontests/syndication/tests.py b/tests/regressiontests/syndication/tests.py index a69a454921..b9d77d9b0b 100644 --- a/tests/regressiontests/syndication/tests.py +++ b/tests/regressiontests/syndication/tests.py @@ -315,14 +315,14 @@ class DeprecatedSyndicationFeedTest(FeedTestCase): Tests for the deprecated API (feed() view and the feed_dict etc). """ def setUp(self): + self.save_warnings_state() warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.syndication.feeds') warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.syndication.views') def tearDown(self): - warnings.resetwarnings() - warnings.simplefilter('ignore', PendingDeprecationWarning) + self.restore_warnings_state() def test_empty_feed_dict(self): """ diff --git a/tests/regressiontests/templates/loaders.py b/tests/regressiontests/templates/loaders.py index c59f838b4b..f0759ec2c6 100644 --- a/tests/regressiontests/templates/loaders.py +++ b/tests/regressiontests/templates/loaders.py @@ -20,6 +20,7 @@ from django.template import TemplateDoesNotExist, Context from django.template.loaders.eggs import load_template_source as lts_egg from django.template.loaders.eggs import Loader as EggLoader from django.template import loader +from django.test.utils import get_warnings_state, restore_warnings_state from django.utils import unittest @@ -68,13 +69,13 @@ class DeprecatedEggLoaderTest(unittest.TestCase): }) self._old_installed_apps = settings.INSTALLED_APPS settings.INSTALLED_APPS = [] + self._warnings_state = get_warnings_state() warnings.filterwarnings("ignore", category=DeprecationWarning, module='django.template.loaders.eggs') def tearDown(self): settings.INSTALLED_APPS = self._old_installed_apps - warnings.resetwarnings() - warnings.simplefilter("ignore", PendingDeprecationWarning) + restore_warnings_state(self._warnings_state) def test_existing(self): "A template can be loaded from an egg" diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index 995306e01d..2e55ba7718 100644 --- a/tests/regressiontests/test_utils/tests.py +++ b/tests/regressiontests/test_utils/tests.py @@ -26,6 +26,35 @@ class SkippingTestCase(TestCase): self.assertRaises(ValueError, test_func) +class SaveRestoreWarningState(TestCase): + + def test_save_restore_warnings_state(self): + """ + Ensure save_warnings_state/restore_warnings_state work correctly. + """ + # In reality this test could be satisfied by many broken implementations + # of save_warnings_state/restore_warnings_state (e.g. just + # warnings.resetwarnings()) , but it is difficult to test more. + import warnings + self.save_warnings_state() + + class MyWarning(Warning): + pass + + # Add a filter that causes an exception to be thrown, so we can catch it + warnings.simplefilter("error", MyWarning) + self.assertRaises(Warning, lambda: warnings.warn("warn", MyWarning)) + + # Now restore. + self.restore_warnings_state() + # After restoring, we shouldn't get an exception. But we don't want a + # warning printed either, so we have to silence the warning. + warnings.simplefilter("ignore", MyWarning) + warnings.warn("warn", MyWarning) + + # Remove the filter we just added. + self.restore_warnings_state() + __test__ = {"API_TEST": r""" # Some checks of the doctest output normalizer. # Standard doctests do fairly