Commit 4dc4d12e authored by Unai Zalakain's avatar Unai Zalakain Committed by Tim Graham
Browse files

Fixed #21598 -- cleaned up template loader overrides in tests

- Template loader overriding is managed with contexts.
- The test loader is a class (function based loaders entered deprecation timeline
  in 1.4).
- Template loader overrider that overrides with test loader added.
parent 756c390f
Loading
Loading
Loading
Loading
+89 −24
Original line number Diff line number Diff line
@@ -146,42 +146,107 @@ def get_runner(settings, test_runner_class=None):
    return test_runner


def setup_test_template_loader(templates_dict, use_cached_loader=False):
class override_template_loaders(object):
    """
    Changes Django to only find templates from within a dictionary (where each
    key is the template name and each value is the corresponding template
    content to return).
    Acts as a function decorator, context manager or start/end manager and
    override the template loaders. It could be used in the following ways:

    Use meth:`restore_template_loaders` to restore the original loaders.
    @override_template_loaders(SomeLoader())
    def test_function(self):
        ...

    with override_template_loaders(SomeLoader(), OtherLoader()) as loaders:
        ...

    loaders = override_template_loaders.override(SomeLoader())
    ...
    override_template_loaders.restore()
    """
    def __init__(self, *loaders):
        self.loaders = loaders
        self.old_loaders = []

    def __enter__(self):
        self.old_loaders = loader.template_source_loaders
        loader.template_source_loaders = self.loaders
        return self.loaders

    def __exit__(self, type, value, traceback):
        loader.template_source_loaders = self.old_loaders

    def __call__(self, test_func):
        @wraps(test_func)
        def inner(*args, **kwargs):
            with self:
                return test_func(*args, **kwargs)
        return inner

    @classmethod
    def override(cls, *loaders):
        if hasattr(loader, RESTORE_LOADERS_ATTR):
            raise Exception("loader.%s already exists" % RESTORE_LOADERS_ATTR)
        setattr(loader, RESTORE_LOADERS_ATTR, loader.template_source_loaders)
        loader.template_source_loaders = loaders
        return loaders

    @classmethod
    def restore(cls):
        loader.template_source_loaders = getattr(loader, RESTORE_LOADERS_ATTR)
        delattr(loader, RESTORE_LOADERS_ATTR)

    def test_template_loader(template_name, template_dirs=None):

class TestTemplateLoader(loader.BaseLoader):
    "A custom template loader that loads templates from a dictionary."
    is_usable = True

    def __init__(self, templates_dict):
        self.templates_dict = templates_dict

    def load_template_source(self, template_name, template_dirs=None,
                             skip_template=None):
        try:
            return (templates_dict[template_name], "test:%s" % template_name)
            return (self.templates_dict[template_name],
                    "test:%s" % template_name)
        except KeyError:
            raise TemplateDoesNotExist(template_name)

    if use_cached_loader:
        template_loader = cached.Loader(('test_template_loader',))
        template_loader._cached_loaders = (test_template_loader,)
    else:
        template_loader = test_template_loader

    setattr(loader, RESTORE_LOADERS_ATTR, loader.template_source_loaders)
    loader.template_source_loaders = (template_loader,)
    return template_loader
class override_with_test_loader(override_template_loaders):
    """
    Acts as a function decorator, context manager or start/end manager and
    override the template loaders with the test loader. It could be used in the
    following ways:

    @override_with_test_loader(templates_dict, use_cached_loader=True)
    def test_function(self):
        ...

def restore_template_loaders():
    """
    Restores the original template loaders after
    :meth:`setup_test_template_loader` has been run.
    with override_with_test_loader(templates_dict) as test_loader:
        ...

    test_loader = override_with_test_loader.override(templates_dict)
    ...
    override_with_test_loader.restore()
    """
    loader.template_source_loaders = getattr(loader, RESTORE_LOADERS_ATTR)
    delattr(loader, RESTORE_LOADERS_ATTR)

    def __init__(self, templates_dict, use_cached_loader=False):
        self.loader = self._get_loader(templates_dict, use_cached_loader)
        super(override_with_test_loader, self).__init__(self.loader)

    def __enter__(self):
        return super(override_with_test_loader, self).__enter__()[0]

    @classmethod
    def override(cls, templates_dict, use_cached_loader=False):
        loader = cls._get_loader(templates_dict, use_cached_loader)
        return super(override_with_test_loader, cls).override(loader)[0]

    @classmethod
    def _get_loader(cls, templates_dict, use_cached_loader=False):
        if use_cached_loader:
            loader = cached.Loader(('TestTemplateLoader',))
            loader._cached_loaders = TestTemplateLoader(templates_dict)
        return TestTemplateLoader(templates_dict)


class override_settings(object):
+125 −156
Original line number Diff line number Diff line
@@ -15,8 +15,8 @@ from django.template import (base as template_base, loader, Context,
    RequestContext, Template, TemplateSyntaxError)
from django.template.loaders import app_directories, filesystem, cached
from django.test import RequestFactory, TestCase
from django.test.utils import (setup_test_template_loader,
    restore_template_loaders, override_settings, extend_sys_path)
from django.test.utils import (override_settings, override_template_loaders,
                               override_with_test_loader, extend_sys_path)
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
from django.utils.encoding import python_2_unicode_compatible
from django.utils.formats import date_format
@@ -208,15 +208,11 @@ class TemplateLoaderTests(TestCase):
            test_template_sources('/DIR1/index.HTML', template_dirs,
                                  ['/DIR1/index.HTML'])

    @override_template_loaders(filesystem.Loader())
    # Turn TEMPLATE_DEBUG on, so that the origin file name will be kept with
    # the compiled templates.
    @override_settings(TEMPLATE_DEBUG=True)
    def test_loader_debug_origin(self):
        old_loaders = loader.template_source_loaders

        try:
            loader.template_source_loaders = (filesystem.Loader(),)

        # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
        # point to a directory containing a login.html file. Also that
        # the file system and app directories loaders both inherit the
@@ -242,8 +238,6 @@ class TemplateLoaderTests(TestCase):
        template_name = template.nodelist[0].source[0].name
        self.assertTrue(template_name.endswith(load_name),
            'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name)
        finally:
            loader.template_source_loaders = old_loaders

    def test_loader_origin(self):
        with self.settings(TEMPLATE_DEBUG=True):
@@ -262,19 +256,15 @@ class TemplateLoaderTests(TestCase):
    # TEMPLATE_DEBUG must be true, otherwise the exception raised
    # during {% include %} processing will be suppressed.
    @override_settings(TEMPLATE_DEBUG=True)
    # Test the base loader class via the app loader. load_template
    # from base is used by all shipped loaders excepting cached,
    # which has its own test.
    @override_template_loaders(app_directories.Loader())
    def test_include_missing_template(self):
        """
        Tests that the correct template is identified as not existing
        when {% include %} specifies a template that does not exist.
        """
        old_loaders = loader.template_source_loaders

        try:
            # Test the base loader class via the app loader. load_template
            # from base is used by all shipped loaders excepting cached,
            # which has its own test.
            loader.template_source_loaders = (app_directories.Loader(),)

        load_name = 'test_include_error.html'
        r = None
        try:
@@ -283,12 +273,14 @@ class TemplateLoaderTests(TestCase):
        except template.TemplateDoesNotExist as e:
            self.assertEqual(e.args[0], 'missing.html')
        self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
        finally:
            loader.template_source_loaders = old_loaders

    # TEMPLATE_DEBUG must be true, otherwise the exception raised
    # during {% include %} processing will be suppressed.
    @override_settings(TEMPLATE_DEBUG=True)
    # Test the base loader class via the app loader. load_template
    # from base is used by all shipped loaders excepting cached,
    # which has its own test.
    @override_template_loaders(app_directories.Loader())
    def test_extends_include_missing_baseloader(self):
        """
        Tests that the correct template is identified as not existing
@@ -296,14 +288,6 @@ class TemplateLoaderTests(TestCase):
        that template has an {% include %} of something that does not
        exist. See #12787.
        """
        old_loaders = loader.template_source_loaders

        try:
            # Test the base loader class via the app loader. load_template
            # from base is used by all shipped loaders excepting cached,
            # which has its own test.
            loader.template_source_loaders = (app_directories.Loader(),)

        load_name = 'test_extends_error.html'
        tmpl = loader.get_template(load_name)
        r = None
@@ -312,8 +296,6 @@ class TemplateLoaderTests(TestCase):
        except template.TemplateDoesNotExist as e:
            self.assertEqual(e.args[0], 'missing.html')
        self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
        finally:
            loader.template_source_loaders = old_loaders

    @override_settings(TEMPLATE_DEBUG=True)
    def test_extends_include_missing_cachedloader(self):
@@ -321,14 +303,9 @@ class TemplateLoaderTests(TestCase):
        Same as test_extends_include_missing_baseloader, only tests
        behavior of the cached loader instead of BaseLoader.
        """

        old_loaders = loader.template_source_loaders

        try:
        cache_loader = cached.Loader(('',))
        cache_loader._cached_loaders = (app_directories.Loader(),)
            loader.template_source_loaders = (cache_loader,)

        with override_template_loaders(cache_loader,):
            load_name = 'test_extends_error.html'
            tmpl = loader.get_template(load_name)
            r = None
@@ -346,8 +323,6 @@ class TemplateLoaderTests(TestCase):
            except template.TemplateDoesNotExist as e:
                self.assertEqual(e.args[0], 'missing.html')
            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
        finally:
            loader.template_source_loaders = old_loaders

    def test_include_template_argument(self):
        """
@@ -544,11 +519,8 @@ class TemplateTests(TestCase):

        template_tests.update(filter_tests)

        cache_loader = setup_test_template_loader(
            dict((name, t[0]) for name, t in six.iteritems(template_tests)),
            use_cached_loader=True,
        )

        templates = dict((name, t[0]) for name, t in six.iteritems(template_tests))
        with override_with_test_loader(templates, use_cached_loader=True) as cache_loader:
            failures = []
            tests = sorted(template_tests.items())

@@ -581,7 +553,6 @@ class TemplateTests(TestCase):
                    template_debug_result = vals[2]

                with translation.override(vals[1].get('LANGUAGE_CODE', 'en-us')):

                    for invalid_str, template_debug, result in [
                            ('', False, normal_string_result),
                            (expected_invalid_str, False, invalid_string_result),
@@ -625,8 +596,6 @@ class TemplateTests(TestCase):
                    expected_invalid_str = 'INVALID'
                    template_base.invalid_var_format_string = False

        restore_template_loaders()

        self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
            ('-' * 70, ("\n%s\n" % ('-' * 70)).join(failures)))

@@ -1887,13 +1856,13 @@ class RequestContextTests(unittest.TestCase):

    def setUp(self):
        templates = {
            'child': Template('{{ var|default:"none" }}'),
            'child': '{{ var|default:"none" }}',
        }
        setup_test_template_loader(templates)
        override_with_test_loader.override(templates)
        self.fake_request = RequestFactory().get('/')

    def tearDown(self):
        restore_template_loaders()
        override_with_test_loader.restore()

    def test_include_only(self):
        """
+4 −12
Original line number Diff line number Diff line
@@ -17,8 +17,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.template.base import TemplateDoesNotExist
from django.test import TestCase, RequestFactory, override_settings
from django.test.utils import (
    setup_test_template_loader, restore_template_loaders)
from django.test.utils import override_with_test_loader
from django.utils.encoding import force_text, force_bytes
from django.utils import six
from django.views.debug import ExceptionReporter
@@ -47,23 +46,16 @@ class DebugViewTests(TestCase):

    def test_403(self):
        # Ensure no 403.html template exists to test the default case.
        setup_test_template_loader({})
        try:
        with override_with_test_loader({}):
            response = self.client.get('/raises403/')
            self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)
        finally:
            restore_template_loaders()

    def test_403_template(self):
        # Set up a test 403.html template.
        setup_test_template_loader(
            {'403.html': 'This is a test template for a 403 Forbidden error.'}
        )
        try:
        with override_with_test_loader({'403.html': 'This is a test template '
                                        'for a 403 Forbidden error.'}):
            response = self.client.get('/raises403/')
            self.assertContains(response, 'test template', status_code=403)
        finally:
            restore_template_loaders()

    def test_404(self):
        response = self.client.get('/raises404/')
+4 −9
Original line number Diff line number Diff line
from __future__ import unicode_literals

from django.test import TestCase
from django.test.utils import (setup_test_template_loader,
    restore_template_loaders, override_settings)
from django.test.utils import override_settings, override_with_test_loader

from ..models import UrlArticle

@@ -41,17 +40,13 @@ class DefaultsTests(TestCase):
        Test that 404.html and 500.html templates are picked by their respective
        handler.
        """
        setup_test_template_loader(
            {'404.html': 'This is a test template for a 404 error.',
             '500.html': 'This is a test template for a 500 error.'}
        )
        try:
        with override_with_test_loader({
                '404.html': 'This is a test template for a 404 error.',
                '500.html': 'This is a test template for a 500 error.'}):
            for code, url in ((404, '/non_existing_url/'), (500, '/server_error/')):
                response = self.client.get(url)
                self.assertContains(response, "test template for a %d error" % code,
                    status_code=code)
        finally:
            restore_template_loaders()

    def test_get_absolute_url_attributes(self):
        "A model can set attributes on the get_absolute_url method"